Skip to content

Commit cc1a02a

Browse files
committed
1 parent 4deb784 commit cc1a02a

File tree

9 files changed

+255
-9
lines changed

9 files changed

+255
-9
lines changed

docs/CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,12 @@
128128

129129
- [WebChatGPT](https://github.com/Simatwa/WebChatGPT/) added as provider
130130
- Awesome-prompts manipulation commands = **CRUD**
131-
- Other minor fixes.
131+
- Other minor fixes.
132+
133+
## v0.2.5
134+
135+
**What's new?**
136+
137+
- New provider : [Bard](https://github.com/acheong08/bard) by **Google**.
138+
- Check whole last response
139+
- Othetr minor fixes.

docs/README.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<a href="https://github.com/Simatwa/python-tgpt/actions/workflows/python-test.yml"><img src="https://github.com/Simatwa/python-tgpt/actions/workflows/python-test.yml/badge.svg" alt="Python Test"/></a>
99
-->
1010
<a href="https://github.com/Simatwa/python-tgpt/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/static/v1?logo=GPL&color=Blue&message=MIT&label=License"/></a>
11-
<a href="https://pypi.org/project/python-tgpt"><img alt="PyPi" src="https://img.shields.io/static/v1?logo=pypi&label=Pypi&message=0.2.4&color=green"/></a>
11+
<a href="https://pypi.org/project/python-tgpt"><img alt="PyPi" src="https://img.shields.io/static/v1?logo=pypi&label=Pypi&message=0.2.5&color=green"/></a>
1212
<a href="https://github.com/psf/black"><img alt="Black" src="https://img.shields.io/static/v1?logo=Black&label=Code-style&message=Black"/></a>
1313
<a href="#"><img alt="Passing" src="https://img.shields.io/static/v1?logo=Docs&label=Docs&message=Passing&color=green"/></a>
1414
<a href="https://github.com/Simatwa/python-tgpt/actions/workflows/python-package.yml"><img src="https://github.com/Simatwa/python-tgpt/actions/workflows/python-package.yml/badge.svg"/></a>
@@ -61,12 +61,13 @@ The name *python-tgpt* draws inspiration from its parent project [tgpt](https://
6161

6262
These are simply the hosts of the LLMs, which include:
6363

64-
1. [Brave Leo](https://brave.com/leo/) (llama-2-13b-chat)
64+
1. [Leo](https://brave.com/leo/) - **Brave**
6565
2. [FakeOpen](https://chat.geekgpt.org/)
6666
3. Koboldai
6767
4. [OpenGPTs](https://opengpts-example-vz4y4ooboq-uc.a.run.app/)
6868
5. [OpenAI](https://chat.openai.com) *(API key required)*
69-
6. [WebChatGPT](https://github.com/Simatwa/WebChatGPT) - **OpenAI** *(No API key required)*
69+
6. [WebChatGPT](https://github.com/Simatwa/WebChatGPT) - **OpenAI** *(Session ID required)*
70+
7. [Bard](https://github.com/acheong08/bard) - **Google** *(Session ID required)*
7071

7172
## Prerequisites
7273

@@ -264,6 +265,21 @@ print(bot.chat("<Your-prompt>"))
264265

265266
</details>
266267

268+
<details>
269+
270+
<summary>
271+
272+
Bard
273+
274+
</summary>
275+
276+
```python
277+
import pytgpt.bard as bard
278+
bot = bard.BARD('<Path-to-bard.google.com.cookies.json>')
279+
print(bot.chat("<Your-prompt>"))
280+
```
281+
282+
</details>
267283

268284

269285
</details>

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ rich==13.3.4
44
clipman==3.1.0
55
pyperclip==1.8.2
66
appdirs==1.4.4
7-
webchatgpt==0.2.6
7+
webchatgpt==0.2.6
8+
GoogleBard==1.4.0

setup.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
setup(
1616
name="python-tgpt",
17-
version="0.2.4",
17+
version="0.2.5",
1818
license="MIT",
1919
author="Smartwa",
2020
maintainer="Smartwa",
@@ -44,6 +44,7 @@
4444
"pyperclip==1.8.2",
4545
"appdirs==1.4.4",
4646
"webchatgpt==0.2.6",
47+
"GoogleBard==1.4.0",
4748
],
4849
python_requires=">=3.9",
4950
keywords=[
@@ -60,6 +61,7 @@
6061
"opengpt",
6162
"koboldai",
6263
"openai",
64+
"bard",
6365
],
6466
long_description=Path.open("README.md", encoding="utf-8").read(),
6567
long_description_content_type="text/markdown",

src/pytgpt/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from .utils import appdir
22

3-
__version__ = "0.2.4"
3+
__version__ = "0.2.5"
44
__author__ = "Smartwa"
55
__repo__ = "https://github.com/Simatwa/python-tgpt"
66

7-
available_providers = ["leo", "openai", "fakeopen", "opengpt", "koboldai"]
7+
available_providers = ["leo", "openai", "fakeopen", "opengpt", "koboldai", "bard"]
88

99
__all__ = [
1010
"appdir",

src/pytgpt/bard/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from .main import BARD
2+
3+
__info__ = "Interact with Google's Bard"
4+
5+
__all__ = ["BARD"]

src/pytgpt/bard/main.py

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
from Bard import Chatbot
2+
from pytgpt.base import Provider
3+
from pytgpt.utils import Conversation
4+
from pytgpt.utils import Optimizers
5+
from os import path
6+
from json import load
7+
from json import dumps
8+
import warnings
9+
10+
warnings.simplefilter("ignore", category=UserWarning)
11+
12+
13+
class BARD(Provider):
14+
def __init__(
15+
self,
16+
auth: str,
17+
proxy: dict = {},
18+
timeout: int = 30,
19+
):
20+
"""Initializes BARD
21+
22+
Args:
23+
auth (str): `__Secure-1PSID` cookie value (session id) or path to `bard.google.com.cookies.json` file
24+
proxy (dict, optional): Http request proxy. Defaults to {}.
25+
timeout (int, optional): Http request timeout. Defaults to 30.
26+
"""
27+
self.conversation = Conversation(False)
28+
self.session_auth = None
29+
assert isinstance(auth, str), f"auth should be of {str} only not '{type(auth)}'"
30+
if path.isfile(auth):
31+
# let's assume auth is a path to exported .json cookie-file
32+
with open(auth) as fh:
33+
entries = load(fh)
34+
for entry in entries:
35+
if entry["name"] == "__Secure-1PSID":
36+
self.session_auth = entry["value"]
37+
assert bool(
38+
self.session_auth
39+
), f"Failed to extract the required cookie value from file '{auth}'"
40+
else:
41+
# Assume auth is the targeted cookie value
42+
self.session_auth = auth
43+
44+
self.session = Chatbot(self.session_auth, proxy, timeout)
45+
self.last_response = {}
46+
self.__available_optimizers = (
47+
method
48+
for method in dir(Optimizers)
49+
if callable(getattr(Optimizers, method)) and not method.startswith("__")
50+
)
51+
52+
def ask(
53+
self,
54+
prompt: str,
55+
stream: bool = False,
56+
raw: bool = False,
57+
optimizer: str = None,
58+
conversationally: bool = False,
59+
) -> dict:
60+
"""Chat with AI
61+
62+
Args:
63+
prompt (str): Prompt to be send.
64+
stream (bool, optional): Flag for streaming response. Defaults to False.
65+
raw (bool, optional): Stream back raw response as received. Defaults to False.
66+
optimizer (str, optional): Prompt optimizer name - `[code, shell_command]`. Defeaults to None
67+
conversationally (bool, optional): Chat conversationally when using optimizer. Defaults to False.
68+
Returns:
69+
dict : {}
70+
```json
71+
{
72+
"content": "General Kenobi! \n\n(I couldn't help but respond with the iconic Star Wars greeting since you used it first. )\n\nIs there anything I can help you with today?\n[Image of Hello there General Kenobi]",
73+
"conversation_id": "c_f13f6217f9a997aa",
74+
"response_id": "r_d3665f95975c368f",
75+
"factualityQueries": null,
76+
"textQuery": [
77+
"hello there",
78+
1
79+
],
80+
"choices": [
81+
{
82+
"id": "rc_ea075c9671bfd8cb",
83+
"content": [
84+
"General Kenobi! \n\n(I couldn't help but respond with the iconic Star Wars greeting since you used it first. )\n\nIs there anything I can help you with today?\n[Image of Hello there General Kenobi]"
85+
]
86+
},
87+
{
88+
"id": "rc_de6dd3fb793a5402",
89+
"content": [
90+
"General Kenobi! (or just a friendly hello, whichever you prefer!). \n\nI see you're a person of culture as well. *Star Wars* references are always appreciated. \n\nHow can I help you today?\n"
91+
]
92+
},
93+
{
94+
"id": "rc_a672ac089caf32db",
95+
"content": [
96+
"General Kenobi! (or just a friendly hello if you're not a Star Wars fan!). \n\nHow can I help you today? Feel free to ask me anything, or tell me what you'd like to chat about. I'm here to assist in any way I can.\n[Image of Obi-Wan Kenobi saying hello there]"
97+
]
98+
}
99+
],
100+
101+
"images": [
102+
"https://i.pinimg.com/originals/40/74/60/407460925c9e419d82b93313f0b42f71.jpg"
103+
]
104+
}
105+
106+
```
107+
"""
108+
conversation_prompt = self.conversation.gen_complete_prompt(prompt)
109+
if optimizer:
110+
if optimizer in self.__available_optimizers:
111+
conversation_prompt = getattr(Optimizers, optimizer)(
112+
conversation_prompt if conversationally else prompt
113+
)
114+
else:
115+
raise Exception(
116+
f"Optimizer is not one of {self.__available_optimizers}"
117+
)
118+
119+
def for_stream():
120+
response = self.session.ask(prompt)
121+
self.last_response.update(response)
122+
self.conversation.update_chat_history(
123+
prompt, self.get_message(self.last_response)
124+
)
125+
yield dumps(response) if raw else response
126+
127+
def for_non_stream():
128+
# let's make use of stream
129+
for _ in for_stream():
130+
pass
131+
return self.last_response
132+
133+
return for_stream() if stream else for_non_stream()
134+
135+
def chat(
136+
self,
137+
prompt: str,
138+
stream: bool = False,
139+
optimizer: str = None,
140+
conversationally: bool = False,
141+
) -> str:
142+
"""Generate response `str`
143+
Args:
144+
prompt (str): Prompt to be send.
145+
stream (bool, optional): Flag for streaming response. Defaults to False.
146+
optimizer (str, optional): Prompt optimizer name - `[code, shell_command]`. Defaults to None.
147+
conversationally (bool, optional): Chat conversationally when using optimizer. Defaults to False.
148+
Returns:
149+
str: Response generated
150+
"""
151+
152+
def for_stream():
153+
for response in self.ask(
154+
prompt, True, optimizer=optimizer, conversationally=conversationally
155+
):
156+
yield self.get_message(response)
157+
158+
def for_non_stream():
159+
return self.get_message(
160+
self.ask(
161+
prompt,
162+
False,
163+
optimizer=optimizer,
164+
conversationally=conversationally,
165+
)
166+
)
167+
168+
return for_stream() if stream else for_non_stream()
169+
170+
def get_message(self, response: dict) -> str:
171+
"""Retrieves message only from response
172+
173+
Args:
174+
response (dict): Response generated by `self.ask`
175+
176+
Returns:
177+
str: Message extracted
178+
"""
179+
assert isinstance(response, dict), "Response should be of dict data-type only"
180+
return response["content"]

src/pytgpt/console.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,18 @@ def __init__(
332332
act=awesome_prompt,
333333
)
334334

335+
elif provider == "bard":
336+
from pytgpt.bard import BARD
337+
338+
assert (
339+
auth
340+
), f"Bard's session-id or path to bard.google.com.cookies.json file is required"
341+
self.bot = BARD(
342+
auth=auth,
343+
proxy=proxies,
344+
timeout=timeout,
345+
)
346+
335347
else:
336348
raise NotImplementedError(
337349
f"The provider `{provider}` is not yet implemented."
@@ -594,6 +606,14 @@ def do_load(self, line):
594606
self.bot.conversation.chat_history = fh.read()
595607
click.secho("Conversation loaded successfully.", fg="cyan")
596608

609+
def do_last_response(self, line):
610+
"""Show whole last response in json format"""
611+
self.output_bond(
612+
"Last Response",
613+
self.bot.last_response,
614+
is_json=True,
615+
)
616+
597617
@busy_bar.run()
598618
def default(self, line, exit_on_error: bool = False):
599619
"""Chat with ChatGPT"""
@@ -1298,7 +1318,7 @@ def all(json, indent, index, color, output):
12981318
formatted_awesome_prompts = dumps(awesome_prompts)
12991319
if json:
13001320
# click.secho(formatted_awesome_prompts, fg=color)
1301-
rich.print_json(formatted_awesome_prompts,indent=indent)
1321+
rich.print_json(formatted_awesome_prompts, indent=indent)
13021322

13031323
else:
13041324
awesome_table = Table(show_lines=True, title="All Awesome-Prompts")

tests/test_bard.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import unittest
2+
import tests.base as base
3+
from os import getenv
4+
from pytgpt.bard import BARD
5+
6+
7+
class TestLeo(base.llmBase):
8+
def setUp(self):
9+
self.bot = BARD(getenv("bard_cookie_file"))
10+
self.prompt = base.prompt
11+
12+
13+
if __name__ == "__main__":
14+
unittest.main()

0 commit comments

Comments
 (0)