This commit is contained in:
2026-04-06 09:02:13 +02:00
parent 6c6533275b
commit 75a1931f6d
33 changed files with 52 additions and 1049 deletions
-38
View File
@@ -1,38 +0,0 @@
clone:
git:
image: bash
commands:
- cd /home/0xmrtt
- if [ -d Bavarder ]; then
- cd Bavarder
- git pull
- else
- git clone https://git.exozy.me/Bavarder/Bavarder.git
- fi
pipeline:
build:
image: fish
secrets: [ access_token, codeberg_token ]
commands:
- nix-env -iA nixpkgs.flatpak-builder
- fish_add_path ~/.nix-profile/bin
- cd /home/0xmrtt/Bavarder
- flatpak-builder --repo=repo --force-clean build build-aux/flatpak/io.github.Bavarder.Bavarder.json
- flatpak build-bundle repo bavarder.flatpak io.github.Bavarder.Bavarder
- ls -la
- curl --user 0xmrtt:$ACCESS_TOKEN --upload-file bavarder.flatpak "https://git.exozy.me/api/packages/$CI_REPO_OWNER/generic/$CI_REPO_NAME/$CI_BUILD_NUMBER/bavarder.flatpak"
- curl --user 0xmrtt:$CODEBERG_TOKEN --upload-file bavarder.flatpak "https://codeberg.org/api/packages/$CI_REPO_OWNER/generic/$CI_REPO_NAME/$CI_BUILD_NUMBER/bavarder.flatpak"
translate:
image: fish
commands:
- nix-env -iA nixpkgs.gettext
- fish_add_path ~/.nix-profile/bin
- cd /home/0xmrtt/Bavarder
- bash po/update-pot.sh
- git add po/Bavarder.pot
- git commit -m "Update"
- git push origin main
when:
event: [manual]
-76
View File
@@ -1,76 +0,0 @@
diff --git a/src/main.py b/src/main.py
index 79f5401..f82cd0f 100644
--- a/src/main.py
+++ b/src/main.py
@@ -35,7 +35,6 @@ from .constants import app_id
from .providers import PROVIDERS
import json
-from gpt4all import GPT4All
import os
user_config_dir = os.environ.get(
@@ -306,15 +305,7 @@ class BavarderApplication(Adw.Application):
def setup_chat(self):
if not self.models:
self.list_models()
-
- if not self.models:
- return False
- else:
- if self.model is None:
- if self.model_name not in self.models:
- self.download_model(self.model_name)
- self.model = GPT4All(self.model_name, model_path=model_path)
- return True
+ return bool(self.models)
def download_model(self, model=None):
if model:
diff --git a/src/views/preferences_window.py b/src/views/preferences_window.py
index e057aff..73e4ea3 100644
--- a/src/views/preferences_window.py
+++ b/src/views/preferences_window.py
@@ -5,8 +5,6 @@ from bavarder.providers.provider_item import Provider
from bavarder.widgets.model_item import Model
from bavarder.widgets.download_row import DownloadRow
-from gpt4all import GPT4All
-
@Gtk.Template(resource_path=f"{rootdir}/ui/preferences_window.ui")
class PreferencesWindow(Adw.PreferencesWindow):
__gtype_name__ = "Preferences"
@@ -34,7 +32,6 @@ class PreferencesWindow(Adw.PreferencesWindow):
def setup(self):
self.setup_signals()
self.load_providers()
- self.load_models()
self.bot_name.set_text(self.app.bot_name)
self.user_name.set_text(self.app.user_name)
@@ -47,25 +44,6 @@ class PreferencesWindow(Adw.PreferencesWindow):
p = Provider(self.app, self, provider)
self.provider_group.add(p)
- def load_models(self):
- self.general_page.remove(self.model_group)
- self.model_group = Adw.PreferencesGroup()
- self.model_group.set_title(_("Models"))
-
- for model in self.app.models:
- p = Model(self.app, self, model)
- self.model_group.add(p)
- else:
- self.no_models_available = Adw.ExpanderRow()
- self.no_models_available.set_title(_("List of available models"))
-
- for model in GPT4All.list_models():
- self.no_models_available.add_row(DownloadRow(self.app, self, model))
-
- self.model_group.add(self.no_models_available)
-
- self.general_page.add(self.model_group)
-
@Gtk.Template.Callback()
def clear_all_chats_clicked(self, widget, *args):
dialog = Adw.MessageDialog(
@@ -54,21 +54,6 @@
]
},
"pypi-dependencies.json",
{
"name": "gpt4all",
"buildsystem": "simple",
"build-commands": [
"cd gpt4all-backend/ && mkdir build && cd build && cmake .. && cmake --build . --parallel",
"cd gpt4all-bindings/python && pip wheel --no-deps -w dist . && ls && ls dist && pip3 install \"dist/gpt4all-1.0.6-py3-none-any.whl\" --verbose --exists-action=i --no-index --prefix=${FLATPAK_DEST} --no-build-isolation"
],
"sources": [
{
"type": "git",
"url": "https://github.com/nomic-ai/gpt4all",
"branch": "main"
}
]
},
{
"name" : "bavarder",
"builddir" : true,
@@ -54,21 +54,6 @@
]
},
"pypi-dependencies.json",
{
"name": "gpt4all",
"buildsystem": "simple",
"build-commands": [
"cd gpt4all-backend/ && mkdir build && cd build && cmake .. && cmake --build . --parallel",
"cd gpt4all-bindings/python && pip wheel --no-deps -w dist . && ls && ls dist && pip3 install \"dist/gpt4all-1.0.8-py3-none-any.whl\" --verbose --exists-action=i --no-index --prefix=${FLATPAK_DEST} --no-build-isolation"
],
"sources": [
{
"type": "git",
"url": "https://github.com/nomic-ai/gpt4all",
"commit": "39acbc837816ea9c7673bfe5cf83aeaed95b2c5f"
}
]
},
{
"name" : "bavarder",
"builddir" : true,
+20 -19
View File
@@ -6,16 +6,22 @@
flake-utils.url = "github:numtide/flake-utils";
};
outputs = {
self,
nixpkgs,
flake-utils,
...
}: let
systems = ["aarch64-linux" "x86_64-linux"];
in
outputs =
{
self,
nixpkgs,
flake-utils,
...
}:
let
systems = [
"aarch64-linux"
"x86_64-linux"
];
in
flake-utils.lib.eachSystem systems (
system: let
system:
let
pkgs = nixpkgs.legacyPackages.${system};
bavarder = pkgs.python3Packages.buildPythonApplication rec {
@@ -25,12 +31,6 @@
src = ./.;
patches = [
# Removes gpt4all support. It would be lots of work to package it properly
# and we already have ollama with working ROCm + CUDA in nixpkgs.
./0001-remove-gpt4all-support.patch
];
nativeBuildInputs = with pkgs; [
appstream-glib
blueprint-compiler
@@ -43,7 +43,7 @@
wrapGAppsHook4
];
buildInputs = with pkgs; [
buildInputs = with pkgs; [
gtksourceview5
libadwaita
libportal
@@ -60,15 +60,16 @@
];
};
in {
in
{
formatter = pkgs.alejandra;
checks.bavarder = bavarder;
packages.default = bavarder;
devShells.default = pkgs.mkShell.override {stdenv = pkgs.python3Packages.stdenv;} {
devShells.default = pkgs.mkShell.override { stdenv = pkgs.python3Packages.stdenv; } {
inherit (bavarder) nativeBuildInputs buildInputs propagatedBuildInputs;
};
}
);
}
}
+1 -43
View File
@@ -20,50 +20,8 @@ parts:
meson-parameters:
- --prefix=/usr
override-prime: ''
gpt4all:
source: https://github.com/nomic-ai/gpt4all.git
source-commit: '39acbc837816ea9c7673bfe5cf83aeaed95b2c5f'
plugin: python
override-build: |
craftctl default
pip install --prefix=$CRAFT_PART_INSTALL/usr gpt4all
sed -i '1c#!/usr/bin/python3' -i $CRAFT_PART_INSTALL/usr/local/bin/*
python-packages:
- certifi==2023.7.22
- charset-normalizer==3.2.0
- idna==3.4
- requests==2.31.0
- urllib3==2.0.4
- tqdm==4.66.1
- Babel==2.12.1
- openai==0.27.8
- aiohttp==3.8.5
- aiosignal==1.3.1
- async-timeout==4.0.3
- attrs==23.1.0
- frozenlist==1.4.0
- multidict==6.0.4
- yarl==1.9.2
- lxml
organize:
bin: usr/bin
lib/python3.10/site-packages: usr/lib/python3/dist-packages
usr/local/bin: usr/bin
usr/local/lib/python3.10/dist-packages: usr/lib/python3/dist-packages
prime:
- -usr/bin/activate*
- -usr/bin/Activate.ps1
- -usr/bin/python*
- -usr/bin/pip*
- -include
- -lib
- -lib64
- -bin
- -share
- -pyvenv.cfg
bavarder:
after: [blueprint-compiler, gpt4all]
after: [blueprint-compiler]
plugin: meson
source: https://codeberg.org/Bavarder/Bavarder.git
source-tag: '1.0.0'
+12 -80
View File
@@ -34,7 +34,6 @@ from .constants import app_id
from .providers import PROVIDERS
import json
from gpt4all import GPT4All
import os
user_config_dir = os.environ.get(
@@ -54,7 +53,7 @@ model_path = os.path.join(user_cache_dir, "bavarder", "models")
class BavarderApplication(Adw.Application):
"""The main application singleton class."""
model_name = "ggml-model-gpt4all-falcon-q4_0.bin"
models = set()
model = None
action_running_in_background = False
@@ -121,17 +120,11 @@ class BavarderApplication(Adw.Application):
self.bot_name = self.settings.get_string("bot-name")
self.user_name = self.settings.get_string("user-name")
def on_set_provider_action(self, action, *args):
self.current_provider = args[0].get_string()
Gio.SimpleAction.set_state(self.lookup_action("set_provider"), args[0])
def on_set_model_action(self, action, *args):
previous = self.model_name
self.model_name = args[0].get_string()
if previous != self.model_name:
# reset model for loading the new one
self.model = None
Gio.SimpleAction.set_state(self.lookup_action("set_model"), args[0])
def save(self):
@@ -183,7 +176,7 @@ class BavarderApplication(Adw.Application):
def win(self):
"""The application's main window."""
return self.props.active_window
def new_window(self, window=None):
if window:
win = self.props.active_window
@@ -191,7 +184,7 @@ class BavarderApplication(Adw.Application):
win = BavarderWindow(application=self)
self.number_of_win += 1
win.connect("close-request", self.on_close)
self.providers = {}
@@ -256,83 +249,25 @@ class BavarderApplication(Adw.Application):
pass
def ask(self, prompt, chat):
if self.local_mode:
if not self.setup_chat(): # NO MODELS:
return _("Please download a model from Preferences by clicking on the Dot Menu at the top!")
l = list(self.providers.values())
for p in l:
if p.enabled and p.slug == self.current_provider:
response = self.providers[self.current_provider].ask(prompt, chat)
break
else:
for p in ["Hi", "Hello"]:
if p.lower() in prompt.lower():
return _("Hello, I am Bavarder, a Chit-Chat AI")
system_template = f"""You are a helpful and friendly AI assistant with the name {self.bot_name}. The name of the user are {self.user_name}. Respond very concisely."""
with self.model.chat_session(self.model_settings.get("system_template", system_template)):
self.model.current_chat_session = chat["content"].copy()
response = self.model.generate(
prompt=prompt,
top_k=int(self.model_settings.get("top_k", 40)),
top_p=float(self.model_settings.get("top_p", 0.5)),
temp=float(self.model_settings.get("temperature", 0.9)),
max_tokens=int(self.model_settings.get("max_tokens", 500)),
repeat_penalty=float(self.model_settings.get("repetition_penalty", 1.20)),
repeat_last_n=int(self.model_settings.get("repeat_last_n", 64)),
n_batch=int(self.model_settings.get("n_batch", 10)),
)
response = _("Please enable a provider from the Dot Menu")
else:
l = list(self.providers.values())
for p in l:
if p.enabled and p.slug == self.current_provider:
response = self.providers[self.current_provider].ask(prompt, chat)
break
else:
response = _("Please enable a provider from the Dot Menu")
return response
@property
def model_settings(self):
try:
return self.data["models"][self.model_name]
except KeyError:
try:
self.data["models"][self.model_name] = {}
except KeyError:
self.data["models"] = {}
self.data["models"][self.model_name] = {}
return self.data["models"][self.model_name]
def setup_chat(self):
if not self.models:
self.list_models()
if not self.models:
return False
else:
if self.model is None:
if self.model_name not in self.models:
self.download_model(self.model_name)
self.model = GPT4All(self.model_name, model_path=model_path)
return True
def download_model(self, model=None):
if model:
self.model_name = model
GPT4All.retrieve_model(self.model_name, model_path=model_path, verbose=True)
self.models.add(self.model_name)
def check_network(self):
return False
def list_models(self):
self.models = set()
for root, dirs, files in os.walk(model_path):
for model in files:
self.models.add(model)
def delete_model(self, model):
os.remove(os.path.join(model_path, model))
self.list_models()
def check_network(self):
return False
def clear_all_chats(self):
self.data["chats"] = []
@@ -342,6 +277,3 @@ def main(version):
"""The application's entry point."""
app = BavarderApplication()
return app.run(sys.argv)
+3 -36
View File
@@ -1,40 +1,7 @@
from .blenderbot import BlenderBotProvider
from .catgpt import CatGPTProvider
from .dialogpt import DialoGPTProvider
from .stablebeluga2 import StableBeluga2Provider
from .openaigpt35turbo import OpenAIGPT35TurboProvider
from .googleflant5xxl import GoogleFlant5XXLProvider
from .openaigpt4 import OpenAIGPT4Provider
from .gpt2 import GPT2Provider
from .openassistantsft1pythia12b import HuggingFaceOpenAssistantSFT1PythiaProvider
from .robertasquad2 import RobertaSquad2Provider
from .local import LocalProvider
from .aihorde import AIHordeProvider
from .stablediffusion import StableDiffusionProvider
from .analogdiffusion import AnalogDiffusionProvider
from .nitrodiffusion import NitroDiffusionProvider
from .openjourney import OpenJourneyProvider
from .openaiimage import DallE2, DallE3
from .portraitplus import PortraitPlusProvider
from .litert_lm import LiteRTLMProvider
PROVIDERS = {
AIHordeProvider,
BlenderBotProvider,
CatGPTProvider,
DialoGPTProvider,
OpenAIGPT35TurboProvider,
OpenAIGPT4Provider,
GoogleFlant5XXLProvider,
GPT2Provider,
LocalProvider,
StableDiffusionProvider,
AnalogDiffusionProvider,
NitroDiffusionProvider,
OpenJourneyProvider,
DallE2,
DallE3,
PortraitPlusProvider,
# StableBeluga2Provider,
# HuggingFaceOpenAssistantSFT1PythiaProvider,
# RobertaSquad2Provider
}
LiteRTLMProvider,
}
-150
View File
@@ -1,150 +0,0 @@
from .base import BaseProvider
import json
import requests
import time
from gi.repository import Adw, Gtk
class AIHordeProvider(BaseProvider):
name = "AI Horde"
ASYNC_URL = "https://stablehorde.net/api/v2/generate/text/async"
STATUS_URL = "https://stablehorde.net/api/v2/generate/text/status/"
API_KEY = "0000000000"
model = "PygmalionAI/pygmalion-7b"
description = "AI Horde is a crowdsourced distributed cluster of Image generation workers and text generation workers."
def ask(self, prompt, chat, **kwargs):
self.API_KEY = self.data.get("api_key", "0000000000")
chat = chat["content"]
self.headers = {
"Client-Agent": "bavarder:1:linux",
"apikey": self.API_KEY,
}
data = {
"prompt": prompt,
"models": [
self.model
]
}
r = requests.post(self.ASYNC_URL, json=data, headers=self.headers)
if r.status_code == 202:
rid = r.json()["id"]
else:
return _(f"I'm sorry, I don't know what to say! ({r.status_code}, {r.json()['message']})")
# do the request every seconds and check if it's finished
while True:
r = self.check_status(rid)
if r:
return r
else:
time.sleep(1)
return _("I'm sorry, I don't know what to say!")
def check_status(self, rid):
r = requests.get(self.STATUS_URL + rid)
rj = r.json()
if r.status_code == 200:
if rj["done"]:
return r.json()["generations"][0]["text"]
return None
def get_settings_rows(self):
self.rows = []
self.api_row = Adw.PasswordEntryRow()
self.api_row.connect("apply", self.on_apply)
self.api_row.props.text = self.data.get('api_key') or self.API_KEY
self.api_row.props.title = _("API Key")
self.api_row.set_show_apply_button(True)
self.api_row.add_suffix(self.how_to_get_a_token())
self.rows.append(self.api_row)
r = requests.get("https://stablehorde.net/api/v2/status/models?type=text")
if r.status_code != 200:
return self.rows
else:
rj = r.json()
models_row = Adw.ActionRow()
models_row.set_title(_("Models"))
models_row.set_subtitle(_("Select a model to use"))
go_to_sub_button = Gtk.Button.new_from_icon_name("go-next-symbolic")
go_to_sub_button.set_valign(Gtk.Align.CENTER)
go_to_sub_button.set_tooltip_text(_("Go to the models page"))
go_to_sub_button.add_css_class("flat")
go_to_sub_button.connect("clicked", self.open_subpage)
models_row.add_suffix(go_to_sub_button)
self.page = Adw.NavigationPage()
prefpage = Adw.PreferencesPage()
group = Adw.PreferencesGroup()
self.selected_row = Adw.ActionRow()
self.selected_row.set_title(_("Selected model"))
if self.model:
self.selected_row.set_subtitle(self.model)
else:
self.selected_row.set_subtitle(_("No model selected"))
group.add(self.selected_row)
for model in rj:
mr = Adw.ActionRow()
mr.props.title = model["name"]
mr.props.subtitle = f"Performance {model['performance']} - Jobs {model['jobs']} - Queued {model['queued']}"
apply_button = Gtk.Button.new_from_icon_name("object-select-symbolic")
apply_button.connect("clicked", self.on_apply_model, model["name"])
apply_button.set_valign(Gtk.Align.CENTER)
apply_button.set_tooltip_text(_("Select this model"))
apply_button.add_css_class("flat")
mr.add_suffix(apply_button)
group.add(mr)
toolbar = Adw.ToolbarView()
header = Adw.HeaderBar()
label = Gtk.Label()
label.set_label(_("Models"))
header.set_title_widget(label)
toolbar.add_top_bar(header)
prefpage.add(group)
toolbar.set_content(prefpage)
self.page.set_child(toolbar)
self.rows.append(models_row)
return self.rows
def open_subpage(self, widget):
self.app.preferences_window.push_subpage(self.page)
def on_apply(self, widget):
self.API_KEY = self.api_row.get_text()
self.data["api_key"] = self.API_KEY
def on_apply_model(self, widget, name):
self.model = name
if self.model:
self.selected_row.set_subtitle(self.model)
else:
self.selected_row.set_subtitle(_("No model selected"))
-6
View File
@@ -1,6 +0,0 @@
from .basehfimage import BaseHFImageProvider
class AnalogDiffusionProvider(BaseHFImageProvider):
name = "Analog Diffusion"
provider = "wavymulder/Analog-Diffusion"
description = "Analog Diffusion is a model that can generate images from a prompt."
-61
View File
@@ -1,61 +0,0 @@
from .baseimage import BaseImageProvider
import requests
import json
from gi.repository import Gtk, Adw, GLib
from PIL import Image, UnidentifiedImageError
import io
class BaseHFImageProvider(BaseImageProvider):
provider = None
def ask(self, prompt, chat, **kwargs):
chat = chat["content"]
API_URL = f"https://api-inference.huggingface.co/models/{self.provider}"
def query(payload):
if self.data.get('api_key'):
headers = {"Authorization": f"Bearer {self.data['api_key']}"}
response = requests.post(API_URL, json=payload, headers=headers)
else:
response = requests.post(API_URL, json=payload)
if response.status_code == 403:
return _("You've reached the rate limit! Please add a token to the preferences. You can get the token by following this [guide](https://bavarder.codeberg.page/help/huggingface/)")
elif response.status_code != 200:
return _("Sorry, I don't know what to say! (Error: {response.status_code})")
return response.content
prompt = self.make_prompt(prompt, chat)
output = query({
"inputs": prompt,
"negative_prompts": "",
})
if output:
try:
return Image.open(io.BytesIO(output))
except UnidentifiedImageError:
return output
def get_settings_rows(self):
self.rows = []
self.api_row = Adw.PasswordEntryRow()
self.api_row.connect("apply", self.on_apply)
self.api_row.props.text = self.data.get('api_key') or ""
self.api_row.props.title = _("API Key")
self.api_row.set_show_apply_button(True)
self.api_row.add_suffix(self.how_to_get_a_token())
self.rows.append(self.api_row)
return self.rows
def on_apply(self, widget):
api_key = self.api_row.get_text()
self.data["api_key"] = api_key
def make_prompt(self, prompt, chat):
return prompt
-10
View File
@@ -1,10 +0,0 @@
from .base import BaseProvider, ProviderType
import requests
from gi.repository import Gtk, Adw, GLib
class BaseImageProvider(BaseProvider):
provider_type = ProviderType.IMAGE
-7
View File
@@ -1,7 +0,0 @@
from .hfbasechat import BaseHFChatProvider, ProviderType
class BlenderBotProvider(BaseHFChatProvider):
name = "BlenderBot"
description = "An open domain chatbot"
provider = "facebook/blenderbot-400M-distill"
provider_type = ProviderType.TEXT
-25
View File
@@ -1,25 +0,0 @@
from random import choice, randint
from .base import BaseProvider
class CatGPTProvider(BaseProvider):
name = "Cat GPT"
description = _("Chit-Chat with a Cat")
def ask(self, prompt, _):
return " ".join([self.pick_generator()() for i in range(randint(1, 12))])
def pick_generator(self):
return choice(
[
lambda: "meow" * randint(1, 3),
lambda: "mew" * randint(1, 3),
lambda: "miau" * randint(1, 3),
lambda: "miaou" * randint(1, 3),
lambda: "miao" * randint(1, 3),
lambda: "nya" * randint(1, 3),
lambda: "m" + "r" * randint(1, 6) + "p",
lambda: "pur" + "r" * randint(1, 6),
lambda: "nya" * randint(1, 3) + "ny" + "a" * randint(1, 10),
]
)
-7
View File
@@ -1,7 +0,0 @@
from .hfbasechat import BaseHFChatProvider, ProviderType
class DialoGPTProvider(BaseHFChatProvider):
name = "DialoGPT"
description = "A State-of-the-Art Large-scale Pretrained Response generation model"
provider = "microsoft/DialoGPT-large"
provider_type = ProviderType.CHAT
-7
View File
@@ -1,7 +0,0 @@
from .hfbasechat import BaseHFChatProvider, ProviderType
class GoogleFlant5XXLProvider(BaseHFChatProvider):
name = "Google Flan T5 XXL"
description = "A better Text-To-Text Transfer Transformer (T5) model"
provider = "google/flan-t5-xxl"
provider_type = ProviderType.TEXT
-7
View File
@@ -1,7 +0,0 @@
from .hfbasechat import BaseHFChatProvider, ProviderType
class GPT2Provider(BaseHFChatProvider):
name = "GPT 2"
description = "GPT-2 is a transformers model pretrained on a very large corpus of English data in a self-supervised fashion"
provider = "gpt2"
provider_type = ProviderType.TEXT
-70
View File
@@ -1,70 +0,0 @@
from .base import BaseProvider, ProviderType
import requests
from gi.repository import Gtk, Adw, GLib
class BaseHFChatProvider(BaseProvider):
provider = None
chat_mode = True
def ask(self, prompt, chat, **kwargs):
chat = chat["content"]
API_URL = f"https://api-inference.huggingface.co/models/{self.provider}"
def query(payload):
if self.data.get('api_key'):
headers = {"Authorization": f"Bearer {self.data['api_key']}"}
response = requests.post(API_URL, json=payload, headers=headers)
else:
response = requests.post(API_URL, json=payload)
return response.json()
if self.provider_type == ProviderType.CHAT:
output = query({
"inputs": {
"past_user_inputs": [i['content'] for i in chat if i['role'] == self.app.user_name],
"generated_responses": [i['content'] for i in chat if i['role'] == self.app.bot_name],
"text": prompt
},
})
else:
prompt = self.make_prompt(prompt, chat)
output = query({
"inputs": prompt,
})
if 'generated_text' in output:
return output['generated_text']
elif 'error' in output:
match output['error']:
case "Rate limit reached. Please log in or use your apiToken":
return _("You've reached the rate limit! Please add a token to the preferences. You can get the token by following this [guide](https://bavarder.codeberg.page/help/huggingface/)")
elif isinstance(output, list):
if 'generated_text' in output[0]:
return output[0]['generated_text']
else:
return _("Sorry, I don't know what to say! (Error: {output})")
def get_settings_rows(self):
self.rows = []
self.api_row = Adw.PasswordEntryRow()
self.api_row.connect("apply", self.on_apply)
self.api_row.props.text = self.data.get('api_key') or ""
self.api_row.props.title = _("API Key")
self.api_row.set_show_apply_button(True)
self.api_row.add_suffix(self.how_to_get_a_token())
self.rows.append(self.api_row)
return self.rows
def on_apply(self, widget):
api_key = self.api_row.get_text()
self.data["api_key"] = api_key
def make_prompt(self, prompt, chat):
return prompt
+1 -22
View File
@@ -2,31 +2,10 @@ providers_dir = join_paths(MODULE_DIR, 'providers')
providers_sources = [
'__init__.py',
'analogdiffusion.py',
'aihorde.py',
'base.py',
'basehfimage.py',
'baseimage.py',
'blenderbot.py',
'catgpt.py',
'dialogpt.py',
'googleflant5xxl.py',
'gpt2.py',
'hfbasechat.py',
'local.py',
'nitrodiffusion.py',
'openai.py',
'openaigpt35turbo.py',
'openaigpt4.py',
'openaiimage.py',
'openassistantsft1pythia12b.py',
'openjourney.py',
'petals.py',
'portraitplus.py',
'litert_lm.py',
'provider_item.py',
'stablebeluga2.py',
'robertasquad2.py',
'stablediffusion.py',
]
PY_INSTALLDIR.install_sources(providers_sources, subdir: providers_dir)
-6
View File
@@ -1,6 +0,0 @@
from .basehfimage import BaseHFImageProvider
class NitroDiffusionProvider(BaseHFImageProvider):
name = "Nitro Diffusion"
provider = "nitrosocke/Nitro-Diffusion"
description = "Nitro Diffusion is a model that can generate images from a prompt."
-102
View File
@@ -1,102 +0,0 @@
from .base import BaseProvider
import openai
from openai import OpenAI
import socket
import os
from gi.repository import Gtk, Adw, GLib
class BaseOpenAIProvider(BaseProvider):
model = None
api_key_title = "API Key"
def __init__(self, app, window):
super().__init__(app, window)
try:
self.client = OpenAI(
api_key=os.environ.get("OPENAI_API_KEY"),
)
except openai.OpenAIError:
self.client = OpenAI(
api_key="",
)
if self.data.get("api_key"):
self.client.api_key = self.data["api_key"]
if self.data.get("api_base"):
self.client.base_url = self.data["api_base"]
def ask(self, prompt, chat):
_chat = []
for c in chat["content"]:
if c["role"] == self.app.bot_name:
role = "assistant"
else:
role = "user"
_chat.append({"role": role, "content": c["content"]})
chat = _chat
if self.model:
prompt = self.chunk(prompt)
try:
response = self.client.chat.completions.create(
model=self.model,
messages=chat,
).choices[0].message.content
except openai.AuthenticationError:
return _("Your API key is invalid, please check your preferences.")
except openai.BadRequestError:
return _("You don't have access to this model, please check your plan and billing details.")
except openai.RateLimitError:
return _("You exceeded your current quota, please check your plan and billing details.")
except openai.APIConnectionError:
return _("I'm having trouble connecting to the API, please check your internet connection.")
except socket.gaierror:
return _("I'm having trouble connecting to the API, please check your internet connection.")
else:
return response
else:
return _("No model selected, you can choose one in preferences")
def get_settings_rows(self):
self.rows = []
self.api_row = Adw.PasswordEntryRow()
self.api_row.connect("apply", self.on_apply)
self.api_row.props.text = self.client.api_key or ""
self.api_row.props.title = self.api_key_title
self.api_row.set_show_apply_button(True)
self.api_row.add_suffix(self.how_to_get_a_token())
self.rows.append(self.api_row)
self.api_url_row = Adw.EntryRow()
self.api_url_row.connect("apply", self.on_apply)
self.api_url_row.props.text=str(self.client.base_url) or ""
self.api_url_row.props.title = "API Url"
self.api_url_row.set_show_apply_button(True)
self.api_url_row.add_suffix(self.how_to_get_base_url())
self.rows.append(self.api_url_row)
return self.rows
def on_apply(self, widget):
api_key = self.api_row.get_text()
self.client.api_key = api_key
self.client.base_url = self.api_url_row.get_text()
self.data["api_key"] = self.client.api_key
self.data["api_base"] = str(self.client.base_url)
def how_to_get_base_url(self):
about_button = Gtk.Button()
about_button.set_icon_name("dialog-information-symbolic")
about_button.set_tooltip_text("How to choose base url")
about_button.add_css_class("flat")
about_button.set_valign(Gtk.Align.CENTER)
about_button.connect("clicked", self.open_documentation)
return about_button
-7
View File
@@ -1,7 +0,0 @@
from .openai import BaseOpenAIProvider
class OpenAIGPT35TurboProvider(BaseOpenAIProvider):
name = "OpenAI GPT 3.5 Turbo"
description = "Most capable GPT-3.5 model and optimized for chat."
model = "gpt-3.5-turbo"
-8
View File
@@ -1,8 +0,0 @@
from .openai import BaseOpenAIProvider
class OpenAIGPT4Provider(BaseOpenAIProvider):
name = "OpenAI GPT 4"
model = "gpt-4"
description = "More capable than any GPT-3.5 model, able to do more complex tasks, and optimized for chat."
api_key_title = "API Key (Require a plan with access to the GPT-4 model)"
-118
View File
@@ -1,118 +0,0 @@
from .baseimage import BaseImageProvider
import openai
from openai import OpenAI
import socket
import os
import json
from gi.repository import Gtk, Adw, GLib
class BaseOpenAIImageProvider(BaseImageProvider):
model = None
api_key_title = "API Key"
def __init__(self, app, window):
super().__init__(app, window)
try:
self.client = OpenAI(
api_key=os.environ.get("OPENAI_API_KEY"),
)
except openai.OpenAIError:
self.client = OpenAI(
api_key="",
)
if self.data.get("api_key"):
self.client.api_key = self.data["api_key"]
if self.data.get("api_base"):
self.client.base_url = self.data["api_base"]
def ask(self, prompt, chat):
if self.model:
prompt = self.chunk(prompt)
try:
response = self.client.images.generate(
model=self.model,
prompt=prompt,
size="1024x1024",
quality="standard",
n=1,
)
image_url = response.data[0].url
image_bytes = requests.get(image_url).content
except openai.AuthenticationError:
return _("Your API key is invalid, please check your preferences.")
except openai.BadRequestError:
return _("You don't have access to this model, please check your plan and billing details.")
except openai.RateLimitError:
return _("You exceeded your current quota, please check your plan and billing details.")
except openai.APIConnectionError:
return _("I'm having trouble connecting to the API, please check your internet connection.")
except socket.gaierror:
return _("I'm having trouble connecting to the API, please check your internet connection.")
else:
if image_bytes:
try:
return Image.open(io.BytesIO(image_bytes))
except UnidentifiedImageError:
error = json.loads(image_bytes)["error"]
return error
else:
return None
else:
return _("No model selected, you can choose one in preferences")
def get_settings_rows(self):
self.rows = []
self.api_row = Adw.PasswordEntryRow()
self.api_row.connect("apply", self.on_apply)
self.api_row.props.text = self.client.api_key or ""
self.api_row.props.title = self.api_key_title
self.api_row.set_show_apply_button(True)
self.api_row.add_suffix(self.how_to_get_a_token())
self.rows.append(self.api_row)
self.api_url_row = Adw.EntryRow()
self.api_url_row.connect("apply", self.on_apply)
self.api_url_row.props.text=str(self.client.base_url) or ""
self.api_url_row.props.title = "API Url"
self.api_url_row.set_show_apply_button(True)
self.api_url_row.add_suffix(self.how_to_get_base_url())
self.rows.append(self.api_url_row)
return self.rows
def on_apply(self, widget):
api_key = self.api_row.get_text()
self.client.api_key = api_key
self.client.base_url = self.api_url_row.get_text()
self.data["api_key"] = self.client.api_key
self.data["api_base"] = str(self.client.base_url)
def how_to_get_base_url(self):
about_button = Gtk.Button()
about_button.set_icon_name("dialog-information-symbolic")
about_button.set_tooltip_text("How to choose base url")
about_button.add_css_class("flat")
about_button.set_valign(Gtk.Align.CENTER)
about_button.connect("clicked", self.open_documentation)
return about_button
class DallE2(BaseOpenAIImageProvider):
name = "DALL·E 2"
model = "dall-e-2"
description = "DALL·E is a AI system that can create realistic images and art from a description in natural language. "
class DallE3(BaseOpenAIImageProvider):
name = "DALL·E 3"
model = "dall-e-3"
description = "DALL·E is a AI system that can create realistic images and art from a description in natural language. "
@@ -1,16 +0,0 @@
from .hfbasechat import BaseHFChatProvider
class HuggingFaceOpenAssistantSFT1PythiaProvider(BaseHFChatProvider):
name = "Open-Assistant SFT-1 12B"
provider = "OpenAssistant/oasst-sft-4-pythia-12b-epoch-3.5"
description = "OpenAssistant's SFT-1 Pythia 12B model"
def make_prompt(self, prompt, chat):
p = ""
for i in range(0, len(chat)):
if chat[i]['role'] == self.app.bot_name:
p += f"<|assistant|>{chat[i]['content']}<|endoftext|>"
else:
p += f"<|prompter|>{chat[i]['content']}<|endoftext|>"
p += f"<|prompter|> {prompt}<|endoftext|>"
return p
-6
View File
@@ -1,6 +0,0 @@
from .basehfimage import BaseHFImageProvider
class OpenJourneyProvider(BaseHFImageProvider):
name = "Open Journey"
provider = "prompthero/openjourney-v4"
description = "Open Journey is a model that can generate images from a prompt."
-32
View File
@@ -1,32 +0,0 @@
from .base import BaseProvider
import json
import requests
class BasePetalsProvider(BaseProvider):
provider = None
API_URL = "https://chat.petals.dev/"
ENDPOINT = "/api/v1/generate"
model = None
def ask(self, prompt, chat, **kwargs):
try:
API_URL = self.data["api_url"]
except KeyError:
API_URL = self.API_URL
API_URL += self.ENDPOINT
chat = chat["content"]
r = f"{API_URL}?model={self.model}&do_sample=1&temperature=0.75&top_p=0.9&max_length=1000&inputs={prompt}"
output = requests.post(r).json()
if output["ok"]:
return output["outputs"]
else:
return _("I'm sorry, I don't know what to say!")
-6
View File
@@ -1,6 +0,0 @@
from .basehfimage import BaseHFImageProvider
class PortraitPlusProvider(BaseHFImageProvider):
name = "Portrait Plus"
model = "wavymulder/portraitplus"
description = "Portrait Plus is a model that can generate images from a prompt."
-16
View File
@@ -1,16 +0,0 @@
from .hfbasechat import BaseHFChatProvider
class RobertaSquad2Provider(BaseHFChatProvider):
name = "Roberta Squad2"
provider = "deepset/roberta-base-squad2"
description = "A model for Question Answering on SQuAD2"
def make_prompt(self, prompt, chat):
context = ""
for message in chat:
if chat['role'] == self.app.user_name:
context += f" {message['content']}"
return {
"question": prompt,
"context": context
}
-5
View File
@@ -1,5 +0,0 @@
from .petals import BasePetalsProvider
class StableBeluga2Provider(BasePetalsProvider):
name = "stable-beluga2"
model = "stabilityai/StableBeluga2"
-6
View File
@@ -1,6 +0,0 @@
from .basehfimage import BaseHFImageProvider
class StableDiffusionProvider(BaseHFImageProvider):
name = "Stable Diffusion"
provider = "stabilityai/stable-diffusion-2-1"
description = "Stable Diffusion is a model that can generate images from a prompt."
+8 -14
View File
@@ -3,9 +3,6 @@ from gi.repository import Gtk, Adw, Gio
from bavarder.constants import app_id, rootdir
from bavarder.providers.provider_item import Provider
from bavarder.widgets.model_item import Model
from bavarder.widgets.download_row import DownloadRow
from gpt4all import GPT4All
@Gtk.Template(resource_path=f"{rootdir}/ui/preferences_window.ui")
class PreferencesWindow(Adw.PreferencesWindow):
@@ -51,18 +48,15 @@ class PreferencesWindow(Adw.PreferencesWindow):
self.general_page.remove(self.model_group)
self.model_group = Adw.PreferencesGroup()
self.model_group.set_title(_("Models"))
for model in self.app.models:
p = Model(self.app, self, model)
self.model_group.add(p)
if self.app.models:
for model in self.app.models:
p = Model(self.app, self, model)
self.model_group.add(p)
else:
self.no_models_available = Adw.ExpanderRow()
self.no_models_available.set_title(_("List of available models"))
for model in GPT4All.list_models():
self.no_models_available.add_row(DownloadRow(self.app, self, model))
self.model_group.add(self.no_models_available)
no_models = Adw.ActionRow()
no_models.set_title(_("No local models available"))
self.model_group.add(no_models)
self.general_page.add(self.model_group)
+7 -13
View File
@@ -463,20 +463,14 @@ class BavarderWindow(Adw.ApplicationWindow):
"time": self.get_time(),
}
if self.app.local_mode:
if self.app.setup_chat():
c["model"] = self.app.model_name
else:
c["model"] = "bavarder"
else:
l = list(self.app.providers.values())
l = list(self.app.providers.values())
for p in l:
if p.enabled and p.slug == self.app.current_provider:
c["model"] = self.app.current_provider
break
else:
c["model"] = "bavarder"
for p in l:
if p.enabled and p.slug == self.app.current_provider:
c["model"] = self.app.current_provider
break
else:
c["model"] = "bavarder"
self.content.append(c)