Merge 9abb009c4c into e32877f3a4
This commit is contained in:
commit
90b5f77e21
6
.gitignore
vendored
6
.gitignore
vendored
@ -3,13 +3,15 @@
|
||||
# Project-related files
|
||||
|
||||
# Run logs
|
||||
backend/run_logs/*
|
||||
screenshottocode/run_logs/*
|
||||
|
||||
# Weird Docker setup related files
|
||||
backend/backend/*
|
||||
screenshottocode/screenshottocode/*
|
||||
|
||||
# Env vars
|
||||
frontend/dist/*
|
||||
frontend/.env.local
|
||||
screenshottocode/.env
|
||||
.env
|
||||
|
||||
# Mac files
|
||||
|
||||
33
README.md
33
README.md
@ -26,29 +26,31 @@ See the [Examples](#-examples) section below for more demos.
|
||||
|
||||
## 🛠 Getting Started
|
||||
|
||||
The app has a React/Vite frontend and a FastAPI backend. You will need an OpenAI API key with access to the GPT-4 Vision API.
|
||||
The app has a React/Vite frontend and a FastAPI backend. You will need an OpenAI API key with access to the GPT-4 Vision API or Google API key for Gemini Pro Vision.
|
||||
|
||||
Run the backend (I use Poetry for package management - `pip install poetry` if you don't have it):
|
||||
## Installation
|
||||
Install package with your preferred python package management app.
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
echo "OPENAI_API_KEY=sk-your-key" > .env
|
||||
poetry install
|
||||
poetry shell
|
||||
poetry run uvicorn main:app --reload --port 7001
|
||||
pip install https://github.com/theonlyamos/screenshot-to-code/archive/v0.1.0.zip
|
||||
```
|
||||
|
||||
Run the frontend:
|
||||
## Usage
|
||||
After installation, you can run the package from the terminal/command prompt and access the app on http://localhost:7001.
|
||||
Model Configuration can also be found on the settings modal of the web ui.
|
||||
|
||||
### Run with GPT-4 Vision
|
||||
You can run this with GPT-4 Vision by using the following command line:
|
||||
```bash
|
||||
cd frontend
|
||||
yarn
|
||||
yarn dev
|
||||
screenshottocode --api-key "<Your Api Key>"
|
||||
```
|
||||
|
||||
Open http://localhost:5173 to use the app.
|
||||
|
||||
If you prefer to run the backend on a different port, update VITE_WS_BACKEND_URL in `frontend/.env.local`
|
||||
### Run with Gemini Pro Vision
|
||||
You can also run this with Gemini Pro Vision by using the following command line instead:
|
||||
```bash
|
||||
screenshottocode --model gemini --api-key "<Your Api Key>"
|
||||
```
|
||||
Using Gemini Pro Vision model is a lot slower than using GPT-4 Vision since Gemini has a maximum output token of 2048. This means the results are fetched iteratively.
|
||||
|
||||
For debugging purposes, if you don't want to waste GPT4-Vision credits, you can run the backend in mock mode (which streams a pre-recorded response):
|
||||
|
||||
@ -62,7 +64,8 @@ MOCK=true poetry run uvicorn main:app --reload --port 7001
|
||||
|
||||
## Docker
|
||||
|
||||
If you have Docker installed on your system, in the root directory, run:
|
||||
If you have Docker installed on your system, in the root directory,
|
||||
clone the reposition and then run:
|
||||
|
||||
```bash
|
||||
echo "OPENAI_API_KEY=sk-your-key" > .env
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
# Useful for debugging purposes when you don't want to waste GPT4-Vision credits
|
||||
# Setting to True will stream a mock response instead of calling the OpenAI API
|
||||
# TODO: Should only be set to true when value is 'True', not any abitrary truthy value
|
||||
import os
|
||||
|
||||
|
||||
SHOULD_MOCK_AI_RESPONSE = bool(os.environ.get("MOCK", False))
|
||||
|
||||
# Set to True when running in production (on the hosted version)
|
||||
# Used as a feature flag to enable or disable certain features
|
||||
IS_PROD = os.environ.get("IS_PROD", False)
|
||||
@ -1,36 +0,0 @@
|
||||
from typing import Awaitable, Callable, List
|
||||
from openai import AsyncOpenAI
|
||||
from openai.types.chat import ChatCompletionMessageParam, ChatCompletionChunk
|
||||
|
||||
MODEL_GPT_4_VISION = "gpt-4-vision-preview"
|
||||
|
||||
|
||||
async def stream_openai_response(
|
||||
messages: List[ChatCompletionMessageParam],
|
||||
api_key: str,
|
||||
base_url: str | None,
|
||||
callback: Callable[[str], Awaitable[None]],
|
||||
) -> str:
|
||||
client = AsyncOpenAI(api_key=api_key, base_url=base_url)
|
||||
|
||||
model = MODEL_GPT_4_VISION
|
||||
|
||||
# Base parameters
|
||||
params = {"model": model, "messages": messages, "stream": True, "timeout": 600}
|
||||
|
||||
# Add 'max_tokens' only if the model is a GPT4 vision model
|
||||
if model == MODEL_GPT_4_VISION:
|
||||
params["max_tokens"] = 4096
|
||||
params["temperature"] = 0
|
||||
|
||||
stream = await client.chat.completions.create(**params) # type: ignore
|
||||
full_response = ""
|
||||
async for chunk in stream: # type: ignore
|
||||
assert isinstance(chunk, ChatCompletionChunk)
|
||||
content = chunk.choices[0].delta.content or ""
|
||||
full_response += content
|
||||
await callback(content)
|
||||
|
||||
await client.close()
|
||||
|
||||
return full_response
|
||||
550
backend/poetry.lock
generated
550
backend/poetry.lock
generated
@ -1,550 +0,0 @@
|
||||
# This file is automatically @generated by Poetry 1.4.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "anyio"
|
||||
version = "3.7.1"
|
||||
description = "High level compatibility layer for multiple asynchronous event loop implementations"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"},
|
||||
{file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
|
||||
idna = ">=2.8"
|
||||
sniffio = ">=1.1"
|
||||
|
||||
[package.extras]
|
||||
doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"]
|
||||
test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
|
||||
trio = ["trio (<0.22)"]
|
||||
|
||||
[[package]]
|
||||
name = "beautifulsoup4"
|
||||
version = "4.12.2"
|
||||
description = "Screen-scraping library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.0"
|
||||
files = [
|
||||
{file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"},
|
||||
{file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
soupsieve = ">1.2"
|
||||
|
||||
[package.extras]
|
||||
html5lib = ["html5lib"]
|
||||
lxml = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2023.11.17"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"},
|
||||
{file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.7"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
|
||||
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "distro"
|
||||
version = "1.8.0"
|
||||
description = "Distro - an OS platform information API"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "distro-1.8.0-py3-none-any.whl", hash = "sha256:99522ca3e365cac527b44bde033f64c6945d90eb9f769703caaec52b09bbd3ff"},
|
||||
{file = "distro-1.8.0.tar.gz", hash = "sha256:02e111d1dc6a50abb8eed6bf31c3e48ed8b0830d1ea2a1b78c61765c2513fdd8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.2.0"
|
||||
description = "Backport of PEP 654 (exception groups)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
|
||||
{file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
test = ["pytest (>=6)"]
|
||||
|
||||
[[package]]
|
||||
name = "fastapi"
|
||||
version = "0.95.2"
|
||||
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "fastapi-0.95.2-py3-none-any.whl", hash = "sha256:d374dbc4ef2ad9b803899bd3360d34c534adc574546e25314ab72c0c4411749f"},
|
||||
{file = "fastapi-0.95.2.tar.gz", hash = "sha256:4d9d3e8c71c73f11874bcf5e33626258d143252e329a01002f767306c64fb982"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0"
|
||||
starlette = ">=0.27.0,<0.28.0"
|
||||
|
||||
[package.extras]
|
||||
all = ["email-validator (>=1.1.1)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
|
||||
dev = ["pre-commit (>=2.17.0,<3.0.0)", "ruff (==0.0.138)", "uvicorn[standard] (>=0.12.0,<0.21.0)"]
|
||||
doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer-cli (>=0.0.13,<0.0.14)", "typer[all] (>=0.6.1,<0.8.0)"]
|
||||
test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6.5.0,<8.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.982)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.7)", "pyyaml (>=5.3.1,<7.0.0)", "ruff (==0.0.138)", "sqlalchemy (>=1.3.18,<1.4.43)", "types-orjson (==3.6.2)", "types-ujson (==5.7.0.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.14.0"
|
||||
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
|
||||
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpcore"
|
||||
version = "1.0.2"
|
||||
description = "A minimal low-level HTTP client."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7"},
|
||||
{file = "httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
certifi = "*"
|
||||
h11 = ">=0.13,<0.15"
|
||||
|
||||
[package.extras]
|
||||
asyncio = ["anyio (>=4.0,<5.0)"]
|
||||
http2 = ["h2 (>=3,<5)"]
|
||||
socks = ["socksio (>=1.0.0,<2.0.0)"]
|
||||
trio = ["trio (>=0.22.0,<0.23.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.25.2"
|
||||
description = "The next generation HTTP client."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "httpx-0.25.2-py3-none-any.whl", hash = "sha256:a05d3d052d9b2dfce0e3896636467f8a5342fb2b902c819428e1ac65413ca118"},
|
||||
{file = "httpx-0.25.2.tar.gz", hash = "sha256:8b8fcaa0c8ea7b05edd69a094e63a2094c4efcb48129fb757361bc423c0ad9e8"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
anyio = "*"
|
||||
certifi = "*"
|
||||
httpcore = ">=1.0.0,<2.0.0"
|
||||
idna = "*"
|
||||
sniffio = "*"
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotli", "brotlicffi"]
|
||||
cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"]
|
||||
http2 = ["h2 (>=3,<5)"]
|
||||
socks = ["socksio (>=1.0.0,<2.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.6"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
|
||||
{file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.0.0"
|
||||
description = "brain-dead simple config-ini parsing"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
|
||||
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "1.3.7"
|
||||
description = "The official Python library for the openai API"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7.1"
|
||||
files = [
|
||||
{file = "openai-1.3.7-py3-none-any.whl", hash = "sha256:e5c51367a910297e4d1cd33d2298fb87d7edf681edbe012873925ac16f95bee0"},
|
||||
{file = "openai-1.3.7.tar.gz", hash = "sha256:18074a0f51f9b49d1ae268c7abc36f7f33212a0c0d08ce11b7053ab2d17798de"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
anyio = ">=3.5.0,<4"
|
||||
distro = ">=1.7.0,<2"
|
||||
httpx = ">=0.23.0,<1"
|
||||
pydantic = ">=1.9.0,<3"
|
||||
sniffio = "*"
|
||||
tqdm = ">4"
|
||||
typing-extensions = ">=4.5,<5"
|
||||
|
||||
[package.extras]
|
||||
datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "23.2"
|
||||
description = "Core utilities for Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
|
||||
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.3.0"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"},
|
||||
{file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["pre-commit", "tox"]
|
||||
testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "1.10.13"
|
||||
description = "Data validation and settings management using python type hints"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "pydantic-1.10.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:efff03cc7a4f29d9009d1c96ceb1e7a70a65cfe86e89d34e4a5f2ab1e5693737"},
|
||||
{file = "pydantic-1.10.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ecea2b9d80e5333303eeb77e180b90e95eea8f765d08c3d278cd56b00345d01"},
|
||||
{file = "pydantic-1.10.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1740068fd8e2ef6eb27a20e5651df000978edce6da6803c2bef0bc74540f9548"},
|
||||
{file = "pydantic-1.10.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84bafe2e60b5e78bc64a2941b4c071a4b7404c5c907f5f5a99b0139781e69ed8"},
|
||||
{file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc0898c12f8e9c97f6cd44c0ed70d55749eaf783716896960b4ecce2edfd2d69"},
|
||||
{file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:654db58ae399fe6434e55325a2c3e959836bd17a6f6a0b6ca8107ea0571d2e17"},
|
||||
{file = "pydantic-1.10.13-cp310-cp310-win_amd64.whl", hash = "sha256:75ac15385a3534d887a99c713aa3da88a30fbd6204a5cd0dc4dab3d770b9bd2f"},
|
||||
{file = "pydantic-1.10.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c553f6a156deb868ba38a23cf0df886c63492e9257f60a79c0fd8e7173537653"},
|
||||
{file = "pydantic-1.10.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e08865bc6464df8c7d61439ef4439829e3ab62ab1669cddea8dd00cd74b9ffe"},
|
||||
{file = "pydantic-1.10.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e31647d85a2013d926ce60b84f9dd5300d44535a9941fe825dc349ae1f760df9"},
|
||||
{file = "pydantic-1.10.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:210ce042e8f6f7c01168b2d84d4c9eb2b009fe7bf572c2266e235edf14bacd80"},
|
||||
{file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8ae5dd6b721459bfa30805f4c25880e0dd78fc5b5879f9f7a692196ddcb5a580"},
|
||||
{file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f8e81fc5fb17dae698f52bdd1c4f18b6ca674d7068242b2aff075f588301bbb0"},
|
||||
{file = "pydantic-1.10.13-cp311-cp311-win_amd64.whl", hash = "sha256:61d9dce220447fb74f45e73d7ff3b530e25db30192ad8d425166d43c5deb6df0"},
|
||||
{file = "pydantic-1.10.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4b03e42ec20286f052490423682016fd80fda830d8e4119f8ab13ec7464c0132"},
|
||||
{file = "pydantic-1.10.13-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f59ef915cac80275245824e9d771ee939133be38215555e9dc90c6cb148aaeb5"},
|
||||
{file = "pydantic-1.10.13-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a1f9f747851338933942db7af7b6ee8268568ef2ed86c4185c6ef4402e80ba8"},
|
||||
{file = "pydantic-1.10.13-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:97cce3ae7341f7620a0ba5ef6cf043975cd9d2b81f3aa5f4ea37928269bc1b87"},
|
||||
{file = "pydantic-1.10.13-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:854223752ba81e3abf663d685f105c64150873cc6f5d0c01d3e3220bcff7d36f"},
|
||||
{file = "pydantic-1.10.13-cp37-cp37m-win_amd64.whl", hash = "sha256:b97c1fac8c49be29486df85968682b0afa77e1b809aff74b83081cc115e52f33"},
|
||||
{file = "pydantic-1.10.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c958d053453a1c4b1c2062b05cd42d9d5c8eb67537b8d5a7e3c3032943ecd261"},
|
||||
{file = "pydantic-1.10.13-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c5370a7edaac06daee3af1c8b1192e305bc102abcbf2a92374b5bc793818599"},
|
||||
{file = "pydantic-1.10.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d6f6e7305244bddb4414ba7094ce910560c907bdfa3501e9db1a7fd7eaea127"},
|
||||
{file = "pydantic-1.10.13-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3a3c792a58e1622667a2837512099eac62490cdfd63bd407993aaf200a4cf1f"},
|
||||
{file = "pydantic-1.10.13-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c636925f38b8db208e09d344c7aa4f29a86bb9947495dd6b6d376ad10334fb78"},
|
||||
{file = "pydantic-1.10.13-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:678bcf5591b63cc917100dc50ab6caebe597ac67e8c9ccb75e698f66038ea953"},
|
||||
{file = "pydantic-1.10.13-cp38-cp38-win_amd64.whl", hash = "sha256:6cf25c1a65c27923a17b3da28a0bdb99f62ee04230c931d83e888012851f4e7f"},
|
||||
{file = "pydantic-1.10.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8ef467901d7a41fa0ca6db9ae3ec0021e3f657ce2c208e98cd511f3161c762c6"},
|
||||
{file = "pydantic-1.10.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:968ac42970f57b8344ee08837b62f6ee6f53c33f603547a55571c954a4225691"},
|
||||
{file = "pydantic-1.10.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9849f031cf8a2f0a928fe885e5a04b08006d6d41876b8bbd2fc68a18f9f2e3fd"},
|
||||
{file = "pydantic-1.10.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56e3ff861c3b9c6857579de282ce8baabf443f42ffba355bf070770ed63e11e1"},
|
||||
{file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f00790179497767aae6bcdc36355792c79e7bbb20b145ff449700eb076c5f96"},
|
||||
{file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:75b297827b59bc229cac1a23a2f7a4ac0031068e5be0ce385be1462e7e17a35d"},
|
||||
{file = "pydantic-1.10.13-cp39-cp39-win_amd64.whl", hash = "sha256:e70ca129d2053fb8b728ee7d1af8e553a928d7e301a311094b8a0501adc8763d"},
|
||||
{file = "pydantic-1.10.13-py3-none-any.whl", hash = "sha256:b87326822e71bd5f313e7d3bfdc77ac3247035ac10b0c0618bd99dcf95b1e687"},
|
||||
{file = "pydantic-1.10.13.tar.gz", hash = "sha256:32c8b48dcd3b2ac4e78b0ba4af3a2c2eb6048cb75202f0ea7b34feb740efc340"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
typing-extensions = ">=4.2.0"
|
||||
|
||||
[package.extras]
|
||||
dotenv = ["python-dotenv (>=0.10.4)"]
|
||||
email = ["email-validator (>=1.0.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "7.4.3"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"},
|
||||
{file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
|
||||
iniconfig = "*"
|
||||
packaging = "*"
|
||||
pluggy = ">=0.12,<2.0"
|
||||
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "python-dotenv"
|
||||
version = "1.0.0"
|
||||
description = "Read key-value pairs from a .env file and set them as environment variables"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"},
|
||||
{file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
cli = ["click (>=5.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.3.0"
|
||||
description = "Sniff out which async library your code is running under"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
|
||||
{file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "soupsieve"
|
||||
version = "2.5"
|
||||
description = "A modern CSS selector implementation for Beautiful Soup."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"},
|
||||
{file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "starlette"
|
||||
version = "0.27.0"
|
||||
description = "The little ASGI library that shines."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"},
|
||||
{file = "starlette-0.27.0.tar.gz", hash = "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
anyio = ">=3.4.0,<5"
|
||||
|
||||
[package.extras]
|
||||
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.0.1"
|
||||
description = "A lil' TOML parser"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tqdm"
|
||||
version = "4.66.1"
|
||||
description = "Fast, Extensible Progress Meter"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "tqdm-4.66.1-py3-none-any.whl", hash = "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386"},
|
||||
{file = "tqdm-4.66.1.tar.gz", hash = "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[package.extras]
|
||||
dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"]
|
||||
notebook = ["ipywidgets (>=6)"]
|
||||
slack = ["slack-sdk"]
|
||||
telegram = ["requests"]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.8.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.8+"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"},
|
||||
{file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uvicorn"
|
||||
version = "0.24.0.post1"
|
||||
description = "The lightning-fast ASGI server."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "uvicorn-0.24.0.post1-py3-none-any.whl", hash = "sha256:7c84fea70c619d4a710153482c0d230929af7bcf76c7bfa6de151f0a3a80121e"},
|
||||
{file = "uvicorn-0.24.0.post1.tar.gz", hash = "sha256:09c8e5a79dc466bdf28dead50093957db184de356fcdc48697bad3bde4c2588e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=7.0"
|
||||
h11 = ">=0.8"
|
||||
typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
|
||||
|
||||
[[package]]
|
||||
name = "websockets"
|
||||
version = "12.0"
|
||||
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"},
|
||||
{file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"},
|
||||
{file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"},
|
||||
{file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"},
|
||||
{file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"},
|
||||
{file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"},
|
||||
{file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"},
|
||||
{file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"},
|
||||
{file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"},
|
||||
{file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"},
|
||||
{file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"},
|
||||
{file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"},
|
||||
{file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"},
|
||||
{file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"},
|
||||
{file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"},
|
||||
{file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"},
|
||||
{file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"},
|
||||
{file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"},
|
||||
{file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"},
|
||||
{file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"},
|
||||
{file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"},
|
||||
{file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"},
|
||||
{file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"},
|
||||
{file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"},
|
||||
{file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"},
|
||||
{file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"},
|
||||
{file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"},
|
||||
{file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"},
|
||||
{file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"},
|
||||
{file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"},
|
||||
{file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"},
|
||||
{file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"},
|
||||
{file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"},
|
||||
{file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"},
|
||||
{file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"},
|
||||
{file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"},
|
||||
{file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"},
|
||||
{file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"},
|
||||
{file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"},
|
||||
{file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"},
|
||||
{file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"},
|
||||
{file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"},
|
||||
{file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"},
|
||||
{file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"},
|
||||
{file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"},
|
||||
{file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"},
|
||||
{file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"},
|
||||
{file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"},
|
||||
{file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"},
|
||||
{file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"},
|
||||
{file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"},
|
||||
{file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"},
|
||||
{file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"},
|
||||
{file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"},
|
||||
{file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"},
|
||||
{file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"},
|
||||
{file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"},
|
||||
{file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"},
|
||||
{file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"},
|
||||
{file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"},
|
||||
{file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"},
|
||||
{file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"},
|
||||
{file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"},
|
||||
{file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"},
|
||||
{file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"},
|
||||
{file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"},
|
||||
{file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"},
|
||||
{file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"},
|
||||
{file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"},
|
||||
{file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"},
|
||||
{file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"},
|
||||
{file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"},
|
||||
]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "c31ed2a1ce006749d6f34d8d6aebcbc58d306b9f8925b40cc35972a74979e5c7"
|
||||
@ -1,23 +0,0 @@
|
||||
[tool.poetry]
|
||||
name = "backend"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["Abi Raja <abimanyuraja@gmail.com>"]
|
||||
license = "MIT"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
fastapi = "^0.95.0"
|
||||
uvicorn = "^0.24.0.post1"
|
||||
websockets = "^12.0"
|
||||
openai = "^1.2.4"
|
||||
python-dotenv = "^1.0.0"
|
||||
beautifulsoup4 = "^4.12.2"
|
||||
httpx = "^0.25.1"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^7.4.3"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
@ -1,12 +0,0 @@
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/")
|
||||
async def get_status():
|
||||
return HTMLResponse(
|
||||
content="<h3>Your backend is running correctly. Please open the front-end URL (default is http://localhost:5173) to use screenshot-to-code.</h3>"
|
||||
)
|
||||
BIN
dist/screenshottocode-0.1.0-py3-none-any.whl
vendored
Normal file
BIN
dist/screenshottocode-0.1.0-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
dist/screenshottocode-0.1.0.tar.gz
vendored
Normal file
BIN
dist/screenshottocode-0.1.0.tar.gz
vendored
Normal file
Binary file not shown.
@ -59,6 +59,7 @@
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.3",
|
||||
"postcss": "^8.4.31",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"tailwindcss": "^3.3.5",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^4.4.5",
|
||||
|
||||
@ -55,7 +55,9 @@ function App() {
|
||||
// Settings
|
||||
const [settings, setSettings] = usePersistedState<Settings>(
|
||||
{
|
||||
model: 'gpt-4-vision',
|
||||
openAiApiKey: null,
|
||||
googleApiKey: null,
|
||||
openAiBaseURL: null,
|
||||
screenshotOneApiKey: null,
|
||||
isImageGenerationEnabled: true,
|
||||
|
||||
@ -139,7 +139,7 @@ function ImageUpload({ setReferenceImages }: Props) {
|
||||
);
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
<section className="container cursor-pointer">
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
|
||||
<div {...getRootProps({ style: style as any })}>
|
||||
<input {...getInputProps()} />
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
@ -37,6 +37,20 @@ function SettingsDialog({ settings, setSettings }: Props) {
|
||||
}));
|
||||
};
|
||||
|
||||
const [isGemini, setIsGemini] = useState(false);
|
||||
|
||||
const toggleModelSettings = (model: string)=>{
|
||||
setIsGemini(model === 'models/gemini-pro-vision')
|
||||
setSettings((s) => ({
|
||||
...s,
|
||||
model,
|
||||
}))
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
setIsGemini(settings.model === 'models/gemini-pro-vision')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger>
|
||||
@ -71,6 +85,45 @@ function SettingsDialog({ settings, setSettings }: Props) {
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col space-y-4">
|
||||
<Label htmlFor="model-selection">
|
||||
<div>Select model to use</div>
|
||||
<div className="font-light mt-2">
|
||||
More fun with it but if you want to save money, turn it off.
|
||||
</div>
|
||||
</Label>
|
||||
<select
|
||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
||||
value={settings.model || ""}
|
||||
onChange={(e)=>{toggleModelSettings(e.target.value)}}>
|
||||
<option value="gpt-4-vision">GPT 4 Vision</option>
|
||||
<option value="models/gemini-pro-vision">Gemini Pro Vision</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex flex-col space-y-4">
|
||||
{isGemini
|
||||
?
|
||||
<>
|
||||
<Label htmlFor="google-api-key">
|
||||
<div>Google API key</div>
|
||||
<div className="font-light mt-2 leading-relaxed">
|
||||
Only stored in your browser. Never stored on servers. Overrides
|
||||
your .env config.
|
||||
</div>
|
||||
</Label>
|
||||
<Input
|
||||
id="google-api-key"
|
||||
placeholder="Google API key"
|
||||
value={settings.googleApiKey || ""}
|
||||
onChange={(e) =>
|
||||
setSettings((s) => ({
|
||||
...s,
|
||||
googleApiKey: e.target.value,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</>
|
||||
:
|
||||
<>
|
||||
<Label htmlFor="openai-api-key">
|
||||
<div>OpenAI API key</div>
|
||||
<div className="font-light mt-2 leading-relaxed">
|
||||
@ -78,7 +131,6 @@ function SettingsDialog({ settings, setSettings }: Props) {
|
||||
your .env config.
|
||||
</div>
|
||||
</Label>
|
||||
|
||||
<Input
|
||||
id="openai-api-key"
|
||||
placeholder="OpenAI API key"
|
||||
@ -90,6 +142,8 @@ function SettingsDialog({ settings, setSettings }: Props) {
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
|
||||
{!IS_RUNNING_ON_CLOUD && (
|
||||
<>
|
||||
|
||||
@ -13,7 +13,9 @@ export enum GeneratedCodeConfig {
|
||||
}
|
||||
|
||||
export interface Settings {
|
||||
model: string;
|
||||
openAiApiKey: string | null;
|
||||
googleApiKey: string | null;
|
||||
openAiBaseURL: string | null;
|
||||
screenshotOneApiKey: string | null;
|
||||
isImageGenerationEnabled: boolean;
|
||||
|
||||
@ -3,6 +3,7 @@ import { defineConfig, loadEnv } from "vite";
|
||||
import checker from "vite-plugin-checker";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { createHtmlPlugin } from "vite-plugin-html";
|
||||
import copy from 'rollup-plugin-copy';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default ({ mode }) => {
|
||||
@ -21,6 +22,12 @@ export default ({ mode }) => {
|
||||
},
|
||||
},
|
||||
}),
|
||||
copy({
|
||||
targets: [
|
||||
{ src: 'dist/index.html', dest: path.resolve('../screenshottocode/templates/')},
|
||||
{ src: 'dist/assets', dest: path.resolve('../screenshottocode/') }
|
||||
]
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
|
||||
@ -1295,11 +1295,38 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.20.7"
|
||||
|
||||
"@types/fs-extra@^8.0.1":
|
||||
version "8.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.5.tgz#33aae2962d3b3ec9219b5aca2555ee00274f5927"
|
||||
integrity sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/glob@^7.1.1":
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
|
||||
integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==
|
||||
dependencies:
|
||||
"@types/minimatch" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/json-schema@^7.0.12":
|
||||
version "7.0.15"
|
||||
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz"
|
||||
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
|
||||
|
||||
"@types/minimatch@*":
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca"
|
||||
integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==
|
||||
|
||||
"@types/node@*":
|
||||
version "20.10.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.5.tgz#47ad460b514096b7ed63a1dae26fad0914ed3ab2"
|
||||
integrity sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==
|
||||
dependencies:
|
||||
undici-types "~5.26.4"
|
||||
|
||||
"@types/node@^20.9.0":
|
||||
version "20.9.0"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz"
|
||||
@ -1798,6 +1825,11 @@ color-name@~1.1.4:
|
||||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
colorette@^1.1.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40"
|
||||
integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==
|
||||
|
||||
colorette@^2.0.16:
|
||||
version "2.0.20"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
|
||||
@ -2206,7 +2238,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
||||
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
|
||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||
|
||||
fast-glob@^3.2.11, fast-glob@^3.2.7, fast-glob@^3.2.9, fast-glob@^3.3.0:
|
||||
fast-glob@^3.0.3, fast-glob@^3.2.11, fast-glob@^3.2.7, fast-glob@^3.2.9, fast-glob@^3.3.0:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz"
|
||||
integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
|
||||
@ -2307,6 +2339,15 @@ fs-extra@^11.1.0:
|
||||
jsonfile "^6.0.1"
|
||||
universalify "^2.0.0"
|
||||
|
||||
fs-extra@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
|
||||
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.0"
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
|
||||
@ -2392,6 +2433,20 @@ globals@^13.19.0:
|
||||
dependencies:
|
||||
type-fest "^0.20.2"
|
||||
|
||||
globby@10.0.1:
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.1.tgz#4782c34cb75dd683351335c5829cc3420e606b22"
|
||||
integrity sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==
|
||||
dependencies:
|
||||
"@types/glob" "^7.1.1"
|
||||
array-union "^2.1.0"
|
||||
dir-glob "^3.0.1"
|
||||
fast-glob "^3.0.3"
|
||||
glob "^7.1.3"
|
||||
ignore "^5.1.1"
|
||||
merge2 "^1.2.3"
|
||||
slash "^3.0.0"
|
||||
|
||||
globby@^11.1.0:
|
||||
version "11.1.0"
|
||||
resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz"
|
||||
@ -2467,6 +2522,11 @@ human-signals@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28"
|
||||
integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==
|
||||
|
||||
ignore@^5.1.1:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78"
|
||||
integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==
|
||||
|
||||
ignore@^5.2.0, ignore@^5.2.4:
|
||||
version "5.2.4"
|
||||
resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz"
|
||||
@ -2541,6 +2601,11 @@ is-path-inside@^3.0.3:
|
||||
resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz"
|
||||
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
||||
|
||||
is-plain-object@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b"
|
||||
integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==
|
||||
|
||||
is-stream@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac"
|
||||
@ -2608,6 +2673,13 @@ jsonc-parser@^3.2.0:
|
||||
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76"
|
||||
integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==
|
||||
|
||||
jsonfile@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
|
||||
integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==
|
||||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
jsonfile@^6.0.1:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz"
|
||||
@ -2719,7 +2791,7 @@ merge-stream@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
|
||||
|
||||
merge2@^1.3.0, merge2@^1.4.1:
|
||||
merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz"
|
||||
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
|
||||
@ -3230,6 +3302,17 @@ rimraf@^3.0.2:
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
rollup-plugin-copy@^3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-copy/-/rollup-plugin-copy-3.5.0.tgz#7ffa2a7a8303e143876fa64fb5eed9022d304eeb"
|
||||
integrity sha512-wI8D5dvYovRMx/YYKtUNt3Yxaw4ORC9xo6Gt9t22kveWz1enG9QrhVlagzwrxSC455xD1dHMKhIJkbsQ7d48BA==
|
||||
dependencies:
|
||||
"@types/fs-extra" "^8.0.1"
|
||||
colorette "^1.1.0"
|
||||
fs-extra "^8.1.0"
|
||||
globby "10.0.1"
|
||||
is-plain-object "^3.0.0"
|
||||
|
||||
rollup@^3.27.1:
|
||||
version "3.29.4"
|
||||
resolved "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz"
|
||||
@ -3568,6 +3651,11 @@ undici-types@~5.26.4:
|
||||
resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz"
|
||||
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
|
||||
|
||||
universalify@^0.1.0:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
|
||||
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
||||
|
||||
universalify@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz"
|
||||
|
||||
43
pyproject.toml
Normal file
43
pyproject.toml
Normal file
@ -0,0 +1,43 @@
|
||||
[tool.poetry]
|
||||
name = "screenshottocode"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = [
|
||||
"Abi Raja <abimanyuraja@gmail.com>",
|
||||
"Amos Amissah <theonlyamos@gmail.com>"
|
||||
]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
homepage = "https://screenshottocode.com/"
|
||||
repository = "https://github.com/theonlyamos/screenshot-to-code"
|
||||
include = ["screenshottocode/templates/*", "screenshottocode/assets/*"]
|
||||
classifiers = [
|
||||
"Topic :: Software Development :: Build Tools",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules"
|
||||
]
|
||||
|
||||
[tool.poetry.urls]
|
||||
"Bug Tracker" = "https://github.com/abi/screenshot-to-code/issues"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
fastapi = "^0.95.0"
|
||||
uvicorn = "^0.24.0.post1"
|
||||
websockets = "^12.0"
|
||||
openai = "^1.2.4"
|
||||
python-dotenv = "^1.0.0"
|
||||
beautifulsoup4 = "^4.12.2"
|
||||
httpx = "^0.25.1"
|
||||
google-generativeai = "0.3.0"
|
||||
google-ai-generativelanguage = "0.4.0"
|
||||
pillow = "^10.1.0"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^7.4.3"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
screenshottocode = "screenshottocode.cli:main"
|
||||
1
screenshottocode/assets/index-a937a359.css
Normal file
1
screenshottocode/assets/index-a937a359.css
Normal file
File diff suppressed because one or more lines are too long
342
screenshottocode/assets/index-f02835e2.js
Normal file
342
screenshottocode/assets/index-f02835e2.js
Normal file
File diff suppressed because one or more lines are too long
53
screenshottocode/cli.py
Normal file
53
screenshottocode/cli.py
Normal file
@ -0,0 +1,53 @@
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import argparse
|
||||
from argparse import Namespace
|
||||
import uvicorn
|
||||
|
||||
from .config import Config
|
||||
from .main import app
|
||||
|
||||
def start(args: Namespace):
|
||||
host = args.host if not args.production else '0.0.0.0'
|
||||
|
||||
Config.MODEL = args.model
|
||||
Config.IS_MODEL_GEMINI = args.model == 'gemini'
|
||||
Config.API_KEY = args.api_key
|
||||
if args.production:
|
||||
Config.MODEL = args.production
|
||||
|
||||
uvicorn.run(
|
||||
app,
|
||||
host=host,
|
||||
port=args.port,
|
||||
proxy_headers=True)
|
||||
|
||||
def get_arguments():
|
||||
global parser
|
||||
|
||||
parser.add_argument('-m', '--model', default='gpt-4-vision', type=str, nargs='?', choices=['gpt-4-vision', 'gemini'],
|
||||
help='Name of Model to use')
|
||||
parser.add_argument('--api-key', default='', help='Set api key of selected model')
|
||||
parser.add_argument('--production', action='store_true', help='Run project in production mode')
|
||||
parser.add_argument('--host', type=str, default='127.0.0.1', help='Host address to run on')
|
||||
parser.add_argument('--port', type=int, default=7001, help='Host port to run on')
|
||||
|
||||
parser.add_argument('-v','--version', action='version', version=f'%(prog)s {Config.VERSION}')
|
||||
parser.set_defaults(func=start)
|
||||
return parser.parse_args()
|
||||
|
||||
def main():
|
||||
global parser
|
||||
try:
|
||||
parser = argparse.ArgumentParser(description="This simple app converts a screenshot to code (HTML/Tailwind CSS, or React or Bootstrap)")
|
||||
args = get_arguments()
|
||||
args.func(args)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(str(e))
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
14
screenshottocode/config.py
Normal file
14
screenshottocode/config.py
Normal file
@ -0,0 +1,14 @@
|
||||
# Useful for debugging purposes when you don't want to waste GPT4-Vision credits
|
||||
# Setting to True will stream a mock response instead of calling the OpenAI API
|
||||
# TODO: Should only be set to true when value is 'True', not any abitrary truthy value
|
||||
import os
|
||||
|
||||
class Config:
|
||||
SHOULD_MOCK_AI_RESPONSE = bool(os.environ.get("MOCK", False))
|
||||
MODEL = os.environ.get("MODEL", 'gpt-4-vision')
|
||||
IS_MODEL_GEMINI = MODEL == 'gemini'
|
||||
VERSION = '0.1.0'
|
||||
API_KEY: str = ''
|
||||
# Set to True when running in production (on the hosted version)
|
||||
# Used as a feature flag to enable or disable certain features
|
||||
IS_PROD = os.environ.get("IS_PROD", False)
|
||||
103
screenshottocode/llm.py
Normal file
103
screenshottocode/llm.py
Normal file
@ -0,0 +1,103 @@
|
||||
import io
|
||||
from typing import Awaitable, Callable, List, Any
|
||||
from openai import AsyncOpenAI
|
||||
from openai.types.chat import ChatCompletionMessageParam, ChatCompletionChunk
|
||||
import google.generativeai as genai
|
||||
from PIL import Image
|
||||
import base64
|
||||
import os
|
||||
|
||||
|
||||
MODEL_GPT_4_VISION = "gpt-4-vision-preview"
|
||||
MODEL_GEMINI_PRO_VISION = "models/gemini-pro-vision"
|
||||
|
||||
async def format_response(response: str)-> str:
|
||||
response = response.strip()
|
||||
if response.startswith('```'):
|
||||
response = '\n'.join(response.split('\n')[1:-2])
|
||||
return response
|
||||
|
||||
async def format_messages_for_gemnini(messages: List[Any]):
|
||||
system_promt = messages[0]['content']
|
||||
image_base64 = messages[1]['content'][0]['image_url']['url']
|
||||
additional_prompt = messages[1]['content'][1]['text']
|
||||
|
||||
base64_data = image_base64.split(",")[1]
|
||||
decoded_bytes = base64.b64decode(base64_data)
|
||||
|
||||
image_bytes = io.BytesIO(decoded_bytes)
|
||||
|
||||
image = Image.open(image_bytes)
|
||||
|
||||
return [system_promt, image, additional_prompt]
|
||||
|
||||
async def stream_openai_response(
|
||||
messages: List[ChatCompletionMessageParam],
|
||||
api_key: str,
|
||||
base_url: str | None,
|
||||
callback: Callable[[str], Awaitable[None]],
|
||||
) -> str:
|
||||
client = AsyncOpenAI(api_key=api_key, base_url=base_url)
|
||||
|
||||
model = MODEL_GPT_4_VISION
|
||||
|
||||
# Base parameters
|
||||
params = {"model": model, "messages": messages, "stream": True, "timeout": 600}
|
||||
|
||||
# Add 'max_tokens' only if the model is a GPT4 vision model
|
||||
if model == MODEL_GPT_4_VISION:
|
||||
params["max_tokens"] = 4096
|
||||
params["temperature"] = 0
|
||||
|
||||
stream = await client.chat.completions.create(**params) # type: ignore
|
||||
full_response = ""
|
||||
async for chunk in stream: # type: ignore
|
||||
assert isinstance(chunk, ChatCompletionChunk)
|
||||
content = chunk.choices[0].delta.content or ""
|
||||
full_response += content
|
||||
await callback(content)
|
||||
|
||||
await client.close()
|
||||
|
||||
return full_response
|
||||
|
||||
async def stream_gemini_response(
|
||||
messages: List[Any] = [],
|
||||
api_key: str = os.getenv('GOOGLE_API_KEY', ''),
|
||||
callback: Callable[[str], Awaitable[None]] | None = None,
|
||||
) -> str:
|
||||
genai.configure(api_key=api_key)
|
||||
|
||||
model = genai.GenerativeModel('gemini-pro-vision')
|
||||
general_config = {
|
||||
"max_output_tokens": 2048,
|
||||
"temperature": 0.4,
|
||||
"top_p": 1,
|
||||
"top_k": 32
|
||||
}
|
||||
|
||||
formatted_messages = await format_messages_for_gemnini(messages)
|
||||
|
||||
response = model.generate_content(
|
||||
formatted_messages,
|
||||
stream=True,
|
||||
generation_config=general_config # type: ignore
|
||||
)
|
||||
response.resolve()
|
||||
result = await format_response(response.text)
|
||||
|
||||
while "</html>" not in result:
|
||||
continue_prompt = "Generate the rest of the code below"
|
||||
formatted_messages.append(continue_prompt)
|
||||
formatted_messages.append(result)
|
||||
|
||||
response = model.generate_content(
|
||||
formatted_messages,
|
||||
stream=True,
|
||||
generation_config=general_config # type: ignore
|
||||
)
|
||||
response.resolve()
|
||||
result += await format_response(response.text)
|
||||
|
||||
result = result.strip()
|
||||
return result
|
||||
@ -1,12 +1,14 @@
|
||||
# Load environment variables first
|
||||
from dotenv import load_dotenv
|
||||
from pathlib import Path
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from routes import screenshot, generate_code, home, evals
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from .routes import screenshot, generate_code, home, evals
|
||||
|
||||
app = FastAPI(openapi_url=None, docs_url=None, redoc_url=None)
|
||||
|
||||
@ -19,6 +21,12 @@ app.add_middleware(
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Mount assets folder
|
||||
assets = Path(__file__).resolve().parent / "assets"
|
||||
if not assets.exists():
|
||||
assets.mkdir()
|
||||
app.mount('/assets', StaticFiles(directory=assets, html=True), name='static')
|
||||
|
||||
# Add routes
|
||||
app.include_router(generate_code.router)
|
||||
app.include_router(screenshot.router)
|
||||
1001
screenshottocode/poetry.lock
generated
Normal file
1001
screenshottocode/poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,14 +2,14 @@ from typing import List, Union
|
||||
|
||||
from openai.types.chat import ChatCompletionMessageParam, ChatCompletionContentPartParam
|
||||
|
||||
from imported_code_prompts import (
|
||||
from .imported_code_prompts import (
|
||||
IMPORTED_CODE_BOOTSTRAP_SYSTEM_PROMPT,
|
||||
IMPORTED_CODE_IONIC_TAILWIND_SYSTEM_PROMPT,
|
||||
IMPORTED_CODE_REACT_TAILWIND_SYSTEM_PROMPT,
|
||||
IMPORTED_CODE_TAILWIND_SYSTEM_PROMPT,
|
||||
IMPORTED_CODE_SVG_SYSTEM_PROMPT,
|
||||
)
|
||||
from screenshot_system_prompts import (
|
||||
from .screenshot_system_prompts import (
|
||||
BOOTSTRAP_SYSTEM_PROMPT,
|
||||
IONIC_TAILWIND_SYSTEM_PROMPT,
|
||||
REACT_TAILWIND_SYSTEM_PROMPT,
|
||||
43
screenshottocode/pyproject.toml
Normal file
43
screenshottocode/pyproject.toml
Normal file
@ -0,0 +1,43 @@
|
||||
[tool.poetry]
|
||||
name = "screenshottocode"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = [
|
||||
"Abi Raja <abimanyuraja@gmail.com>",
|
||||
"Amos Amissah <theonlyamos@gmail.com>"
|
||||
]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
homepage = "https://screenshottocode.com/"
|
||||
repository = "https://github.com/theonlyamos/screenshot-to-code"
|
||||
include = ['templates/*', 'assets/*']
|
||||
classifiers = [
|
||||
"Topic :: Software Development :: Build Tools",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules"
|
||||
]
|
||||
|
||||
[tool.poetry.urls]
|
||||
"Bug Tracker" = "https://github.com/abi/screenshot-to-code/issues"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
fastapi = "^0.95.0"
|
||||
uvicorn = "^0.24.0.post1"
|
||||
websockets = "^12.0"
|
||||
openai = "^1.2.4"
|
||||
python-dotenv = "^1.0.0"
|
||||
beautifulsoup4 = "^4.12.2"
|
||||
httpx = "^0.25.1"
|
||||
google-generativeai = "0.3.0"
|
||||
google-ai-generativelanguage = "0.4.0"
|
||||
pillow = "^10.1.0"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^7.4.3"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
screenshottocode = "screenshottocode.cli:main"
|
||||
@ -1,8 +1,8 @@
|
||||
import os
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
from eval_utils import image_to_data_url
|
||||
from eval_config import EVALS_DIR
|
||||
from ..eval_utils import image_to_data_url
|
||||
from ..eval_config import EVALS_DIR
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
@ -2,18 +2,18 @@ import os
|
||||
import traceback
|
||||
from fastapi import APIRouter, WebSocket
|
||||
import openai
|
||||
from config import IS_PROD, SHOULD_MOCK_AI_RESPONSE
|
||||
from llm import stream_openai_response
|
||||
from ..config import Config
|
||||
from ..llm import stream_gemini_response, stream_openai_response
|
||||
from openai.types.chat import ChatCompletionMessageParam
|
||||
from mock_llm import mock_completion
|
||||
from ..mock_llm import mock_completion
|
||||
from typing import Dict, List
|
||||
from image_generation import create_alt_url_mapping, generate_images
|
||||
from prompts import assemble_imported_code_prompt, assemble_prompt
|
||||
from access_token import validate_access_token
|
||||
from ..image_generation import create_alt_url_mapping, generate_images
|
||||
from ..prompts import assemble_imported_code_prompt, assemble_prompt
|
||||
from ..access_token import validate_access_token
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
from utils import pprint_prompt # type: ignore
|
||||
from ..utils import pprint_prompt # type: ignore
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
@ -63,12 +63,18 @@ async def stream_code(websocket: WebSocket):
|
||||
|
||||
# Get the OpenAI API key from the request. Fall back to environment variable if not provided.
|
||||
# If neither is provided, we throw an error.
|
||||
openai_api_key = None
|
||||
openai_api_key = Config.API_KEY if not Config.IS_MODEL_GEMINI else None
|
||||
openai_base_url = None
|
||||
google_api_key = Config.API_KEY if Config.IS_MODEL_GEMINI else None
|
||||
|
||||
should_generate_images = False
|
||||
|
||||
if "accessCode" in params and params["accessCode"]:
|
||||
print("Access code - using platform API key")
|
||||
res = await validate_access_token(params["accessCode"])
|
||||
if res["success"]:
|
||||
openai_api_key = os.environ.get("PLATFORM_OPENAI_API_KEY")
|
||||
google_api_key = os.environ.get("PLATFORM_GOOGLE_API_KEY")
|
||||
else:
|
||||
await websocket.send_json(
|
||||
{
|
||||
@ -78,28 +84,41 @@ async def stream_code(websocket: WebSocket):
|
||||
)
|
||||
return
|
||||
else:
|
||||
if params['model'] == 'models/gemini-pro-vision':
|
||||
print('Using the Gemini Pro Vision Model')
|
||||
|
||||
Config.MODEL = 'gemini'
|
||||
Config.IS_MODEL_GEMINI = True
|
||||
google_api_key = params['googleApiKey']
|
||||
if google_api_key:
|
||||
Config.API_KEY = google_api_key
|
||||
print("Using Google API key from client-side settings dialog")
|
||||
else:
|
||||
print('Using the GPT 4 Vision Model')
|
||||
if params["openAiApiKey"]:
|
||||
openai_api_key = params["openAiApiKey"]
|
||||
if openai_api_key:
|
||||
Config.API_KEY = openai_api_key
|
||||
print("Using OpenAI API key from client-side settings dialog")
|
||||
else:
|
||||
openai_api_key = os.environ.get("OPENAI_API_KEY")
|
||||
if openai_api_key:
|
||||
print("Using OpenAI API key from environment variable")
|
||||
|
||||
if not openai_api_key:
|
||||
print("OpenAI API key not found")
|
||||
if (not Config.IS_MODEL_GEMINI and not Config.API_KEY) or (Config.IS_MODEL_GEMINI and not Config.API_KEY):
|
||||
print("API key not found")
|
||||
await websocket.send_json(
|
||||
{
|
||||
"type": "error",
|
||||
"value": "No OpenAI API key found. Please add your API key in the settings dialog or add it to backend/.env file.",
|
||||
"value": "No API key found. Please add your API key in the settings dialog or add it to backend/.env file.",
|
||||
}
|
||||
)
|
||||
return
|
||||
|
||||
if not Config.IS_MODEL_GEMINI:
|
||||
# Get the OpenAI Base URL from the request. Fall back to environment variable if not provided.
|
||||
openai_base_url = None
|
||||
# Disable user-specified OpenAI Base URL in prod
|
||||
if not os.environ.get("IS_PROD"):
|
||||
if not os.environ.get("Config.IS_PROD"):
|
||||
if "openAiBaseURL" in params and params["openAiBaseURL"]:
|
||||
openai_base_url = params["openAiBaseURL"]
|
||||
print("Using OpenAI Base URL from client-side settings dialog")
|
||||
@ -184,15 +203,21 @@ async def stream_code(websocket: WebSocket):
|
||||
|
||||
image_cache = create_alt_url_mapping(params["history"][-2])
|
||||
|
||||
pprint_prompt(prompt_messages)
|
||||
|
||||
if SHOULD_MOCK_AI_RESPONSE:
|
||||
# pprint_prompt(prompt_messages)
|
||||
completion = ""
|
||||
if Config.SHOULD_MOCK_AI_RESPONSE:
|
||||
completion = await mock_completion(process_chunk)
|
||||
else:
|
||||
elif Config.IS_MODEL_GEMINI and Config.API_KEY:
|
||||
completion = await stream_gemini_response(
|
||||
prompt_messages,
|
||||
api_key=Config.API_KEY,
|
||||
callback=lambda x: process_chunk(x),
|
||||
)
|
||||
elif Config.API_KEY:
|
||||
try:
|
||||
completion = await stream_openai_response(
|
||||
prompt_messages,
|
||||
api_key=openai_api_key,
|
||||
api_key=Config.API_KEY,
|
||||
base_url=openai_base_url,
|
||||
callback=lambda x: process_chunk(x),
|
||||
)
|
||||
@ -202,7 +227,7 @@ async def stream_code(websocket: WebSocket):
|
||||
"Incorrect OpenAI key. Please make sure your OpenAI API key is correct, or create a new OpenAI API key on your OpenAI dashboard."
|
||||
+ (
|
||||
" Alternatively, you can purchase code generation credits directly on this website."
|
||||
if IS_PROD
|
||||
if Config.IS_PROD
|
||||
else ""
|
||||
)
|
||||
)
|
||||
@ -214,7 +239,7 @@ async def stream_code(websocket: WebSocket):
|
||||
+ ". Please make sure you have followed the instructions correctly to obtain an OpenAI key with GPT vision access: https://github.com/abi/screenshot-to-code/blob/main/Troubleshooting.md"
|
||||
+ (
|
||||
" Alternatively, you can purchase code generation credits directly on this website."
|
||||
if IS_PROD
|
||||
if Config.IS_PROD
|
||||
else ""
|
||||
)
|
||||
)
|
||||
@ -225,7 +250,7 @@ async def stream_code(websocket: WebSocket):
|
||||
"OpenAI error - 'You exceeded your current quota, please check your plan and billing details.'"
|
||||
+ (
|
||||
" Alternatively, you can purchase code generation credits directly on this website."
|
||||
if IS_PROD
|
||||
if Config.IS_PROD
|
||||
else ""
|
||||
)
|
||||
)
|
||||
@ -260,5 +285,14 @@ async def stream_code(websocket: WebSocket):
|
||||
await websocket.send_json(
|
||||
{"type": "status", "value": "Image generation failed but code is complete."}
|
||||
)
|
||||
except RuntimeError as e:
|
||||
traceback.print_exc()
|
||||
print("Code generation failed", e)
|
||||
# Send set code even if image generation fails since that triggers
|
||||
# the frontend to update history
|
||||
await websocket.send_json({"type": "setCode", "value": completion})
|
||||
await websocket.send_json(
|
||||
{"type": "status", "value": "Code generation failed but code is complete."}
|
||||
)
|
||||
|
||||
await websocket.close()
|
||||
17
screenshottocode/routes/home.py
Normal file
17
screenshottocode/routes/home.py
Normal file
@ -0,0 +1,17 @@
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import HTMLResponse
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/")
|
||||
async def get_status():
|
||||
html_path = Path(__file__, '..', '..', 'templates', 'index.html').resolve()
|
||||
html_content = ''
|
||||
with html_path.open('rt') as file:
|
||||
html_content = file.read()
|
||||
return HTMLResponse(
|
||||
content=html_content
|
||||
)
|
||||
1
screenshottocode/templates/index.html
Normal file
1
screenshottocode/templates/index.html
Normal file
@ -0,0 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="UTF-8"/><link rel="icon" type="image/svg+xml" href="https://picoapps.xyz/favicon.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="preconnect" href="https://fonts.googleapis.com"/><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/><link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;700&display=swap" rel="stylesheet"/><title>Screenshot to Code</title><meta property="og:title" content="Screenshot to Code"/><meta property="og:description" content="Convert any screenshot or design to clean code"/><meta property="og:image" content="https://screenshottocode.com/brand/twitter-summary-card.png"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="628"/><meta property="og:url" content="https://screenshottocode.com"/><meta property="og:type" content="website"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:site" content="@picoapps"/><meta name="twitter:title" content="Screenshot to Code"/><meta name="twitter:description" content="Convert any screenshot or design to clean code"/><meta name="twitter:image" content="https://screenshottocode.com/brand/twitter-summary-card.png"/><script type="module" crossorigin src="./assets/index-f02835e2.js"></script><link rel="stylesheet" href="./assets/index-a937a359.css"></head><body><div id="root"></div></body></html>
|
||||
Loading…
Reference in New Issue
Block a user