This repository has been archived by the owner on Jul 31, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Add Bard client that supports starting a conversation, sending prompts, resetting and closing the conversation - Add first test - Add tests pipeline
- Loading branch information
Showing
8 changed files
with
344 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
name: Test | ||
|
||
on: | ||
push: | ||
branches: | ||
- master | ||
pull_request: | ||
branches: | ||
- master | ||
|
||
env: | ||
SECURE_1PSID: ${{ secrets.SECURE_1PSID }} | ||
SECURE_1PSIDTS: ${{ secrets.SECURE_1PSIDTS }} | ||
|
||
jobs: | ||
build-and-test: | ||
runs-on: ubuntu-latest | ||
|
||
strategy: | ||
matrix: | ||
python-version: ["3.10", "3.11"] | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v4 | ||
|
||
- name: Set up Python ${{ matrix.python-version }} | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: ${{ matrix.python-version }} | ||
|
||
- name: Install poetry | ||
run: | | ||
curl -sSL https://install.python-poetry.org | python3 - | ||
- name: Install dependencies | ||
run: poetry install | ||
|
||
- name: Run type checker | ||
run: poetry run mypy --ignore-missing-imports bard/ | ||
|
||
- name: Run tests | ||
run: poetry run pytest --cov |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,150 @@ | ||
import json | ||
import random | ||
import re | ||
from os import environ | ||
|
||
from aiohttp import ClientSession | ||
|
||
from bard.constants import BARD_STREAM_GENERATE_URL, BARD_URL, HEADERS | ||
from bard.exceptions import AskException, CreateConversationException | ||
from bard.utils import double_json_stringify | ||
|
||
|
||
class BardClient: | ||
def __init__(self) -> None: | ||
pass | ||
def __init__( | ||
self, secure_1psid: str | None = None, secure_1psidts: str | None = None | ||
) -> None: | ||
""" | ||
Client for Bard. | ||
""" | ||
self.secure_1psid = secure_1psid if secure_1psid else environ["SECURE_1PSID"] | ||
self.secure_1psidts = ( | ||
secure_1psidts if secure_1psidts else environ["SECURE_1PSIDTS"] | ||
) | ||
self.conversation_id: str | None = None | ||
self.response_id: str | None = None | ||
self.choice_id: str | None = None | ||
self.session: ClientSession | None = None | ||
|
||
async def __aenter__(self) -> "BardClient": | ||
await self.start_conversation() | ||
return self | ||
|
||
async def __aexit__(self, exc_type, exc_value, traceback) -> None: | ||
await self.close_conversation() | ||
|
||
async def _get_session(self, force_close: bool = False) -> ClientSession: | ||
# Use cookies to create a conversation. | ||
cookies = { | ||
"__Secure-1PSID": self.secure_1psid, | ||
"__Secure-1PSIDTS": self.secure_1psidts, | ||
} | ||
|
||
if self.session and force_close: | ||
await self.session.close() | ||
|
||
if not self.session: | ||
self.session = ClientSession( | ||
headers=HEADERS, | ||
cookies=cookies, | ||
) | ||
|
||
return self.session | ||
|
||
def _build_ask_parameters(self) -> dict: | ||
return { | ||
"bl": "boq_assistant-bard-web-server_20231031.09_p7", | ||
"_reqid": "".join(str(random.randint(0, 9)) for _ in range(7)), | ||
"rt": "c", | ||
} | ||
|
||
def _build_ask_arguments(self, prompt: str) -> dict: | ||
request_data = [ | ||
[prompt], | ||
None, | ||
[self.conversation_id, self.response_id, self.choice_id], | ||
] | ||
|
||
return { | ||
"f.req": double_json_stringify(request_data), | ||
"at": self.snlm0e, | ||
} | ||
|
||
async def start_conversation(self) -> None: | ||
""" | ||
Connect to Bard and create a new conversation. | ||
""" | ||
session = await self._get_session(force_close=True) | ||
|
||
async with session.get(BARD_URL) as response: | ||
if response.status != 200: | ||
raise CreateConversationException( | ||
f"Failed to create conversation, received status: {response.status}" | ||
) | ||
|
||
response_text = await response.text() | ||
|
||
snlm0e_dict = re.search(r"\"SNlM0e\":\"(?P<value>.*?)\"", response_text) | ||
|
||
if not snlm0e_dict: | ||
raise CreateConversationException( | ||
"Failed to create conversation, SNlM0e value was not found." | ||
) | ||
|
||
self.snlm0e = snlm0e_dict.group("value") | ||
|
||
async def ask(self, prompt: str) -> str: | ||
""" | ||
Send a prompt to Bard and return the answer. | ||
Parameters | ||
---------- | ||
prompt: str | ||
The prompt that needs to be sent to Bard. | ||
Returns | ||
------- | ||
str | ||
The response from Bard. | ||
""" | ||
parameters = self._build_ask_parameters() | ||
arguments = self._build_ask_arguments(prompt) | ||
|
||
session = await self._get_session() | ||
|
||
async with session.post( | ||
BARD_STREAM_GENERATE_URL, params=parameters, data=arguments | ||
) as response: | ||
if response.status != 200: | ||
raise AskException( | ||
f"Failed to get response, received status: {response.status}" | ||
) | ||
|
||
response_text = await response.text() | ||
response_data = json.loads(response_text.splitlines()[3]) | ||
|
||
message = json.loads(response_data[0][2]) | ||
|
||
self.conversation_id = message[1][0] | ||
self.response_id = message[1][1] | ||
self.choice_id = message[4][0][0] | ||
|
||
return message[4][0][1][0] | ||
|
||
async def reset_conversation(self) -> None: | ||
""" | ||
Clear current conversation information and connection and start new ones. | ||
""" | ||
await self.close_conversation() | ||
await self.start_conversation() | ||
|
||
async def close_conversation(self) -> None: | ||
""" | ||
Close all connections to Bard. Clear conversation information. | ||
""" | ||
if self.session and not self.session.closed: | ||
await self.session.close() | ||
|
||
self.conversation_id = None | ||
self.response_id = None | ||
self.choice_id = None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36" | ||
|
||
HEADERS = { | ||
"Accept": "*/*", | ||
"Accept-Encoding": "gzip, deflate, br", | ||
"Accept-Language": "en-US,en;q=0.9", | ||
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", | ||
"Origin": "https://bard.google.com", | ||
"Referer": "https://bard.google.com/", | ||
"Sec-Ch-Ua": '"Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24"', | ||
"Sec-Ch-Ua-Mobile": "?0", | ||
"Sec-Ch-Ua-Platform": "Windows", | ||
"Sec-Fetch-Dest": "empty", | ||
"Sec-Fetch-Mode": "cors", | ||
"Sec-Fetch-Site": "same-origin", | ||
"User-Agent": USER_AGENT, | ||
"X-Same-Domain": "1", | ||
} | ||
|
||
BARD_URL = "https://bard.google.com/chat" | ||
BARD_STREAM_GENERATE_URL = "https://bard.google.com/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
class CreateConversationException(Exception): | ||
pass | ||
|
||
|
||
class AskException(Exception): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import json | ||
|
||
|
||
def double_json_stringify(data) -> str: | ||
return json.dumps([None, json.dumps(data)]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[tool.poetry] | ||
name = "bard-py" | ||
version = "0.1.0" | ||
version = "0.2.0" | ||
description = "Python Client for Bard." | ||
authors = ["vsakkas <[email protected]>"] | ||
license = "MIT" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import pytest | ||
|
||
from bard import BardClient | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_ask() -> None: | ||
async with BardClient() as bard: | ||
_ = await bard.ask("Tell me a joke") |