Repository: acheong08/EdgeGPT Branch: main Commit: 9aed44b641f8 Files: 38 Total size: 117.1 KB Directory structure: gitextract_qfdnm_l6/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── custom.md │ │ └── feature_request.yml │ └── workflows/ │ ├── GHCR.yml │ ├── PyPi.yml │ └── tests.yml ├── .gitignore ├── .pre-commit-config.yaml ├── Dockerfile ├── LICENSE ├── Makefile ├── docs/ │ ├── CODE_OF_CONDUCT.md │ ├── README_es.md │ ├── README_ja.md │ ├── README_zh-cn.md │ ├── README_zh-tw.md │ └── wiki/ │ ├── EdgeGPT.md │ ├── Home.md │ └── ImageGen.md ├── example.env ├── requirements.txt ├── setup.cfg ├── setup.py ├── src/ │ └── EdgeGPT/ │ ├── EdgeGPT.py │ ├── EdgeUtils.py │ ├── ImageGen.py │ ├── __init__.py │ ├── chathub.py │ ├── constants.py │ ├── conversation.py │ ├── conversation_style.py │ ├── exceptions.py │ ├── locale.py │ ├── main.py │ ├── request.py │ └── utilities.py └── test_base.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: Bug Report description: DO NOT OPEN A DUPLICATE title: "[Bug]: " body: - type: checkboxes attributes: label: Is there an existing issue for this? description: Please search to see if an issue already exists for the bug you encountered, and that it hasn't been fixed in a recent build/commit. options: - label: I have searched the existing issues and checked the recent builds/commits required: true - type: markdown attributes: value: | *Please fill this form with as much information as possible, don't forget to fill "What OS..." and "What browsers" and *provide screenshots if possible** - type: textarea id: what-did attributes: label: What happened? description: Tell us what happened in a very clear and simple way validations: required: true - type: textarea id: steps attributes: label: Steps to reproduce the problem description: Please provide us with precise step by step information on how to reproduce the bug value: | 1. Go to .... 2. Press .... 3. ... validations: required: true - type: textarea id: what-should attributes: label: What should have happened? description: Tell what you think the normal behavior should be validations: required: true - type: textarea id: version attributes: label: Version where the problem happens description: "`python3 -m pip show revChatGPT`" validations: required: true - type: input id: python-version attributes: label: What Python version are you running this with? description: "`python3 -V`" - type: dropdown id: platforms attributes: label: What is your operating system ? multiple: true options: - Windows - Linux - MacOS - iOS - Android - Other/Cloud - type: textarea id: cmdargs attributes: label: Command Line Arguments description: Are you using any launching parameters/command line arguments (modified webui-user .bat/.sh) ? If yes, please write them below. Write "No" otherwise. render: Shell validations: required: true - type: textarea id: logs attributes: label: Console logs description: Please provide **full** cmd/terminal logs from the moment you started UI to the end of it, after your bug happened. If it's very long, provide a link to pastebin or similar service. render: Shell validations: required: true - type: textarea id: misc attributes: label: Additional information description: Please provide us with any relevant additional info or context. ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Discussions, questions, and niche issues in Github url: https://github.com/acheong08/EdgeGPT/discussions/new/choose about: For anything that is not an explicit bug - name: Discuss in the Discord url: https://discord.gg/9K2BvbXEHT about: 'Personal server: Chill discussions and more detailed updates' - name: Communicate in the Discord url: https://discord.gg/WMNtbDUjUv about: 'Public server: Large technical community with many high profile members like @transitive-bullshit' ================================================ FILE: .github/ISSUE_TEMPLATE/custom.md ================================================ --- name: "Are you sure you are not opening a duplicate? From now on, I will not be reading all issues. Use 👍 reaction on an issue to upvote it. I will be reading the top 5 issues each day." about: "DO NOT PRESS THIS" title: '' labels: '' assignees: '' --- ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.yml ================================================ name: Feature request description: Suggest an idea for this project title: "[Feature Request]: " body: - type: checkboxes attributes: label: Is there an existing issue for this? description: Please search to see if an issue already exists for the feature you want, and that it's not implemented in a recent build/commit. options: - label: I have searched the existing issues and checked the recent builds/commits required: true - type: markdown attributes: value: | *Please fill this form with as much information as possible, provide screenshots and/or illustrations of the feature if possible* - type: textarea id: feature attributes: label: What would your feature do ? description: Tell us about your feature in a very clear and simple way, and what problem it would solve validations: required: true - type: textarea id: workflow attributes: label: Proposed workflow description: Please provide us with step by step information on how you'd like the feature to be accessed and used value: | 1. Go to .... 2. Press .... 3. ... validations: required: true - type: textarea id: misc attributes: label: Additional information description: Add any other context or screenshots about the feature request here. ================================================ FILE: .github/workflows/GHCR.yml ================================================ name: Publish Docker 🐳 images 📦 to GitHub Container Registry on: release: types: [published] jobs: build-release-and-publish-to-ghcr: if: github.event_name == 'release' # Explicitly grant the `secrets.GITHUB_TOKEN` permissions. permissions: # Grant the ability to write to GitHub Packages (push Docker images to # GitHub Container Registry). packages: write name: Build and publish Release Docker 🐳 images 📦 to GitHub Container Registry runs-on: ubuntu-latest steps: - name: Checkout main uses: actions/checkout@v3 with: fetch-depth: 0 - name: Get actual patch version id: actual_patch_version run: echo ::set-output name=ACTUAL_PATCH_VERSION::$(echo $GITHUB_REF | cut -d / -f 3 ) - name: Get actual minor version id: actual_minor_version run: echo ::set-output name=ACTUAL_MINOR_VERSION::$(echo $GITHUB_REF | cut -d / -f 3 | cut -d "." -f 1,2) - name: Get actual major version id: actual_major_version run: echo ::set-output name=ACTUAL_MAJOR_VERSION::$(echo $GITHUB_REF | cut -d / -f 3 | cut -d "." -f 1) - name: Docker compliant registry for image pushes id: registry run: echo "REGISTRY=$(echo 'ghcr.io/${{github.repository }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@v4 with: images: ghcr.io/${{ github.repository }} - name: Set up QEMU uses: docker/setup-qemu-action@v2 with: platforms: 'arm64' - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to GitHub Container Registry uses: docker/login-action@v2 with: registry: ghcr.io # This is the user that triggered the Workflow. In this case, it will # either be the user whom created the Release or manually triggered # the workflow_dispatch. username: ${{ github.actor }} # `secrets.GITHUB_TOKEN` is a secret that's automatically generated by # GitHub Actions at the start of a workflow run to identify the job. # This is used to authenticate against GitHub Container Registry. # See https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret # for more detailed information. password: ${{ secrets.GH_TOKEN }} - name: Build and push alpine based image uses: docker/build-push-action@v4 with: file: Dockerfile context: . push: true # push the image to ghcr tags: | ${{ env.REGISTRY }}:latest ${{ env.REGISTRY }}:${{ steps.actual_patch_version.outputs.ACTUAL_PATCH_VERSION }}, ${{ env.REGISTRY }}:${{ steps.actual_minor_version.outputs.ACTUAL_MINOR_VERSION }} ${{ env.REGISTRY }}:${{ steps.actual_major_version.outputs.ACTUAL_MAJOR_VERSION }} ${{ env.REGISTRY }}:${{github.sha}} labels: ${{ steps.meta.outputs.labels }} platforms: linux/amd64,linux/arm64 cache-from: type=gha cache-to: type=gha,mode=max ================================================ FILE: .github/workflows/PyPi.yml ================================================ name: Upload Python Package on: release: types: [published] permissions: contents: read jobs: deploy: runs-on: ubuntu-latest if: github.event_name == 'release' steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11.0' - name: Install dependencies run: make - name: Build package run: make build - name: Publish package uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} ================================================ FILE: .github/workflows/tests.yml ================================================ name: tests on: push: pull_request: pull_request_target: types: [ ready_for_review, review_requested, edited] jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: [3.8, 3.11 ] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies env: EDGE_COOKIES: ${{ secrets.EDGE_COOKIES }} run: | python -m pip install pytest pytest-asyncio python -m pip install . - name: Test with pytest run: | pytest . -s ================================================ FILE: .gitignore ================================================ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ pip-wheel-metadata/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ cookies.json output/* README.md *.json # Pycharm .idea/* ================================================ FILE: .pre-commit-config.yaml ================================================ repos: - repo: https://github.com/asottile/reorder_python_imports rev: v3.9.0 hooks: - id: reorder-python-imports args: [--py37-plus] - repo: https://github.com/asottile/add-trailing-comma rev: v2.3.0 hooks: - id: add-trailing-comma args: [--py36-plus] - repo: https://github.com/asottile/pyupgrade rev: v3.3.1 hooks: - id: pyupgrade args: [--py37-plus] - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: debug-statements - id: double-quote-string-fixer - id: name-tests-test - id: requirements-txt-fixer - repo: https://github.com/psf/black rev: 22.10.0 hooks: - id: black ================================================ FILE: Dockerfile ================================================ FROM docker.io/library/python:3.11.3-alpine3.16@sha256:0ba61d06b14e5438aa3428ee46c7ccdc8df5b63483bc91ae050411407eb5cbf4 AS builder WORKDIR /EdgeGPT COPY requirements.txt requirements.txt RUN pip3 install --no-cache-dir -r requirements.txt COPY . . RUN apk add --no-cache make && \ make init && make build && make ci && apk del make && \ rm -Rf /root/.cache/pip ENTRYPOINT ["python3", "-m" , "EdgeGPT.EdgeGPT"] ================================================ FILE: LICENSE ================================================ This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to ================================================ FILE: Makefile ================================================ .PHONY: docs init: python -m pip install --upgrade pip python -m pip install -r ./requirements.txt --upgrade python -m pip install build setuptools wheel flake8 --upgrade build: python -m build ci: python -m flake8 src --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics python -m flake8 src --count --select=E9,F63,F7,F82 --show-source --statistics python setup.py install ================================================ FILE: docs/CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at acheong@student.dalat.org. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. ================================================ FILE: docs/README_es.md ================================================
EdgeGPT # Edge GPT _Ingeniería inversa al nuevo chat integrado en Bing_ English - 简体中文 - 繁體中文 - Español - 日本語

PyPI version Python version Total downloads

# Configuración ## Instalación ```bash python3 -m pip install EdgeGPT --upgrade ``` ## Requisitos - python 3.8+ - Una cuenta de Microsoft con acceso a (Opcional, dependiendo de tu país) - Estar localizado en un país con acceso al nuevo Bing (VPN necesaria para usarios de China) - [Selenium](https://pypi.org/project/selenium/) (para la recolección automática de cookies) ## Autenticación !!! ES PROBABLE QUE YA NO SEA NECESARIO !!! **En algunas regiones**, Microsoft ha **abierto** la función de chat para todos, por lo que es posible que **puedas saltarte este paso**. Puedes comprobarlo con un navegador (con el user-agent modificado para parecer Edge), **intentando abrir un chat sin haber iniciado sesión**. Se ha encontrado que es posible que sea **dependiente de tu dirección IP**. Por ejemplo, si intentas acceder a la función de chat desde una dirección IP que se conoce que **pertenece al rango de un centro de datos** (vServers, servidores raíz, VPN, proxies conocidos, ...), **es posible que tengas que iniciar sesión** y sin embargo puedas acceder sin problemas a las funciones desde tu casa. Si recibes el siguiente error, puedes probar **usando cookies** y viendo si funciona: `Exception: Authentication failed. You have not been accepted into the beta.` ### Recolección de cookies 1. Necesitas un navegador que _parezca_ Microsoft Edge. * a) (Fácil) Instala la última versión de Microsoft Edge * b) (Avanzado) De forma alternativa, puedes usar cualquier navegador y cambiar el user-agent para que parezca que estás usando Edge (e.g., `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51`). Puedes hacer esto con extensiones del estilo "User-Agent Switcher and Manager" para [Chrome](https://chrome.google.com/webstore/detail/user-agent-switcher-and-m/bhchdcejhohfmigjafbampogmaanbfkg) o [Firefox](https://addons.mozilla.org/en-US/firefox/addon/user-agent-string-switcher/). 2. Abrir [bing.com/chat](https://bing.com/chat) 3. Si ves la nueva función de chat, es que puedes continuar... 4. Instala la extensión cookie editor para [Chrome](https://chrome.google.com/webstore/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm) o [Firefox](https://addons.mozilla.org/en-US/firefox/addon/cookie-editor/) 5. Ve a [bing.com](https://bing.com) 6. Abre la extensión 7. Pulsa en "Export" abajo a la derecha, luego "Export as JSON" (Esto guarda la cookie en el portapapeles) 8. Pega el contenido en un fichero `bing_cookies_*.json`. * NOTA: Los **ficheros de cookies DEBEN seguir el siguiente formato de nombre `bing_cookies_*.json`**, de manera que puedan ser reconocidos por los mecanismos internos de procesado de cookies. ### Uso de la cookie en la librería: ```python cookies = json.loads(open("./path/to/cookies.json", encoding="utf-8").read()) # might omit cookies option bot = await Chatbot.create(cookies=cookies) ```
# Cómo usar Chatbot ## Ejecución desde la línea de comandos ``` $ python3 -m EdgeGPT.EdgeGPT -h EdgeGPT - A demo of reverse engineering the Bing GPT chatbot Repo: github.com/acheong08/EdgeGPT By: Antonio Cheong !help for help Type !exit to exit usage: EdgeGPT.py [-h] [--enter-once] [--search-result] [--no-stream] [--rich] [--proxy PROXY] [--wss-link WSS_LINK] [--style {creative,balanced,precise}] [--prompt PROMPT] [--cookie-file COOKIE_FILE] [--history-file HISTORY_FILE] [--locale LOCALE] options: -h, --help show this help message and exit --enter-once --search-result --no-stream --rich --proxy PROXY Proxy URL (e.g. socks5://127.0.0.1:1080) --wss-link WSS_LINK WSS URL(e.g. wss://sydney.bing.com/sydney/ChatHub) --style {creative,balanced,precise} --prompt PROMPT prompt to start with --cookie-file COOKIE_FILE path to cookie file --history-file HISTORY_FILE path to history file --locale LOCALE your locale (e.g. en-US, zh-CN, en-IE, en-GB) ``` (China/US/UK/Norway disponen de mejor soporte para la localización) ## Ejecución en Python ### 1. La clase `Chatbot` y la librería `asyncio` para un control más exhaustivo Usa Async para una mejor experiencia, por ejemplo: ```python import asyncio, json from EdgeGPT.EdgeGPT import Chatbot, ConversationStyle async def main(): bot = await Chatbot.create() # Passing cookies is "optional", as explained above response = await bot.ask(prompt="Hello world", conversation_style=ConversationStyle.creative, simplify_response=True) print(json.dumps(response, indent=2)) # Returns """ { "text": str "author": str "sources": list[dict] "sources_text": str "suggestions": list[str] "messages_left": int } """ await bot.close() if __name__ == "__main__": asyncio.run(main()) ``` ### 2) Las clases auxiliares `Query` y `Cookie` Realiza una petición a la IA de Bing Chat (usa el estilo 'precise' de conversación por defecto) y devuelve la respuesta final sin ver todo el resultado de la API: Recuerda almacenar las cookies en el formato: `bing_cookies_*.json`. ```python from EdgeGPT.EdgeUtils import Query, Cookie q = Query("What are you? Give your answer as Python code") print(q) ``` También puedes cambiar el estilo de conversación o el fichero de cookie a usar: ```python q = Query( "What are you? Give your answer as Python code", style="creative", # or: 'balanced', 'precise' cookie_file="./bing_cookies_alternative.json" ) ``` Obtén rápidamente la respuesta, fragmentos de código, lista de fuentes/referencias, o las preguntas sugeridas usando los siguientes atributos: ```python q.output q.code q.suggestions q.sources # for the full json output q.sources_dict # for a dictionary of titles and urls ``` Obtén la pregunta inicial o el estilo de conversación usado: ```python q.prompt q.style repr(q) ``` Accede a la lista de peticiones realizadas: ```python Query.index # A list of Query objects; updated dynamically Query.request_count # A tally of requests made using each cookie file ``` Finalmente, la clase `Cookie` admite múltiples ficheros de cookie, de manera que si has creado ficheros adicionales de cookies usando el formato de nombrado `bing_cookies_*.json`, las peticiones intentarán usar automáticamente el siguiente fichero (alfabéticamente) si has sobrepasado el límite diario de peticiones (actualmente limitado a 200). Principales atributos que tienes a disposición: ```python Cookie.current_file_index Cookie.dirpath Cookie.search_pattern # default is `bing_cookies_*.json` Cookie.files() # list as files that match .search_pattern Cookie.current_filepath Cookie.current_data Cookie.import_next() Cookie.image_token Cookie.ignore_files ``` --- ## Ejecución en Docker Este ejemplo asume que dispones de un fichero `cookies.json` en tu directorio actual ```bash docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt ``` Puedes añadir argumentos adicionales de la siguiente manera ```bash docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt --rich --style creative ```
# Cómo usar Image generator ## Ejecución desde la línea de comandos ```bash $ python3 -m ImageGen.ImageGen -h usage: ImageGen.py [-h] [-U U] [--cookie-file COOKIE_FILE] --prompt PROMPT [--output-dir OUTPUT_DIR] [--quiet] [--asyncio] optional arguments: -h, --help show this help message and exit -U U Auth cookie from browser --cookie-file COOKIE_FILE File containing auth cookie --prompt PROMPT Prompt to generate images for --output-dir OUTPUT_DIR Output directory --quiet Disable pipeline messages --asyncio Run ImageGen using asyncio ``` ## Ejecución en Python ### 1) La clase auxiliar `ImageQuery` Genera imágenes de acuerdo a la petición y las descarga en el directorio actual ```python from EdgeGPT.EdgeUtils import ImageQuery q=ImageQuery("Meerkats at a garden party in Devon") ``` Cambia el directorio de descarga para las demás imágenes que se descarguen durante el resto de sesión ``` Query.image_dirpath = Path("./to_another_folder") ``` ### 2) Usa las clases `ImageGen` y `asyncio` para un control más minucioso ```python from EdgeGPT.ImageGen import ImageGen import argparse import json async def async_image_gen(args) -> None: async with ImageGenAsync(args.U, args.quiet) as image_generator: images = await image_generator.get_images(args.prompt) await image_generator.save_images(images, output_dir=args.output_dir) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("-U", help="Auth cookie from browser", type=str) parser.add_argument("--cookie-file", help="File containing auth cookie", type=str) parser.add_argument( "--prompt", help="Prompt to generate images for", type=str, required=True, ) parser.add_argument( "--output-dir", help="Output directory", type=str, default="./output", ) parser.add_argument( "--quiet", help="Disable pipeline messages", action="store_true" ) parser.add_argument( "--asyncio", help="Run ImageGen using asyncio", action="store_true" ) args = parser.parse_args() # Load auth cookie with open(args.cookie_file, encoding="utf-8") as file: cookie_json = json.load(file) for cookie in cookie_json: if cookie.get("name") == "_U": args.U = cookie.get("value") break if args.U is None: raise Exception("Could not find auth cookie") if not args.asyncio: # Create image generator image_generator = ImageGen(args.U, args.quiet) image_generator.save_images( image_generator.get_images(args.prompt), output_dir=args.output_dir, ) else: asyncio.run(async_image_gen(args)) ```
# Historial de estrellas [![Gráfica historial de estrellas](https://api.star-history.com/svg?repos=acheong08/EdgeGPT&type=Date)](https://star-history.com/#acheong08/EdgeGPT&Date)
# Contribuidores Este proyecto existe gracias a todas las personas que apoyan y contribuyen.
================================================ FILE: docs/README_ja.md ================================================
EdgeGPT # Edge GPT _Bing の新バージョンのチャット機能のリバースエンジニアリング_ English - 简体中文 - 繁體中文 - Español - 日本語

PyPI version Python version Total downloads

--- ## 設定 ### パッケージをインストール ```bash python3 -m pip install EdgeGPT --upgrade ``` ### 要件 - python 3.8+ - に早期アクセスできる Microsoft アカウント(必須) - New Bing のサポート国で必要(中国本土のVPNは必須)
### アクセスの確認 (必須) - Microsoft Edge の最新バージョンをインストール - また、任意のブラウザを使用し、ユーザーエージェントを Edge を使用しているように設定することもできます(例:`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0 Safari/537.36 Edg/111.0.1661.51`)。[Chrome](https://chrome.google.com/webstore/detail/user-agent-switcher-and-m/bhchdcejhohfmigjafbampogmaanbfkg) や [Firefox](https://addons.mozilla.org/en-US/firefox/addon/user-agent-string-switcher/) の"User-Agent Switcher and Manager"のような拡張機能を使えば、簡単に行えます。 - [bing.com/chat](https://bing.com/chat) を開く - チャット機能が表示されたら、準備完了
### 認証の取得 (必須) - [Chrome](https://chrome.google.com/webstore/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm) または [Firefox](https://addons.mozilla.org/en-US/firefox/addon/cookie-editor/) の Cookie エディター拡張機能をインストール - `bing.com` へ移動 - 拡張機能を開く - 右下の"エクスポート"から"JSONとしてエクスポート"をクリック(これで Cookie がクリップボードに保存されます) - クッキーをファイル `cookies.json` に貼り付け
## チャットボット ## 使用方法 ### クイックスタート ``` $ python3 -m EdgeGPT.EdgeGPT -h EdgeGPT - A demo of reverse engineering the Bing GPT chatbot Repo: github.com/acheong08/EdgeGPT By: Antonio Cheong !help for help Type !exit to exit Enter twice to send message or set --enter-once to send one line message usage: EdgeGPT.py [-h] [--enter-once] [--no-stream] [--rich] [--proxy PROXY] [--wss-link WSS_LINK] [--style {creative,balanced,precise}] [--cookie-file COOKIE_FILE] options: -h, --help show this help message and exit --enter-once --no-stream --rich --proxy PROXY Proxy URL (e.g. socks5://127.0.0.1:1080) --wss-link WSS_LINK WSS URL(e.g. wss://sydney.bing.com/sydney/ChatHub) --style {creative,balanced,precise} --cookie-file COOKIE_FILE needed if environment variable COOKIE_FILE is not set ``` --- ## Docker での実行 これは、現在の作業ディレクトリに cookies.json ファイルがあることを前提としています ``` bash docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt ``` 次のように追加のフラグを追加できます ``` bash docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt --rich --style creative ``` ### 開発者デモ Cookie を渡す 3 つの方法: - 環境変数: `export COOKIE_FILE=/path/to/cookies.json` 。 - 引数 `cookie_path` には、次のように `cookies.json` へのパスを指定する: ```python bot = Chatbot(cookie_path='./cookies.json') ``` - 次のように、引数 `cookies` で直接クッキーを渡します: ```python with open('./cookies.json', 'r') as f: cookies = json.load(f) bot = Chatbot(cookies=cookies) ``` 最高のエクスペリエンスを得るには Async を使用してください より高度な使用例の参照コード: ```python import asyncio from EdgeGPT.EdgeGPT import Chatbot, ConversationStyle async def main(): bot = await Chatbot.create() print(await bot.ask(prompt="Hello world", conversation_style=ConversationStyle.creative, wss_link="wss://sydney.bing.com/sydney/ChatHub")) await bot.close() if __name__ == "__main__": asyncio.run(main()) ```
## 画像ジェネレーター ```bash $ python3 -m ImageGen.ImageGen -h usage: ImageGen.py [-h] [-U U] [--cookie-file COOKIE_FILE] --prompt PROMPT [--output-dir OUTPUT_DIR] [--quiet] [--asyncio] optional arguments: -h, --help show this help message and exit -U U Auth cookie from browser --cookie-file COOKIE_FILE File containing auth cookie --prompt PROMPT Prompt to generate images for --output-dir OUTPUT_DIR Output directory --quiet Disable pipeline messages --asyncio Run ImageGen using asyncio ``` ### 開発者デモ ```python from EdgeGPT.ImageGen import ImageGen import argparse import json async def async_image_gen(args) -> None: async with ImageGenAsync(args.U, args.quiet) as image_generator: images = await image_generator.get_images(args.prompt) await image_generator.save_images(images, output_dir=args.output_dir) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("-U", help="Auth cookie from browser", type=str) parser.add_argument("--cookie-file", help="File containing auth cookie", type=str) parser.add_argument( "--prompt", help="Prompt to generate images for", type=str, required=True, ) parser.add_argument( "--output-dir", help="Output directory", type=str, default="./output", ) parser.add_argument( "--quiet", help="Disable pipeline messages", action="store_true" ) parser.add_argument( "--asyncio", help="Run ImageGen using asyncio", action="store_true" ) args = parser.parse_args() # 認証クッキーを読み込む with open(args.cookie_file, encoding="utf-8") as file: cookie_json = json.load(file) for cookie in cookie_json: if cookie.get("name") == "_U": args.U = cookie.get("value") break if args.U is None: raise Exception("Could not find auth cookie") if not args.asyncio: # 画像ジェネレーターの作成 image_generator = ImageGen(args.U, args.quiet) image_generator.save_images( image_generator.get_images(args.prompt), output_dir=args.output_dir, ) else: asyncio.run(async_image_gen(args)) ```
## Star ヒストリー [![Star History Chart](https://api.star-history.com/svg?repos=acheong08/EdgeGPT&type=Date)](https://star-history.com/#acheong08/EdgeGPT&Date) ## コントリビューター このプロジェクトが存在するのはコントリビュートしてくださるすべての方々のおかげです。 ================================================ FILE: docs/README_zh-cn.md ================================================
EdgeGPT # Edge GPT _新必应聊天功能的逆向工程_ English - 简体中文 - 繁體中文 - Español - 日本語

PyPI version Python version Total downloads

# 设置 ## 安装模块 ```bash python3 -m pip install EdgeGPT --upgrade ``` ## 要求 - python 3.8+ - 一个可以访问必应聊天的微软账户 (可选,视所在地区而定) - 需要在 New Bing 支持的国家或地区(中国大陆需使用VPN) - [Selenium](https://pypi.org/project/selenium/) (对于需要自动配置cookie的情况) ## 身份验证 基本上不需要了。 **在部分地区**,微软已将聊天功能**开放**给所有人,或许可以**省略这一步**了。可以使用浏览器来确认(将 UA 设置为能表示为 Edge 的),**试一下能不能不登录就可以开始聊天**。 可能也得**看当前所在 IP 地址**。例如,如果试图从一个已知**属于数据中心范围**的 IP 来访问聊天功能(虚拟服务器、根服务器、虚拟专网、公共代理等),**可能就需要登录**;但是要是用家里的 IP 地址访问聊天功能,就没有问题。 如果收到这样的错误,可以试试**提供一个 cookie** 看看能不能解决: `Exception: Authentication failed. You have not been accepted into the beta.` ### 收集 cookie 1. 获取一个看着像 Microsoft Edge 的浏览器。 - a) (简单) 安装最新版本的 Microsoft Edge - b) (高级) 或者, 您可以使用任何浏览器并将用户代理设置为Edge的用户代理 (例如 `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51`). 您可以使用像 "User-Agent Switcher and Manager" [Chrome](https://chrome.google.com/webstore/detail/user-agent-switcher-and-m/bhchdcejhohfmigjafbampogmaanbfkg) 和 [Firefox](https://addons.mozilla.org/en-US/firefox/addon/user-agent-string-switcher/) 这样的扩展轻松完成此操作。 2. 打开 [bing.com/chat](https://bing.com/chat) 3. 如果您看到聊天功能,就接着下面的步骤... 4. 安装 [Chrome](https://chrome.google.com/webstore/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm) 或 [Firefox](https://addons.mozilla.org/en-US/firefox/addon/cookie-editor/) 的 cookie editor 扩展 5. 移步到 [bing.com](https://bing.com) 6. 打开扩展程序 7. 点击右下角的"导出" ,然后点击"导出为 JSON" (将会把内容保存到你的剪贴板上) 8. 把你剪贴板上的内容粘贴到 `bing_cookies_*.json` 文件中 - 注意:**Cookie 文件名必须遵守正则表达式 `bing_cookies_*.json`**,这样才能让本模块的 cookie 处理程序识别到。 ### 在代码中使用 cookie: ```python cookies = json.loads(open("./path/to/cookies.json", encoding="utf-8").read()) # 可能会忽略 cookie 选项 bot = await Chatbot.create(cookies=cookies) ```
# 如何使用聊天机器人 ## 从命令行运行 ``` $ python3 -m EdgeGPT.EdgeGPT -h EdgeGPT - A demo of reverse engineering the Bing GPT chatbot Repo: github.com/acheong08/EdgeGPT By: Antonio Cheong !help for help Type !exit to exit usage: EdgeGPT.py [-h] [--enter-once] [--search-result] [--no-stream] [--rich] [--proxy PROXY] [--wss-link WSS_LINK] [--style {creative,balanced,precise}] [--prompt PROMPT] [--cookie-file COOKIE_FILE] [--history-file HISTORY_FILE] [--locale LOCALE] options: -h, --help show this help message and exit --enter-once --search-result --no-stream --rich --proxy PROXY Proxy URL (e.g. socks5://127.0.0.1:1080) --wss-link WSS_LINK WSS URL(e.g. wss://sydney.bing.com/sydney/ChatHub) --style {creative,balanced,precise} --prompt PROMPT prompt to start with --cookie-file COOKIE_FILE path to cookie file --history-file HISTORY_FILE path to history file --locale LOCALE your locale (e.g. en-US, zh-CN, en-IE, en-GB) ``` (中/美/英/挪具有更好的本地化支持) ## 在 Python 运行 ### 1. 使用 `Chatbot` 类和 `asyncio` 类以进行更精细的控制 使用 async 获得最佳体验,例如: ```python import asyncio, json from EdgeGPT.EdgeGPT import Chatbot, ConversationStyle async def main(): bot = await Chatbot.create() # 导入 cookie 是“可选”的,如前所述 response = await bot.ask(prompt="Hello world", conversation_style=ConversationStyle.creative, simplify_response=True) print(json.dumps(response, indent=2)) # 返回下面这些 """ { "text": str, "author": str, "sources": list[dict], "sources_text": str, "suggestions": list[str], "messages_left": int } """ await bot.close() if __name__ == "__main__": asyncio.run(main()) ``` ### 2) `Query` 和 `Cookie` 助手类 创建一个简易的必应聊天 AI 查询(默认使用精确模式),这样可以只查看主要的文字输出,而不会打出整个 API 响应内容: 注意按照特定格式保存 cookie:`bing_cookies_*.json`. ```python from EdgeGPT.EdgeUtils import Query, Cookie q = Query("你是谁?用python代码给出回答") print(q) ``` 保存 cookie 文件的默认目录是 `HOME/bing_cookies`,不过可以这样修改: ```python Cookie.dir_path = Path(r"...") ``` 也可以修改对话模式,或者指定要使用的 cookie 文件: ```python q = Query( "你是谁?用python代码给出回答", style="creative", # 或者平衡模式 'balanced' 或者精确模式 'precise' cookie_file="./bing_cookies_alternative.json" ) # 使用 `help(Query)` 查看其他支持的参数。 ``` 使用以下属性快速提取文本输出、代码片段、来源/参考列表或建议的后续问题: ```python q.output # 或者: print(q) q.sources q.sources_dict q.suggestions q.code q.code_blocks q.code_block_formatsgiven) ``` 获得原始 prompt 和指定的对话模式: ```python q.prompt q.ignore_cookies q.style q.simplify_response q.locale repr(q) ``` 通过 import `Query` 获取之前的查询: ```python Query.index # 一个查询对象的列表;是动态更新的 Query.image_dir_path ``` 最后,`Cookie` 类支持多个 Cookie 文件,因此,如果您使用命名约定 `bing_cookies_*.json` 创建其他 Cookie 文件,那么如果超过了每日请求配额(目前设置为 200),查询将自动尝试使用下一个文件(按字母顺序)。 这些是可以访问的主要属性: ```python Cookie.current_file_index Cookie.current_file_path Cookie.current_data Cookie.dir_path Cookie.search_pattern Cookie.files Cookie.image_token Cookie.import_next Cookie.rotate_cookies Cookie.ignore_files Cookie.supplied_files Cookie.request_count ``` --- ## 使用 Docker 运行 假设当前工作目录有一个文件 `cookies.json` ```bash docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt ``` 可以像这样添加任意参数 ```bash docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt --rich --style creative ```
# 如何使用图片生成器 ## 从命令行运行 ```bash $ python3 -m ImageGen.ImageGen -h usage: ImageGen.py [-h] [-U U] [--cookie-file COOKIE_FILE] --prompt PROMPT [--output-dir OUTPUT_DIR] [--quiet] [--asyncio] optional arguments: -h, --help show this help message and exit -U U Auth cookie from browser --cookie-file COOKIE_FILE File containing auth cookie --prompt PROMPT Prompt to generate images for --output-dir OUTPUT_DIR Output directory --quiet Disable pipeline messages --asyncio Run ImageGen using asyncio ``` ## 在 Python 运行 ### 1) `ImageQuery` 助手类 使用一个简易提示生成图像,并下载到当前工作目录: ```python from EdgeGPT.EdgeUtils import ImageQuery q=ImageQuery("Meerkats at a garden party in Devon") ``` 在此会话中修改所有后续图像的下载目录: ``` Query.image_dirpath = Path("./to_another_folder") ``` ### 2) 使用 `ImageGen` 类和 `asyncio` 类以进行更精细的控制 ```python from EdgeGPT.ImageGen import ImageGen import argparse import json async def async_image_gen(args) -> None: async with ImageGenAsync(args.U, args.quiet) as image_generator: images = await image_generator.get_images(args.prompt) await image_generator.save_images(images, output_dir=args.output_dir) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("-U", help="从浏览器获取的身份认证 cookie", type=str) parser.add_argument("--cookie-file", help="包含认证 cookie 的文件", type=str) parser.add_argument( "--prompt", help="用于生成图片的 prompt", type=str, required=True, ) parser.add_argument( "--output-dir", help="输出目录", type=str, default="./output", ) parser.add_argument( "--quiet", help="禁用管道消息", action="store_true" ) parser.add_argument( "--asyncio", help="使用 asyncio 运行 ImageGen", action="store_true" ) args = parser.parse_args() # 加载认证 cookie with open(args.cookie_file, encoding="utf-8") as file: cookie_json = json.load(file) for cookie in cookie_json: if cookie.get("name") == "_U": args.U = cookie.get("value") break if args.U is None: raise Exception("未能找到认证 Cookie") if not args.asyncio: # 创建图像生成器 image_generator = ImageGen(args.U, args.quiet) image_generator.save_images( image_generator.get_images(args.prompt), output_dir=args.output_dir, ) else: asyncio.run(async_image_gen(args)) ```
# Star 历史 [![Star History Chart](https://api.star-history.com/svg?repos=acheong08/EdgeGPT&type=Date)](https://star-history.com/#acheong08/EdgeGPT&Date)
# 贡献者 这个项目的存在要归功于所有做出贡献的人。
================================================ FILE: docs/README_zh-tw.md ================================================
EdgeGPT # Edge GPT _新必應聊天功能的逆向工程_ English - 简体中文 - 繁體中文 - Español - 日本語

PyPI version Python version Total downloads

# 設置 ## 安裝模組 ```bash python3 -m pip install EdgeGPT --upgrade ``` ## 要求 - python 3.8+ - 一個可以訪問必應聊天的微軟帳戶 (可選,取決於所在地區) - 需要在 New Bing 支持的國家或地區(中國大陸需使用VPN) - [Selenium](https://pypi.org/project/selenium/) (對於需要自動配置cookie的情況) ## 認證 基本上不需要了。 **在某些地區**,微軟已將聊天功能**開放**給所有人,或許可以**省略這一步**了。您可以使用瀏覽器進行檢查(將 UA 設置為能表示為 Edge 的),**嘗試能否在不登錄的情況下開始聊天**。 可能也得**看當前所在 IP 位址**。例如,如果試圖從一個已知**屬於數據中心範圍**的 IP 來訪問聊天功能(虛擬伺服器、根伺服器、虛擬專網、公共代理等),**可能就需要登錄**;但是要是用家裡的 IP 位址訪問聊天功能,就沒有問題。 如果收到這樣的錯誤,可以試試**提供一個 cookie** 看看能不能解決: `Exception: Authentication failed. You have not been accepted into the beta.` ### 收集 cookie 1. 獲取一個看著像 Microsoft Edge 的瀏覽器。 * a) (簡單) 安裝最新版本的 Microsoft Edge * b) (高級) 或者, 您可以使用任何瀏覽器並將用戶代理設置為Edge的用戶代理 (例如 `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51`). 您可以使用像 "User-Agent Switcher and Manager" [Chrome](https://chrome.google.com/webstore/detail/user-agent-switcher-and-m/bhchdcejhohfmigjafbampogmaanbfkg) 和 [Firefox](https://addons.mozilla.org/en-US/firefox/addon/user-agent-string-switcher/) 這樣的擴展輕鬆完成此操作. 2. 打開 [bing.com/chat](https://bing.com/chat) 3. 如果您看到聊天功能,就接著下面的步驟... 4. 安裝 [Chrome](https://chrome.google.com/webstore/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm) 或 [Firefox](https://addons.mozilla.org/en-US/firefox/addon/cookie-editor/) 的 cookie editor 擴展 5. 轉到 [bing.com](https://bing.com) 6. 打開擴展程式 7. 單擊右下角的「匯出」,然後按「匯出為 JSON」(這會將您的 cookie 保存到剪貼簿) 8. 將您剪貼簿上的 cookie 粘貼到檔 `bing_cookies_*.json` 中 * 注意:**cookie 檔名必須遵循正則表示式 `bing_cookies_*.json`**,這樣才能讓本模組的 cookie 處理程式識別到。 ### 在代碼中使用 cookie: ```python cookies = json.loads(open("./path/to/cookies.json", encoding="utf-8").read()) # 可能会忽略 cookie 选项 bot = await Chatbot.create(cookies=cookies) ```
# 如何使用聊天機器人 ## 從命令列運行 ``` $ python3 -m EdgeGPT.EdgeGPT -h EdgeGPT - A demo of reverse engineering the Bing GPT chatbot Repo: github.com/acheong08/EdgeGPT By: Antonio Cheong !help for help Type !exit to exit usage: EdgeGPT.py [-h] [--enter-once] [--search-result] [--no-stream] [--rich] [--proxy PROXY] [--wss-link WSS_LINK] [--style {creative,balanced,precise}] [--prompt PROMPT] [--cookie-file COOKIE_FILE] [--history-file HISTORY_FILE] [--locale LOCALE] options: -h, --help show this help message and exit --enter-once --search-result --no-stream --rich --proxy PROXY Proxy URL (e.g. socks5://127.0.0.1:1080) --wss-link WSS_LINK WSS URL(e.g. wss://sydney.bing.com/sydney/ChatHub) --style {creative,balanced,precise} --prompt PROMPT prompt to start with --cookie-file COOKIE_FILE path to cookie file --history-file HISTORY_FILE path to history file --locale LOCALE your locale (e.g. en-US, zh-CN, en-IE, en-GB) ``` (中/美/英/挪具有更好的本地化支援) ## 在 Python 運行 ### 1. 使用 `Chatbot` 類和 `asyncio` 類以進行更精細的控制 使用 async 獲得最佳體驗,例如: ```python import asyncio, json from EdgeGPT.EdgeGPT import Chatbot, ConversationStyle async def main(): bot = await Chatbot.create() # 導入 cookie 是“可選”的,如前所述 response = await bot.ask(prompt="Hello world", conversation_style=ConversationStyle.creative, simplify_response=True) print(json.dumps(response, indent=2)) # 返回如下 """ { "text": str, "author": str, "sources": list[dict], "sources_text": str, "suggestions": list[str], "messages_left": int } """ await bot.close() if __name__ == "__main__": asyncio.run(main()) ``` ### 2) `Query` 和 `Cookie` 助手類 創建一個簡單的必應聊天 AI 查詢(預設情況下使用“精確”對話樣式),這樣可以僅查看主要文本輸出,而不是整個 API 回應: 注意按照特定格式儲存 cookie: ```bing_cookies_*.json```。 ```python from EdgeGPT.EdgeUtils import Query, Cookie q = Query("你是誰?用python代碼給出回答") print(q) ``` 儲存 Cookie 檔的預設目錄是 `HOME/bing_cookies`,但您可以通過以下方式更改它: ```python Cookie.dir_path = Path(r"...") ``` 或者更改要使用的對話風格或 Cookie 檔: ```python q = Query( "你是誰?用python代碼給出回答", style="creative", # 或者平衡模式 'balanced' 或者精確模式 'precise' cookies="./bing_cookies_alternative.json" ) # 使用 `help(Query)` 查看其他支持的參數。 ``` 使用以下屬性快速提取文字輸出、代碼片段、來源/參考清單或建議的後續問題: ```python q.output # 或者: print(q) q.sources q.sources_dict q.suggestions q.code q.code_blocks q.code_block_formatsgiven) ``` 抓取原始 prompt 與您指定的對話風格: ```python q.prompt q.ignore_cookies q.style q.simplify_response q.locale repr(q) ``` 通過 import `Query` 獲取進行的先前查詢: ```python Query.index # 一个查詢物件的串列;是動態更新的 Query.image_dir_path ``` 最後,`Cookie` 類支援多個 cookie 檔,因此,如果您使用命名約定 `bing_cookies_*.json` 創建其他 cookie 檔,則如果您的請求數已超出每日配額(當前設置為 200),您的查詢將自動嘗試使用下一個檔(按字母順序)。 以下是您可以獲得的主要屬性: ```python Cookie.current_file_index Cookie.current_file_path Cookie.current_data Cookie.dir_path Cookie.search_pattern Cookie.files Cookie.image_token Cookie.import_next Cookie.rotate_cookies Cookie.ignore_files Cookie.supplied_files Cookie.request_count ``` --- ## 使用 Docker 運行 假設在當前工作目錄中有一個檔 `cookies.json` ```bash docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt ``` 可以像這樣添加任意參數 ```bash docker run --rm -it -v $(pwd)/cookies.json:/cookies.json:ro -e COOKIE_FILE='/cookies.json' ghcr.io/acheong08/edgegpt --rich --style creative ```
# 如何使用圖像生成器 ## 從命令列運行 ```bash $ python3 -m ImageGen.ImageGen -h usage: ImageGen.py [-h] [-U U] [--cookie-file COOKIE_FILE] --prompt PROMPT [--output-dir OUTPUT_DIR] [--quiet] [--asyncio] optional arguments: -h, --help show this help message and exit -U U Auth cookie from browser --cookie-file COOKIE_FILE File containing auth cookie --prompt PROMPT Prompt to generate images for --output-dir OUTPUT_DIR Output directory --quiet Disable pipeline messages --asyncio Run ImageGen using asyncio ``` ## 在 Python 運行 ### 1) `ImageQuery` 助手類 根據一個簡單的提示產生圖像並下載到目前工作目錄: ```python from EdgeGPT.EdgeUtils import ImageQuery q=ImageQuery("Meerkats at a garden party in Devon") ``` 在此工作階段中修改所有後續圖像的下載目錄: ``` Query.image_dirpath = Path("./to_another_folder") ``` ### 2) 使用 `ImageGen` 類和 `asyncio` 類以進行更精細的控制 ```python from EdgeGPT.ImageGen import ImageGen import argparse import json async def async_image_gen(args) -> None: async with ImageGenAsync(args.U, args.quiet) as image_generator: images = await image_generator.get_images(args.prompt) await image_generator.save_images(images, output_dir=args.output_dir) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("-U", help="來自瀏覽器的身份驗證 cookie", type=str) parser.add_argument("--cookie-file", help="包含身份驗證 cookie 的檔", type=str) parser.add_argument( "--prompt", help="用于產生圖像的 prompt", type=str, required=True, ) parser.add_argument( "--output-dir", help="輸出目錄", type=str, default="./output", ) parser.add_argument( "--quiet", help="禁用管道消息", action="store_true" ) parser.add_argument( "--asyncio", help="使用 asyncio 運行 ImageGen", action="store_true" ) args = parser.parse_args() # 載入身份驗證 cookie with open(args.cookie_file, encoding="utf-8") as file: cookie_json = json.load(file) for cookie in cookie_json: if cookie.get("name") == "_U": args.U = cookie.get("value") break if args.U is None: raise Exception("找不到身份驗證 Cookie") if not args.asyncio: # 創建圖片生成器 image_generator = ImageGen(args.U, args.quiet) image_generator.save_images( image_generator.get_images(args.prompt), output_dir=args.output_dir, ) else: asyncio.run(async_image_gen(args)) ```
# Star 歷史 [![Star History Chart](https://api.star-history.com/svg?repos=acheong08/EdgeGPT&type=Date)](https://star-history.com/#acheong08/EdgeGPT&Date)
# 貢獻者 這個專案的存在要歸功於所有做出貢獻的人。
================================================ FILE: docs/wiki/EdgeGPT.md ================================================ # EdgeGPT.EdgeGPT Main.py ## Chatbot Objects ```python class Chatbot() ``` Combines everything to make it seamless #### save\_conversation ```python async def save_conversation(filename: str) -> None ``` Save the conversation to a file #### load\_conversation ```python async def load_conversation(filename: str) -> None ``` Load the conversation from a file #### get\_conversation ```python async def get_conversation() -> dict ``` Gets the conversation history from conversation_id (requires load_conversation) #### get\_activity ```python async def get_activity() -> dict ``` Gets the recent activity (requires cookies) #### ask ```python async def ask(prompt: str, wss_link: str = "wss://sydney.bing.com/sydney/ChatHub", conversation_style: CONVERSATION_STYLE_TYPE = None, webpage_context: str | None = None, search_result: bool = False, locale: str = guess_locale(), simplify_response: bool = False) -> dict ``` Ask a question to the bot Response: { item (dict): messages (list[dict]): adaptiveCards (list[dict]): body (list[dict]): text (str): Response } To get the response, you can do: response["item"]["messages"][1]["adaptiveCards"][0]["body"][0]["text"] #### ask\_stream ```python async def ask_stream( prompt: str, wss_link: str = "wss://sydney.bing.com/sydney/ChatHub", conversation_style: CONVERSATION_STYLE_TYPE = None, raw: bool = False, webpage_context: str | None = None, search_result: bool = False, locale: str = guess_locale() ) -> Generator[bool, dict | str, None] ``` Ask a question to the bot #### close ```python async def close() -> None ``` Close the connection #### delete\_conversation ```python async def delete_conversation(conversation_id: str = None, conversation_signature: str = None, client_id: str = None) -> None ``` Delete the chat in the server and close the connection #### reset ```python async def reset(delete=False) -> None ``` Reset the conversation ================================================ FILE: docs/wiki/Home.md ================================================ Welcome to the EdgeGPT wiki! ## Languages - English - [简体中文](https://github.com/CoolPlayLin/ChatGPT-Wiki/tree/main/docs/EdgeGPT) ================================================ FILE: docs/wiki/ImageGen.md ================================================ # ImageGen ## ImageGen Objects ```python class ImageGen() ``` Image generation by Microsoft Bing **Arguments**: - `auth_cookie` - str #### get\_images ```python def get_images(prompt: str) -> list ``` Fetches image links from Bing **Arguments**: - `prompt` - str #### save\_images ```python def save_images(links: list, output_dir: str) -> None ``` Saves images to output directory ================================================ FILE: example.env ================================================ export BING_U="" ================================================ FILE: requirements.txt ================================================ aiohttp BingImageCreator certifi httpx prompt_toolkit requests rich ================================================ FILE: setup.cfg ================================================ [metadata] description_file=README.md license_files=LICENSE ================================================ FILE: setup.py ================================================ from pathlib import Path from setuptools import find_packages from setuptools import setup DOCS_PATH = Path(__file__).parents[0] / "docs/README.md" PATH = Path("README.md") if not PATH.exists(): with Path.open(DOCS_PATH, encoding="utf-8") as f1: with Path.open(PATH, "w+", encoding="utf-8") as f2: f2.write(f1.read()) setup( name="EdgeGPT", version="0.13.2", license="The Unlicense", author="Antonio Cheong", author_email="acheong@student.dalat.org", description="Reverse engineered Edge Chat API", packages=find_packages("src"), package_dir={"": "src"}, url="https://github.com/acheong08/EdgeGPT", project_urls={"Bug Report": "https://github.com/acheong08/EdgeGPT/issues/new"}, entry_points={ "console_scripts": [ "edge-gpt = EdgeGPT.main:main", "edge-gpt-image = EdgeGPT.ImageGen:main", ], }, install_requires=[ "httpx[socks]>=0.24.0", "aiohttp", "websockets", "rich", "certifi", "prompt_toolkit", "requests", "BingImageCreator>=0.4.4", ], long_description=Path.open(PATH, encoding="utf-8").read(), long_description_content_type="text/markdown", py_modules=["EdgeGPT", "EdgeUtils", "ImageGen"], classifiers=[ "License :: OSI Approved :: The Unlicense (Unlicense)", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries :: Python Modules", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", ], ) ================================================ FILE: src/EdgeGPT/EdgeGPT.py ================================================ """ Main.py """ from __future__ import annotations import json from pathlib import Path from typing import Generator from .chathub import * from .conversation import * from .conversation_style import * from .request import * from .utilities import * class Chatbot: """ Combines everything to make it seamless """ def __init__( self, proxy: str | None = None, cookies: list[dict] | None = None, ) -> None: self.proxy: str | None = proxy self.chat_hub: ChatHub = ChatHub( Conversation(self.proxy, cookies=cookies), proxy=self.proxy, cookies=cookies, ) @staticmethod async def create( proxy: str | None = None, cookies: list[dict] | None = None, ) -> Chatbot: self = Chatbot.__new__(Chatbot) self.proxy = proxy self.chat_hub = ChatHub( await Conversation.create(self.proxy, cookies=cookies), proxy=self.proxy, cookies=cookies, ) return self async def save_conversation(self, filename: str) -> None: """ Save the conversation to a file """ with open(filename, "w") as f: conversation_id = self.chat_hub.request.conversation_id conversation_signature = self.chat_hub.request.conversation_signature client_id = self.chat_hub.request.client_id invocation_id = self.chat_hub.request.invocation_id f.write( json.dumps( { "conversation_id": conversation_id, "conversation_signature": conversation_signature, "client_id": client_id, "invocation_id": invocation_id, }, ), ) async def load_conversation(self, filename: str) -> None: """ Load the conversation from a file """ with open(filename) as f: conversation = json.load(f) self.chat_hub.request = ChatHubRequest( conversation_signature=conversation["conversation_signature"], client_id=conversation["client_id"], conversation_id=conversation["conversation_id"], invocation_id=conversation["invocation_id"], ) async def get_conversation(self) -> dict: """ Gets the conversation history from conversation_id (requires load_conversation) """ return await self.chat_hub.get_conversation() async def get_activity(self) -> dict: """ Gets the recent activity (requires cookies) """ return await self.chat_hub.get_activity() async def ask( self, prompt: str, wss_link: str = "wss://sydney.bing.com/sydney/ChatHub", conversation_style: CONVERSATION_STYLE_TYPE = None, webpage_context: str | None = None, search_result: bool = False, locale: str = guess_locale(), simplify_response: bool = False, ) -> dict: """ Ask a question to the bot Response: { item (dict): messages (list[dict]): adaptiveCards (list[dict]): body (list[dict]): text (str): Response } To get the response, you can do: response["item"]["messages"][1]["adaptiveCards"][0]["body"][0]["text"] """ async for final, response in self.chat_hub.ask_stream( prompt=prompt, conversation_style=conversation_style, wss_link=wss_link, webpage_context=webpage_context, search_result=search_result, locale=locale, ): if final: if not simplify_response: return response messages_left = response["item"]["throttling"][ "maxNumUserMessagesInConversation" ] - response["item"]["throttling"].get( "numUserMessagesInConversation", 0, ) if messages_left == 0: raise Exception("Max messages reached") message = "" for msg in reversed(response["item"]["messages"]): if msg.get("adaptiveCards") and msg["adaptiveCards"][0]["body"][ 0 ].get("text"): message = msg break if not message: raise Exception("No message found") suggestions = [ suggestion["text"] for suggestion in message.get("suggestedResponses", []) ] adaptive_cards = message.get("adaptiveCards", []) adaptive_text = ( adaptive_cards[0]["body"][0].get("text") if adaptive_cards else None ) sources = ( adaptive_cards[0]["body"][0].get("text") if adaptive_cards else None ) sources_text = ( adaptive_cards[0]["body"][-1].get("text") if adaptive_cards else None ) return { "text": message["text"], "author": message["author"], "sources": sources, "sources_text": sources_text, "suggestions": suggestions, "messages_left": messages_left, "max_messages": response["item"]["throttling"][ "maxNumUserMessagesInConversation" ], "adaptive_text": adaptive_text, } return {} async def ask_stream( self, prompt: str, wss_link: str = "wss://sydney.bing.com/sydney/ChatHub", conversation_style: CONVERSATION_STYLE_TYPE = None, raw: bool = False, webpage_context: str | None = None, search_result: bool = False, locale: str = guess_locale(), ) -> Generator[bool, dict | str, None]: """ Ask a question to the bot """ async for response in self.chat_hub.ask_stream( prompt=prompt, conversation_style=conversation_style, wss_link=wss_link, raw=raw, webpage_context=webpage_context, search_result=search_result, locale=locale, ): yield response async def close(self) -> None: """ Close the connection """ await self.chat_hub.close() async def delete_conversation( self, conversation_id: str = None, conversation_signature: str = None, client_id: str = None, ) -> None: """ Delete the chat in the server """ await self.chat_hub.delete_conversation( conversation_id=conversation_id, conversation_signature=conversation_signature, client_id=client_id, ) async def reset(self, delete=False) -> None: """ Reset the conversation """ if delete: await self.remove_and_close() else: await self.close() self.chat_hub = ChatHub( await Conversation.create(self.proxy, cookies=self.chat_hub.cookies), proxy=self.proxy, cookies=self.chat_hub.cookies, ) if __name__ == "__main__": from .main import main main() ================================================ FILE: src/EdgeGPT/EdgeUtils.py ================================================ import asyncio import contextlib import json import re from pathlib import Path from log2d import Log from .EdgeGPT import Chatbot from .EdgeGPT import ConversationStyle from .ImageGen import ImageGen Log("BingChat") log = Log.BingChat.debug # shortcut to create a log entry class Cookie: """ Convenience class for Bing Cookie files, data, and configuration. This Class is updated dynamically by the Query class to allow cycling through >1 cookie/credentials file e.g. when daily request limits (current 200 per account per day) are exceeded. """ current_file_index = 0 dir_path = Path.home().resolve() / "bing_cookies" current_file_path = dir_path # Avoid Path errors when no cookie file used search_pattern = "bing_cookies_*.json" ignore_files = set() request_count = {} supplied_files = set() rotate_cookies = True @classmethod def files(cls: type) -> list[Path]: """ Return a sorted list of all cookie files matching .search_pattern in cls.dir_path, plus any supplied files, minus any ignored files. """ all_files = set(Path(cls.dir_path).glob(cls.search_pattern)) if hasattr(cls, "supplied_files"): supplied_files = {x for x in cls.supplied_files if x.is_file()} all_files.update(supplied_files) return sorted(all_files - cls.ignore_files) @classmethod def import_data(cls: type) -> None: """ Read the active cookie file and populate the following attributes: .current_file_path .current_data .image_token """ if not cls.files(): log("No files in Cookie.dir_path") return try: cls.current_file_path = cls.files()[cls.current_file_index] except IndexError: log(f"Invalid file index [{cls.current_file_index}]") log("Files in Cookie.dir_path:") for file in cls.files(): log(f"{file}") return log(f"Importing cookies from: {cls.current_file_path.name}") with open(cls.current_file_path, encoding="utf-8") as file: cls.current_data = json.load(file) cls.image_token = [ x for x in cls.current_data if x.get("name").startswith("_U") ] cls.image_token = cls.image_token[0].get("value") @classmethod def import_next(cls: type, discard: bool = False) -> None: """ Cycle through to the next cookies file then import it. discard (bool): True -Mark the previous file to be ignored for the remainder of the current session. Otherwise cycle through all available cookie files (sharing the workload and 'resting' when not in use). """ if not hasattr(cls, "current_file_path"): cls.import_data() return with contextlib.suppress(AttributeError): # Will fail on first instantiation because no current_file_path if discard: cls.ignore_files.add(cls.current_file_path) else: Cookie.current_file_index += 1 if Cookie.current_file_index >= len(cls.files()): Cookie.current_file_index = 0 Cookie.import_data() class Query: """ A convenience class that wraps around EdgeGPT.Chatbot to encapsulate input, config, and output all together. Relies on Cookie class for authentication unless ignore_cookies=True """ index = [] image_dir_path = Path.cwd().resolve() / "bing_images" def __init__( self, prompt: str, style: ConversationStyle = "precise", content_type: str = "text", cookie_files: set[Path] = None, ignore_cookies: bool = False, echo: bool = True, echo_prompt: bool = False, locale: str = "en-GB", simplify_response: bool = True, ) -> None: """ Arguments: prompt: Text to enter into Bing Chat style: creative, balanced, or precise content_type: "text" for Bing Chat; "image" for Dall-e ignore_cookies (bool): Ignore cookie data altogether echo: Print something to confirm request made echo_prompt: Print confirmation of the evaluated prompt simplify_response: True -> single simplified prompt/response exchange cookie_files: iterable of Paths or strings of cookie files (json) Files in Cookie.dir_path will also be used if they exist. This defaults to the current working directory, so set Cookie.dir_path before creating a Query if your cookie files are elsewhere. """ self.__class__.index += [self] self.prompt = prompt self.locale = locale self.simplify_response = simplify_response self.ignore_cookies = ignore_cookies if not ignore_cookies: if cookie_files: # Convert singular argument to an iterable: if isinstance(cookie_files, (str, Path)): cookie_files = {cookie_files} # Check all elements exist and are Paths: cookie_files = { Path(x).resolve() for x in cookie_files if isinstance(x, (str, Path)) and x } Cookie.supplied_files = cookie_files files = Cookie.files() # includes .supplied_files if Cookie.rotate_cookies: Cookie.import_next() else: Cookie.import_data() if content_type == "text": self.style = style self.log_and_send_query(echo, echo_prompt) if content_type == "image": self.create_image() def log_and_send_query(self, echo: bool, echo_prompt: bool) -> None: self.response = asyncio.run(self.send_to_bing(echo, echo_prompt)) if not hasattr(Cookie, "current_data"): name = "" else: name = Cookie.current_file_path.name if not Cookie.request_count.get(name): Cookie.request_count[name] = 1 else: Cookie.request_count[name] += 1 def create_image(self) -> None: image_generator = ImageGen(Cookie.image_token) image_generator.save_images( image_generator.get_images(self.prompt), output_dir=self.__class__.image_dir_path, ) async def send_to_bing(self, echo: bool = True, echo_prompt: bool = False) -> str: """Creat, submit, then close a Chatbot instance. Return the response""" retries = len(Cookie.files()) or 1 while retries: if not hasattr(Cookie, "current_data"): bot = await Chatbot.create() else: bot = await Chatbot.create(cookies=Cookie.current_data) if echo_prompt: log(f"{self.prompt=}") if echo: log("Waiting for response...") if self.style.lower() not in "creative balanced precise".split(): self.style = "precise" try: return await bot.ask( prompt=self.prompt, conversation_style=getattr(ConversationStyle, self.style), simplify_response=self.simplify_response, locale=self.locale, ) except Exception as ex: log( f"Exception: [{Cookie.current_file_path.name} may have exceeded the daily limit]\n{ex}", ) Cookie.import_next(discard=True) retries -= 1 finally: await bot.close() return None @property def output(self) -> str: """The response from a completed Chatbot request""" if not self.simplify_response: return [ x.get("text") or x.get("hiddenText") for x in self.response["item"]["messages"] if x["author"] == "bot" ] try: return self.response["text"] except TypeError as te: raise TypeError( f"{te}\n(No response received - probably rate throttled...)", ) from te @property def sources(self) -> list[list[dict]]: """The source names and details parsed from a completed Chatbot request""" if self.simplify_response: return self.response["sources_text"] return [ x.get("sourceAttributions") or [] for x in self.response["item"]["messages"] if x["author"] == "bot" ] @property def sources_dict(self) -> dict[int, str]: """The source names and details as a dictionary""" if self.simplify_response: text = self.response["sources_text"] sources = enumerate(re.findall(r"\((http.*?)\)", text)) return {index + 1: value for index, value in sources} all_sources = [] name = "providerDisplayName" url = "seeMoreUrl" for sources in self.sources: if not sources: continue data = { index + 1: source[url] for index, source in enumerate(sources) if name in source and url in source } all_sources += [data] return all_sources @property def code_block_formats(self) -> list[str]: """ Extract a list of programming languages/formats used in code blocks """ regex = r"``` *(\b\w+\b\+*) *" if self.simplify_response: return re.findall(regex, self.output) return re.findall(regex, "\n".join(self.output)) @property def code_blocks(self) -> list[str]: """ Return a list of code blocks (```) or snippets (`) as strings. If the response contains a mix of snippets and code blocks, return the code blocks only. This method is not suitable if the main text response includes either of the delimiters but not as part of an actual snippet or code block. For example: 'In Markdown, the back-tick (`) is used to denote a code snippet' """ final_blocks = [] if isinstance(self.output, str): # I.e. simplify_response is True separator = "```" if "```" in self.output else "`" code_blocks = self.output.split(separator)[1:-1:2] if separator == "`": return code_blocks else: code_blocks = [] for response in self.output: separator = "```" if "```" in response else "`" code_blocks.extend(response.split(separator)[1:-1:2]) code_blocks = [x for x in code_blocks if x] # Remove language name if present: for block in code_blocks: lines = block.splitlines() code = lines[1:] if re.match(" *\\w+ *", lines[0]) else lines final_blocks += ["\n".join(code).removeprefix(separator)] return [x for x in final_blocks if x] @property def code(self) -> str: """ Extract and join any snippets of code or formatted data in the response """ return "\n\n".join(self.code_blocks) @property def suggestions(self) -> list[str]: """Follow-on questions suggested by the Chatbot""" if self.simplify_response: return self.response["suggestions"] try: return [ x["text"] for x in self.response["item"]["messages"][1]["suggestedResponses"] ] except KeyError: return None def __repr__(self) -> str: return f"" def __str__(self) -> str: return self.output if self.simplify_response else "\n\n".join(self.output) class ImageQuery(Query): def __init__(self, prompt: str, **kwargs) -> None: kwargs["content_type"] = "image" super().__init__(prompt, **kwargs) def __repr__(self) -> str: return f"" def test_cookie_rotation() -> None: for i in range(1, 50): q = Query( f"What is {i} in Roman numerals? Give the answer in JSON", style="precise", ) log(f"{i}: {Cookie.current_file_path.name}") log(q.code) log(f"Cookie count: {Cookie.request_count.get(Cookie.current_file_path.name)}") def test_features() -> Query: try: q = Query( f"What is {i} in Roman numerals? Give the answer in JSON", style="precise", ) log(f"{i}: {Cookie.current_file_path.name}") print(f"{Cookie.current_file_index=}") print(f"{Cookie.current_file_path=}") print(f"{Cookie.current_data=}") print(f"{Cookie.dir_path=}") print(f"{Cookie.search_pattern=}") print(f"{Cookie.files()=}") print(f"{Cookie.image_token=}") print(f"{Cookie.import_next(discard=True)=}") print(f"{Cookie.rotate_cookies=}") print(f"{Cookie.files()=}") print(f"{Cookie.ignore_files=}") print(f"{Cookie.supplied_files=}") print( f"{Cookie.request_count=}" ) # Keeps a tally of requests made in using each cookie file during this session print(f"{q=}") print(f"{q.prompt=}") print(f"{q.ignore_cookies=}") print(f"{q.style=}") print(f"{q.simplify_response=}") print(f"{q.locale=}") print(f"{q.output=}") print(q) print(f"{q.sources=}") print(f"{q.sources_dict=}") print(f"{q.suggestions=}") print(f"{q.code=}") # All code as a single string print(f"{q.code_blocks=}") # Individual code blocks print( f"{q.code_block_formats=}" ) # The language/format of each code block (if given) print(f"{Query.index=}") # Keeps an index of Query objects created print(f"{Query.image_dir_path=}") except Exception as E: raise Exception(E) from E finally: return q ================================================ FILE: src/EdgeGPT/ImageGen.py ================================================ # Open pull requests and issues at https://github.com/acheong08/BingImageCreator import BingImageCreator ImageGen = BingImageCreator.ImageGen ImageGenAsync = BingImageCreator.ImageGenAsync main = BingImageCreator.main if __name__ == "__main__": main() ================================================ FILE: src/EdgeGPT/__init__.py ================================================ ================================================ FILE: src/EdgeGPT/chathub.py ================================================ import asyncio import json import os import ssl import sys from time import time from typing import Generator from typing import List from typing import Union import aiohttp import certifi import httpx from BingImageCreator import ImageGenAsync from .constants import DELIMITER from .constants import HEADERS from .constants import HEADERS_INIT_CONVER from .conversation import Conversation from .conversation_style import CONVERSATION_STYLE_TYPE from .request import ChatHubRequest from .utilities import append_identifier from .utilities import get_ran_hex from .utilities import guess_locale ssl_context = ssl.create_default_context() ssl_context.load_verify_locations(certifi.where()) class ChatHub: def __init__( self, conversation: Conversation, proxy: str = None, cookies: Union[List[dict], None] = None, ) -> None: self.aio_session = None self.request: ChatHubRequest self.loop: bool self.task: asyncio.Task self.request = ChatHubRequest( conversation_signature=conversation.struct["conversationSignature"], client_id=conversation.struct["clientId"], conversation_id=conversation.struct["conversationId"], ) self.cookies = cookies self.proxy: str = proxy proxy = ( proxy or os.environ.get("all_proxy") or os.environ.get("ALL_PROXY") or os.environ.get("https_proxy") or os.environ.get("HTTPS_PROXY") or None ) if proxy is not None and proxy.startswith("socks5h://"): proxy = "socks5://" + proxy[len("socks5h://") :] self.session = httpx.AsyncClient( proxies=proxy, timeout=900, headers=HEADERS_INIT_CONVER, ) async def get_conversation( self, conversation_id: str = None, conversation_signature: str = None, client_id: str = None, ) -> dict: conversation_id = conversation_id or self.request.conversation_id conversation_signature = ( conversation_signature or self.request.conversation_signature ) client_id = client_id or self.request.client_id url = f"https://sydney.bing.com/sydney/GetConversation?conversationId={conversation_id}&source=cib&participantId={client_id}&conversationSignature={conversation_signature}&traceId={get_ran_hex()}" response = await self.session.get(url) return response.json() async def get_activity(self) -> dict: url = "https://www.bing.com/turing/conversation/chats" headers = HEADERS_INIT_CONVER.copy() if self.cookies is not None: for cookie in self.cookies: if cookie["name"] == "_U": headers["Cookie"] = f"SUID=A; _U={cookie['value']};" break response = await self.session.get(url, headers=headers) return response.json() async def ask_stream( self, prompt: str, wss_link: str = None, conversation_style: CONVERSATION_STYLE_TYPE = None, raw: bool = False, webpage_context: Union[str, None] = None, search_result: bool = False, locale: str = guess_locale(), ) -> Generator[bool, Union[dict, str], None]: """ """ cookies = {} if self.cookies is not None: for cookie in self.cookies: cookies[cookie["name"]] = cookie["value"] self.aio_session = aiohttp.ClientSession(cookies=cookies) # Check if websocket is closed wss = await self.aio_session.ws_connect( wss_link or "wss://sydney.bing.com/sydney/ChatHub", ssl=ssl_context, headers=HEADERS, proxy=self.proxy, ) await self._initial_handshake(wss) # Construct a ChatHub request self.request.update( prompt=prompt, conversation_style=conversation_style, webpage_context=webpage_context, search_result=search_result, locale=locale, ) # Send request await wss.send_str(append_identifier(self.request.struct)) draw = False resp_txt = "" result_text = "" resp_txt_no_link = "" retry_count = 5 while not wss.closed: msg = await wss.receive_str() if not msg: retry_count -= 1 if retry_count == 0: raise Exception("No response from server") continue if isinstance(msg, str): objects = msg.split(DELIMITER) else: continue for obj in objects: if int(time()) % 6 == 0: await wss.send_str(append_identifier({"type": 6})) if obj is None or not obj: continue response = json.loads(obj) # print(response) if response.get("type") == 1 and response["arguments"][0].get( "messages", ): if not draw: if ( response["arguments"][0]["messages"][0].get( "messageType", ) == "GenerateContentQuery" ): try: async with ImageGenAsync( all_cookies=self.cookies, ) as image_generator: images = await image_generator.get_images( response["arguments"][0]["messages"][0]["text"], ) for i, image in enumerate(images): resp_txt = f"{resp_txt}\n![image{i}]({image})" draw = True except Exception as e: print(e) continue if ( ( response["arguments"][0]["messages"][0]["contentOrigin"] != "Apology" ) and not draw and not raw ): resp_txt = result_text + response["arguments"][0][ "messages" ][0]["adaptiveCards"][0]["body"][0].get("text", "") resp_txt_no_link = result_text + response["arguments"][0][ "messages" ][0].get("text", "") if response["arguments"][0]["messages"][0].get( "messageType", ): resp_txt = ( resp_txt + response["arguments"][0]["messages"][0][ "adaptiveCards" ][0]["body"][0]["inlines"][0].get("text") + "\n" ) result_text = ( result_text + response["arguments"][0]["messages"][0][ "adaptiveCards" ][0]["body"][0]["inlines"][0].get("text") + "\n" ) if not raw: yield False, resp_txt elif response.get("type") == 2: if response["item"]["result"].get("error"): await self.close() raise Exception( f"{response['item']['result']['value']}: {response['item']['result']['message']}", ) if draw: cache = response["item"]["messages"][1]["adaptiveCards"][0][ "body" ][0]["text"] response["item"]["messages"][1]["adaptiveCards"][0]["body"][0][ "text" ] = (cache + resp_txt) if ( response["item"]["messages"][-1]["contentOrigin"] == "Apology" and resp_txt ): response["item"]["messages"][-1]["text"] = resp_txt_no_link response["item"]["messages"][-1]["adaptiveCards"][0]["body"][0][ "text" ] = resp_txt print( "Preserved the message from being deleted", file=sys.stderr, ) await wss.close() if not self.aio_session.closed: await self.aio_session.close() yield True, response return if response.get("type") != 2: if response.get("type") == 6: await wss.send_str(append_identifier({"type": 6})) elif response.get("type") == 7: await wss.send_str(append_identifier({"type": 7})) elif raw: yield False, response async def _initial_handshake(self, wss) -> None: await wss.send_str(append_identifier({"protocol": "json", "version": 1})) await wss.receive_str() await wss.send_str(append_identifier({"type": 6})) async def delete_conversation( self, conversation_id: str = None, conversation_signature: str = None, client_id: str = None, ) -> None: conversation_id = conversation_id or self.request.conversation_id conversation_signature = ( conversation_signature or self.request.conversation_signature ) client_id = client_id or self.request.client_id url = "https://sydney.bing.com/sydney/DeleteSingleConversation" await self.session.post( url, json={ "conversationId": conversation_id, "conversationSignature": conversation_signature, "participant": {"id": client_id}, "source": "cib", "optionsSets": ["autosave"], }, ) async def close(self) -> None: await self.session.aclose() ================================================ FILE: src/EdgeGPT/constants.py ================================================ import socket import uuid take_ip_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) take_ip_socket.connect(("8.8.8.8", 80)) FORWARDED_IP: str = take_ip_socket.getsockname()[0] take_ip_socket.close() DELIMITER = "\x1e" HEADERS = { "accept": "application/json", "accept-language": "en-US;q=0.9", "accept-encoding": "gzip, deflate, br, zsdch", "content-type": "application/json", "sec-ch-ua": '"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"', "sec-ch-ua-arch": '"x86"', "sec-ch-ua-bitness": '"64"', "sec-ch-ua-full-version": '"115.0.1901.188"', "sec-ch-ua-full-version-list": '"Not/A)Brand";v="99.0.0.0", "Microsoft Edge";v="115.0.1901.188", "Chromium";v="115.0.5790.114"', "sec-ch-ua-mobile": "?0", "sec-ch-ua-model": "", "sec-ch-ua-platform": '"Windows"', "sec-ch-ua-platform-version": '"15.0.0"', "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin", "sec-ms-gec-version": "1-115.0.1901.188", "x-ms-client-request-id": str(uuid.uuid4()), "x-ms-useragent": "azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.3 OS/Windows", "Referer": "https://www.bing.com/search?", "Referrer-Policy": "origin-when-cross-origin", "x-forwarded-for": FORWARDED_IP, } HEADERS_INIT_CONVER = { "authority": "www.bing.com", "accept": "application/json", "accept-language": "en-US;q=0.9", "cache-control": "max-age=0", "sec-ch-ua": '"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"', "sec-ch-ua-arch": '"x86"', "sec-ch-ua-bitness": '"64"', "sec-ch-ua-full-version": '"115.0.1901.188"', "sec-ch-ua-full-version-list": '"Not/A)Brand";v="99.0.0.0", "Microsoft Edge";v="115.0.1901.188", "Chromium";v="115.0.5790.114"', "sec-ch-ua-mobile": "?0", "sec-ch-ua-model": '""', "sec-ch-ua-platform": '"Windows"', "sec-ch-ua-platform-version": '"15.0.0"', "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.188", "x-edge-shopping-flag": "1", "x-forwarded-for": FORWARDED_IP, } ================================================ FILE: src/EdgeGPT/conversation.py ================================================ import json import os from typing import List from typing import Union import httpx from .constants import HEADERS_INIT_CONVER from .exceptions import NotAllowedToAccess class Conversation: def __init__( self, proxy: Union[str, None] = None, async_mode: bool = False, cookies: Union[List[dict], None] = None, ) -> None: if async_mode: return self.struct: dict = { "conversationId": None, "clientId": None, "conversationSignature": None, "result": {"value": "Success", "message": None}, } self.proxy = proxy proxy = ( proxy or os.environ.get("all_proxy") or os.environ.get("ALL_PROXY") or os.environ.get("https_proxy") or os.environ.get("HTTPS_PROXY") or None ) if proxy is not None and proxy.startswith("socks5h://"): proxy = "socks5://" + proxy[len("socks5h://") :] self.session = httpx.Client( proxies=proxy, timeout=900, headers=HEADERS_INIT_CONVER, ) if cookies: for cookie in cookies: self.session.cookies.set(cookie["name"], cookie["value"]) # Send GET request response = self.session.get( url=os.environ.get("BING_PROXY_URL") or "https://edgeservices.bing.com/edgesvc/turing/conversation/create", ) if response.status_code != 200: print(f"Status code: {response.status_code}") print(response.text) print(response.url) raise Exception("Authentication failed") try: self.struct = response.json() except (json.decoder.JSONDecodeError, NotAllowedToAccess) as exc: raise Exception( "Authentication failed. You have not been accepted into the beta.", ) from exc if self.struct["result"]["value"] == "UnauthorizedRequest": raise NotAllowedToAccess(self.struct["result"]["message"]) @staticmethod async def create( proxy: Union[str, None] = None, cookies: Union[List[dict], None] = None, ) -> "Conversation": self = Conversation(async_mode=True) self.struct = { "conversationId": None, "clientId": None, "conversationSignature": None, "result": {"value": "Success", "message": None}, } self.proxy = proxy proxy = ( proxy or os.environ.get("all_proxy") or os.environ.get("ALL_PROXY") or os.environ.get("https_proxy") or os.environ.get("HTTPS_PROXY") or None ) if proxy is not None and proxy.startswith("socks5h://"): proxy = "socks5://" + proxy[len("socks5h://") :] transport = httpx.AsyncHTTPTransport(retries=900) # Convert cookie format to httpx format formatted_cookies = None if cookies: formatted_cookies = httpx.Cookies() for cookie in cookies: formatted_cookies.set(cookie["name"], cookie["value"]) async with httpx.AsyncClient( proxies=proxy, timeout=30, headers=HEADERS_INIT_CONVER, transport=transport, cookies=formatted_cookies, ) as client: # Send GET request response = await client.get( url=os.environ.get("BING_PROXY_URL") or "https://www.bing.com/turing/conversation/create", follow_redirects=True, ) if response.status_code != 200: print(f"Status code: {response.status_code}") print(response.text) print(response.url) raise Exception("Authentication failed") try: self.struct = response.json() except (json.decoder.JSONDecodeError, NotAllowedToAccess) as exc: print(response.text) raise Exception( "Authentication failed. You have not been accepted into the beta.", ) from exc if self.struct["result"]["value"] == "UnauthorizedRequest": raise NotAllowedToAccess(self.struct["result"]["message"]) return self ================================================ FILE: src/EdgeGPT/conversation_style.py ================================================ from enum import Enum try: from typing import Literal, Union except ImportError: from typing_extensions import Literal from typing import Optional class ConversationStyle(Enum): creative = [ "nlu_direct_response_filter", "deepleo", "disable_emoji_spoken_text", "responsible_ai_policy_235", "enablemm", "h3imaginative", "objopinion", "dsblhlthcrd", "dv3sugg", "autosave", "clgalileo", "gencontentv3", ] balanced = [ "nlu_direct_response_filter", "deepleo", "disable_emoji_spoken_text", "responsible_ai_policy_235", "enablemm", "galileo", "saharagenconv5", "objopinion", "dsblhlthcrd", "dv3sugg", "autosave", ] precise = [ "nlu_direct_response_filter", "deepleo", "disable_emoji_spoken_text", "responsible_ai_policy_235", "enablemm", "h3precise", "objopinion", "dsblhlthcrd", "dv3sugg", "autosave", "clgalileo", "gencontentv3", ] CONVERSATION_STYLE_TYPE = Optional[ Union[ConversationStyle, Literal["creative", "balanced", "precise"]] ] ================================================ FILE: src/EdgeGPT/exceptions.py ================================================ class NotAllowedToAccess(Exception): pass ================================================ FILE: src/EdgeGPT/locale.py ================================================ from enum import Enum try: from typing import Literal, Union except ImportError: from typing_extensions import Literal from typing import Optional class LocationHint(Enum): USA = { "locale": "en-US", "LocationHint": [ { "country": "United States", "state": "California", "city": "Los Angeles", "timezoneoffset": 8, "countryConfidence": 8, "Center": { "Latitude": 34.0536909, "Longitude": -118.242766, }, "RegionType": 2, "SourceType": 1, }, ], } CHINA = { "locale": "zh-CN", "LocationHint": [ { "country": "China", "state": "", "city": "Beijing", "timezoneoffset": 8, "countryConfidence": 8, "Center": { "Latitude": 39.9042, "Longitude": 116.4074, }, "RegionType": 2, "SourceType": 1, }, ], } EU = { "locale": "en-IE", "LocationHint": [ { "country": "Norway", "state": "", "city": "Oslo", "timezoneoffset": 1, "countryConfidence": 8, "Center": { "Latitude": 59.9139, "Longitude": 10.7522, }, "RegionType": 2, "SourceType": 1, }, ], } UK = { "locale": "en-GB", "LocationHint": [ { "country": "United Kingdom", "state": "", "city": "London", "timezoneoffset": 0, "countryConfidence": 8, "Center": { "Latitude": 51.5074, "Longitude": -0.1278, }, "RegionType": 2, "SourceType": 1, }, ], } LOCATION_HINT_TYPES = Optional[Union[LocationHint, Literal["USA", "CHINA", "EU", "UK"]]] ================================================ FILE: src/EdgeGPT/main.py ================================================ import argparse import asyncio import json import re import sys from pathlib import Path from EdgeGPT.EdgeGPT import Chatbot from prompt_toolkit import PromptSession from prompt_toolkit.auto_suggest import AutoSuggestFromHistory from prompt_toolkit.completion import WordCompleter from prompt_toolkit.history import InMemoryHistory from prompt_toolkit.key_binding import KeyBindings from rich.live import Live from rich.markdown import Markdown def create_session() -> PromptSession: kb = KeyBindings() @kb.add("enter") def _(event) -> None: buffer_text = event.current_buffer.text if buffer_text.startswith("!"): event.current_buffer.validate_and_handle() else: event.current_buffer.insert_text("\n") @kb.add("escape") def _(event) -> None: if event.current_buffer.complete_state: # event.current_buffer.cancel_completion() event.current_buffer.text = "" return PromptSession(key_bindings=kb, history=InMemoryHistory()) def create_completer(commands: list, pattern_str: str = "$") -> WordCompleter: return WordCompleter(words=commands, pattern=re.compile(pattern_str)) def _create_history_logger(f) -> callable: def logger(*args, **kwargs) -> None: tmp = sys.stdout sys.stdout = f print(*args, **kwargs, flush=True) sys.stdout = tmp return logger async def get_input_async( session: PromptSession = None, completer: WordCompleter = None, ) -> str: """ Multiline input function. """ return await session.prompt_async( completer=completer, multiline=True, auto_suggest=AutoSuggestFromHistory(), ) async def async_main(args: argparse.Namespace) -> None: """ Main function """ print("Initializing...") print("Enter `alt+enter` or `escape+enter` to send a message") # Read and parse cookies cookies = None if args.cookie_file: file_path = Path(args.cookie_file) if file_path.exists(): with file_path.open("r", encoding="utf-8") as f: cookies = json.load(f) bot = await Chatbot.create(proxy=args.proxy, cookies=cookies) session = create_session() completer = create_completer(["!help", "!exit", "!reset"]) initial_prompt = args.prompt # Log chat history def p_hist(*args, **kwargs) -> None: pass if args.history_file: history_file_path = Path(args.history_file) f = history_file_path.open("a+", encoding="utf-8") p_hist = _create_history_logger(f) while True: print("\nYou:") p_hist("\nYou:") if initial_prompt: question = initial_prompt print(question) initial_prompt = None else: question = ( input() if args.enter_once else await get_input_async(session=session, completer=completer) ) print() p_hist(question + "\n") if question == "!exit": await bot.close() break if question == "!help": print( """ !help - Show this help message !exit - Exit the program !reset - Reset the conversation """, ) continue if question == "!reset": await bot.reset() continue print("Bot:") p_hist("Bot:") if args.no_stream: response = ( await bot.ask( prompt=question, conversation_style=args.style, wss_link=args.wss_link, search_result=args.search_result, locale=args.locale, ) )["item"]["messages"][-1]["adaptiveCards"][0]["body"][0]["text"] print(response) p_hist(response) else: wrote = 0 if args.rich: md = Markdown("") with Live(md, auto_refresh=False) as live: async for final, response in bot.ask_stream( prompt=question, conversation_style=args.style, wss_link=args.wss_link, search_result=args.search_result, locale=args.locale, ): if not final: if not wrote: p_hist(response, end="") else: p_hist(response[wrote:], end="") if wrote > len(response): print(md) print(Markdown("***Bing revoked the response.***")) wrote = len(response) md = Markdown(response) live.update(md, refresh=True) else: async for final, response in bot.ask_stream( prompt=question, conversation_style=args.style, wss_link=args.wss_link, search_result=args.search_result, locale=args.locale, ): if not final: if not wrote: print(response, end="", flush=True) p_hist(response, end="") else: print(response[wrote:], end="", flush=True) p_hist(response[wrote:], end="") wrote = len(response) print() p_hist() if args.history_file: f.close() await bot.close() def main() -> None: print( """ EdgeGPT - A demo of reverse engineering the Bing GPT chatbot Repo: github.com/acheong08/EdgeGPT By: Antonio Cheong !help for help Type !exit to exit """, ) parser = argparse.ArgumentParser() parser.add_argument("--enter-once", action="store_true") parser.add_argument("--search-result", action="store_true") parser.add_argument("--no-stream", action="store_true") parser.add_argument("--rich", action="store_true") parser.add_argument( "--proxy", help="Proxy URL (e.g. socks5://127.0.0.1:1080)", type=str, ) parser.add_argument( "--wss-link", help="WSS URL(e.g. wss://sydney.bing.com/sydney/ChatHub)", type=str, default="wss://sydney.bing.com/sydney/ChatHub", ) parser.add_argument( "--style", choices=["creative", "balanced", "precise"], default="balanced", ) parser.add_argument( "--prompt", type=str, default="", required=False, help="prompt to start with", ) parser.add_argument( "--cookie-file", type=str, default="", required=False, help="path to cookie file", ) parser.add_argument( "--history-file", type=str, default="", required=False, help="path to history file", ) parser.add_argument( "--locale", type=str, default="en-US", required=False, help="your locale", ) args = parser.parse_args() asyncio.run(async_main(args)) ================================================ FILE: src/EdgeGPT/request.py ================================================ import uuid from datetime import datetime from typing import Union from .conversation_style import CONVERSATION_STYLE_TYPE from .conversation_style import ConversationStyle from .utilities import get_location_hint_from_locale from .utilities import get_ran_hex from .utilities import guess_locale class ChatHubRequest: def __init__( self, conversation_signature: str, client_id: str, conversation_id: str, invocation_id: int = 3, ) -> None: self.struct: dict = {} self.client_id: str = client_id self.conversation_id: str = conversation_id self.conversation_signature: str = conversation_signature self.invocation_id: int = invocation_id def update( self, prompt: str, conversation_style: CONVERSATION_STYLE_TYPE, webpage_context: Union[str, None] = None, search_result: bool = False, locale: str = guess_locale(), ) -> None: options = [ "deepleo", "enable_debug_commands", "disable_emoji_spoken_text", "enablemm", ] if conversation_style: if not isinstance(conversation_style, ConversationStyle): conversation_style = getattr(ConversationStyle, conversation_style) options = conversation_style.value message_id = str(uuid.uuid4()) # Get the current local time now_local = datetime.now() # Get the current UTC time now_utc = datetime.utcnow() # Calculate the time difference between local and UTC time timezone_offset = now_local - now_utc # Get the offset in hours and minutes offset_hours = int(timezone_offset.total_seconds() // 3600) offset_minutes = int((timezone_offset.total_seconds() % 3600) // 60) # Format the offset as a string offset_string = f"{offset_hours:+03d}:{offset_minutes:02d}" # Get current time timestamp = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") + offset_string self.struct = { "arguments": [ { "source": "cib", "optionsSets": options, "allowedMessageTypes": [ "ActionRequest", "Chat", "Context", "InternalSearchQuery", "InternalSearchResult", "Disengaged", "InternalLoaderMessage", "Progress", "RenderCardRequest", "AdsQuery", "SemanticSerp", "GenerateContentQuery", "SearchQuery", ], "sliceIds": [ "winmuid1tf", "styleoff", "ccadesk", "smsrpsuppv4cf", "ssrrcache", "contansperf", "crchatrev", "winstmsg2tf", "creatgoglt", "creatorv2t", "sydconfigoptt", "adssqovroff", "530pstho", "517opinion", "418dhlth", "512sprtic1s0", "emsgpr", "525ptrcps0", "529rweas0", "515oscfing2s0", "524vidansgs0", ], "verbosity": "verbose", "traceId": get_ran_hex(32), "isStartOfSession": self.invocation_id == 3, "message": { "locale": locale, "market": locale, "region": locale[-2:], # en-US -> US "locationHints": get_location_hint_from_locale(locale), "timestamp": timestamp, "author": "user", "inputMethod": "Keyboard", "text": prompt, "messageType": "Chat", "messageId": message_id, "requestId": message_id, }, "tone": conversation_style.name.capitalize(), # Make first letter uppercase "requestId": message_id, "conversationSignature": self.conversation_signature, "participant": { "id": self.client_id, }, "conversationId": self.conversation_id, }, ], "invocationId": str(self.invocation_id), "target": "chat", "type": 4, } if search_result: have_search_result = [ "InternalSearchQuery", "InternalSearchResult", "InternalLoaderMessage", "RenderCardRequest", ] self.struct["arguments"][0]["allowedMessageTypes"] += have_search_result if webpage_context: self.struct["arguments"][0]["previousMessages"] = [ { "author": "user", "description": webpage_context, "contextType": "WebPage", "messageType": "Context", "messageId": "discover-web--page-ping-mriduna-----", }, ] self.invocation_id += 1 # print(timestamp) ================================================ FILE: src/EdgeGPT/utilities.py ================================================ import json import locale import random import sys from typing import Union from .constants import DELIMITER from .locale import LocationHint def append_identifier(msg: dict) -> str: # Convert dict to json string return json.dumps(msg, ensure_ascii=False) + DELIMITER def get_ran_hex(length: int = 32) -> str: return "".join(random.choice("0123456789abcdef") for _ in range(length)) def get_location_hint_from_locale(locale: str) -> Union[dict, None]: locale = locale.lower() if locale == "en-gb": hint = LocationHint.UK.value elif locale == "en-ie": hint = LocationHint.EU.value elif locale == "zh-cn": hint = LocationHint.CHINA.value else: hint = LocationHint.USA.value return hint.get("LocationHint") def guess_locale() -> str: if sys.platform.startswith("win"): return "en-us" loc, _ = locale.getlocale() return loc.replace("_", "-") if loc else "en-us" ================================================ FILE: test_base.py ================================================ import json import pytest from EdgeGPT.EdgeGPT import Chatbot from EdgeGPT.EdgeGPT import ConversationStyle pytest_plugins = ("pytest_asyncio",) from os import getenv @pytest.mark.asyncio() async def test_ask() -> None: bot = await Chatbot.create(cookies=getenv("EDGE_COOKIES")) response = await bot.ask( prompt="find me some information about the new ai released by meta.", conversation_style=ConversationStyle.balanced, simplify_response=True, ) await bot.close() print(json.dumps(response, indent=2)) assert response