Your task is to review a pull request. Instructions:
- Write in name of g4f copilot. Don't use placeholder.
- Write the review in GitHub Markdown format.
- Enclose your response in backticks ```markdown```
- Thank the author for contributing to the project.

Pull request author: None
Pull request title: [pull] main from xtekky:main
Pull request description:
---
See [Commits](/cysk003/xtekky-gpt4free/pull/91/commits) and [Changes](/cysk003/xtekky-gpt4free/pull/91/files) for more details.

-----
Created by [<img src="https://prod.download/pull-18h-svg" valign="bottom"/> **pull[bot]**](https://github.com/wei/pull) (v2.0.0-alpha.1)

_Can you help keep this open source service alive? **[💖 Please sponsor : )](https://prod.download/pull-pr-sponsor)**_
---

Diff:
```diff
diff --git a/README.md b/README.md
index d2292f34f13..4e58f3fe8d8 100644
--- a/README.md
+++ b/README.md
@@ -42,7 +42,7 @@ docker pull hlohaus789/g4f
   Join us at [telegram.me/g4f_channel](https://telegram.me/g4f_channel).  
 
 - **Get support in our Discord Community** 🤝💻  
-  Reach out for help in our [Support Group: discord.gg/qXA4Wf4Fsm](https://discord.gg/qXA4Wf4Fsm).  
+  Reach out for help in our [Support Group: https://discord.gg/5E39JUWUFa](https://discord.gg/5E39JUWUFa).  
 
 - **Subscribe to our Discord News Channel** 💬🆕️  
   Stay informed about updates via our [News Channel: discord.gg/5E39JUWUFa](https://discord.gg/5E39JUWUFa).  
@@ -213,9 +213,13 @@ from g4f.gui import run_gui
 
 run_gui()
 ```
-**Or, run via CLI:**
+** Run via CLI (To start the Flask Server):**
 ```bash
-python -m g4f.cli gui -port 8080 -debug
+python -m g4f.cli gui --port 8080 --debug
+```
+**Or, start the FastAPI Server:**
+```bash
+python -m g4f --port 8080 --debug
 ```
 
 > **Learn More About the GUI:** For detailed instructions on how to set up, configure, and use the GPT4Free GUI, refer to the [GUI Documentation](docs/gui.md) . This guide includes step-by-step details on provider selection, managing conversations, using advanced features like speech recognition, and more.
diff --git a/g4f/Provider/Blackbox.py b/g4f/Provider/Blackbox.py
index 1897573d04c..09d8c19657c 100644
--- a/g4f/Provider/Blackbox.py
+++ b/g4f/Provider/Blackbox.py
@@ -13,7 +13,7 @@
 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
 from ..image import ImageResponse, to_data_uri
 from ..cookies import get_cookies_dir
-from .helper import format_prompt
+from .helper import format_prompt, format_image_prompt
 from ..providers.response import FinishReason, JsonConversation, Reasoning
 
 class Conversation(JsonConversation):
@@ -216,9 +216,8 @@ async def create_async_generator(
         
         async with ClientSession(headers=headers) as session:
             if model == "ImageGeneration2":
-                prompt = messages[-1]["content"]
                 data = {
-                    "query": prompt,
+                    "query": format_image_prompt(messages, prompt),
                     "agentMode": True
                 }
                 headers['content-type'] = 'text/plain;charset=UTF-8'
@@ -307,8 +306,7 @@ async def create_async_generator(
                     image_url_match = re.search(r'!\[.*?\]\((.*?)\)', text_to_yield)
                     if image_url_match:
                         image_url = image_url_match.group(1)
-                        prompt = messages[-1]["content"]
-                        yield ImageResponse(images=[image_url], alt=prompt)
+                        yield ImageResponse(image_url, format_image_prompt(messages, prompt))
                 else:
                     if "<think>" in text_to_yield and "</think>" in text_to_yield:
                         parts = text_to_yield.split('<think>', 1)
diff --git a/g4f/Provider/Copilot.py b/g4f/Provider/Copilot.py
index 8e961fb59d7..f63b05637bb 100644
--- a/g4f/Provider/Copilot.py
+++ b/g4f/Provider/Copilot.py
@@ -28,6 +28,7 @@
 from ..providers.asyncio import get_running_loop
 from ..requests import get_nodriver
 from ..image import ImageResponse, to_bytes, is_accepted_format
+from .helper import get_last_user_message
 from .. import debug
 
 class Conversation(JsonConversation):
@@ -139,7 +140,7 @@ def create_completion(
             else:
                 conversation_id = conversation.conversation_id
                 if prompt is None:
-                    prompt = messages[-1]["content"]
+                    prompt = get_last_user_message(messages)
                 debug.log(f"Copilot: Use conversation: {conversation_id}")
 
             uploaded_images = []
@@ -206,7 +207,7 @@ def create_completion(
                 yield Parameters(**{"cookies": {c.name: c.value for c in session.cookies.jar}})
 
 async def get_access_token_and_cookies(url: str, proxy: str = None, target: str = "ChatAI",):
-    browser = await get_nodriver(proxy=proxy, user_data_dir="copilot")
+    browser, stop_browser = await get_nodriver(proxy=proxy, user_data_dir="copilot")
     try:
         page = await browser.get(url)
         access_token = None
@@ -233,7 +234,7 @@ async def get_access_token_and_cookies(url: str, proxy: str = None, target: str
         await page.close()
         return access_token, cookies
     finally:
-        browser.stop()
+        stop_browser()
 
 def readHAR(url: str):
     api_key = None
diff --git a/g4f/Provider/OIVSCode.py b/g4f/Provider/OIVSCode.py
index ef88c5edee6..fae8c1797be 100644
--- a/g4f/Provider/OIVSCode.py
+++ b/g4f/Provider/OIVSCode.py
@@ -9,7 +9,6 @@ class OIVSCode(OpenaiTemplate):
     working = True
     needs_auth = False
     
-    default_model = "gpt-4o-mini-2024-07-18"
+    default_model = "gpt-4o-mini"
     default_vision_model = default_model
-    vision_models = [default_model, "gpt-4o-mini"]    
-    model_aliases = {"gpt-4o-mini": "gpt-4o-mini-2024-07-18"}
\ No newline at end of file
+    vision_models = [default_model, "gpt-4o-mini"]    
\ No newline at end of file
diff --git a/g4f/Provider/PerplexityLabs.py b/g4f/Provider/PerplexityLabs.py
index a1050575e4e..d8477064d15 100644
--- a/g4f/Provider/PerplexityLabs.py
+++ b/g4f/Provider/PerplexityLabs.py
@@ -5,7 +5,7 @@
 
 from ..typing import AsyncResult, Messages
 from ..requests import StreamSession, raise_for_status
-from ..providers.response import Reasoning, FinishReason
+from ..providers.response import FinishReason
 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
 
 API_URL = "https://www.perplexity.ai/socket.io/"
@@ -87,22 +87,7 @@ async def create_async_generator(
                         continue
                     try:
                         data = json.loads(message[2:])[1]
-                        new_content = data["output"][last_message:]
-
-                        if "<think>" in new_content:
-                            yield Reasoning(None, "thinking")
-                            is_thinking = True
-                        if "</think>" in new_content:
-                            new_content = new_content.split("</think>", 1)
-                            yield Reasoning(f"{new_content[0]}</think>")
-                            yield Reasoning(None, "finished")
-                            yield new_content[1]
-                            is_thinking = False
-                        elif is_thinking:
-                            yield Reasoning(new_content)
-                        else:
-                            yield new_content
-
+                        yield data["output"][last_message:]
                         last_message = len(data["output"])
                         if data["final"]:
                             yield FinishReason("stop")
diff --git a/g4f/Provider/PollinationsAI.py b/g4f/Provider/PollinationsAI.py
index f866480115e..122682885f0 100644
--- a/g4f/Provider/PollinationsAI.py
+++ b/g4f/Provider/PollinationsAI.py
@@ -7,7 +7,7 @@
 from typing import Optional
 from aiohttp import ClientSession
 
-from .helper import filter_none
+from .helper import filter_none, format_image_prompt
 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
 from ..typing import AsyncResult, Messages, ImagesType
 from ..image import to_data_uri
@@ -127,7 +127,7 @@ async def create_async_generator(
         if model in cls.image_models:
            yield await cls._generate_image(
                 model=model,
-                prompt=messages[-1]["content"] if prompt is None else prompt,
+                prompt=format_image_prompt(messages, prompt),
                 proxy=proxy,
                 width=width,
                 height=height,
diff --git a/g4f/Provider/You.py b/g4f/Provider/You.py
index 7e5df0fd537..52546280a5e 100644
--- a/g4f/Provider/You.py
+++ b/g4f/Provider/You.py
@@ -77,7 +77,7 @@ async def create_async_generator(
             except MissingRequirementsError:
                 pass
             if not cookies or "afUserId" not in cookies:
-                browser = await get_nodriver(proxy=proxy)
+                browser, stop_browser = await get_nodriver(proxy=proxy)
                 try:
                     page = await browser.get(cls.url)
                     await page.wait_for('[data-testid="user-profile-button"]', timeout=900)
@@ -86,7 +86,7 @@ async def create_async_generator(
                         cookies[c.name] = c.value
                     await page.close()
                 finally:
-                    browser.stop()
+                    stop_browser()
         async with StreamSession(
             proxy=proxy,
             impersonate="chrome",
diff --git a/g4f/Provider/__init__.py b/g4f/Provider/__init__.py
index 0e0b2f4d7a3..55ee07e011e 100644
--- a/g4f/Provider/__init__.py
+++ b/g4f/Provider/__init__.py
@@ -9,6 +9,7 @@
 from .needs_auth       import *
 from .not_working      import *
 from .local            import *
+from .hf               import HuggingFace, HuggingChat, HuggingFaceAPI, HuggingFaceInference
 from .hf_space         import HuggingSpace
 from .mini_max         import HailuoAI, MiniMax
 from .template         import OpenaiTemplate, BackendApi
diff --git a/g4f/Provider/needs_auth/HuggingChat.py b/g4f/Provider/hf/HuggingChat.py
similarity index 78%
rename from g4f/Provider/needs_auth/HuggingChat.py
rename to g4f/Provider/hf/HuggingChat.py
index 316a8317b86..29f5b9c64c9 100644
--- a/g4f/Provider/needs_auth/HuggingChat.py
+++ b/g4f/Provider/hf/HuggingChat.py
@@ -14,7 +14,7 @@
     has_curl_cffi = False
 
 from ..base_provider import ProviderModelMixin, AsyncAuthedProvider, AuthResult
-from ..helper import format_prompt
+from ..helper import format_prompt, format_image_prompt, get_last_user_message
 from ...typing import AsyncResult, Messages, Cookies, ImagesType
 from ...errors import MissingRequirementsError, MissingAuthError, ResponseError
 from ...image import to_bytes
@@ -22,6 +22,7 @@
 from ...requests.raise_for_status import raise_for_status
 from ...providers.response import JsonConversation, ImageResponse, Sources, TitleGeneration, Reasoning, RequestLogin
 from ...cookies import get_cookies
+from .models import default_model, fallback_models, image_models, model_aliases
 from ... import debug
 
 class Conversation(JsonConversation):
@@ -30,52 +31,14 @@ def __init__(self, models: dict):
 
 class HuggingChat(AsyncAuthedProvider, ProviderModelMixin):
     url = "https://huggingface.co/chat"
-    
+
     working = True
     use_nodriver = True
     supports_stream = True
     needs_auth = True
-    
-    default_model = "Qwen/Qwen2.5-72B-Instruct"
-    default_image_model = "black-forest-labs/FLUX.1-dev"
-    image_models = [    
-       default_image_model,
-        "black-forest-labs/FLUX.1-schnell",
-    ]
-    fallback_models = [
-        default_model,
-        'meta-llama/Llama-3.3-70B-Instruct',
-        'CohereForAI/c4ai-command-r-plus-08-2024',
-        'deepseek-ai/DeepSeek-R1-Distill-Qwen-32B',
-        'Qwen/QwQ-32B-Preview',
-        'nvidia/Llama-3.1-Nemotron-70B-Instruct-HF',
-        'Qwen/Qwen2.5-Coder-32B-Instruct',
-        'meta-llama/Llama-3.2-11B-Vision-Instruct',
-        'mistralai/Mistral-Nemo-Instruct-2407',
-        'microsoft/Phi-3.5-mini-instruct',
-    ] + image_models
-    model_aliases = {
-        ### Chat ###
-        "qwen-2.5-72b": "Qwen/Qwen2.5-Coder-32B-Instruct",
-        "llama-3.3-70b": "meta-llama/Llama-3.3-70B-Instruct",
-        "command-r-plus": "CohereForAI/c4ai-command-r-plus-08-2024",
-        "deepseek-r1": "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
-        "qwq-32b": "Qwen/QwQ-32B-Preview",
-        "nemotron-70b": "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF",
-        "qwen-2.5-coder-32b": "Qwen/Qwen2.5-Coder-32B-Instruct",
-        "llama-3.2-11b": "meta-llama/Llama-3.2-11B-Vision-Instruct",
-        "mistral-nemo": "mistralai/Mistral-Nemo-Instruct-2407",
-        "phi-3.5-mini": "microsoft/Phi-3.5-mini-instruct",
-        ### Image ###
-        "flux-dev": "black-forest-labs/FLUX.1-dev",
-        "flux-schnell": "black-forest-labs/FLUX.1-schnell",
-        ### Used in other providers ###
-        "qwen-2-vl-7b": "Qwen/Qwen2-VL-7B-Instruct",
-        "gemma-2-27b": "google/gemma-2-27b-it",
-        "qwen-2-72b": "Qwen/Qwen2-72B-Instruct",
-        "qvq-72b": "Qwen/QVQ-72B-Preview",
-        "sd-3.5": "stabilityai/stable-diffusion-3.5-large",
-    }
+    default_model = default_model
+    model_aliases = model_aliases
+    image_models = image_models
 
     @classmethod
     def get_models(cls):
@@ -94,7 +57,7 @@ def add_quotation_mark(match):
                 cls.vision_models = [model["id"] for model in models if model["multimodal"]]
             except Exception as e:
                 debug.log(f"HuggingChat: Error reading models: {type(e).__name__}: {e}")
-                cls.models = [*cls.fallback_models]
+                cls.models = [*fallback_models]
         return cls.models
 
     @classmethod
@@ -108,9 +71,7 @@ async def on_auth_async(cls, cookies: Cookies = None, proxy: str = None, **kwarg
                 headers=DEFAULT_HEADERS
             )
             return
-        login_url = os.environ.get("G4F_LOGIN_URL")
-        if login_url:
-            yield RequestLogin(cls.__name__, login_url)
+        yield RequestLogin(cls.__name__, os.environ.get("G4F_LOGIN_URL") or "")
         yield AuthResult(
             **await get_args_from_nodriver(
                 cls.url,
@@ -143,6 +104,7 @@ async def create_authed(
 
         if model not in conversation.models:
             conversationId = cls.create_conversation(session, model)
+            debug.log(f"Conversation created: {json.dumps(conversationId[8:] + '...')}")
             messageId = cls.fetch_message_id(session, conversationId)
             conversation.models[model] = {"conversationId": conversationId, "messageId": messageId}
             if return_conversation:
@@ -151,9 +113,7 @@ async def create_authed(
         else:
             conversationId = conversation.models[model]["conversationId"]
             conversation.models[model]["messageId"] = cls.fetch_message_id(session, conversationId)
-            inputs = messages[-1]["content"]
-
-        debug.log(f"Use: {json.dumps(conversation.models[model])}")
+            inputs = get_last_user_message(messages)
 
         settings = {
             "inputs": inputs,
@@ -204,8 +164,7 @@ async def create_authed(
                 break
             elif line["type"] == "file":
                 url = f"https://huggingface.co/chat/conversation/{conversationId}/output/{line['sha']}"
-                prompt = messages[-1]["content"] if prompt is None else prompt
-                yield ImageResponse(url, alt=prompt, options={"cookies": auth_result.cookies})
+                yield ImageResponse(url, format_image_prompt(messages, prompt), options={"cookies": auth_result.cookies})
             elif line["type"] == "webSearch" and "sources" in line:
                 sources = Sources(line["sources"])
             elif line["type"] == "title":
@@ -226,6 +185,8 @@ def create_conversation(cls, session: Session, model: str):
         response = session.post('https://huggingface.co/chat/conversation', json=json_data)
         if response.status_code == 401:
             raise MissingAuthError(response.text)
+        if response.status_code == 400:
+            raise ResponseError(f"{response.text}: Model: {model}")
         raise_for_status(response)
         return response.json().get('conversationId')
 
diff --git a/g4f/Provider/needs_auth/HuggingFaceAPI.py b/g4f/Provider/hf/HuggingFaceAPI.py
similarity index 86%
rename from g4f/Provider/needs_auth/HuggingFaceAPI.py
rename to g4f/Provider/hf/HuggingFaceAPI.py
index c9a15260541..fdbb1f7c6ad 100644
--- a/g4f/Provider/needs_auth/HuggingFaceAPI.py
+++ b/g4f/Provider/hf/HuggingFaceAPI.py
@@ -1,8 +1,9 @@
 from __future__ import annotations
 
-from ..template import OpenaiTemplate
-from .HuggingChat import HuggingChat
+from ..template.OpenaiTemplate import OpenaiTemplate
+from .models import model_aliases
 from ...providers.types import Messages
+from .HuggingChat import HuggingChat
 from ... import debug
 
 class HuggingFaceAPI(OpenaiTemplate):
@@ -16,13 +17,16 @@ class HuggingFaceAPI(OpenaiTemplate):
     default_model = "meta-llama/Llama-3.2-11B-Vision-Instruct"
     default_vision_model = default_model
     vision_models = [default_vision_model, "Qwen/Qwen2-VL-7B-Instruct"]
-    model_aliases = HuggingChat.model_aliases
+    model_aliases = model_aliases
 
     @classmethod
     def get_models(cls, **kwargs):
         if not cls.models:
             HuggingChat.get_models()
-            cls.models = list(set(HuggingChat.text_models + cls.vision_models))
+            cls.models = HuggingChat.text_models.copy()
+            for model in cls.vision_models:
+                if model not in cls.models:
+                    cls.models.append(model)
         return cls.models
 
     @classmethod
diff --git a/g4f/Provider/needs_auth/HuggingFace.py b/g4f/Provider/hf/HuggingFaceInference.py
similarity index 92%
rename from g4f/Provider/needs_auth/HuggingFace.py
rename to g4f/Provider/hf/HuggingFaceInference.py
index 07e110b365a..df866ba3345 100644
--- a/g4f/Provider/needs_auth/HuggingFace.py
+++ b/g4f/Provider/hf/HuggingFaceInference.py
@@ -11,37 +11,32 @@
 from ...requests import StreamSession, raise_for_status
 from ...providers.response import FinishReason
 from ...image import ImageResponse
+from ..helper import format_image_prompt
+from .models import default_model, default_image_model, model_aliases, fallback_models
 from ... import debug
 
-from .HuggingChat import HuggingChat
-
-class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
+class HuggingFaceInference(AsyncGeneratorProvider, ProviderModelMixin):
     url = "https://huggingface.co"
-    login_url = "https://huggingface.co/settings/tokens"
     working = True
-    supports_message_history = True
-    default_model = HuggingChat.default_model
-    default_image_model = HuggingChat.default_image_model
-    model_aliases = HuggingChat.model_aliases
-    extra_models = [
-        "meta-llama/Llama-3.2-11B-Vision-Instruct",
-        "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF",
-        "NousResearch/Hermes-3-Llama-3.1-8B",
-    ]
+
+    default_model = default_model
+    default_image_model = default_image_model
+    model_aliases = model_aliases
 
     @classmethod
     def get_models(cls) -> list[str]:
         if not cls.models:
+            models = fallback_models.copy()
             url = "https://huggingface.co/api/models?inference=warm&pipeline_tag=text-generation"
-            models = [model["id"] for model in requests.get(url).json()]
-            models.extend(cls.extra_models)
-            models.sort()
+            extra_models = [model["id"] for model in requests.get(url).json()]
+            extra_models.sort()
+            models.extend([model for model in extra_models if model not in models])
             if not cls.image_models:
                 url = "https://huggingface.co/api/models?pipeline_tag=text-to-image"
                 cls.image_models = [model["id"] for model in requests.get(url).json() if model["trendingScore"] >= 20]
                 cls.image_models.sort()
-                models.extend(cls.image_models)
-            cls.models = list(set(models))
+                models.extend([model for model in cls.image_models if model not in models])
+            cls.models = models
         return cls.models
 
     @classmethod
@@ -85,7 +80,7 @@ async def create_async_generator(
         payload = None
         if cls.get_models() and model in cls.image_models:
             stream = False
-            prompt = messages[-1]["content"] if prompt is None else prompt
+            prompt = format_image_prompt(messages, prompt)
             payload = {"inputs": prompt, "parameters": {"seed": random.randint(0, 2**32), **extra_data}}
         else:
             params = {
diff --git a/g4f/Provider/hf/__init__.py b/g4f/Provider/hf/__init__.py
new file mode 100644
index 00000000000..5b69d133995
--- /dev/null
+++ b/g4f/Provider/hf/__init__.py
@@ -0,0 +1,65 @@
+from __future__ import annotations
+
+import random
+
+from ...typing import AsyncResult, Messages
+from ...providers.response import ImageResponse
+from ...errors import ModelNotSupportedError
+from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
+from .HuggingChat import HuggingChat
+from .HuggingFaceAPI import HuggingFaceAPI
+from .HuggingFaceInference import HuggingFaceInference
+from .models import model_aliases
+from ... import debug
+
+class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
+    url = "https://huggingface.co"
+    login_url = "https://huggingface.co/settings/tokens"
+    working = True
+    supports_message_history = True
+
+    @classmethod
+    def get_models(cls) -> list[str]:
+        if not cls.models:
+            cls.models = HuggingFaceInference.get_models()
+            cls.image_models = HuggingFaceInference.image_models
+        return cls.models
+
+    model_aliases = model_aliases
+
+    @classmethod
+    async def create_async_generator(
+        cls,
+        model: str,
+        messages: Messages,
+        **kwargs
+    ) -> AsyncResult:
+        if "api_key" not in kwargs and "images" not in kwargs and random.random() >= 0.5:
+            try:
+                is_started = False
+                async for chunk in HuggingFaceInference.create_async_generator(model, messages, **kwargs):
+                    if isinstance(chunk, (str, ImageResponse)):
+                        is_started = True
+                    yield chunk
+                if is_started:
+                    return
+            except Exception as e:
+                if is_started:
+                    raise e
+                debug.log(f"Inference failed: {e.__class__.__name__}: {e}")
+        if not cls.image_models:
+            cls.get_models()
+        if model in cls.image_models:
+            if "api_key" not in kwargs:
+                async for chunk in HuggingChat.create_async_generator(model, messages, **kwargs):
+                    yield chunk
+            else:
+                async for chunk in HuggingFaceInference.create_async_generator(model, messages, **kwargs):
+                    yield chunk
+            return
+        try:
+            async for chunk in HuggingFaceAPI.create_async_generator(model, messages, **kwargs):
+                yield chunk
+        except ModelNotSupportedError:
+            async for chunk in HuggingFaceInference.create_async_generator(model, messages, **kwargs):
+                yield chunk
\ No newline at end of file
diff --git a/g4f/Provider/hf/models.py b/g4f/Provider/hf/models.py
new file mode 100644
index 00000000000..00c652f7d1b
--- /dev/null
+++ b/g4f/Provider/hf/models.py
@@ -0,0 +1,46 @@
+default_model = "Qwen/Qwen2.5-72B-Instruct"
+default_image_model = "black-forest-labs/FLUX.1-dev"
+image_models = [    
+    default_image_model,
+    "black-forest-labs/FLUX.1-schnell",
+]
+fallback_models = [
+    default_model,
+    'meta-llama/Llama-3.3-70B-Instruct',
+    'CohereForAI/c4ai-command-r-plus-08-2024',
+    'deepseek-ai/DeepSeek-R1-Distill-Qwen-32B',
+    'Qwen/QwQ-32B-Preview',
+    'nvidia/Llama-3.1-Nemotron-70B-Instruct-HF',
+    'Qwen/Qwen2.5-Coder-32B-Instruct',
+    'meta-llama/Llama-3.2-11B-Vision-Instruct',
+    'mistralai/Mistral-Nemo-Instruct-2407',
+    'microsoft/Phi-3.5-mini-instruct',
+] + image_models
+model_aliases = {
+    ### Chat ###
+    "qwen-2.5-72b": "Qwen/Qwen2.5-Coder-32B-Instruct",
+    "llama-3.3-70b": "meta-llama/Llama-3.3-70B-Instruct",
+    "command-r-plus": "CohereForAI/c4ai-command-r-plus-08-2024",
+    "deepseek-r1": "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
+    "qwq-32b": "Qwen/QwQ-32B-Preview",
+    "nemotron-70b": "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF",
+    "qwen-2.5-coder-32b": "Qwen/Qwen2.5-Coder-32B-Instruct",
+    "llama-3.2-11b": "meta-llama/Llama-3.2-11B-Vision-Instruct",
+    "mistral-nemo": "mistralai/Mistral-Nemo-Instruct-2407",
+    "phi-3.5-mini": "microsoft/Phi-3.5-mini-instruct",
+    ### Image ###
+    "flux": "black-forest-labs/FLUX.1-dev",
+    "flux-dev": "black-forest-labs/FLUX.1-dev",
+    "flux-schnell": "black-forest-labs/FLUX.1-schnell",
+    ### Used in other providers ###
+    "qwen-2-vl-7b": "Qwen/Qwen2-VL-7B-Instruct",
+    "gemma-2-27b": "google/gemma-2-27b-it",
+    "qwen-2-72b": "Qwen/Qwen2-72B-Instruct",
+    "qvq-72b": "Qwen/QVQ-72B-Preview",
+    "sd-3.5": "stabilityai/stable-diffusion-3.5-large",
+}
+extra_models = [
+    "meta-llama/Llama-3.2-11B-Vision-Instruct",
+    "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF",
+    "NousResearch/Hermes-3-Llama-3.1-8B",
+]
\ No newline at end of file
diff --git a/g4f/Provider/hf_space/BlackForestLabsFlux1Dev.py b/g4f/Provider/hf_space/BlackForestLabsFlux1Dev.py
index b4357bff074..48add133327 100644
--- a/g4f/Provider/hf_space/BlackForestLabsFlux1Dev.py
+++ b/g4f/Provider/hf_space/BlackForestLabsFlux1Dev.py
@@ -7,6 +7,7 @@
 from ...image import ImageResponse, ImagePreview
 from ...errors import ResponseError
 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
+from ..helper import format_image_prompt
 
 class BlackForestLabsFlux1Dev(AsyncGeneratorProvider, ProviderModelMixin):
     url = "https://black-forest-labs-flux-1-dev.hf.space"
@@ -44,7 +45,7 @@ async def create_async_generator(
         if api_key is not None:
             headers["Authorization"] = f"Bearer {api_key}"
         async with ClientSession(headers=headers) as session:
-            prompt = messages[-1]["content"] if prompt is None else prompt
+            prompt = format_image_prompt(messages, prompt)
             data = {
                 "data": [prompt, seed, randomize_seed, width, height, guidance_scale, num_inference_steps]
             }
diff --git a/g4f/Provider/hf_space/BlackForestLabsFlux1Schnell.py b/g4f/Provider/hf_space/BlackForestLabsFlux1Schnell.py
index 11d2ee8e5c3..2a1a2583f39 100644
--- a/g4f/Provider/hf_space/BlackForestLabsFlux1Schnell.py
+++ b/g4f/Provider/hf_space/BlackForestLabsFlux1Schnell.py
@@ -8,6 +8,7 @@
 from ...errors import ResponseError
 from ...requests.raise_for_status import raise_for_status
 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
+from ..helper import format_image_prompt
 
 class BlackForestLabsFlux1Schnell(AsyncGeneratorProvider, ProviderModelMixin):
     url = "https://black-forest-labs-flux-1-schnell.hf.space"
@@ -42,7 +43,7 @@ async def create_async_generator(
         height = max(32, height - (height % 8))
 
         if prompt is None:
-            prompt = messages[-1]["content"]
+            prompt = format_image_prompt(messages)
 
         payload = {
             "data": [
diff --git a/g4f/Provider/hf_space/CohereForAI.py b/g4f/Provider/hf_space/CohereForAI.py
index 8fda88a54b4..1ecdd8f12ae 100644
--- a/g4f/Provider/hf_space/CohereForAI.py
+++ b/g4f/Provider/hf_space/CohereForAI.py
@@ -6,7 +6,7 @@
 from ...typing import AsyncResult, Messages
 from ...requests import raise_for_status
 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
-from ..helper import format_prompt
+from ..helper import format_prompt, get_last_user_message
 from ...providers.response import JsonConversation, TitleGeneration
 
 class CohereForAI(AsyncGeneratorProvider, ProviderModelMixin):
@@ -58,7 +58,7 @@ async def create_async_generator(
         ) as session:
             system_prompt = "\n".join([message["content"] for message in messages if message["role"] == "system"])
             messages = [message for message in messages if message["role"] != "system"]
-            inputs = format_prompt(messages) if conversation is None else messages[-1]["content"]
+            inputs = format_prompt(messages) if conversation is None else get_last_user_message(messages)
             if conversation is None or conversation.model != model or conversation.preprompt != system_prompt:
                 data = {"model": model, "preprompt": system_prompt}
                 async with session.post(cls.conversation_url, json=data, proxy=proxy) as response:
@@ -78,7 +78,6 @@ async def create_async_generator(
                 data = node["data"]
                 message_id = data[data[data[data[0]["messages"]][-1]]["id"]]
             data = FormData()
-            inputs = messages[-1]["content"]
             data.add_field(
                 "data",
                 json.dumps({"inputs": inputs, "id": message_id, "is_retry": False, "is_continue": False, "web_search": False, "tools": []}),
diff --git a/g4f/Provider/hf_space/Janus_Pro_7B.py b/g4f/Provider/hf_space/Janus_Pro_7B.py
index 43e5f518874..92be38a0b35 100644
--- a/g4f/Provider/hf_space/Janus_Pro_7B.py
+++ b/g4f/Provider/hf_space/Janus_Pro_7B.py
@@ -8,8 +8,8 @@
 
 from ...typing import AsyncResult, Messages, Cookies
 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
-from ..helper import format_prompt
-from ...providers.response import JsonConversation, ImageResponse
+from ..helper import format_prompt, format_image_prompt
+from ...providers.response import JsonConversation, ImageResponse, Notification
 from ...requests.aiohttp import StreamSession, StreamResponse
 from ...requests.raise_for_status import raise_for_status
 from ...cookies import get_cookies
@@ -38,7 +38,7 @@ def run(cls, method: str, session: StreamSession, prompt: str, conversation: Jso
                     "headers": {
                         "content-type": "application/json",
                         "x-zerogpu-token": conversation.zerogpu_token,
-                        "x-zerogpu-uuid": conversation.uuid,
+                        "x-zerogpu-uuid": conversation.zerogpu_uuid,
                         "referer": cls.referer,
                     },
                     "json": {"data":[None,prompt,42,0.95,0.1],"event_data":None,"fn_index":2,"trigger_id":10,"session_hash":conversation.session_hash},
@@ -48,7 +48,7 @@ def run(cls, method: str, session: StreamSession, prompt: str, conversation: Jso
                     "headers": {
                         "content-type": "application/json",
                         "x-zerogpu-token": conversation.zerogpu_token,
-                        "x-zerogpu-uuid": conversation.uuid,
+                        "x-zerogpu-uuid": conversation.zerogpu_uuid,
                         "referer": cls.referer,
                     },
                     "json": {"data":[prompt,1234,5,1],"event_data":None,"fn_index":3,"trigger_id":20,"session_hash":conversation.session_hash},
@@ -82,33 +82,14 @@ def generate_session_hash():
             method = "image"
 
         prompt = format_prompt(messages) if prompt is None and conversation is None else prompt
-        prompt = messages[-1]["content"] if prompt is None else prompt
+        prompt = format_image_prompt(messages, prompt)
 
         session_hash = generate_session_hash() if conversation is None else getattr(conversation, "session_hash")
         async with StreamSession(proxy=proxy, impersonate="chrome") as session:
             session_hash = generate_session_hash() if conversation is None else getattr(conversation, "session_hash")
-            user_uuid = None if conversation is None else getattr(conversation, "user_uuid", None)
-            zerogpu_token = "[object Object]"
-
-            cookies = get_cookies("huggingface.co", raise_requirements_error=False) if cookies is None else cookies
-            if cookies:
-                # Get current UTC time + 10 minutes
-                dt = (datetime.now(timezone.utc) + timedelta(minutes=10)).isoformat(timespec='milliseconds')
-                encoded_dt = urllib.parse.quote(dt)
-                async with session.get(f"https://huggingface.co/api/spaces/deepseek-ai/Janus-Pro-7B/jwt?expiration={encoded_dt}&include_pro_status=true", cookies=cookies) as response:
-                    zerogpu_token = (await response.json())
-                    zerogpu_token = zerogpu_token["token"]
-            if user_uuid is None:
-                async with session.get(cls.url, cookies=cookies) as response:
-                    match = re.search(r"&quot;token&quot;:&quot;([^&]+?)&quot;", await response.text())
-                    if match:
-                        zerogpu_token = match.group(1)
-                    match = re.search(r"&quot;sessionUuid&quot;:&quot;([^&]+?)&quot;", await response.text())
-                    if match:
-                        user_uuid = match.group(1)
-
+            zerogpu_uuid, zerogpu_token = await get_zerogpu_token(session, conversation, cookies)
             if conversation is None or not hasattr(conversation, "session_hash"):
-                conversation = JsonConversation(session_hash=session_hash, zerogpu_token=zerogpu_token, uuid=user_uuid)
+                conversation = JsonConversation(session_hash=session_hash, zerogpu_token=zerogpu_token, zerogpu_uuid=zerogpu_uuid)
             conversation.zerogpu_token = zerogpu_token
             if return_conversation:
                 yield conversation
@@ -124,7 +105,7 @@ def generate_session_hash():
                         try:
                             json_data = json.loads(decoded_line[6:])
                             if json_data.get('msg') == 'log':
-                                debug.log(json_data["log"])
+                                yield Notification(json_data["log"])
 
                             if json_data.get('msg') == 'process_generating':
                                 if 'output' in json_data and 'data' in json_data['output']:
@@ -141,4 +122,27 @@ def generate_session_hash():
                                 break
 
                         except json.JSONDecodeError:
-                            debug.log("Could not parse JSON:", decoded_line)
\ No newline at end of file
+                            debug.log("Could not parse JSON:", decoded_line)
+
+async def get_zerogpu_token(session: StreamSession, conversation: JsonConversation, cookies: Cookies = None):
+    zerogpu_uuid = None if conversation is None else getattr(conversation, "zerogpu_uuid", None)
+    zerogpu_token = "[object Object]"
+
+    cookies = get_cookies("huggingface.co", raise_requirements_error=False) if cookies is None else cookies
+    if zerogpu_uuid is None:
+        async with session.get(Janus_Pro_7B.url, cookies=cookies) as response:
+            match = re.search(r"&quot;token&quot;:&quot;([^&]+?)&quot;", await response.text())
+            if match:
+                zerogpu_token = match.group(1)
+            match = re.search(r"&quot;sessionUuid&quot;:&quot;([^&]+?)&quot;", await response.text())
+            if match:
+                zerogpu_uuid = match.group(1)
+    if cookies:
+        # Get current UTC time + 10 minutes
+        dt = (datetime.now(timezone.utc) + timedelta(minutes=10)).isoformat(timespec='milliseconds')
+        encoded_dt = urllib.parse.quote(dt)
+        async with session.get(f"https://huggingface.co/api/spaces/deepseek-ai/Janus-Pro-7B/jwt?expiration={encoded_dt}&include_pro_status=true", cookies=cookies) as response:
+            zerogpu_token = (await response.json())
+            zerogpu_token = zerogpu_token["token"]
+    
+    return zerogpu_uuid, zerogpu_token
\ No newline at end of file
diff --git a/g4f/Provider/hf_space/Qwen_Qwen_2_5M_Demo.py b/g4f/Provider/hf_space/Qwen_Qwen_2_5M_Demo.py
index 66a46f7a97f..f1a5091ec82 100644
--- a/g4f/Provider/hf_space/Qwen_Qwen_2_5M_Demo.py
+++ b/g4f/Provider/hf_space/Qwen_Qwen_2_5M_Demo.py
@@ -8,6 +8,7 @@
 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
 from ..helper import format_prompt
 from ...providers.response import JsonConversation, Reasoning
+from ..helper import get_last_user_message
 from ... import debug
 
 class Qwen_Qwen_2_5M_Demo(AsyncGeneratorProvider, ProviderModelMixin):
@@ -41,7 +42,7 @@ def generate_session_hash():
         if return_conversation:
             yield JsonConversation(session_hash=session_hash)
 
-        prompt = format_prompt(messages) if conversation is None else messages[-1]["content"]
+        prompt = format_prompt(messages) if conversation is None else get_last_user_message(messages)
 
         headers = {
             'accept': '*/*',
diff --git a/g4f/Provider/hf_space/StableDiffusion35Large.py b/g4f/Provider/hf_space/StableDiffusion35Large.py
index e047181d00a..82d349d8746 100644
--- a/g4f/Provider/hf_space/StableDiffusion35Large.py
+++ b/g4f/Provider/hf_space/StableDiffusion35Large.py
@@ -7,6 +7,7 @@
 from ...image import ImageResponse, ImagePreview
 from ...errors import ResponseError
 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
+from ..helper import format_image_prompt
 
 class StableDiffusion35Large(AsyncGeneratorProvider, ProviderModelMixin):
     url = "https://stabilityai-stable-diffusion-3-5-large.hf.space"
@@ -42,7 +43,7 @@ async def create_async_generator(
         if api_key is not None:
             headers["Authorization"] = f"Bearer {api_key}"
         async with ClientSession(headers=headers) as session:
-            prompt = messages[-1]["content"] if prompt is None else prompt
+            prompt = format_image_prompt(messages, prompt)
             data = {
                 "data": [prompt, negative_prompt, seed, randomize_seed, width, height, guidance_scale, num_inference_steps]
             }
diff --git a/g4f/Provider/hf_space/VoodoohopFlux1Schnell.py b/g4f/Provider/hf_space/VoodoohopFlux1Schnell.py
index d4a052f5ce8..3496f718698 100644
--- a/g4f/Provider/hf_space/VoodoohopFlux1Schnell.py
+++ b/g4f/Provider/hf_space/VoodoohopFlux1Schnell.py
@@ -7,6 +7,7 @@
 from ...image import ImageResponse
 from ...errors import ResponseError
 from ...requests.raise_for_status import raise_for_status
+from ..helper import format_image_prompt
 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
 
 class VoodoohopFlux1Schnell(AsyncGeneratorProvider, ProviderModelMixin):
@@ -37,10 +38,7 @@ async def create_async_generator(
     ) -> AsyncResult:
         width = max(32, width - (width % 8))
         height = max(32, height - (height % 8))
-
-        if prompt is None:
-            prompt = messages[-1]["content"]
-
+        prompt = format_image_prompt(messages, prompt)
         payload = {
             "data": [
                 prompt,
diff --git a/g4f/Provider/mini_max/HailuoAI.py b/g4f/Provider/mini_max/HailuoAI.py
index d8c145a12e0..9b5a8929dae 100644
--- a/g4f/Provider/mini_max/HailuoAI.py
+++ b/g4f/Provider/mini_max/HailuoAI.py
@@ -10,6 +10,7 @@
 from ..mini_max.crypt import CallbackResults, get_browser_callback, generate_yy_header, get_body_to_yy
 from ...requests import get_args_from_nodriver, raise_for_status
 from ...providers.response import AuthResult, JsonConversation, RequestLogin, TitleGeneration
+from ..helper import get_last_user_message
 from ... import debug
 
 class Conversation(JsonConversation):
@@ -62,7 +63,7 @@ async def create_authed(
                 conversation = None
             form_data = {
                 "characterID": 1 if conversation is None else getattr(conversation, "characterID", 1),
-                "msgContent": format_prompt(messages) if conversation is None else messages[-1]["content"],
+                "msgContent": format_prompt(messages) if conversation is None else get_last_user_message(messages),
                 "chatID": 0 if conversation is None else getattr(conversation, "chatID", 0),
                 "searchMode": 0
             }
diff --git a/g4f/Provider/needs_auth/Anthropic.py b/g4f/Provider/needs_auth/Anthropic.py
index cdc8f680a64..6e356c9e907 100644
--- a/g4f/Provider/needs_auth/Anthropic.py
+++ b/g4f/Provider/needs_auth/Anthropic.py
@@ -98,7 +98,7 @@ async def create_async_generator(
                     "text": messages[-1]["content"]
                 }
             ]
-        system = "\n".join([message for message in messages if message.get("role") == "system"])
+        system = "\n".join([message["content"] for message in messages if message.get("role") == "system"])
         if system:
             messages = [message for message in messages if message.get("role") != "system"]
         else:
diff --git a/g4f/Provider/needs_auth/BingCreateImages.py b/g4f/Provider/needs_auth/BingCreateImages.py
index c2d403d7d2e..b198d201df1 100644
--- a/g4f/Provider/needs_auth/BingCreateImages.py
+++ b/g4f/Provider/needs_auth/BingCreateImages.py
@@ -6,6 +6,7 @@
 from ...typing import AsyncResult, Messages, Cookies
 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
 from ..bing.create_images import create_images, create_session
+from ..helper import format_image_prompt
 
 class BingCreateImages(AsyncGeneratorProvider, ProviderModelMixin):
     label = "Microsoft Designer in Bing"
@@ -35,7 +36,7 @@ async def create_async_generator(
         **kwargs
     ) -> AsyncResult:
         session = BingCreateImages(cookies, proxy, api_key)
-        yield await session.generate(messages[-1]["content"] if prompt is None else prompt)
+        yield await session.generate(format_image_prompt(messages, prompt))
 
     async def generate(self, prompt: str) -> ImageResponse:
         """
diff --git a/g4f/Provider/needs_auth/DeepInfra.py b/g4f/Provider/needs_auth/DeepInfra.py
index a5f6350a52d..b1d7c1d2759 100644
--- a/g4f/Provider/needs_auth/DeepInfra.py
+++ b/g4f/Provider/needs_auth/DeepInfra.py
@@ -5,6 +5,7 @@
 from ...requests import StreamSession, raise_for_status
 from ...image import ImageResponse
 from ..template import OpenaiTemplate
+from ..helper import format_image_prompt
 
 class DeepInfra(OpenaiTemplate):
     url = "https://deepinfra.com"
@@ -55,7 +56,7 @@ async def create_async_generator(
     ) -> AsyncResult:
         if model in cls.get_image_models():
             yield cls.create_async_image(
-                messages[-1]["content"] if prompt is None else prompt,
+                format_image_prompt(messages, prompt),
                 model,
                 **kwargs
             )
diff --git a/g4f/Provider/needs_auth/DeepSeekAPI.py b/g4f/Provider/needs_auth/DeepSeekAPI.py
index ad7f32fe97c..365ac92eaa9 100644
--- a/g4f/Provider/needs_auth/DeepSeekAPI.py
+++ b/g4f/Provider/needs_auth/DeepSeekAPI.py
@@ -31,7 +31,7 @@ def _make_request(self, method: str, endpoint: str, json_data: dict, pow_require
                 challenge = self._get_pow_challenge()
                 pow_response = self.pow_solver.solve_challenge(challenge)
                 headers = self._get_headers(pow_response)
-                
+
             response = requests.request(
                 method=method,
                 url=url,
diff --git a/g4f/Provider/needs_auth/Gemini.py b/g4f/Provider/needs_auth/Gemini.py
index e6d71255535..8c555e851c0 100644
--- a/g4f/Provider/needs_auth/Gemini.py
+++ b/g4f/Provider/needs_auth/Gemini.py
@@ -25,6 +25,7 @@
 from ...requests import get_nodriver
 from ...errors import MissingAuthError
 from ...image import ImageResponse, to_bytes
+from ..helper import get_last_user_message
 from ... import debug
 
 REQUEST_HEADERS = {
@@ -78,7 +79,7 @@ async def nodriver_login(cls, proxy: str = None) -> AsyncIterator[str]:
             if debug.logging:
                 print("Skip nodriver login in Gemini provider")
             return
-        browser = await get_nodriver(proxy=proxy, user_data_dir="gemini")
+        browser, stop_browser = await get_nodriver(proxy=proxy, user_data_dir="gemini")
         try:
             login_url = os.environ.get("G4F_LOGIN_URL")
             if login_url:
@@ -91,7 +92,7 @@ async def nodriver_login(cls, proxy: str = None) -> AsyncIterator[str]:
             await page.close()
             cls._cookies = cookies
         finally:
-            browser.stop()
+            stop_browser()
 
     @classmethod
     async def create_async_generator(
@@ -107,7 +108,7 @@ async def create_async_generator(
         language: str = "en",
         **kwargs
     ) -> AsyncResult:
-        prompt = format_prompt(messages) if conversation is None else messages[-1]["content"]
+        prompt = format_prompt(messages) if conversation is None else get_last_user_message(messages)
         cls._cookies = cookies or cls._cookies or get_cookies(".google.com", False, True)
         base_connector = get_connector(connector, proxy)
 
diff --git a/g4f/Provider/needs_auth/GithubCopilot.py b/g4f/Provider/needs_auth/GithubCopilot.py
index 1deca50cea8..4b9db8e9e33 100644
--- a/g4f/Provider/needs_auth/GithubCopilot.py
+++ b/g4f/Provider/needs_auth/GithubCopilot.py
@@ -7,7 +7,7 @@
 from ...typing import AsyncResult, Messages, Cookies
 from ...requests.raise_for_status import raise_for_status
 from ...requests.aiohttp import get_connector
-from ...providers.helper import format_prompt
+from ...providers.helper import format_prompt, get_last_user_message
 from ...cookies import get_cookies
 
 class Conversation(BaseConversation):
@@ -78,7 +78,7 @@ async def create_async_generator(
                     conversation_id = (await response.json()).get("thread_id")
             if return_conversation:
                 yield Conversation(conversation_id)
-                content = messages[-1]["content"]
+                content = get_last_user_message(messages)
             else:
                 content = format_prompt(messages)
             json_data = {
diff --git a/g4f/Provider/needs_auth/MicrosoftDesigner.py b/g4f/Provider/needs_auth/MicrosoftDesigner.py
index c413c19b5bf..3ce8e618cfb 100644
--- a/g4f/Provider/needs_auth/MicrosoftDesigner.py
+++ b/g4f/Provider/needs_auth/MicrosoftDesigner.py
@@ -14,7 +14,7 @@
 from ...requests import get_nodriver
 from ..Copilot import get_headers, get_har_files
 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
-from ..helper import get_random_hex
+from ..helper import get_random_hex, format_image_prompt
 from ... import debug
 
 class MicrosoftDesigner(AsyncGeneratorProvider, ProviderModelMixin):
@@ -39,7 +39,7 @@ async def create_async_generator(
         image_size = "1024x1024"
         if model != cls.default_image_model and model in cls.image_models:
             image_size = model
-        yield await cls.generate(messages[-1]["content"] if prompt is None else prompt, image_size, proxy)
+        yield await cls.generate(format_image_prompt(messages, prompt), image_size, proxy)
 
     @classmethod
     async def generate(cls, prompt: str, image_size: str, proxy: str = None) -> ImageResponse:
@@ -143,7 +143,7 @@ def readHAR(url: str) -> tuple[str, str]:
     return api_key, user_agent
 
 async def get_access_token_and_user_agent(url: str, proxy: str = None):
-    browser = await get_nodriver(proxy=proxy, user_data_dir="designer")
+    browser, stop_browser = await get_nodriver(proxy=proxy, user_data_dir="designer")
     try:
         page = await browser.get(url)
         user_agent = await page.evaluate("navigator.userAgent")
@@ -168,4 +168,4 @@ async def get_access_token_and_user_agent(url: str, proxy: str = None):
         await page.close()
         return access_token, user_agent
     finally:
-        browser.stop()
\ No newline at end of file
+        stop_browser()
\ No newline at end of file
diff --git a/g4f/Provider/needs_auth/OpenaiChat.py b/g4f/Provider/needs_auth/OpenaiChat.py
index adcecb3f298..8fde1090e15 100644
--- a/g4f/Provider/needs_auth/OpenaiChat.py
+++ b/g4f/Provider/needs_auth/OpenaiChat.py
@@ -98,7 +98,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
     default_model = "auto"
     default_image_model = "dall-e-3"
     image_models = [default_image_model]
-    text_models = [default_model, "gpt-4", "gpt-4o", "gpt-4o-mini", "gpt-4o-canmore", "o1", "o1-preview", "o1-mini"]
+    text_models = [default_model, "gpt-4", "gpt-4o", "gpt-4o-mini", "o1", "o1-preview", "o1-mini"]
     vision_models = text_models
     models = text_models + image_models
     synthesize_content_type = "audio/mpeg"
@@ -598,7 +598,7 @@ async def login(
 
     @classmethod
     async def nodriver_auth(cls, proxy: str = None):
-        browser = await get_nodriver(proxy=proxy)
+        browser, stop_browser = await get_nodriver(proxy=proxy)
         try:
             page = browser.main_tab
             def on_request(event: nodriver.cdp.network.RequestWillBeSent):
@@ -648,7 +648,7 @@ def on_request(event: nodriver.cdp.network.RequestWillBeSent):
             cls._create_request_args(RequestConfig.cookies, RequestConfig.headers, user_agent=user_agent)
             cls._set_api_key(cls._api_key)
         finally:
-            browser.stop()
+            stop_browser()
 
     @staticmethod
     def get_default_headers() -> Dict[str, str]:
diff --git a/g4f/Provider/needs_auth/__init__.py b/g4f/Provider/needs_auth/__init__.py
index 691bc5bebb7..e00e3b1ff17 100644
--- a/g4f/Provider/needs_auth/__init__.py
+++ b/g4f/Provider/needs_auth/__init__.py
@@ -13,9 +13,6 @@
 from .GithubCopilot     import GithubCopilot
 from .GlhfChat          import GlhfChat
 from .Groq              import Groq
-from .HuggingChat       import HuggingChat
-from .HuggingFace       import HuggingFace
-from .HuggingFaceAPI    import HuggingFaceAPI
 from .MetaAI            import MetaAI
 from .MetaAIAccount     import MetaAIAccount
 from .MicrosoftDesigner import MicrosoftDesigner
diff --git a/g4f/Provider/template/BackendApi.py b/g4f/Provider/template/BackendApi.py
index caf282194a5..8ef2ee4b11b 100644
--- a/g4f/Provider/template/BackendApi.py
+++ b/g4f/Provider/template/BackendApi.py
@@ -8,11 +8,11 @@
 from ...typing import Messages, AsyncResult
 from ...requests import StreamSession
 from ...providers.base_provider import AsyncGeneratorProvider, ProviderModelMixin
-from ...providers.response import ProviderInfo, JsonConversation, PreviewResponse, SynthesizeData, TitleGeneration, RequestLogin
-from ...providers.response import Parameters, FinishReason, Usage, Reasoning
+from ...providers.response import *
+from ...image import get_image_extension
 from ...errors import ModelNotSupportedError
 from ..needs_auth.OpenaiAccount import OpenaiAccount
-from ..needs_auth.HuggingChat import HuggingChat
+from ..hf.HuggingChat import HuggingChat
 from ... import debug
 
 class BackendApi(AsyncGeneratorProvider, ProviderModelMixin):
@@ -98,8 +98,7 @@ async def create_async_generator(
                         yield PreviewResponse(data[data_type])
                     elif data_type == "content":
                         def on_image(match):
-                            extension = match.group(3).split(".")[-1].split("?")[0]
-                            extension = "" if not extension or len(extension) > 4 else f".{extension}"
+                            extension = get_image_extension(match.group(3))
                             filename = f"{int(time.time())}_{quote_plus(match.group(1)[:100], '')}{extension}"
                             download_url = f"/download/{filename}?url={cls.url}{match.group(3)}"
                             return f"[![{match.group(1)}]({download_url})](/images/{filename})"
@@ -119,6 +118,6 @@ def on_image(match):
                     elif data_type == "finish":
                         yield FinishReason(data[data_type]["reason"])
                     elif data_type == "log":
-                        debug.log(data[data_type])
+                        yield DebugResponse.from_dict(data[data_type])
                     else:
-                        debug.log(f"Unknown data: ({data_type}) {data}")
\ No newline at end of file
+                        yield DebugResponse.from_dict(data)
diff --git a/g4f/Provider/template/OpenaiTemplate.py b/g4f/Provider/template/OpenaiTemplate.py
index 62a44c55c3d..3bf8c104887 100644
--- a/g4f/Provider/template/OpenaiTemplate.py
+++ b/g4f/Provider/template/OpenaiTemplate.py
@@ -4,11 +4,11 @@
 import time
 import requests
 
-from ..helper import filter_none
+from ..helper import filter_none, format_image_prompt
 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin
 from ...typing import Union, Optional, AsyncResult, Messages, ImagesType
 from ...requests import StreamSession, raise_for_status
-from ...providers.response import FinishReason, ToolCalls, Usage, Reasoning, ImageResponse
+from ...providers.response import FinishReason, ToolCalls, Usage, ImageResponse
 from ...errors import MissingAuthError, ResponseError
 from ...image import to_data_uri
 from ... import debug
@@ -82,7 +82,7 @@ async def create_async_generator(
             # Proxy for image generation feature
             if model and model in cls.image_models:
                 data = {
-                    "prompt": messages[-1]["content"] if prompt is None else prompt,
+                    "prompt": format_image_prompt(messages, prompt),
                     "model": model,
                 }
                 async with session.post(f"{api_base.rstrip('/')}/images/generations", json=data, ssl=cls.ssl) as response:
@@ -154,17 +154,7 @@ async def create_async_generator(
                                     delta = delta.lstrip()
                                 if delta:
                                     first = False
-                                    if is_thinking:
-                                        if "</think>" in delta:
-                                            yield Reasoning(None, f"Finished in {round(time.time()-is_thinking, 2)} seconds")
-                                            is_thinking = 0
-                                        else:
-                                            yield Reasoning(delta)
-                                    elif "<think>" in delta:
-                                        is_thinking = time.time()
-                                        yield Reasoning(None, "Is thinking...")
-                                    else:
-                                        yield delta
+                                    yield delta
                             if "usage" in data and data["usage"]:
                                 yield Usage(**data["usage"])
                             if "finish_reason" in choice and choice["finish_reason"] is not None:
diff --git a/g4f/api/__init__.py b/g4f/api/__init__.py
index a52b05117e4..9b5dafd8dd4 100644
--- a/g4f/api/__init__.py
+++ b/g4f/api/__init__.py
@@ -37,7 +37,7 @@
 from g4f.client import AsyncClient, ChatCompletion, ImagesResponse, convert_to_provider
 from g4f.providers.response import BaseConversation, JsonConversation
 from g4f.client.helper import filter_none
-from g4f.image import is_data_uri_an_image, images_dir
+from g4f.image import is_data_uri_an_image, images_dir, copy_images
 from g4f.errors import ProviderNotFoundError, ModelNotFoundError, MissingAuthError, NoValidHarFileError
 from g4f.cookies import read_cookie_files, get_cookies_dir
 from g4f.Provider import ProviderType, ProviderUtils, __providers__
@@ -49,6 +49,7 @@
     ErrorResponseModel, ProviderResponseDetailModel,
     FileResponseModel, UploadResponseModel, Annotated
 )
+from g4f import debug
 
 logger = logging.getLogger(__name__)
 
@@ -71,7 +72,7 @@ def create_app():
     api.register_routes()
     api.register_authorization()
     api.register_validation_exception_handler()
- 
+
     if AppConfig.gui:
         gui_app = WSGIMiddleware(get_gui_app(AppConfig.demo))
         app.mount("/", gui_app)
@@ -539,7 +540,11 @@ async def synthesize(request: Request, provider: str):
             content_type = getattr(provider_handler, "synthesize_content_type", "application/octet-stream")
             return StreamingResponse(response_data, media_type=content_type)
 
-        @self.app.get("/images/{filename}", response_class=FileResponse, responses={
+        @self.app.get("/json/{filename}")
+        async def get_json(filename, request: Request):
+            return ""
+
+        @self.app.get("/images/{filename}", responses={
             HTTP_200_OK: {"content": {"image/*": {}}},
             HTTP_404_NOT_FOUND: {}
         })
@@ -550,15 +555,18 @@ async def get_image(filename, request: Request):
             stat_result.st_size = 0
             if os.path.isfile(target):
                 stat_result.st_size = os.stat(target).st_size
-            stat_result.st_mtime = int(f"{filename.split('_')[0]}")
+            stat_result.st_mtime = int(f"{filename.split('_')[0]}") if filename.startswith("1") else 0
+            headers = {
+                "cache-control": "public, max-age=31536000",
+                "content-type": f"image/{ext.replace('jpg', 'jpeg')[1:] or 'jpeg'}",
+                "content-length": str(stat_result.st_size),
+                "last-modified": formatdate(stat_result.st_mtime, usegmt=True),
+                "etag": f'"{hashlib.md5(filename.encode()).hexdigest()}"',
+            }
             response = FileResponse(
                 target,
-                media_type=f"image/{ext.replace('jpg', 'jepg')}",
-                headers={
-                    "content-length": str(stat_result.st_size),
-                    "last-modified": formatdate(stat_result.st_mtime, usegmt=True),
-                    "etag": f'"{hashlib.md5(filename.encode()).hexdigest()}"'
-                },
+                headers=headers,
+                filename=filename,
             )
             try:
                 if_none_match = request.headers["if-none-match"]
@@ -568,8 +576,25 @@ async def get_image(filename, request: Request):
             except KeyError:
                 pass
             if not os.path.isfile(target):
-                return Response(status_code=HTTP_404_NOT_FOUND)
-            return response
+                source_url = str(request.query_params).split("url=", 1)
+                if len(source_url) > 1:
+                    source_url = source_url[1]
+                    source_url = source_url.replace("%2F", "/").replace("%3A", ":").replace("%3F", "?")
+                    if source_url.startswith("http"):
+                        await copy_images(
+                            [source_url],
+                            target=target)
+                        debug.log(f"Image copied from {source_url}")
+            if not os.path.isfile(target):
+                return ErrorResponse.from_message("File not found", HTTP_404_NOT_FOUND)
+            async def stream():
+                with open(target, "rb") as file:
+                    while True:
+                        chunk = file.read(65536)
+                        if not chunk:
+                            break
+                        yield chunk
+            return StreamingResponse(stream(), headers=headers)
 
 def format_exception(e: Union[Exception, str], config: Union[ChatCompletionsConfig, ImageGenerationConfig] = None, image: bool = False) -> str:
     last_provider = {} if not image else g4f.get_last_provider(True)
diff --git a/g4f/gui/__init__.py b/g4f/gui/__init__.py
index 8dab783af32..cede0f9fd1d 100644
--- a/g4f/gui/__init__.py
+++ b/g4f/gui/__init__.py
@@ -8,11 +8,12 @@
 except ImportError as e:
     import_error = e
 
-def get_gui_app(demo: bool = False):
+def get_gui_app(demo: bool = False, api: bool = False):
     if import_error is not None:
         raise MissingRequirementsError(f'Install "gui" requirements | pip install -U g4f[gui]\n{import_error}')
     app = create_app()
     app.demo = demo
+    app.api = api
 
     site = Website(app)
     for route in site.routes:
diff --git a/g4f/gui/server/api.py b/g4f/gui/server/api.py
index 713cce7258c..6ae90fea0bc 100644
--- a/g4f/gui/server/api.py
+++ b/g4f/gui/server/api.py
@@ -13,8 +13,8 @@
 from ...Provider import ProviderUtils, __providers__
 from ...providers.base_provider import ProviderModelMixin
 from ...providers.retry_provider import BaseRetryProvider
-from ...providers.response import BaseConversation, JsonConversation, FinishReason, Usage, Reasoning, PreviewResponse
-from ...providers.response import SynthesizeData, TitleGeneration, RequestLogin, Parameters, ProviderInfo
+from ...providers.helper import format_image_prompt
+from ...providers.response import *
 from ... import version, models
 from ... import ChatCompletion, get_model_and_provider
 from ... import debug
@@ -183,13 +183,14 @@ def decorated_log(text: str):
                     logger.exception(chunk)
                     yield self._format_json('message', get_error_message(chunk), error=type(chunk).__name__)
                 elif isinstance(chunk, (PreviewResponse, ImagePreview)):
-                    yield self._format_json("preview", chunk.to_string())
+                    yield self._format_json("preview", chunk.to_string(), images=chunk.images, alt=chunk.alt)
                 elif isinstance(chunk, ImageResponse):
                     images = chunk
-                    if download_images:
-                        images = asyncio.run(copy_images(chunk.get_list(), chunk.get("cookies"), proxy))
+                    if download_images or chunk.get("cookies"):
+                        alt = format_image_prompt(kwargs.get("messages"))
+                        images = asyncio.run(copy_images(chunk.get_list(), chunk.get("cookies"), proxy, alt))
                         images = ImageResponse(images, chunk.alt)
-                    yield self._format_json("content", str(images))
+                    yield self._format_json("content", str(images), images=chunk.get_list(), alt=chunk.alt)
                 elif isinstance(chunk, SynthesizeData):
                     yield self._format_json("synthesize", chunk.get_dict())
                 elif isinstance(chunk, TitleGeneration):
@@ -203,7 +204,11 @@ def decorated_log(text: str):
                 elif isinstance(chunk, Usage):
                     yield self._format_json("usage", chunk.get_dict())
                 elif isinstance(chunk, Reasoning):
-                    yield self._format_json("reasoning", token=chunk.token, status=chunk.status)
+                    yield self._format_json("reasoning", token=chunk.token, status=chunk.status, is_thinking=chunk.is_thinking)
+                elif isinstance(chunk, DebugResponse):
+                    yield self._format_json("log", chunk.get_dict())
+                elif isinstance(chunk, Notification):
+                    yield self._format_json("notification", chunk.message)
                 else:
                     yield self._format_json("content", str(chunk))
                 if debug.logs:
@@ -219,6 +224,15 @@ def decorated_log(text: str):
             yield self._format_json('error', type(e).__name__, message=get_error_message(e))
 
     def _format_json(self, response_type: str, content = None, **kwargs):
+        # Make sure it get be formated as JSON
+        if content is not None and not isinstance(content, (str, dict)):
+            content = str(content)
+        kwargs = {
+            key: value
+            if value is isinstance(value, (str, dict))
+            else str(value)
+            for key, value in kwargs.items()
+            if isinstance(key, str)}
         if content is not None:
             return {
                 'type': response_type,
diff --git a/g4f/image.py b/g4f/image.py
index fbcc09a3ba2..e265242fea1 100644
--- a/g4f/image.py
+++ b/g4f/image.py
@@ -7,6 +7,8 @@
 import uuid
 import base64
 import asyncio
+import hashlib
+from urllib.parse import quote_plus
 from io import BytesIO
 from pathlib import Path
 from aiohttp import ClientSession, ClientError
@@ -239,10 +241,16 @@ def to_data_uri(image: ImageType) -> str:
 def ensure_images_dir():
     os.makedirs(images_dir, exist_ok=True)
 
+def get_image_extension(image: str) -> str:
+    if match := re.search(r"(\.(?:jpe?g|png|webp))[$?&]", image):
+        return match.group(1)
+    return ".jpg"
+
 async def copy_images(
     images: list[str],
     cookies: Optional[Cookies] = None,
     proxy: Optional[str] = None,
+    alt: str = None,
     add_url: bool = True,
     target: str = None,
     ssl: bool = None
@@ -256,7 +264,10 @@ async def copy_images(
     ) as session:
         async def copy_image(image: str, target: str = None) -> str:
             if target is None or len(images) > 1:
-                target = os.path.join(images_dir, f"{int(time.time())}_{str(uuid.uuid4())}")
+                hash = hashlib.sha256(image.encode()).hexdigest()
+                target = f"{quote_plus('+'.join(alt.split()[:10])[:100], '')}_{hash}" if alt else str(uuid.uuid4())
+                target = f"{int(time.time())}_{target}{get_image_extension(image)}"
+                target = os.path.join(images_dir, target)
             try:
                 if image.startswith("data:"):
                     with open(target, "wb") as f:
diff --git a/g4f/providers/asyncio.py b/g4f/providers/asyncio.py
index ed46d541ef2..f5b55bbd4b0 100644
--- a/g4f/providers/asyncio.py
+++ b/g4f/providers/asyncio.py
@@ -69,9 +69,13 @@ def to_sync_generator(generator: AsyncIterator, stream: bool = True) -> Iterator
                 loop.close()
 
 # Helper function to convert a synchronous iterator to an async iterator
-async def to_async_iterator(iterator: Iterator) -> AsyncIterator:
-    try:
+async def to_async_iterator(iterator) -> AsyncIterator:
+    if hasattr(iterator, '__aiter__'):
         async for item in iterator:
             yield item
+        return
+    try:
+        for item in iterator:
+            yield item
     except TypeError:
         yield await iterator
\ No newline at end of file
diff --git a/g4f/providers/helper.py b/g4f/providers/helper.py
index ab670fdb337..3ec474814b7 100644
--- a/g4f/providers/helper.py
+++ b/g4f/providers/helper.py
@@ -27,6 +27,23 @@ def format_prompt(messages: Messages, add_special_tokens: bool = False, do_conti
         return formatted
     return f"{formatted}\nAssistant:"
 
+def get_last_user_message(messages: Messages) -> str:
+    user_messages = []
+    last_message = None if len(messages) == 0 else messages[-1]
+    while last_message is not None and messages:
+        last_message = messages.pop()
+        if last_message["role"] == "user":
+            if isinstance(last_message["content"], str):
+                user_messages.append(last_message["content"].strip())
+        else:
+            return "\n".join(user_messages[::-1])
+    return "\n".join(user_messages[::-1])
+
+def format_image_prompt(messages, prompt: str = None) -> str:
+    if prompt is None:
+        return get_last_user_message(messages)
+    return prompt
+
 def format_prompt_max_length(messages: Messages, max_lenght: int) -> str:
     prompt = format_prompt(messages)
     start = len(prompt)
diff --git a/g4f/providers/response.py b/g4f/providers/response.py
index a426b45e607..244399efe04 100644
--- a/g4f/providers/response.py
+++ b/g4f/providers/response.py
@@ -88,44 +88,61 @@ def get_dict(self):
     def reset(self):
         self.__dict__ = {}
 
-class FinishReason(ResponseType, JsonMixin):
-    def __init__(self, reason: str) -> None:
-        self.reason = reason
-
+class HiddenResponse(ResponseType):
     def __str__(self) -> str:
         return ""
 
-class ToolCalls(ResponseType):
+class FinishReason(JsonMixin, HiddenResponse):
+    def __init__(self, reason: str) -> None:
+        self.reason = reason
+
+class ToolCalls(HiddenResponse):
     def __init__(self, list: list):
         self.list = list
 
-    def __str__(self) -> str:
-        return ""
-
     def get_list(self) -> list:
         return self.list
 
-class Usage(ResponseType, JsonMixin):
-    def __str__(self) -> str:
-        return ""
+class Usage(JsonMixin, HiddenResponse):
+    pass
 
-class AuthResult(JsonMixin):
-    def __str__(self) -> str:
-        return ""
+class AuthResult(JsonMixin, HiddenResponse):
+    pass
 
-class TitleGeneration(ResponseType):
+class TitleGeneration(HiddenResponse):
     def __init__(self, title: str) -> None:
         self.title = title
 
+class DebugResponse(JsonMixin, HiddenResponse):
+    @classmethod
+    def from_dict(cls, data: dict) -> None:
+        return cls(**data)
+
+    @classmethod
+    def from_str(cls, data: str) -> None:
+        return cls(error=data)
+
+class Notification(ResponseType):
+    def __init__(self, message: str) -> None:
+        self.message = message
+
     def __str__(self) -> str:
-        return ""
+        return f"{self.message}\n"
 
 class Reasoning(ResponseType):
-    def __init__(self, token: str = None, status: str = None) -> None:
+    def __init__(
+            self,
+            token: str = None,
+            status: str = None,
+            is_thinking: str = None
+        ) -> None:
         self.token = token
         self.status = status
+        self.is_thinking = is_thinking
 
     def __str__(self) -> str:
+        if self.is_thinking is not None:
+            return self.is_thinking
         return f"{self.status}\n" if self.token is None else self.token
 
 class Sources(ResponseType):
@@ -154,14 +171,11 @@ def __str__(self) -> str:
 class JsonConversation(BaseConversation, JsonMixin):
     pass
 
-class SynthesizeData(ResponseType, JsonMixin):
+class SynthesizeData(HiddenResponse, JsonMixin):
     def __init__(self, provider: str, data: dict):
         self.provider = provider
         self.data = data
 
-    def __str__(self) -> str:
-        return ""
-
 class RequestLogin(ResponseType):
     def __init__(self, label: str, login_url: str) -> None:
         self.label = label
@@ -197,13 +211,10 @@ def __str__(self):
     def to_string(self):
         return super().__str__()
 
-class PreviewResponse(ResponseType):
+class PreviewResponse(HiddenResponse):
     def __init__(self, data: str):
         self.data = data
 
-    def __str__(self):
-        return ""
-
     def to_string(self):
         return self.data
 
@@ -211,6 +222,5 @@ class Parameters(ResponseType, JsonMixin):
     def __str__(self):
         return ""
 
-class ProviderInfo(ResponseType, JsonMixin):
-    def __str__(self):
-        return ""
\ No newline at end of file
+class ProviderInfo(JsonMixin, HiddenResponse):
+    pass
\ No newline at end of file
diff --git a/g4f/requests/__init__.py b/g4f/requests/__init__.py
index 03516951b8b..59e90877e21 100644
--- a/g4f/requests/__init__.py
+++ b/g4f/requests/__init__.py
@@ -87,7 +87,7 @@ async def get_args_from_nodriver(
     callback: callable = None,
     cookies: Cookies = None
 ) -> dict:
-    browser = await get_nodriver(proxy=proxy, timeout=timeout)
+    browser, stop_browser = await get_nodriver(proxy=proxy, timeout=timeout)
     try:
         if debug.logging:
             print(f"Open nodriver with url: {url}")
@@ -117,7 +117,7 @@ async def get_args_from_nodriver(
             "proxy": proxy,
         }
     finally:
-        browser.stop()
+        stop_browser()
 
 def merge_cookies(cookies: Iterator[Morsel], response: Response) -> Cookies:
     if cookies is None:
@@ -170,11 +170,10 @@ async def get_nodriver(
             browser = util.get_registered_instances().pop()
         else:
             raise
-    stop = browser.stop
     def on_stop():
         try:
-            stop()
+            if browser.connection:
+                browser.stop()
         finally:
             lock_file.unlink(missing_ok=True)
-    browser.stop = on_stop
-    return browser
\ No newline at end of file
+    return browser, on_stop
\ No newline at end of file
diff --git a/g4f/tools/run_tools.py b/g4f/tools/run_tools.py
index 8a77a113c8e..4c995186846 100644
--- a/g4f/tools/run_tools.py
+++ b/g4f/tools/run_tools.py
@@ -3,12 +3,14 @@
 import re
 import json
 import asyncio
+import time
 from pathlib import Path
 from typing import Optional, Callable, AsyncIterator
 
 from ..typing import Messages
 from ..providers.helper import filter_none
 from ..providers.asyncio import to_async_iterator
+from ..providers.response import Reasoning
 from ..providers.types import ProviderType
 from ..cookies import get_cookies_dir
 from .web_search import do_search, get_search_message
@@ -147,4 +149,26 @@ def on_bucket(match):
                     if has_bucket and isinstance(messages[-1]["content"], str):
                         messages[-1]["content"] += BUCKET_INSTRUCTIONS
 
-    return iter_callback(model=model, messages=messages, provider=provider, **kwargs)
+    is_thinking = 0
+    for chunk in iter_callback(model=model, messages=messages, provider=provider, **kwargs):
+        if not isinstance(chunk, str):
+            yield chunk
+            continue
+        if "<think>" in chunk:
+            chunk = chunk.split("<think>", 1)
+            yield chunk[0]
+            yield Reasoning(is_thinking="<think>")
+            yield Reasoning(chunk[1])
+            yield Reasoning(None, "Is thinking...")
+            is_thinking = time.time()
+        if "</think>" in chunk:
+            chunk = chunk.split("</think>", 1)
+            yield Reasoning(chunk[0])
+            yield Reasoning(is_thinking="</think>")
+            yield Reasoning(None, f"Finished in {round(time.time()-is_thinking, 2)} seconds")
+            yield chunk[1]
+            is_thinking = 0
+        elif is_thinking:
+            yield Reasoning(chunk)
+        else:
+            yield chunk
diff --git a/g4f/tools/web_search.py b/g4f/tools/web_search.py
index a96778bb17c..f1d2043b7b4 100644
--- a/g4f/tools/web_search.py
+++ b/g4f/tools/web_search.py
@@ -192,6 +192,8 @@ async def search(query: str, max_results: int = 5, max_words: int = 2500, backen
         return SearchResults(formatted_results, used_words)
 
 async def do_search(prompt: str, query: str = None, instructions: str = DEFAULT_INSTRUCTIONS, **kwargs) -> str:
+    if instructions and instructions in prompt:
+        return prompt # We have already added search results
     if query is None:
         query = spacy_get_keywords(prompt)
     json_bytes = json.dumps({"query": query, **kwargs}, sort_keys=True).encode(errors="ignore")

```