KiWA001 commited on
Commit
109c212
Β·
1 Parent(s): 6536eb0

Replace Copilot with HuggingChat provider

Browse files

- Remove Microsoft Copilot (blocked by CAPTCHA)
- Add HuggingChat provider with 6 models:
* Omni (auto-router)
* Llama 3.3 70B
* Qwen 2.5 72B
* DeepSeek R1
* Llama 3.1 8B
* Mistral 7B
- Handles welcome modal automatically
- Works without authentication
- Update config, engine, and documentation

KAIGUIDE.md CHANGED
@@ -68,13 +68,20 @@ Uses Playwright Chromium to interact with `gemini.google.com` as a real browser.
68
  - **Files**: `providers/gemini_provider.py`, `test_gemini_browser.py`.
69
  - **Status**: **Experimental**. Requires local Playwright environment.
70
 
71
- ### E. Microsoft Copilot (Browser-Based Provider)
72
- Uses Playwright Chromium to interact with `copilot.microsoft.com` as a real browser.
73
- - **Why Browser**: Microsoft's Copilot requires a browser session to function properly.
74
- - **Input**: Multiple selector strategies for robust input detection (`[data-testid="chat-input"]`, `div[contenteditable="true"]`).
75
- - **Features**: Handles "Continue" buttons automatically for longer responses.
76
- - **Model**: `copilot-gpt-4` (GPT-4 powered responses).
77
- - **Files**: `providers/copilot_provider.py`.
 
 
 
 
 
 
 
78
  - **Status**: **Experimental**. Requires local Playwright environment.
79
  - **Vercel**: **DISABLED** (no Chromium in serverless). Local/Docker only.
80
 
 
68
  - **Files**: `providers/gemini_provider.py`, `test_gemini_browser.py`.
69
  - **Status**: **Experimental**. Requires local Playwright environment.
70
 
71
+ ### E. HuggingChat (Browser-Based Provider)
72
+ Uses Playwright Chromium to interact with `huggingface.co/chat` as a real browser.
73
+ - **Why Browser**: HuggingChat provides access to 100+ open-source models via web interface.
74
+ - **Input**: `textarea` with placeholder text.
75
+ - **Features**:
76
+ - Handles the welcome modal automatically (clicks "Start chatting")
77
+ - Supports model selection from dropdown (optional, defaults to "Omni" router)
78
+ - Access to top models: Llama 3.3 70B, Qwen 2.5 72B, DeepSeek R1, etc.
79
+ - **Models**:
80
+ - `omni` - Auto-routes to best model (default)
81
+ - `meta-llama/Llama-3.3-70B-Instruct` - Meta's latest Llama model
82
+ - `Qwen/Qwen2.5-72B-Instruct` - Alibaba's Qwen model
83
+ - `deepseek-ai/DeepSeek-R1` - DeepSeek reasoning model
84
+ - **Files**: `providers/huggingchat_provider.py`.
85
  - **Status**: **Experimental**. Requires local Playwright environment.
86
  - **Vercel**: **DISABLED** (no Chromium in serverless). Local/Docker only.
87
 
config.py CHANGED
@@ -25,7 +25,9 @@ MODEL_RANKING = [
25
  ("gpt-4o-mini", "g4f", "gpt-4o-mini"),
26
  ("glm-5", "zai", "glm-5"),
27
  ("gemini-3-flash", "gemini", "gemini-3-flash"),
28
- ("copilot-gpt-4", "copilot", "copilot-gpt-4"),
 
 
29
  ("gpt-oss-20b", "pollinations", "openai"),
30
  ("mistral-small-3.2", "pollinations", "mistral"),
31
 
@@ -82,8 +84,13 @@ PROVIDER_MODELS = {
82
  "gemini": [
83
  "gemini-3-flash",
84
  ],
85
- "copilot": [
86
- "copilot-gpt-4",
 
 
 
 
 
87
  ],
88
  "pollinations": [
89
  "gpt-oss-20b",
 
25
  ("gpt-4o-mini", "g4f", "gpt-4o-mini"),
26
  ("glm-5", "zai", "glm-5"),
27
  ("gemini-3-flash", "gemini", "gemini-3-flash"),
28
+ ("huggingchat-omni", "huggingchat", "omni"),
29
+ ("llama-3.3-70b", "huggingchat", "meta-llama/Llama-3.3-70B-Instruct"),
30
+ ("qwen2.5-72b", "huggingchat", "Qwen/Qwen2.5-72B-Instruct"),
31
  ("gpt-oss-20b", "pollinations", "openai"),
32
  ("mistral-small-3.2", "pollinations", "mistral"),
33
 
 
84
  "gemini": [
85
  "gemini-3-flash",
86
  ],
87
+ "huggingchat": [
88
+ "omni",
89
+ "meta-llama/Llama-3.3-70B-Instruct",
90
+ "Qwen/Qwen2.5-72B-Instruct",
91
+ "deepseek-ai/DeepSeek-R1",
92
+ "meta-llama/Llama-3.1-8B-Instruct",
93
+ "mistralai/Mistral-7B-Instruct-v0.3",
94
  ],
95
  "pollinations": [
96
  "gpt-oss-20b",
debug_screenshots/01_initial_load.png ADDED
debug_screenshots/02_after_hydration.png ADDED
debug_screenshots/03_after_typing.png ADDED
debug_screenshots/04_after_send.png ADDED
debug_screenshots/05_response.png ADDED
debug_screenshots/hc_initial.png ADDED
debug_screenshots/hc_page_source.html ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html><html lang="en"><head>
2
+ <meta charset="utf-8">
3
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
4
+ <meta name="theme-color" content="rgb(249, 250, 251)">
5
+ <script>
6
+ (function () {
7
+ try {
8
+ var prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
9
+ var stored = localStorage.getItem("theme");
10
+ var followSystem = stored === null || stored === "system";
11
+ var isDark = stored === "dark" || (followSystem && prefersDark);
12
+ if (isDark) {
13
+ document.documentElement.classList.add("dark");
14
+ document.querySelector('meta[name="theme-color"]').setAttribute("content", "#07090d");
15
+ }
16
+ } catch (e) {}
17
+ })();
18
+
19
+ // For some reason, Sveltekit doesn't let us load env variables from .env here, so we load it from hooks.server.ts
20
+ window.gaId = "";
21
+ </script>
22
+
23
+ <link href="/chat/_app/immutable/assets/0.BxyA4mCL.css" rel="stylesheet">
24
+ <link href="/chat/_app/immutable/assets/models.Dncc1Dh7.css" rel="stylesheet"><!--[--><meta name="description" content="Making the community's best AI chat models available to everyone"> <meta name="twitter:site" content="@huggingface"> <!--[--><meta name="twitter:card" content="summary_large_image"> <meta name="twitter:title" content="HuggingChat - Chat with AI models"> <meta name="twitter:description" content="Making the community's best AI chat models available to everyone"> <meta name="twitter:image" content="https://huggingface.cohttps://huggingface.co/chat/huggingchat/thumbnail.png"> <meta name="twitter:image:alt" content="HuggingChat preview"> <meta property="og:title" content="HuggingChat - Chat with AI models"> <meta property="og:type" content="website"> <meta property="og:url" content="https://huggingface.co/chat"> <meta property="og:image" content="https://huggingface.co/chat/huggingchat/thumbnail.png"> <meta property="og:description" content="Making the community's best AI chat models available to everyone"> <meta property="og:site_name" content="HuggingChat"> <meta property="og:locale" content="en_US"><!--]--> <link rel="icon" href="https://huggingface.co/chat/huggingchat/icon.svg" type="image/svg+xml"> <!--[--><link rel="icon" href="https://huggingface.co/chat/huggingchat/favicon.svg" type="image/svg+xml" media="(prefers-color-scheme: light)"> <link rel="icon" href="https://huggingface.co/chat/huggingchat/favicon-dark.svg" type="image/svg+xml" media="(prefers-color-scheme: dark)"><!--]--> <link rel="apple-touch-icon" href="https://huggingface.co/chat/huggingchat/apple-touch-icon.png"> <link rel="manifest" href="https://huggingface.co/chat/huggingchat/manifest.json"> <!--[--><script async="" src="https://plausible.io/js/pa-Io_oigECawqdlgpf5qvHb.js"></script><!----><!--]--> <!--[!--><!--]--><!--]--><!--[--><!--]--><title>HuggingChat</title>
25
+ <link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/nodes/0.VN2xao_R.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/GndUUp11.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/DzUYxSXu.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/DIeogL5L.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/BdtUgkxI.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/CbcL9FWQ.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/CX1QUvAJ.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/CqkleIqs.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/xysgetnd.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/D0-cVBWo.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/CWj6FrbW.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/CWu0D6G8.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/BH7apXi0.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/Bpq93ek1.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/69_IOA4Y.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/BU5FWNU6.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/jQCsKN1q.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/BUaLhwi4.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/C63Vvf_s.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/CnIGUk-s.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/DZW4JsdA.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/DgiNCJ3I.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/7SkvYd_D.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/Cenn58bB.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/B62QhTgY.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/CPwLOqZt.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/CTQezUnf.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/C9EUFfAi.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/BCVoH2qL.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/nodes/1.CWz-xcMS.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/KlzHiDQJ.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/nodes/4.C1iPylfh.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/BCpzjSyf.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/BuybuBbN.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/DMtCQNue.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/Vz2epAnn.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/BtUAYA0X.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/BMNPVVsa.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/DhUvgpHA.js"><link rel="modulepreload" as="script" crossorigin="" href="/chat/_app/immutable/chunks/DuDrw0Kc.js"></head>
26
+ <body data-sveltekit-preload-data="hover" class="h-dvh dark:bg-gray-900">
27
+ <div id="app" class="contents h-full" inert="true"><!--[--><!--[--><!----><!--[--><!--]--> <!----> <div class="fixed grid h-dvh w-screen grid-cols-1 grid-rows-[auto,1fr] overflow-hidden text-smd md:grid-cols-[290px,1fr] transition-[300ms] [transition-property:grid-template-columns] dark:text-gray-300 md:grid-rows-[1fr]"><button title="Collapse sidebar" class="absolute inset-y-0 z-10 my-auto left-[290px] *:transition-transform group flex h-16 w-6 flex-col items-center justify-center -space-y-1 outline-none *:h-3 *:w-1 *:rounded-full *:hover:bg-gray-400 dark:*:hover:bg-gray-400 max-md:hidden *:bg-gray-300/70 dark:*:bg-gray-600" name="sidebar-toggle" aria-label="Toggle sidebar navigation"><div class="group-hover:rotate-[20deg]"></div> <div class="group-hover:-rotate-[20deg]"></div></button><!----> <!--[!--><!--]--> <nav class="flex h-12 items-center justify-between rounded-b-xl border-b bg-gray-50 px-3 dark:border-gray-800 dark:bg-gray-800/30 dark:shadow-xl md:hidden"><button type="button" class="-ml-3 flex size-12 shrink-0 items-center justify-center text-lg" aria-label="Open menu"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="none" viewBox="0 0 16 16"><path d="M8.795 10.418a.84.84 0 1 1 0 1.681H1.907a.84.84 0 0 1 0-1.681h6.888ZM14.093 3.9a.841.841 0 0 1 0 1.682H1.907a.84.84 0 0 1 0-1.682h12.186Z" fill="currentColor"></path></svg><!----></button> <div class="flex h-full items-center justify-center overflow-hidden"><!--[!--><!--]--></div> <div class="flex items-center"><!--[--><button type="button" class="flex size-8 shrink-0 items-center justify-center text-lg" disabled="" aria-label="Share conversation"><svg xmlns="http://www.w3.org/2000/svg" class="opacity-40" width="1em" height="1em" fill="none" viewBox="0 0 12 12"><path d="M10.4646 6.85139C10.7605 6.85139 11 7.09093 11 7.38679V7.78965C11 8.35479 11.0013 8.82459 10.9581 9.20053C10.9136 9.58762 10.8165 9.94247 10.5745 10.2495C10.478 10.3719 10.3672 10.4826 10.2448 10.5791C9.93774 10.8212 9.58211 10.9183 9.19497 10.9628C8.81915 11.006 8.34979 11.0055 7.78496 11.0055H4.21503C3.6502 11.0055 3.18083 11.006 2.80502 10.9628C2.41788 10.9183 2.06224 10.8212 1.75515 10.5791C1.63274 10.4826 1.52198 10.3718 1.42554 10.2495C1.18354 9.94248 1.08635 9.58761 1.04186 9.20053C0.998661 8.82458 1 8.35479 1 7.78965V7.38679C1.00003 7.09093 1.23954 6.85139 1.53541 6.85139C1.83128 6.85139 2.07078 7.09093 2.07081 7.38679V7.78965C2.07081 8.38023 2.07202 8.77788 2.10656 9.07845C2.13978 9.36728 2.19822 9.49857 2.26701 9.58595C2.31143 9.64228 2.3625 9.69333 2.41873 9.73767C2.50614 9.80657 2.63774 9.86487 2.9271 9.89812C3.2276 9.93264 3.62467 9.93387 4.21503 9.93387H7.78496C8.37532 9.93387 8.77238 9.93264 9.07289 9.89812C9.36227 9.86487 9.49384 9.80658 9.58126 9.73767C9.63752 9.69329 9.68862 9.64222 9.73298 9.58595C9.80176 9.49856 9.86021 9.3673 9.89343 9.07845C9.92796 8.77788 9.92918 8.38023 9.92918 7.78965V7.38679C9.92921 7.09093 10.1687 6.85139 10.4646 6.85139ZM6.01046 1.00034C6.15239 1.0004 6.2885 1.05697 6.3889 1.15729L9.36849 4.13601C9.57767 4.34519 9.57759 4.68454 9.36849 4.89377C9.15925 5.10283 8.8199 5.10294 8.61073 4.89377L6.54586 2.8289V8.02945C6.54586 8.32526 6.30624 8.56559 6.01046 8.56572C5.71472 8.56555 5.47418 8.32523 5.47418 8.02945V2.8289L3.40931 4.89377C3.20011 5.10268 2.86157 5.10279 2.65243 4.89377C2.44341 4.68459 2.44341 4.34519 2.65243 4.13601L5.63114 1.15729C5.73154 1.0569 5.86848 1.00042 6.01046 1.00034Z" fill="currentColor"></path></svg><!----></button><!--]--> <a href="/chat/" class="flex size-8 shrink-0 items-center justify-center text-lg"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="none" viewBox="0 0 16 16"><path d="M7.258 1.856c.333 0 .66.024.979.07-.558.319-.972.86-1.123 1.503A5.254 5.254 0 1 0 9.32 13.513l.275-.127c.334-.17.712-.229 1.08-.17l.158.031.01.003 1.343.36-.359-1.345a1.77 1.77 0 0 1 .137-1.247 5.23 5.23 0 0 0 .538-2.041 2.356 2.356 0 0 0 1.544-1 6.808 6.808 0 0 1-.676 3.742v.001c-.034.066-.031.116-.025.14l.36 1.345a1.572 1.572 0 0 1-1.823 1.945l-.1-.024-1.334-.357a.2.2 0 0 0-.14.018l-.012.005A6.825 6.825 0 1 1 7.259 1.856Zm4.837-1.36c.434 0 .785.352.785.786v1.905h1.9a.785.785 0 0 1 0 1.57h-1.9v1.9a.786.786 0 1 1-1.57 0v-1.9H9.404a.785.785 0 0 1 0-1.57h1.906V1.282c0-.434.352-.787.785-.787Z" fill="currentColor"></path></svg><!----></a></div></nav> <!--[!--><!--]--> <nav style="transform: translateX(-100%); width: 85%; will-change: transform;" class="fixed bottom-0 left-0 top-0 z-30 grid max-h-dvh grid-cols-1
28
+ grid-rows-[auto,1fr,auto,auto] rounded-r-xl bg-white pt-4 dark:bg-gray-900 md:hidden"><div class="sticky top-0 flex flex-none touch-none items-center justify-between px-1.5 py-3.5 max-sm:pt-0"><a class="flex select-none items-center rounded-xl text-lg font-semibold" href="https://huggingface.co/chat/"><img width="32" height="32" class="dark:invert mr-[2px]" alt="HuggingChat logo" src="https://huggingface.co/chat/huggingchat/logo.svg"><!----> HuggingChat</a> <a href="/chat/" class="flex rounded-lg border bg-white px-2 py-0.5 text-center shadow-sm hover:shadow-none dark:border-gray-600 dark:bg-gray-700 sm:text-smd" title="Ctrl/Cmd + Shift + O">New Chat</a></div> <div class="scrollbar-custom flex touch-pan-y flex-col gap-1 overflow-y-auto rounded-r-xl border border-l-0 border-gray-100 from-gray-50 px-3 pb-3 pt-2 text-[.9rem] dark:border-transparent dark:from-gray-800/30 max-sm:bg-gradient-to-t md:bg-gradient-to-l"><div class="flex flex-col gap-0.5"><!--[--><!--[!--><!--]--><!--[!--><!--]--><!--[!--><!--]--><!--[!--><!--]--><!--]--></div> <!--[--><div class="h-2"></div><!--]--></div> <div class="flex touch-none flex-col gap-1 rounded-r-xl border border-l-0 border-gray-100 p-3 text-sm dark:border-transparent md:mt-3 md:bg-gradient-to-l md:from-gray-50 md:dark:from-gray-800/30"><!--[!--><!--]--> <a href="/chat/models" class="flex h-9 flex-none items-center gap-1.5 rounded-lg pl-2.5 pr-2 text-gray-500 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700">Models <span class="ml-auto rounded-md bg-gray-500/5 px-1.5 py-0.5 text-xs text-gray-400 dark:bg-gray-500/20 dark:text-gray-400">126</span></a> <!--[!--><!--]--> <span class="flex gap-1"><a href="/chat/settings/application" class="flex h-9 flex-none flex-grow items-center gap-1.5 rounded-lg pl-2.5 pr-2 text-gray-500 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700">Settings</a> <button aria-label="Toggle theme" class="flex size-9 min-w-[1.5em] flex-none items-center justify-center rounded-lg p-2 text-gray-500 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700"><!--[!--><!----><svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M25.054 43.02C20.02 43.02 15.762 41.278 12.28 37.794C8.79695 34.31 7.05496 30.052 7.05396 25.02C7.05396 20.42 8.55396 16.428 11.554 13.044C14.554 9.66 18.387 7.685 23.054 7.12C23.487 7.053 23.87 7.112 24.204 7.296C24.537 7.48 24.804 7.721 25.004 8.02C25.204 8.318 25.312 8.668 25.33 9.07C25.347 9.471 25.222 9.854 24.954 10.22C24.3955 11.0688 23.9655 11.9955 23.678 12.97C23.3906 13.961 23.2477 14.9882 23.254 16.02C23.254 19.02 24.304 21.57 26.404 23.67C28.504 25.77 31.054 26.82 34.054 26.82C35.087 26.82 36.112 26.67 37.13 26.37C38.096 26.0936 39.0145 25.6721 39.854 25.12C40.22 24.886 40.596 24.778 40.98 24.796C41.364 24.813 41.705 24.904 42.004 25.07C42.337 25.236 42.596 25.486 42.78 25.82C42.964 26.153 43.022 26.553 42.954 27.02C42.487 31.62 40.529 35.436 37.08 38.47C33.63 41.503 29.622 43.02 25.054 43.02Z" fill="#D2D5DB"></path></svg><!--]--></button></span></div> <!--[!--><!--]--><!----></nav><!----> <nav class="grid max-h-dvh grid-cols-1 grid-rows-[auto,1fr,auto] overflow-hidden *:w-[290px] max-md:hidden"><div class="sticky top-0 flex flex-none touch-none items-center justify-between px-1.5 py-3.5 max-sm:pt-0"><a class="flex select-none items-center rounded-xl text-lg font-semibold" href="https://huggingface.co/chat/"><img width="32" height="32" class="dark:invert mr-[2px]" alt="HuggingChat logo" src="https://huggingface.co/chat/huggingchat/logo.svg"><!----> HuggingChat</a> <a href="/chat/" class="flex rounded-lg border bg-white px-2 py-0.5 text-center shadow-sm hover:shadow-none dark:border-gray-600 dark:bg-gray-700 sm:text-smd" title="Ctrl/Cmd + Shift + O">New Chat</a></div> <div class="scrollbar-custom flex touch-pan-y flex-col gap-1 overflow-y-auto rounded-r-xl border border-l-0 border-gray-100 from-gray-50 px-3 pb-3 pt-2 text-[.9rem] dark:border-transparent dark:from-gray-800/30 max-sm:bg-gradient-to-t md:bg-gradient-to-l"><div class="flex flex-col gap-0.5"><!--[--><!--[!--><!--]--><!--[!--><!--]--><!--[!--><!--]--><!--[!--><!--]--><!--]--></div> <!--[--><!--]--></div> <div class="flex touch-none flex-col gap-1 rounded-r-xl border border-l-0 border-gray-100 p-3 text-sm dark:border-transparent md:mt-3 md:bg-gradient-to-l md:from-gray-50 md:dark:from-gray-800/30"><!--[!--><!--]--> <a href="/chat/models" class="flex h-9 flex-none items-center gap-1.5 rounded-lg pl-2.5 pr-2 text-gray-500 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700">Models <span class="ml-auto rounded-md bg-gray-500/5 px-1.5 py-0.5 text-xs text-gray-400 dark:bg-gray-500/20 dark:text-gray-400">126</span></a> <!--[!--><!--]--> <span class="flex gap-1"><a href="/chat/settings/application" class="flex h-9 flex-none flex-grow items-center gap-1.5 rounded-lg pl-2.5 pr-2 text-gray-500 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700">Settings</a> <button aria-label="Toggle theme" class="flex size-9 min-w-[1.5em] flex-none items-center justify-center rounded-lg p-2 text-gray-500 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700"><!--[!--><!----><svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M25.054 43.02C20.02 43.02 15.762 41.278 12.28 37.794C8.79695 34.31 7.05496 30.052 7.05396 25.02C7.05396 20.42 8.55396 16.428 11.554 13.044C14.554 9.66 18.387 7.685 23.054 7.12C23.487 7.053 23.87 7.112 24.204 7.296C24.537 7.48 24.804 7.721 25.004 8.02C25.204 8.318 25.312 8.668 25.33 9.07C25.347 9.471 25.222 9.854 24.954 10.22C24.3955 11.0688 23.9655 11.9955 23.678 12.97C23.3906 13.961 23.2477 14.9882 23.254 16.02C23.254 19.02 24.304 21.57 26.404 23.67C28.504 25.77 31.054 26.82 34.054 26.82C35.087 26.82 36.112 26.67 37.13 26.37C38.096 26.0936 39.0145 25.6721 39.854 25.12C40.22 24.886 40.596 24.778 40.98 24.796C41.364 24.813 41.705 24.904 42.004 25.07C42.337 25.236 42.596 25.486 42.78 25.82C42.964 26.153 43.022 26.553 42.954 27.02C42.487 31.62 40.529 35.436 37.08 38.47C33.63 41.503 29.622 43.02 25.054 43.02Z" fill="#D2D5DB"></path></svg><!--]--></button></span></div> <!--[!--><!--]--><!----></nav> <!--[!--><!--]--> <!--[!--><!----><!--[--><div class="relative z-[-1] min-h-0 min-w-0 svelte-1pgzd69"><!--[!--><!--]--> <div class="scrollbar-custom h-full overflow-y-auto svelte-1pgzd69"><div class="mx-auto flex h-full max-w-3xl flex-col gap-6 px-5 pt-6 sm:gap-8 xl:max-w-4xl xl:pt-10 svelte-1pgzd69"><!--[!--><!--]--> <!--[!--><div class="my-auto grid items-center justify-center gap-8 text-center"><div class="flex -translate-y-16 select-none items-center rounded-xl text-3xl font-semibold md:-translate-y-12 md:text-5xl"><img width="32" height="32" class="size-12 md:size-20 dark:invert mr-0.5" alt="HuggingChat logo" src="https://huggingface.co/chat/huggingchat/logo.svg"><!----> HuggingChat</div></div><!--]--><div aria-hidden="true" data-scroll-sentinel="" style="height: 1px; width: 100%;"></div></div> <!--[!--><!--]--><!----> <!--[!--><!--]--><!----></div> <div class="pointer-events-none absolute inset-x-0 bottom-0 z-0 mx-auto flex w-full max-w-3xl flex-col items-center justify-center bg-gradient-to-t from-white via-white/100 to-white/0 px-3.5 pt-2 dark:border-gray-800 dark:from-gray-900 dark:via-gray-900/100 dark:to-gray-900/0 max-sm:py-0 sm:px-5 md:pb-4 xl:max-w-4xl [&amp;&gt;*]:pointer-events-auto svelte-1pgzd69"><!--[!--><div class="no-scrollbar mb-3 flex w-full select-none justify-start gap-2 overflow-x-auto whitespace-nowrap text-gray-400 dark:text-gray-500 svelte-1pgzd69"><button class="flex items-center rounded-lg bg-gray-100/90 px-2 py-0.5 text-center text-sm backdrop-blur hover:text-gray-500 dark:bg-gray-700/50 dark:hover:text-gray-400 svelte-1pgzd69">Latest world news</button><button class="flex items-center rounded-lg bg-gray-100/90 px-2 py-0.5 text-center text-sm backdrop-blur hover:text-gray-500 dark:bg-gray-700/50 dark:hover:text-gray-400 svelte-1pgzd69">Trending models</button><button class="flex items-center rounded-lg bg-gray-100/90 px-2 py-0.5 text-center text-sm backdrop-blur hover:text-gray-500 dark:bg-gray-700/50 dark:hover:text-gray-400 svelte-1pgzd69">Plan a trip</button><button class="flex items-center rounded-lg bg-gray-100/90 px-2 py-0.5 text-center text-sm backdrop-blur hover:text-gray-500 dark:bg-gray-700/50 dark:hover:text-gray-400 svelte-1pgzd69">Compare technologies</button><button class="flex items-center rounded-lg bg-gray-100/90 px-2 py-0.5 text-center text-sm backdrop-blur hover:text-gray-500 dark:bg-gray-700/50 dark:hover:text-gray-400 svelte-1pgzd69">Find a dataset</button><button class="flex items-center rounded-lg bg-gray-100/90 px-2 py-0.5 text-center text-sm backdrop-blur hover:text-gray-500 dark:bg-gray-700/50 dark:hover:text-gray-400 svelte-1pgzd69">Gift ideas</button><button class="flex items-center rounded-lg bg-gray-100/90 px-2 py-0.5 text-center text-sm backdrop-blur hover:text-gray-500 dark:bg-gray-700/50 dark:hover:text-gray-400 svelte-1pgzd69">Learn something new</button></div><!--]--> <!--[!--><!--]--> <!--[!--><!--]--> <div class="w-full svelte-1pgzd69"><div class="flex w-full *:mb-3 svelte-1pgzd69"><!--[!--><!--]--></div> <form tabindex="-1" aria-label="file dropzone" class="relative flex w-full max-w-4xl flex-1 items-center rounded-xl border bg-gray-100 dark:border-gray-700 dark:bg-gray-800 svelte-1pgzd69"><!--[!--><div class="flex w-full flex-1 rounded-xl border-none bg-transparent svelte-1pgzd69"><!--[!--><div class="flex min-h-full flex-1 flex-col"><textarea rows="1" tabindex="0" inputmode="text" class="scrollbar-custom max-h-[4lh] w-full resize-none overflow-y-auto overflow-x-hidden border-0 bg-transparent px-2.5 py-2.5 outline-none focus:ring-0 focus-visible:ring-0 sm:px-3 md:max-h-[8lh]" placeholder="Ask anything" style="height: 44px;"></textarea> <!--[--><div class="scrollbar-custom -ml-0.5 flex max-w-[calc(100%-40px)] flex-wrap items-center justify-start gap-2.5 px-3 pb-2.5 pt-1.5 text-gray-500 dark:text-gray-400 max-md:flex-nowrap max-md:overflow-x-auto sm:gap-2"><!--[--><div class="flex items-center"><input class="absolute hidden size-0" aria-label="Upload file" type="file" multiple="" accept="text/*,application/json,application/xml,application/csv,image/*" style=""> <!----><!----><!--#s1--><!--[!--><button class="btn size-8 rounded-full border bg-white text-black shadow transition-none enabled:hover:bg-white enabled:hover:shadow-inner dark:border-transparent dark:bg-gray-600/50 dark:text-white dark:hover:enabled:bg-gray-600 sm:size-7" aria-label="Add attachment" id="bits-s1" aria-haspopup="menu" aria-expanded="false" data-state="closed" data-dropdown-menu-trigger="" type="button"><svg viewBox="0 0 24 24" width="1.2em" height="1.2em" class="text-base sm:text-sm"><!----><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14m-7-7v14"></path><!----></svg><!----></button><!--]--><!----><!----> <!----><!--[!--><!--]--><!----><!----><!----><!----> <!--[!--><div class="ml-1.5 inline-flex h-8 items-center gap-1.5 rounded-full border border-blue-500/10 bg-blue-600/10 pl-2 pr-1 text-xs font-semibold text-blue-700 dark:bg-blue-600/20 dark:text-blue-400 sm:h-7" title="MCP servers enabled"><button type="button" title="Manage MCP Servers" class="inline-flex cursor-pointer select-none items-center gap-1 bg-transparent p-0 leading-none text-current focus:outline-none"><span class="flex items-center -space-x-1"><img alt="" class="size-4 rounded bg-white p-px shadow-sm ring-1 ring-black/5 dark:bg-gray-900 dark:ring-white/10" src="https://www.google.com/s2/favicons?sz=64&amp;domain_url=https%3A%2F%2Fexa.ai"><img alt="" class="size-4 rounded bg-white p-px shadow-sm ring-1 ring-black/5 dark:bg-gray-900 dark:ring-white/10" src="https://www.google.com/s2/favicons?sz=64&amp;domain_url=https%3A%2F%2Fhf.co"><!----> <!----></span><!----> MCP (2)</button> <button class="grid size-5 place-items-center rounded-full bg-blue-600/15 text-blue-700 transition-colors hover:bg-blue-600/25 dark:bg-blue-600/25 dark:text-blue-300 dark:hover:bg-blue-600/35" aria-label="Disable all MCP servers" type="button"><svg viewBox="0 0 32 32" width="1.2em" height="1.2em" class="size-3.5"><path fill="currentColor" d="M17.414 16L24 9.414L22.586 8L16 14.586L9.414 8L8 9.414L14.586 16L8 22.586L9.414 24L16 17.414L22.586 24L24 22.586z"></path><!----></svg><!----></button></div><!--]--></div><!--]--></div><!--]--> <!----> <!--[!--><!--]--><!----> <!--[!--><!--]--></div><!--]--> <!--[!--><!--[--><button type="button" class="btn absolute bottom-2 right-10 mr-1.5 size-8 self-end rounded-full border bg-white/50 text-gray-500 transition-none hover:bg-gray-50 hover:text-gray-700 dark:border-transparent dark:bg-gray-600/50 dark:text-gray-300 dark:hover:bg-gray-500 dark:hover:text-white sm:right-9 sm:size-7 svelte-1pgzd69" aria-label="Start voice recording"><svg viewBox="0 0 24 24" width="1.2em" height="1.2em" class="size-4"><!----><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12 19v3m7-12v2a7 7 0 0 1-14 0v-2"></path><rect width="6" height="13" x="9" y="2" rx="3"></rect></g><!----></svg><!----></button><!--]--> <button class="btn absolute bottom-2 right-2 size-8 self-end rounded-full border bg-white text-black shadow transition-none enabled:hover:bg-white enabled:hover:shadow-inner dark:border-transparent dark:bg-gray-600 dark:text-white dark:hover:enabled:bg-black sm:size-7 svelte-1pgzd69" disabled="" type="submit" aria-label="Send message" name="submit"><svg viewBox="0 0 24 24" width="1.2em" height="1.2em"><!----><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m5 12l7-7l7 7m-7 7V5"></path><!----></svg><!----></button><!--]--></div><!--]--></form> <div class="mt-1.5 flex h-5 items-center self-stretch whitespace-nowrap px-0.5 text-xs text-gray-400/90 max-md:mb-2 max-sm:gap-2 svelte-1pgzd69"><!--[--><!--[1--><a href="/chat/settings/omni" class="inline-flex items-center gap-1 hover:underline svelte-1pgzd69"><!--[--><svg class=" hidden dark:inline" width="1em" height="1em" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5.97736 12.1813C6.25011 12.516 6.57428 12.8946 6.98029 13.2741C5.89251 13.8066 4.44063 14.1305 2.34747 14.1306V12.7272C4.02144 12.7272 5.15855 12.5026 5.97736 12.1813ZM10.0789 6.00458C10.3483 6.3067 10.6247 6.56949 10.9725 6.79364C11.5911 7.19216 12.4914 7.49774 14.0526 7.49774V8.90204C12.4915 8.90204 11.5911 9.20765 10.9725 9.60614C10.6249 9.83013 10.3481 10.0924 10.0789 10.3942C9.78258 10.1597 9.52333 9.87047 9.21271 9.48798C9.18183 9.44996 9.14961 9.40984 9.11603 9.36786C9.42491 9.03403 9.77986 8.70638 10.2127 8.42743C10.3378 8.34683 10.4686 8.27118 10.6053 8.19989C10.4686 8.12858 10.3378 8.05297 10.2127 7.97235C9.77958 7.69322 9.42506 7.365 9.11603 7.03094C9.1494 6.98922 9.18201 6.9496 9.21271 6.9118C9.52349 6.52912 9.78237 6.2392 10.0789 6.00458ZM2.34747 2.26923C4.44032 2.26927 5.89256 2.59232 6.98029 3.12469C6.57429 3.50414 6.25012 3.8828 5.97736 4.21747C5.15858 3.89631 4.02115 3.67356 2.34747 3.67352V2.26923Z" fill="url(#paint0_linear_3699_582)"></path><path d="M14.052 3.67331C12.0512 3.67337 10.8161 3.98917 9.97647 4.41441C9.14382 4.83623 8.63688 5.39533 8.12318 6.02791C7.62178 6.64535 7.06413 7.40735 6.18741 7.97235C6.06225 8.053 5.93137 8.12889 5.79462 8.20022C5.93144 8.27158 6.06219 8.34739 6.18741 8.42808C7.06422 8.99314 7.62174 9.75505 8.12318 10.3725C8.6369 11.0051 9.14374 11.5642 9.97647 11.986C10.8161 12.4113 12.0512 12.7271 14.052 12.7271V14.1312C11.9098 14.1311 10.4387 13.7932 9.34279 13.2382C8.24007 12.6797 7.58149 11.9313 7.03377 11.2569C6.47365 10.5671 6.07238 10.0218 5.42786 9.60647C4.80925 9.20786 3.90875 8.90226 2.34735 8.90226V7.49818C3.90859 7.49818 4.80926 7.19251 5.42786 6.79397C6.07232 6.37865 6.47373 5.83323 7.03377 5.14358C7.58147 4.46911 8.24014 3.72078 9.34279 3.16224C10.4387 2.60722 11.9098 2.26929 14.052 2.26923V3.67331Z" fill="url(#paint1_linear_3699_582)"></path><defs><linearGradient id="paint0_linear_3699_582" x1="10.2846" y1="8.06294" x2="-0.714687" y2="8.06294" gradientUnits="userSpaceOnUse"><stop stop-color="white"></stop><stop offset="1" stop-color="white" stop-opacity="0"></stop></linearGradient><linearGradient id="paint1_linear_3699_582" x1="1.34749" y1="8.06326" x2="14.273" y2="8.06326" gradientUnits="userSpaceOnUse"><stop stop-color="white" stop-opacity="0"></stop><stop offset="1" stop-color="white"></stop></linearGradient></defs></svg><svg class=" inline dark:hidden" width="1em" height="1em" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5.97723 12.3813C6.24999 12.716 6.57417 13.0946 6.98016 13.4741C5.89247 14.0066 4.44119 14.3305 2.34833 14.3306V12.9272C4.02198 12.9272 5.1585 12.7025 5.97723 12.3813ZM10.0788 6.20459C10.3481 6.50673 10.6245 6.76948 10.9724 6.99365C11.5909 7.39219 12.4912 7.69774 14.0524 7.69775V9.10205C12.4913 9.10207 11.5909 9.40765 10.9724 9.80615C10.6248 10.0302 10.348 10.2924 10.0788 10.5942C9.78249 10.3597 9.52319 10.0704 9.21259 9.68799C9.18171 9.64997 9.14949 9.60986 9.11591 9.56787C9.42481 9.23402 9.77972 8.9064 10.2126 8.62744C10.3377 8.54682 10.4685 8.47121 10.6052 8.3999C10.4685 8.32859 10.3377 8.25299 10.2126 8.17236C9.77943 7.89322 9.42495 7.56504 9.11591 7.23096C9.1493 7.18921 9.18187 7.14963 9.21259 7.11182C9.52337 6.72913 9.78226 6.43921 10.0788 6.20459ZM2.34833 2.46924C4.44088 2.46933 5.89252 2.7924 6.98016 3.32471C6.57418 3.70415 6.25 4.08282 5.97723 4.41748C5.15853 4.09637 4.0217 3.87361 2.34833 3.87354V2.46924Z" fill="url(#paint0_linear_3699_575)"></path><path d="M14.052 3.87332C12.0512 3.87338 10.8161 4.18918 9.97647 4.61442C9.14382 5.03624 8.63688 5.59534 8.12318 6.22792C7.62178 6.84536 7.06413 7.60736 6.18741 8.17236C6.06225 8.25301 5.93137 8.3289 5.79462 8.40023C5.93144 8.47159 6.06219 8.5474 6.18741 8.62809C7.06422 9.19316 7.62174 9.95506 8.12318 10.5725C8.6369 11.2051 9.14374 11.7642 9.97647 12.186C10.8161 12.6113 12.0512 12.9271 14.052 12.9271V14.3312C11.9098 14.3312 10.4387 13.9932 9.34279 13.4382C8.24007 12.8797 7.58149 12.1313 7.03377 11.4569C6.47365 10.7671 6.07238 10.2218 5.42786 9.80648C4.80925 9.40788 3.90875 9.10227 2.34735 9.10227V7.69819C3.90859 7.69819 4.80926 7.39252 5.42786 6.99398C6.07232 6.57866 6.47373 6.03324 7.03377 5.34359C7.58147 4.66913 8.24014 3.92079 9.34279 3.36225C10.4387 2.80724 11.9098 2.4693 14.052 2.46924V3.87332Z" fill="url(#paint1_linear_3699_575)"></path><defs><linearGradient id="paint0_linear_3699_575" x1="10.2848" y1="8.26295" x2="-0.713577" y2="8.26295" gradientUnits="userSpaceOnUse"><stop></stop><stop offset="1" stop-opacity="0"></stop></linearGradient><linearGradient id="paint1_linear_3699_575" x1="1.34749" y1="8.26327" x2="14.273" y2="8.26327" gradientUnits="userSpaceOnUse"><stop stop-opacity="0"></stop><stop offset="1"></stop></linearGradient></defs></svg><!----> Omni<!--]--> <svg viewBox="0 0 32 32" width="1.2em" height="1.2em" class="-ml-0.5 text-xxs"><!----><path fill="currentColor" d="m24 12l-8 10l-8-10z"></path><!----></svg><!----></a><!--]--><!--]--> <!--[--><span class="max-sm:hidden svelte-1pgzd69">Generated content may be inaccurate or false.</span><!--]--></div></div></div></div><!--]--><!----><!--]--><!----> <!--[--><script>
29
+ (window.plausible =
30
+ window.plausible ||
31
+ function () {
32
+ (plausible.q = plausible.q || []).push(arguments);
33
+ }),
34
+ (plausible.init =
35
+ plausible.init ||
36
+ function (i) {
37
+ plausible.o = i || {};
38
+ });
39
+ plausible.init();
40
+ </script><!----><!--]--></div><!----><!--]--> <!--[!--><div id="svelte-announcer" aria-live="assertive" aria-atomic="true" style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px"><!----></div><!--]--><!--]-->
41
+
42
+
43
+
44
+
45
+
46
+
47
+ <script>
48
+ {
49
+ __sveltekit_15tyi6u = {
50
+ base: "/chat",
51
+ assets: "/chat",
52
+ env: {"PUBLIC_LLM_ROUTER_DISPLAY_NAME":"Omni","PUBLIC_VERSION":"0.20.0","PUBLIC_LLM_ROUTER_LOGO_URL":"https://cdn-uploads.huggingface.co/production/uploads/5f17f0a0925b9863e28ad517/C5V0v1xZXv6M7FXsdJH9b.png","PUBLIC_LLM_ROUTER_ALIAS_ID":"omni","PUBLIC_APP_ASSETS":"huggingchat","PUBLIC_PLAUSIBLE_SCRIPT_URL":"https://plausible.io/js/pa-Io_oigECawqdlgpf5qvHb.js","PUBLIC_ORIGIN":"https://huggingface.co","PUBLIC_COMMIT_SHA":"72227a6","PUBLIC_APP_NAME":"HuggingChat","PUBLIC_APP_DESCRIPTION":"Making the community's best AI chat models available to everyone","PUBLIC_SHARE_PREFIX":"","PUBLIC_GOOGLE_ANALYTICS_ID":"","PUBLIC_APPLE_APP_ID":""}
53
+ };
54
+
55
+ const element = document.currentScript.parentElement;
56
+
57
+ Promise.all([
58
+ import("/chat/_app/immutable/entry/start.DScfOO8g.js"),
59
+ import("/chat/_app/immutable/entry/app.Cap7ung_.js")
60
+ ]).then(([kit, app]) => {
61
+ kit.start(app, element, {
62
+ node_ids: [0, 4],
63
+ data: [null,null],
64
+ form: null,
65
+ error: null
66
+ });
67
+ });
68
+ }
69
+ </script>
70
+ </div>
71
+
72
+ <!-- Google Tag Manager -->
73
+ <script>
74
+ if (window.gaId) {
75
+ const script = document.createElement("script");
76
+ script.src = "https://www.googletagmanager.com/gtag/js?id=" + window.gaId;
77
+ script.async = true;
78
+ document.head.appendChild(script);
79
+
80
+ window.dataLayer = window.dataLayer || [];
81
+ function gtag() {
82
+ dataLayer.push(arguments);
83
+ }
84
+ gtag("js", new Date());
85
+ /// ^ See https://developers.google.com/tag-platform/gtagjs/install
86
+ gtag("config", window.gaId);
87
+ gtag("consent", "default", { ad_storage: "denied", analytics_storage: "denied" });
88
+ /// ^ See https://developers.google.com/tag-platform/gtagjs/reference#consent
89
+ /// TODO: ask the user for their consent and update this with gtag('consent', 'update')
90
+ }
91
+ </script>
92
+
93
+
94
+ <div class="contents" hidden=""><div role="presentation" tabindex="-1" class="fixed inset-0 z-40 flex items-center justify-center bg-black/80 backdrop-blur-sm dark:bg-black/50"><!--[!--><div role="dialog" tabindex="-1" class="scrollbar-custom relative mx-auto max-h-[95dvh] max-w-[90dvw] overflow-y-auto overflow-x-hidden rounded-2xl bg-white shadow-2xl outline-none dark:bg-gray-800 dark:text-gray-200 !max-w-[420px] !m-4"><!--[!--><!--]--> <div class="flex w-full flex-col gap-8 bg-white bg-gradient-to-b to-transparent px-6 pb-7 dark:bg-black dark:from-white/10 dark:to-white/5"><div class="relative -mx-6 grid h-48 select-none place-items-center bg-gradient-to-t from-black/5 dark:from-white/10"><img class="size-full bg-black object-cover" src="https://huggingface.co/chat/huggingchat/omni-welcome.gif" alt="Omni AI model router animation"> <div class="absolute bottom-3 right-3 rounded-lg border border-blue-500/20 bg-blue-500/20 px-2 py-0.5 text-sm font-semibold text-blue-500">Now with MCP!</div></div> <div class="text-gray-700 dark:text-gray-200"><p class="text-[15px] leading-relaxed">Welcome to HuggingChat, the chat app powered by open source AI models.</p> <p class="mt-3 text-[15px] leading-relaxed"><svg class="-translate-y-px hidden dark:inline" width="1em" height="1em" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5.97736 12.1813C6.25011 12.516 6.57428 12.8946 6.98029 13.2741C5.89251 13.8066 4.44063 14.1305 2.34747 14.1306V12.7272C4.02144 12.7272 5.15855 12.5026 5.97736 12.1813ZM10.0789 6.00458C10.3483 6.3067 10.6247 6.56949 10.9725 6.79364C11.5911 7.19216 12.4914 7.49774 14.0526 7.49774V8.90204C12.4915 8.90204 11.5911 9.20765 10.9725 9.60614C10.6249 9.83013 10.3481 10.0924 10.0789 10.3942C9.78258 10.1597 9.52333 9.87047 9.21271 9.48798C9.18183 9.44996 9.14961 9.40984 9.11603 9.36786C9.42491 9.03403 9.77986 8.70638 10.2127 8.42743C10.3378 8.34683 10.4686 8.27118 10.6053 8.19989C10.4686 8.12858 10.3378 8.05297 10.2127 7.97235C9.77958 7.69322 9.42506 7.365 9.11603 7.03094C9.1494 6.98922 9.18201 6.9496 9.21271 6.9118C9.52349 6.52912 9.78237 6.2392 10.0789 6.00458ZM2.34747 2.26923C4.44032 2.26927 5.89256 2.59232 6.98029 3.12469C6.57429 3.50414 6.25012 3.8828 5.97736 4.21747C5.15858 3.89631 4.02115 3.67356 2.34747 3.67352V2.26923Z" fill="url(#paint0_linear_3699_582)"></path><path d="M14.052 3.67331C12.0512 3.67337 10.8161 3.98917 9.97647 4.41441C9.14382 4.83623 8.63688 5.39533 8.12318 6.02791C7.62178 6.64535 7.06413 7.40735 6.18741 7.97235C6.06225 8.053 5.93137 8.12889 5.79462 8.20022C5.93144 8.27158 6.06219 8.34739 6.18741 8.42808C7.06422 8.99314 7.62174 9.75505 8.12318 10.3725C8.6369 11.0051 9.14374 11.5642 9.97647 11.986C10.8161 12.4113 12.0512 12.7271 14.052 12.7271V14.1312C11.9098 14.1311 10.4387 13.7932 9.34279 13.2382C8.24007 12.6797 7.58149 11.9313 7.03377 11.2569C6.47365 10.5671 6.07238 10.0218 5.42786 9.60647C4.80925 9.20786 3.90875 8.90226 2.34735 8.90226V7.49818C3.90859 7.49818 4.80926 7.19251 5.42786 6.79397C6.07232 6.37865 6.47373 5.83323 7.03377 5.14358C7.58147 4.46911 8.24014 3.72078 9.34279 3.16224C10.4387 2.60722 11.9098 2.26929 14.052 2.26923V3.67331Z" fill="url(#paint1_linear_3699_582)"></path><defs><linearGradient id="paint0_linear_3699_582" x1="10.2846" y1="8.06294" x2="-0.714687" y2="8.06294" gradientUnits="userSpaceOnUse"><stop stop-color="white"></stop><stop offset="1" stop-color="white" stop-opacity="0"></stop></linearGradient><linearGradient id="paint1_linear_3699_582" x1="1.34749" y1="8.06326" x2="14.273" y2="8.06326" gradientUnits="userSpaceOnUse"><stop stop-color="white" stop-opacity="0"></stop><stop offset="1" stop-color="white"></stop></linearGradient></defs></svg><svg class="-translate-y-px inline dark:hidden" width="1em" height="1em" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5.97723 12.3813C6.24999 12.716 6.57417 13.0946 6.98016 13.4741C5.89247 14.0066 4.44119 14.3305 2.34833 14.3306V12.9272C4.02198 12.9272 5.1585 12.7025 5.97723 12.3813ZM10.0788 6.20459C10.3481 6.50673 10.6245 6.76948 10.9724 6.99365C11.5909 7.39219 12.4912 7.69774 14.0524 7.69775V9.10205C12.4913 9.10207 11.5909 9.40765 10.9724 9.80615C10.6248 10.0302 10.348 10.2924 10.0788 10.5942C9.78249 10.3597 9.52319 10.0704 9.21259 9.68799C9.18171 9.64997 9.14949 9.60986 9.11591 9.56787C9.42481 9.23402 9.77972 8.9064 10.2126 8.62744C10.3377 8.54682 10.4685 8.47121 10.6052 8.3999C10.4685 8.32859 10.3377 8.25299 10.2126 8.17236C9.77943 7.89322 9.42495 7.56504 9.11591 7.23096C9.1493 7.18921 9.18187 7.14963 9.21259 7.11182C9.52337 6.72913 9.78226 6.43921 10.0788 6.20459ZM2.34833 2.46924C4.44088 2.46933 5.89252 2.7924 6.98016 3.32471C6.57418 3.70415 6.25 4.08282 5.97723 4.41748C5.15853 4.09637 4.0217 3.87361 2.34833 3.87354V2.46924Z" fill="url(#paint0_linear_3699_575)"></path><path d="M14.052 3.87332C12.0512 3.87338 10.8161 4.18918 9.97647 4.61442C9.14382 5.03624 8.63688 5.59534 8.12318 6.22792C7.62178 6.84536 7.06413 7.60736 6.18741 8.17236C6.06225 8.25301 5.93137 8.3289 5.79462 8.40023C5.93144 8.47159 6.06219 8.5474 6.18741 8.62809C7.06422 9.19316 7.62174 9.95506 8.12318 10.5725C8.6369 11.2051 9.14374 11.7642 9.97647 12.186C10.8161 12.6113 12.0512 12.9271 14.052 12.9271V14.3312C11.9098 14.3312 10.4387 13.9932 9.34279 13.4382C8.24007 12.8797 7.58149 12.1313 7.03377 11.4569C6.47365 10.7671 6.07238 10.2218 5.42786 9.80648C4.80925 9.40788 3.90875 9.10227 2.34735 9.10227V7.69819C3.90859 7.69819 4.80926 7.39252 5.42786 6.99398C6.07232 6.57866 6.47373 6.03324 7.03377 5.34359C7.58147 4.66913 8.24014 3.92079 9.34279 3.36225C10.4387 2.80724 11.9098 2.4693 14.052 2.46924V3.87332Z" fill="url(#paint1_linear_3699_575)"></path><defs><linearGradient id="paint0_linear_3699_575" x1="10.2848" y1="8.26295" x2="-0.713577" y2="8.26295" gradientUnits="userSpaceOnUse"><stop></stop><stop offset="1" stop-opacity="0"></stop></linearGradient><linearGradient id="paint1_linear_3699_575" x1="1.34749" y1="8.26327" x2="14.273" y2="8.26327" gradientUnits="userSpaceOnUse"><stop stop-opacity="0"></stop><stop offset="1"></stop></linearGradient></defs></svg><!----> Omni automatically picks the best AI model to give
95
+ you optimal answers depending on your requests.</p> <p class="mt-3 text-[15px] leading-relaxed">You can also choose from any available open source models to chat with directly.</p></div> <button class="k w-full rounded-xl bg-black px-5 py-2.5 text-base font-medium text-white hover:bg-gray-800 dark:bg-white dark:text-black dark:hover:bg-gray-200">Start chatting</button></div><!----></div><!--]--></div><!----></div><!----><!----><!----><!----><!----></body></html>
engine.py CHANGED
@@ -19,7 +19,7 @@ from providers.g4f_provider import G4FProvider
19
  from providers.pollinations_provider import PollinationsProvider
20
  from providers.gemini_provider import GeminiProvider
21
  from providers.zai_provider import ZaiProvider
22
- from providers.copilot_provider import CopilotProvider
23
  from config import MODEL_RANKING, PROVIDER_MODELS, SUPABASE_URL, SUPABASE_KEY
24
  from models import ModelInfo
25
  from sanitizer import sanitize_response
@@ -58,11 +58,11 @@ class AIEngine:
58
  self._providers["gemini"] = GeminiProvider()
59
  logger.info("βœ… Gemini provider enabled")
60
 
61
- # Copilot also uses Playwright
62
- self._providers["copilot"] = CopilotProvider()
63
- logger.info("βœ… Copilot provider enabled")
64
  else:
65
- logger.warning("⚠️ Z.ai/Gemini/Copilot providers disabled (Playwright not installed)")
66
  # Success Tracker: Key = "provider/model_id"
67
  # Value = {success, failure, consecutive_failures, avg_time_ms, total_time_ms, count_samples}
68
  self._stats: dict[str, dict] = {}
 
19
  from providers.pollinations_provider import PollinationsProvider
20
  from providers.gemini_provider import GeminiProvider
21
  from providers.zai_provider import ZaiProvider
22
+ from providers.huggingchat_provider import HuggingChatProvider
23
  from config import MODEL_RANKING, PROVIDER_MODELS, SUPABASE_URL, SUPABASE_KEY
24
  from models import ModelInfo
25
  from sanitizer import sanitize_response
 
58
  self._providers["gemini"] = GeminiProvider()
59
  logger.info("βœ… Gemini provider enabled")
60
 
61
+ # HuggingChat also uses Playwright
62
+ self._providers["huggingchat"] = HuggingChatProvider()
63
+ logger.info("βœ… HuggingChat provider enabled")
64
  else:
65
+ logger.warning("⚠️ Z.ai/Gemini/HuggingChat providers disabled (Playwright not installed)")
66
  # Success Tracker: Key = "provider/model_id"
67
  # Value = {success, failure, consecutive_failures, avg_time_ms, total_time_ms, count_samples}
68
  self._stats: dict[str, dict] = {}
providers/{copilot_provider.py β†’ huggingchat_provider.py} RENAMED
@@ -1,13 +1,13 @@
1
  """
2
- Microsoft Copilot Provider (Browser-Based)
3
- -------------------------------------------
4
- Uses Playwright Chromium to interact with https://copilot.microsoft.com/ as a real browser.
5
 
6
  Strategy:
7
- - Reuses the global Playwright browser instance (shared pattern with Z.ai/Gemini).
8
- - Uses EPHEMERAL contexts (Tabs) per request for robust data isolation.
9
- - Scrapes the AI response from the DOM.
10
- - Handles the "Continue" button for longer responses.
11
  """
12
 
13
  import asyncio
@@ -16,25 +16,25 @@ import re
16
  from providers.base import BaseProvider
17
  from config import PROVIDER_MODELS
18
 
19
- logger = logging.getLogger("kai_api.copilot")
20
 
21
  _playwright = None
22
  _browser = None
23
  _lock = asyncio.Lock()
24
 
25
 
26
- class CopilotProvider(BaseProvider):
27
- """AI provider using Microsoft Copilot via Persistent Playwright Browser."""
28
 
29
- RESPONSE_TIMEOUT = 60
30
- HYDRATION_DELAY = 3.0
31
 
32
  @property
33
  def name(self) -> str:
34
- return "copilot"
35
 
36
  def get_available_models(self) -> list[str]:
37
- return PROVIDER_MODELS.get("copilot", ["copilot-gpt-4"])
38
 
39
  @staticmethod
40
  def is_available() -> bool:
@@ -53,7 +53,7 @@ class CopilotProvider(BaseProvider):
53
  if _browser and _browser.is_connected():
54
  return
55
 
56
- logger.info("πŸš€ Copilot: Launching Persistent Browser...")
57
  from playwright.async_api import async_playwright
58
 
59
  _playwright = await async_playwright().start()
@@ -64,11 +64,9 @@ class CopilotProvider(BaseProvider):
64
  "--no-sandbox",
65
  "--disable-dev-shm-usage",
66
  "--disable-gpu",
67
- "--disable-web-security",
68
- "--disable-features=IsolateOrigins,site-per-process",
69
  ],
70
  )
71
- logger.info("βœ… Copilot: Browser is Ready.")
72
 
73
  async def send_message(
74
  self,
@@ -77,80 +75,81 @@ class CopilotProvider(BaseProvider):
77
  system_prompt: str | None = None,
78
  **kwargs,
79
  ) -> dict:
80
- """Send a message via Copilot browser automation."""
81
  if not self.is_available():
82
  raise RuntimeError("Playwright not installed.")
83
 
84
  await self._ensure_browser()
85
- selected_model = model or "copilot-gpt-4"
86
 
87
  # Create Ephemeral Context
88
  context = await _browser.new_context(
89
  viewport={"width": 1920, "height": 1080},
90
- user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
91
  "AppleWebKit/537.36 (KHTML, like Gecko) "
92
- "Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
93
  locale="en-US",
94
- timezone_id="America/New_York",
95
  )
96
 
97
  # Hide webdriver flag
98
  await context.add_init_script("""
99
  Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
100
- Object.defineProperty(navigator, 'plugins', {get: () => [1, 2, 3, 4, 5]});
101
- window.chrome = { runtime: {} };
102
  """)
103
 
104
  page = await context.new_page()
105
 
106
  try:
107
- logger.info(f"Copilot request: {selected_model}")
108
-
109
- # Navigate to Copilot
110
- await page.goto("https://copilot.microsoft.com/", timeout=60000)
111
-
112
- # Wait for the chat input to be ready
113
- # Copilot uses contenteditable divs
114
- input_selectors = [
115
- '[data-testid="chat-input"]',
116
- 'div[contenteditable="true"]',
117
- '[role="textbox"]',
118
- 'textarea',
119
- '.input-area div[contenteditable]',
120
- ]
121
-
122
- input_selector = None
123
- for sel in input_selectors:
124
- try:
125
- await page.wait_for_selector(sel, timeout=10000)
126
- input_selector = sel
127
- logger.info(f"βœ… Copilot: Found input selector: {sel}")
128
- break
129
- except:
130
- continue
131
-
132
- if not input_selector:
133
- raise RuntimeError("Could not find Copilot chat input")
134
 
135
  await asyncio.sleep(self.HYDRATION_DELAY)
136
 
 
 
 
 
 
 
 
 
 
 
137
  # Type the message
138
  full_prompt = prompt
139
  if system_prompt:
140
  full_prompt = f"[System: {system_prompt}]\n\n{prompt}"
141
 
142
- await page.click(input_selector)
143
- await page.keyboard.type(full_prompt, delay=10)
144
  await asyncio.sleep(0.5)
 
 
145
  await page.keyboard.press("Enter")
146
-
147
- logger.info("Copilot: Message sent...")
148
 
149
  # Wait for response
150
  response_text = await self._wait_for_response(page)
151
 
152
  if not response_text:
153
- raise ValueError("Empty response from Copilot")
154
 
155
  return {
156
  "response": response_text,
@@ -158,43 +157,70 @@ class CopilotProvider(BaseProvider):
158
  }
159
 
160
  except Exception as e:
161
- logger.error(f"Copilot Error: {e}")
162
  raise
163
  finally:
164
  await context.close()
165
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  async def _wait_for_response(self, page) -> str:
167
  """Wait for and extract the AI response from the DOM."""
168
  last_text = ""
169
  stable_count = 0
170
- required_stable = 4
171
 
172
  for i in range(self.RESPONSE_TIMEOUT * 2):
173
  await asyncio.sleep(0.5)
174
 
175
- # Check for "Continue" button and click it
176
- try:
177
- continue_btn = await page.query_selector(
178
- 'button:has-text("Continue"), button:has-text("Continue anyway")'
179
- )
180
- if continue_btn and await continue_btn.is_visible():
181
- logger.info("Copilot: Clicking 'Continue' button...")
182
- await continue_btn.click()
183
- await asyncio.sleep(1)
184
- except:
185
- pass
186
 
187
  # Extract response text
188
  current_text = await page.evaluate("""
189
  () => {
 
190
  const selectors = [
191
- '[data-testid="assistant-message"]',
192
- '.message-content',
193
- '[data-message-author-role="assistant"]',
194
- '.ac-textBlock',
195
- '[class*="response"]',
196
- '[class*="message"] div',
197
  '.markdown-body',
 
 
198
  ];
199
 
200
  for (const sel of selectors) {
@@ -224,20 +250,19 @@ class CopilotProvider(BaseProvider):
224
  last_text = clean
225
 
226
  if i % 10 == 9:
227
- logger.info(f"Copilot: Stream... {len(last_text)} chars")
228
 
229
  if last_text:
230
- logger.warning("Copilot: Timeout, returning partial.")
231
  return last_text
232
 
233
- raise TimeoutError("Copilot no response")
234
 
235
  def _clean_response(self, text: str) -> str:
236
- """Clean up Copilot response text."""
237
  clean = text.strip()
238
-
239
- # Remove common UI artifacts
240
- clean = re.sub(r"^(Copilot\s*|Microsoft Copilot\s*)", "", clean, flags=re.IGNORECASE)
241
  clean = re.sub(r"\n+\s*\n+", "\n\n", clean)
242
-
243
  return clean.strip()
 
1
  """
2
+ HuggingChat Provider (Browser-Based)
3
+ -------------------------------------
4
+ Uses Playwright Chromium to interact with https://huggingface.co/chat as a real browser.
5
 
6
  Strategy:
7
+ - Handles the welcome modal by clicking "Start chatting"
8
+ - Uses EPHEMERAL contexts per request
9
+ - Supports model selection via the model dropdown
10
+ - Scrapes AI response from the DOM
11
  """
12
 
13
  import asyncio
 
16
  from providers.base import BaseProvider
17
  from config import PROVIDER_MODELS
18
 
19
+ logger = logging.getLogger("kai_api.huggingchat")
20
 
21
  _playwright = None
22
  _browser = None
23
  _lock = asyncio.Lock()
24
 
25
 
26
+ class HuggingChatProvider(BaseProvider):
27
+ """AI provider using HuggingChat via Persistent Playwright Browser."""
28
 
29
+ RESPONSE_TIMEOUT = 90
30
+ HYDRATION_DELAY = 2.0
31
 
32
  @property
33
  def name(self) -> str:
34
+ return "huggingchat"
35
 
36
  def get_available_models(self) -> list[str]:
37
+ return PROVIDER_MODELS.get("huggingchat", ["omni", "meta-llama/Llama-3.3-70B-Instruct"])
38
 
39
  @staticmethod
40
  def is_available() -> bool:
 
53
  if _browser and _browser.is_connected():
54
  return
55
 
56
+ logger.info("πŸš€ HuggingChat: Launching Persistent Browser...")
57
  from playwright.async_api import async_playwright
58
 
59
  _playwright = await async_playwright().start()
 
64
  "--no-sandbox",
65
  "--disable-dev-shm-usage",
66
  "--disable-gpu",
 
 
67
  ],
68
  )
69
+ logger.info("βœ… HuggingChat: Browser is Ready.")
70
 
71
  async def send_message(
72
  self,
 
75
  system_prompt: str | None = None,
76
  **kwargs,
77
  ) -> dict:
78
+ """Send a message via HuggingChat browser automation."""
79
  if not self.is_available():
80
  raise RuntimeError("Playwright not installed.")
81
 
82
  await self._ensure_browser()
83
+ selected_model = model or "omni"
84
 
85
  # Create Ephemeral Context
86
  context = await _browser.new_context(
87
  viewport={"width": 1920, "height": 1080},
88
+ user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
89
  "AppleWebKit/537.36 (KHTML, like Gecko) "
90
+ "Chrome/120.0.0.0 Safari/537.36",
91
  locale="en-US",
 
92
  )
93
 
94
  # Hide webdriver flag
95
  await context.add_init_script("""
96
  Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
 
 
97
  """)
98
 
99
  page = await context.new_page()
100
 
101
  try:
102
+ logger.info(f"HuggingChat request: {selected_model}")
103
+
104
+ # Navigate to HuggingChat
105
+ await page.goto("https://huggingface.co/chat", timeout=60000)
106
+
107
+ # Handle welcome modal if present
108
+ try:
109
+ start_btn = await page.wait_for_selector(
110
+ 'button:has-text("Start chatting")',
111
+ timeout=5000
112
+ )
113
+ if start_btn:
114
+ logger.info("HuggingChat: Clicking 'Start chatting' button...")
115
+ await start_btn.click()
116
+ await asyncio.sleep(1)
117
+ except:
118
+ logger.info("HuggingChat: No welcome modal found, continuing...")
119
+
120
+ # If specific model requested (not omni), try to select it
121
+ if selected_model and selected_model != "omni":
122
+ await self._select_model(page, selected_model)
 
 
 
 
 
 
123
 
124
  await asyncio.sleep(self.HYDRATION_DELAY)
125
 
126
+ # Find and use the chat input
127
+ input_selector = 'textarea[placeholder*="Ask anything"]'
128
+
129
+ try:
130
+ await page.wait_for_selector(input_selector, timeout=10000)
131
+ except:
132
+ # Fallback selectors
133
+ input_selector = 'textarea'
134
+ await page.wait_for_selector(input_selector, timeout=5000)
135
+
136
  # Type the message
137
  full_prompt = prompt
138
  if system_prompt:
139
  full_prompt = f"[System: {system_prompt}]\n\n{prompt}"
140
 
141
+ await page.fill(input_selector, full_prompt)
 
142
  await asyncio.sleep(0.5)
143
+
144
+ # Press Enter to send
145
  await page.keyboard.press("Enter")
146
+ logger.info("HuggingChat: Message sent...")
 
147
 
148
  # Wait for response
149
  response_text = await self._wait_for_response(page)
150
 
151
  if not response_text:
152
+ raise ValueError("Empty response from HuggingChat")
153
 
154
  return {
155
  "response": response_text,
 
157
  }
158
 
159
  except Exception as e:
160
+ logger.error(f"HuggingChat Error: {e}")
161
  raise
162
  finally:
163
  await context.close()
164
 
165
+ async def _select_model(self, page, model: str):
166
+ """Try to select a specific model from the dropdown."""
167
+ try:
168
+ # Click the model selector button
169
+ model_btn = await page.wait_for_selector(
170
+ 'button:has-text("Models")',
171
+ timeout=5000
172
+ )
173
+ if model_btn:
174
+ await model_btn.click()
175
+ await asyncio.sleep(1)
176
+
177
+ # Try to find and click the specific model
178
+ model_option = await page.query_selector(
179
+ f'text={model}'
180
+ )
181
+ if model_option:
182
+ await model_option.click()
183
+ logger.info(f"HuggingChat: Selected model {model}")
184
+ await asyncio.sleep(1)
185
+ else:
186
+ # Close dropdown if model not found
187
+ await page.keyboard.press("Escape")
188
+ logger.warning(f"HuggingChat: Model {model} not found, using default")
189
+ except Exception as e:
190
+ logger.warning(f"HuggingChat: Could not select model: {e}")
191
+
192
  async def _wait_for_response(self, page) -> str:
193
  """Wait for and extract the AI response from the DOM."""
194
  last_text = ""
195
  stable_count = 0
196
+ required_stable = 3
197
 
198
  for i in range(self.RESPONSE_TIMEOUT * 2):
199
  await asyncio.sleep(0.5)
200
 
201
+ # Check for loading/spinner and skip
202
+ is_loading = await page.evaluate("""
203
+ () => {
204
+ const spinners = document.querySelectorAll('[class*="loading"], [class*="spinner"], .animate-pulse');
205
+ return spinners.length > 0;
206
+ }
207
+ """)
208
+
209
+ if is_loading:
210
+ continue
 
211
 
212
  # Extract response text
213
  current_text = await page.evaluate("""
214
  () => {
215
+ // Look for the last assistant message
216
  const selectors = [
217
+ '[data-message-role="assistant"]',
218
+ '.assistant-message',
219
+ '[class*="prose"]',
220
+ 'article',
 
 
221
  '.markdown-body',
222
+ '[class*="message-content"]',
223
+ '.chat-message:last-child .content',
224
  ];
225
 
226
  for (const sel of selectors) {
 
250
  last_text = clean
251
 
252
  if i % 10 == 9:
253
+ logger.info(f"HuggingChat: Stream... {len(last_text)} chars")
254
 
255
  if last_text:
256
+ logger.warning("HuggingChat: Timeout, returning partial.")
257
  return last_text
258
 
259
+ raise TimeoutError("HuggingChat no response")
260
 
261
  def _clean_response(self, text: str) -> str:
262
+ """Clean up HuggingChat response text."""
263
  clean = text.strip()
264
+
265
+ # Remove common artifacts
 
266
  clean = re.sub(r"\n+\s*\n+", "\n\n", clean)
267
+
268
  return clean.strip()
test_copilot_browser.py DELETED
@@ -1,61 +0,0 @@
1
- """
2
- Test script for Microsoft Copilot Provider
3
- Run this to verify the Copilot browser automation works.
4
- """
5
-
6
- import asyncio
7
- import sys
8
- import os
9
-
10
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
11
-
12
- from providers.copilot_provider import CopilotProvider
13
-
14
-
15
- async def test_copilot():
16
- """Test the Copilot provider."""
17
- print("πŸ§ͺ Testing Microsoft Copilot Provider...")
18
- print("-" * 50)
19
-
20
- provider = CopilotProvider()
21
-
22
- # Check if Playwright is available
23
- if not provider.is_available():
24
- print("❌ Playwright not installed. Run: pip install playwright && playwright install chromium")
25
- return False
26
-
27
- print("βœ… Playwright is available")
28
- print(f"πŸ“‹ Available models: {provider.get_available_models()}")
29
- print()
30
-
31
- # Test prompts
32
- test_prompts = [
33
- "Say 'Hello from Copilot test' and nothing else.",
34
- "What is 2+2? Answer with just the number.",
35
- ]
36
-
37
- for i, prompt in enumerate(test_prompts, 1):
38
- print(f"\nπŸ“ Test {i}: {prompt[:50]}...")
39
- print("-" * 50)
40
-
41
- try:
42
- result = await provider.send_message(prompt)
43
- print(f"βœ… SUCCESS!")
44
- print(f"πŸ€– Model: {result['model']}")
45
- print(f"πŸ’¬ Response: {result['response'][:200]}...")
46
- print()
47
- except Exception as e:
48
- print(f"❌ FAILED: {e}")
49
- import traceback
50
- traceback.print_exc()
51
- return False
52
-
53
- print("\n" + "=" * 50)
54
- print("πŸŽ‰ All Copilot tests passed!")
55
- print("=" * 50)
56
- return True
57
-
58
-
59
- if __name__ == "__main__":
60
- success = asyncio.run(test_copilot())
61
- sys.exit(0 if success else 1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
test_huggingchat_browser.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Test script for HuggingChat Provider
3
+ Run this to verify the HuggingChat browser automation works.
4
+ """
5
+
6
+ import asyncio
7
+ import sys
8
+ import os
9
+
10
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
11
+
12
+ from providers.huggingchat_provider import HuggingChatProvider
13
+
14
+
15
+ async def test_huggingchat():
16
+ """Test the HuggingChat provider."""
17
+ print("πŸ§ͺ Testing HuggingChat Provider...")
18
+ print("-" * 50)
19
+
20
+ provider = HuggingChatProvider()
21
+
22
+ # Check if Playwright is available
23
+ if not provider.is_available():
24
+ print("❌ Playwright not installed. Run: pip install playwright && playwright install chromium")
25
+ return False
26
+
27
+ print("βœ… Playwright is available")
28
+ print(f"πŸ“‹ Available models: {provider.get_available_models()}")
29
+ print()
30
+
31
+ # Test with default Omni router
32
+ print("\nπŸ“ Test 1: Using Omni router (default)")
33
+ print("-" * 50)
34
+
35
+ try:
36
+ result = await provider.send_message("Say 'Hello from HuggingChat test' and nothing else.")
37
+ print(f"βœ… SUCCESS!")
38
+ print(f"πŸ€– Model: {result['model']}")
39
+ print(f"πŸ’¬ Response: {result['response'][:200]}...")
40
+ print()
41
+ except Exception as e:
42
+ print(f"❌ FAILED: {e}")
43
+ import traceback
44
+ traceback.print_exc()
45
+ return False
46
+
47
+ # Test with specific model
48
+ print("\nπŸ“ Test 2: Using Llama 3.3 70B")
49
+ print("-" * 50)
50
+
51
+ try:
52
+ result = await provider.send_message(
53
+ "What is 2+2? Answer with just the number.",
54
+ model="meta-llama/Llama-3.3-70B-Instruct"
55
+ )
56
+ print(f"βœ… SUCCESS!")
57
+ print(f"πŸ€– Model: {result['model']}")
58
+ print(f"πŸ’¬ Response: {result['response'][:200]}...")
59
+ print()
60
+ except Exception as e:
61
+ print(f"❌ FAILED: {e}")
62
+ import traceback
63
+ traceback.print_exc()
64
+ # Don't fail completely if specific model doesn't work
65
+
66
+ print("\n" + "=" * 50)
67
+ print("πŸŽ‰ HuggingChat tests completed!")
68
+ print("=" * 50)
69
+ return True
70
+
71
+
72
+ if __name__ == "__main__":
73
+ success = asyncio.run(test_huggingchat())
74
+ sys.exit(0 if success else 1)