Skip to content

Commit 2142584

Browse files
authored
Merge pull request #261 from FrancescoCaracciolo/master
2 parents 4b93c8b + 75ee85a commit 2142584

File tree

10 files changed

+73
-52
lines changed

10 files changed

+73
-52
lines changed

modules/python3-gtts.json

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,38 @@
77
"sources": [
88
{
99
"type": "file",
10-
"url": "https://files.pythonhosted.org/packages/1c/d5/c84e1a17bf61d4df64ca866a1c9a913874b4e9bdc131ec689a0ad013fb36/certifi-2024.7.4-py3-none-any.whl",
11-
"sha256": "c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"
10+
"url": "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl",
11+
"sha256": "2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057"
1212
},
1313
{
1414
"type": "file",
15-
"url": "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz",
16-
"sha256": "f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"
15+
"url": "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz",
16+
"sha256": "5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"
1717
},
1818
{
1919
"type": "file",
20-
"url": "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl",
21-
"sha256": "ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"
20+
"url": "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl",
21+
"sha256": "63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"
2222
},
2323
{
2424
"type": "file",
25-
"url": "https://files.pythonhosted.org/packages/59/a8/e3434904445eacf03b857ac001755d8ffac49b4f3339d63592b4eda009dc/gTTS-2.5.1-py3-none-any.whl",
26-
"sha256": "273ec8a5077b25e60ca5a266ed254b54d1f14032b0af3ba00092d14966148664"
25+
"url": "https://files.pythonhosted.org/packages/e3/6c/8b8b1fdcaee7e268536f1bb00183a5894627726b54a9ddc6fc9909888447/gTTS-2.5.4-py3-none-any.whl",
26+
"sha256": "5dd579377f9f5546893bc26315ab1f846933dc27a054764b168f141065ca8436"
2727
},
2828
{
2929
"type": "file",
30-
"url": "https://files.pythonhosted.org/packages/e5/3e/741d8c82801c347547f8a2a06aa57dbb1992be9e948df2ea0eda2c8b79e8/idna-3.7-py3-none-any.whl",
31-
"sha256": "82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"
30+
"url": "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl",
31+
"sha256": "946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"
3232
},
3333
{
3434
"type": "file",
35-
"url": "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl",
36-
"sha256": "70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"
35+
"url": "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl",
36+
"sha256": "27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"
3737
},
3838
{
3939
"type": "file",
40-
"url": "https://files.pythonhosted.org/packages/ca/1c/89ffc63a9605b583d5df2be791a27bc1a42b7c32bab68d3c8f2f73a98cd4/urllib3-2.2.2-py3-none-any.whl",
41-
"sha256": "a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"
40+
"url": "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl",
41+
"sha256": "e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"
4242
}
4343
]
4444
}

src/controller.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ def load_extensions(self):
242242

243243
def load_integrations(self):
244244
"""Load integrations"""
245-
self.integrationsloader = ExtensionLoader(self.extension_path, pip_path=self.pip_path, settings=self.settings)
245+
self.integrationsloader = ExtensionLoader(self.extension_path, pip_path=self.pip_path, settings=self.settings, extension_cache=self.extensions_cache)
246246
self.integrationsloader.load_integrations(AVAILABLE_INTEGRATIONS)
247247
self.set_ui_controller(self.ui_controller)
248248

src/handlers/llm/gemini_handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class GeminiHandler(LLMHandler):
1919
Official Google Gemini APIs, they support history and system prompts
2020
"""
2121

22-
default_models = [("Gemini 2.0 Flash","gemini-2.0-flash"), ("Gemini 2.0 Flash Lite", "gemini-2.0-flash-lite")]
22+
default_models = [("Gemini 2.0 Flash","gemini-2.0-flash"), ("Gemini 2.5 Flash", "gemini-2.5-flash")]
2323

2424
def __init__(self, settings, path):
2525
super().__init__(settings, path)

src/handlers/stt/whispercpp_handler.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def get_extra_settings(self) -> list:
4444
"default": "tiny",
4545
"website": "https://github.com/openai/whisper/blob/main/model-card.md#model-details",
4646
},
47-
ExtraSettings.EntrySetting("language", _("Language"), _("Language of the recognition."), "auto"),
47+
ExtraSettings.EntrySetting("language", _("Language"), _("Language of the recognition. For example en, it..."), "auto"),
4848
ExtraSettings.NestedSetting("model_library", _("Model Library"), _("Manage Whisper models"), self.get_model_library()),
4949
ExtraSettings.NestedSetting("advanced_settings", _("Advanced Settings"), _("More advanced settings"), [
5050
ExtraSettings.ScaleSetting("temperature", _("Temperature"), _("Temperature to use"), 0.0, 0.0, 1.0, 2),
@@ -75,6 +75,7 @@ def get_percentage(self, model: str):
7575
def install_model(self, model_name):
7676
if self.is_model_installed(model_name):
7777
os.remove(os.path.join(self.path, "whisper", "whisper.cpp/models/ggml-" + model_name + ".bin"))
78+
self.settings_update()
7879
else:
7980
path = os.path.join(self.path, "whisper/whisper.cpp/models/download-ggml-model.sh")
8081
f = subprocess.check_output(get_spawn_command() + ["bash", "-c", f"sh {path} " + model_name])

src/handlers/tts/kokoro_handler.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
class KokoroTTSHandler(TTSHandler):
99
key = "kokoro"
1010
def install(self):
11-
extra_deps = "pyopenjtalk fugashi jaconv mojimoji mecab-python3 unidic-lite"
12-
install_module("kokoro==0.9.4 soundfile espeakng-loader " + extra_deps, self.pip_path)
11+
extra_deps = "fugashi jaconv mojimoji mecab-python3 unidic-lite"
12+
index_url = " --extra-index-url https://download.pytorch.org/whl/cpu --trusted-host download.pytorch.org"
13+
install_module("kokoro==0.9.4 soundfile espeakng-loader " + extra_deps + index_url, self.pip_path, update=False)
1314
if not self.is_installed():
1415
self.throw("Kokoro installation failed", ErrorSeverity.ERROR)
1516

src/integrations/websearch.py

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from ..extensions import NewelleExtension
33
from ..ui.widgets import WebSearchWidget
44
from gi.repository import Gtk, GLib
5+
import os
6+
import json
57

68

79
class WebsearchIntegration(NewelleExtension):
@@ -12,6 +14,7 @@ def __init__(self, pip_path, extension_path, settings):
1214
super().__init__(pip_path, extension_path, settings)
1315
self.widgets = {}
1416
self.load_widet_cache()
17+
self.msgid = 0
1518

1619
def get_replace_codeblocks_langs(self) -> list:
1720
return ["search"]
@@ -20,53 +23,54 @@ def provides_both_widget_and_answer(self, codeblock: str, lang: str) -> bool:
2023
return True
2124

2225
def get_answer(self, codeblock: str, lang: str) -> str | None:
26+
msgid = self.msgid
2327
if self.websearch.supports_streaming_query():
24-
text, sources = self.websearch.query_streaming(codeblock, lambda title, link, favicon, codeblock=codeblock: self.add_website(codeblock, title, link, favicon))
28+
text, sources = self.websearch.query_streaming(codeblock, lambda title, link, favicon, codeblock=codeblock, msgid=msgid: self.add_website(codeblock, title, link, favicon, msgid))
2529
else:
2630
text, sources = self.websearch.query(codeblock)
2731
for source in sources:
28-
self.add_website(codeblock, source, source, "")
29-
self.finish(codeblock, text)
32+
self.add_website(codeblock, source, source, "", msgid)
33+
self.finish(codeblock, text, sources, msgid)
3034
return "Here is the web search result for query '"+ codeblock + "':\n" + text
3135

32-
def finish(self, codeblock: str, result: str):
33-
self.widget_cache[codeblock]["result"] = result
36+
def finish(self, codeblock: str, result: str, sources, msgid):
37+
self.widget_cache[msgid]["result"] = result
3438
self.save_widget_cache()
3539
search_widget = self.widgets.get(codeblock, None)
3640
if search_widget is not None:
3741
GLib.idle_add(search_widget.finish, result)
3842

39-
def add_website(self, term, title, link, favicon):
43+
def add_website(self, term, title, link, favicon, msgid):
4044
search_widget = self.widgets.get(term, None)
4145
if search_widget is not None:
4246
GLib.idle_add(search_widget.add_website, title, link, favicon)
43-
self.widget_cache[term]["websites"].append((title, link, favicon))
47+
self.widget_cache[msgid]["websites"].append((title, link, favicon))
4448

45-
def restore_gtk_widget(self, codeblock: str, lang: str) -> Gtk.Widget | None:
46-
search_widget = self.widgets.get(codeblock, None)
47-
if search_widget is not None and False:
48-
return search_widget
49+
def load_search_widget(self, query, sources, result):
50+
widget = WebSearchWidget(query)
51+
for title, link, favicon in tuple(sources):
52+
widget.add_website(title, link, favicon)
53+
widget.finish(result)
54+
widget.connect("website-clicked", lambda widget,link : self.ui_controller.open_link(link, False, not self.settings.get_boolean("external-browser")))
55+
return widget
56+
57+
def restore_gtk_widget(self, codeblock: str, lang: str, msgid) -> Gtk.Widget | None:
58+
cache = self.widget_cache.get(msgid, None)
59+
if cache is not None:
60+
return self.load_search_widget(codeblock, cache["websites"], cache["result"])
4961
else:
50-
cache = self.widget_cache.get(codeblock, None)
51-
if cache is not None:
52-
search_widget = WebSearchWidget(codeblock)
53-
search_widget.connect("website-clicked", lambda widget,link : self.ui_controller.open_link(link, False, not self.settings.get_boolean("external-browser")))
54-
if "websites" in cache:
55-
for title, link, favicon in cache["websites"]:
56-
search_widget.add_website(title, link, favicon)
57-
search_widget.finish(cache["result"])
58-
else:
59-
search_widget = WebSearchWidget(codeblock)
60-
search_widget.finish("No result found")
62+
search_widget = WebSearchWidget(codeblock)
63+
search_widget.finish("No result found")
6164
return search_widget
6265

63-
def get_gtk_widget(self, codeblock: str, lang: str) -> Gtk.Widget | None:
66+
def get_gtk_widget(self, codeblock: str, lang: str, msgid) -> Gtk.Widget | None:
67+
self.msgid = msgid
6468
search_widget = WebSearchWidget(search_term=codeblock)
6569
search_widget.connect("website-clicked", lambda widget,link : self.ui_controller.open_link(link, False, not self.settings.get_boolean("external-browser")))
6670
self.widgets[codeblock] = search_widget
67-
self.widget_cache[codeblock] = {}
68-
self.widget_cache[codeblock]["websites"] = []
69-
self.widget_cache[codeblock]["result"] = "No result found"
71+
self.widget_cache[msgid] = {}
72+
self.widget_cache[msgid]["websites"] = []
73+
self.widget_cache[msgid]["result"] = "No result found"
7074
return search_widget
7175

7276
def postprocess_history(self, history: list, bot_response: str) -> tuple[list, str]:
@@ -78,8 +82,16 @@ def postprocess_history(self, history: list, bot_response: str) -> tuple[list, s
7882
return history, bot_response
7983

8084
def save_widget_cache(self):
81-
self.set_setting("widget_cache", self.widget_cache)
85+
with open(os.path.join(self.extension_path, "websearch_cache.json"), "w+") as f:
86+
json.dump(self.widget_cache, f)
8287

8388
def load_widet_cache(self):
84-
self.widget_cache = self.get_setting("widget_cache", False, {})
89+
if os.path.exists(os.path.join(self.extension_path, "websearch_cache.json")):
90+
with open(os.path.join(self.extension_path, "websearch_cache.json")) as f:
91+
self.widget_cache = json.load(f)
92+
for key in self.widget_cache.copy():
93+
self.widget_cache[int(key)] = self.widget_cache[key]
94+
del self.widget_cache[key]
95+
else:
96+
self.widget_cache = {}
8597

src/ui/stdout_monitor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def show_window(self):
2727
return
2828

2929
# Create the window
30-
self.window = Gtk.Window()
30+
self.window = Gtk.Window(decorated=False)
3131
self.window.set_title(_("Program Output Monitor"))
3232
self.window.set_default_size(800, 600)
3333
self.window.set_transient_for(self.parent_window)

src/ui/widgets/thinking.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ class ThinkingWidget(Gtk.Box):
1414

1515
def __init__(self, **kwargs):
1616
super().__init__(orientation=Gtk.Orientation.VERTICAL, **kwargs)
17-
self.add_css_class("code")
1817
self.add_css_class("card")
1918
self.set_margin_top(10)
2019
self.set_margin_end(10)

src/utility/pip.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def runtime_find_module(full_module_name):
4848
except Exception as _:
4949
return None
5050

51-
def install_module(module, path):
51+
def install_module(module, path, update=True):
5252
# Manage pip path locking
5353
global PIP_INSTALLED
5454
LOCK_SEMAPHORE.acquire()
@@ -58,17 +58,25 @@ def install_module(module, path):
5858
LOCKS[path] = lock
5959
LOCK_SEMAPHORE.release()
6060
lock.acquire()
61+
# Set temp path
62+
origTemp = os.environ.get("TMPDIR")
63+
os.environ["TMPDIR"] = os.path.join(os.getcwd(), "tmp")
6164
try:
6265
if find_module("pip") is None and not PIP_INSTALLED:
6366
print("Downloading pip...")
6467
subprocess.check_output(["bash", "-c", "cd " + os.path.dirname(path) + " && wget https://bootstrap.pypa.io/get-pip.py && python get-pip.py"])
6568
subprocess.check_output(["bash", "-c", "cd " + os.path.dirname(path) + " && rm get-pip.py || true"])
6669
PIP_INSTALLED = True
67-
r = subprocess.run([sys.executable, "-m", "pip", "install","--target", path, "--upgrade"] + module.split(" ") , capture_output=False)
70+
command = [sys.executable, "-m", "pip", "install","--target", path]
71+
if update:
72+
command.append("--upgrade")
73+
r = subprocess.run(command + module.split(" ") , capture_output=False)
6874
print(module + " installed")
6975
except Exception as e:
7076
PIP_INSTALLED = False
7177
print("Error installing " + module + " " + str(e))
7278
r = None
79+
if origTemp:
80+
os.environ["TMPDIR"] = origTemp
7381
lock.release()
7482
return r

src/utility/strings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def markwon_to_pango(markdown_text):
6565
processed_text = re.sub(r'~(.*?)~', r'<span strikethrough="true">\1</span>', processed_text)
6666

6767
# Convert exponents and subscripts (handle digits or parenthesized text)
68-
processed_text = re.sub(r'_(\d+|\([^)]+\))', lambda m: f'<sub>{m.group(1).strip("()")}</sub>', processed_text)
68+
processed_text = re.sub(r'_(\d+|\([^)]+\))\b', lambda m: f'<sub>{m.group(1).strip("()")}</sub>', processed_text)
6969
processed_text = re.sub(r'\^(\d+|\([^)]+\))', lambda m: f'<sup>{m.group(1).strip("()")}</sup>', processed_text)
7070

7171
# Convert links

0 commit comments

Comments
 (0)