support video uploads through the interface
This commit is contained in:
parent
c2f230a8c9
commit
4937a92f42
7
backend/custom_types.py
Normal file
7
backend/custom_types.py
Normal file
@ -0,0 +1,7 @@
|
||||
from typing import Literal
|
||||
|
||||
|
||||
InputMode = Literal[
|
||||
"image",
|
||||
"video",
|
||||
]
|
||||
@ -1,12 +1,20 @@
|
||||
import asyncio
|
||||
from typing import Awaitable, Callable
|
||||
|
||||
from custom_types import InputMode
|
||||
|
||||
async def mock_completion(process_chunk: Callable[[str], Awaitable[None]]) -> str:
|
||||
code_to_return = NO_IMAGES_NYTIMES_MOCK_CODE
|
||||
|
||||
for i in range(0, len(code_to_return), 10):
|
||||
await process_chunk(code_to_return[i : i + 10])
|
||||
async def mock_completion(
|
||||
process_chunk: Callable[[str], Awaitable[None]], input_mode: InputMode
|
||||
) -> str:
|
||||
code_to_return = (
|
||||
MORTGAGE_CALCULATOR_VIDEO_PROMPT_MOCK
|
||||
if input_mode == "video"
|
||||
else NO_IMAGES_NYTIMES_MOCK_CODE
|
||||
)
|
||||
|
||||
for i in range(0, len(code_to_return), 100):
|
||||
await process_chunk(code_to_return[i : i + 100])
|
||||
await asyncio.sleep(0.01)
|
||||
|
||||
return code_to_return
|
||||
@ -206,3 +214,232 @@ NO_IMAGES_NYTIMES_MOCK_CODE = """
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
MORTGAGE_CALCULATOR_VIDEO_PROMPT_MOCK = """
|
||||
<thinking>
|
||||
The user flow in the video seems to be:
|
||||
1. The calculator starts with some default values for loan amount, loan term, interest rate, etc.
|
||||
2. The user toggles the "Include taxes & fees" checkbox which shows an explanation tooltip.
|
||||
3. The user selects different loan terms from the dropdown, which updates the monthly payment amount.
|
||||
4. The user enters a custom loan amount.
|
||||
5. The user selects a different loan term (30-yr fixed FHA).
|
||||
6. The user enters additional details like home price, down payment, state, credit score, property tax, home insurance, and HOA fees.
|
||||
7. The calculator updates the total monthly payment breakdown.
|
||||
|
||||
To build this:
|
||||
- Use a container div for the whole calculator
|
||||
- Have sections for Monthly Payment, Purchase Budget, loan details, additional costs
|
||||
- Use input fields, dropdowns, and checkboxes for user input
|
||||
- Update values dynamically using JavaScript when inputs change
|
||||
- Show/hide explanation tooltip when checkbox is toggled
|
||||
- Update monthly payment whenever loan amount, interest rate or term is changed
|
||||
- Allow selecting loan term from a dropdown
|
||||
- Update total monthly payment breakdown as additional costs are entered
|
||||
- Style everything to match the screenshots using Tailwind utility classes
|
||||
</thinking>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="max-w-lg mx-auto bg-white shadow-lg rounded-lg p-6 font-sans">
|
||||
<div class="text-2xl font-semibold mb-6">Mortgage Calculator</div>
|
||||
|
||||
<div class="flex justify-between text-sm uppercase font-semibold text-gray-500 border-b mb-4">
|
||||
<div class="pb-2 border-b-2 border-blue-500">Monthly payment</div>
|
||||
<div class="pb-2">Purchase budget</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center mb-4">
|
||||
<input type="checkbox" class="mr-2 taxes-toggle" id="taxesToggle">
|
||||
<label for="taxesToggle" class="text-sm">Include taxes & fees</label>
|
||||
<i class="fas fa-info-circle ml-1 text-gray-400 cursor-pointer" id="taxesInfo"></i>
|
||||
<div class="hidden bg-gray-100 text-xs p-2 ml-2 rounded" id="taxesTooltip">Your total monthly payment is more than just your mortgage. It can include property taxes, homeowners insurance, and HOA fees, among other things.</div>
|
||||
</div>
|
||||
|
||||
<div class="text-3xl font-semibold mb-4">$<span id="monthlyPayment">1,696</span></div>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="text-sm font-semibold mb-2">Loan amount</div>
|
||||
<input type="text" value="240,000" class="w-full border rounded px-2 py-1 text-lg" id="loanAmount">
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="text-sm font-semibold mb-2">Loan term</div>
|
||||
<select class="w-full border rounded px-2 py-1 text-lg" id="loanTerm">
|
||||
<option>30-yr fixed</option>
|
||||
<option>30-yr fixed FHA</option>
|
||||
<option>30-yr fixed VA</option>
|
||||
<option>30-yr fixed USDA</option>
|
||||
<option>20-yr fixed</option>
|
||||
<option>15-yr fixed</option>
|
||||
<option>10 / 6 ARM</option>
|
||||
<option>7 / 6 ARM</option>
|
||||
<option>5 / 6 ARM</option>
|
||||
<option>3 / 6 ARM</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between mb-4">
|
||||
<div>
|
||||
<div class="text-sm font-semibold mb-2">Interest</div>
|
||||
<div class="text-lg">7.61 %</div>
|
||||
</div>
|
||||
<i class="fas fa-info-circle text-gray-400 cursor-pointer self-center"></i>
|
||||
</div>
|
||||
|
||||
<div class="border-t pt-4">
|
||||
<div class="mb-4">
|
||||
<div class="text-sm font-semibold mb-2">Home price</div>
|
||||
<input type="text" value="300,000" class="w-full border rounded px-2 py-1 text-lg" id="homePrice">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="text-sm font-semibold mb-2">Down payment</div>
|
||||
<div class="flex">
|
||||
<input type="text" value="60,000" class="w-full border rounded-l px-2 py-1 text-lg" id="downPayment">
|
||||
<div class="bg-gray-200 rounded-r px-4 py-1 text-lg flex items-center">20 %</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border-t pt-4">
|
||||
<div class="mb-4">
|
||||
<div class="text-sm font-semibold mb-2">State</div>
|
||||
<select class="w-full border rounded px-2 py-1 text-lg" id="state">
|
||||
<option>New York</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="text-sm font-semibold mb-2">Credit score</div>
|
||||
<select class="w-full border rounded px-2 py-1 text-lg" id="creditScore">
|
||||
<option>700 - 719</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border-t pt-4">
|
||||
<div class="mb-4">
|
||||
<div class="text-sm font-semibold mb-2">Property tax (yearly)</div>
|
||||
<input type="text" value="3,750" class="w-full border rounded px-2 py-1 text-lg" id="propertyTax">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="text-sm font-semibold mb-2">Home insurance (yearly)</div>
|
||||
<input type="text" value="1,050" class="w-full border rounded px-2 py-1 text-lg" id="homeInsurance">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="text-sm font-semibold mb-2">Private mortgage insurance (monthly)</div>
|
||||
<input type="text" value="0" class="w-full border rounded px-2 py-1 text-lg" id="mortgageInsurance">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="text-sm font-semibold mb-2">Homeowners association (monthly)</div>
|
||||
<input type="text" value="0" class="w-full border rounded px-2 py-1 text-lg" id="hoaFees">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between pt-4">
|
||||
<div class="text-sm">
|
||||
<div class="font-semibold">Total monthly payment</div>
|
||||
<div class="text-3xl font-semibold">$<span id="totalPayment">2,036</span></div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-sm">Loan</div>
|
||||
<div class="text-lg">$<span id="loanPayment">1,635</span></div>
|
||||
<div class="text-sm">Taxes & fees</div>
|
||||
<div>$<span id="taxesPayment">401</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-xs text-gray-500 mt-4">
|
||||
<div>Disclaimer • Feedback</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var loanAmount = 240000;
|
||||
var interestRate = 7.61;
|
||||
var loanTerm = 30;
|
||||
var homePrice = 300000;
|
||||
var downPayment = 60000;
|
||||
var propertyTax = 3750;
|
||||
var homeInsurance = 1050;
|
||||
var mortgageInsurance = 0;
|
||||
var hoaFees = 0;
|
||||
|
||||
function updateCalculations() {
|
||||
var principal = loanAmount;
|
||||
var monthlyInterest = interestRate / 100 / 12;
|
||||
var numPayments = loanTerm * 12;
|
||||
var monthlyPayment = principal * monthlyInterest / (1 - (Math.pow(1/(1 + monthlyInterest), numPayments)));
|
||||
var totalPayment = monthlyPayment;
|
||||
var taxesPayment = (propertyTax + homeInsurance) / 12;
|
||||
|
||||
if ($('#taxesToggle').is(':checked')) {
|
||||
totalPayment += taxesPayment + mortgageInsurance + hoaFees;
|
||||
}
|
||||
|
||||
$('#monthlyPayment').text(Math.round(monthlyPayment).toLocaleString());
|
||||
$('#loanPayment').text(Math.round(monthlyPayment).toLocaleString());
|
||||
$('#taxesPayment').text(Math.round(taxesPayment + mortgageInsurance + hoaFees).toLocaleString());
|
||||
$('#totalPayment').text(Math.round(totalPayment).toLocaleString());
|
||||
}
|
||||
|
||||
$('#taxesInfo').hover(function() {
|
||||
$('#taxesTooltip').removeClass('hidden');
|
||||
}, function() {
|
||||
$('#taxesTooltip').addClass('hidden');
|
||||
});
|
||||
|
||||
$('#loanTerm').change(function() {
|
||||
loanTerm = parseInt($(this).val().split('-')[0]);
|
||||
updateCalculations();
|
||||
});
|
||||
|
||||
$('#loanAmount').change(function() {
|
||||
loanAmount = parseInt($(this).val().replace(/,/g, ''));
|
||||
updateCalculations();
|
||||
});
|
||||
|
||||
$('#homePrice').change(function() {
|
||||
homePrice = parseInt($(this).val().replace(/,/g, ''));
|
||||
loanAmount = homePrice - downPayment;
|
||||
$('#loanAmount').val(loanAmount.toLocaleString());
|
||||
updateCalculations();
|
||||
});
|
||||
|
||||
$('#downPayment').change(function() {
|
||||
downPayment = parseInt($(this).val().replace(/,/g, ''));
|
||||
loanAmount = homePrice - downPayment;
|
||||
$('#loanAmount').val(loanAmount.toLocaleString());
|
||||
updateCalculations();
|
||||
});
|
||||
|
||||
$('#propertyTax').change(function() {
|
||||
propertyTax = parseInt($(this).val().replace(/,/g, ''));
|
||||
updateCalculations();
|
||||
});
|
||||
|
||||
$('#homeInsurance').change(function() {
|
||||
homeInsurance = parseInt($(this).val().replace(/,/g, ''));
|
||||
updateCalculations();
|
||||
});
|
||||
|
||||
$('#mortgageInsurance').change(function() {
|
||||
mortgageInsurance = parseInt($(this).val().replace(/,/g, ''));
|
||||
updateCalculations();
|
||||
});
|
||||
|
||||
$('#hoaFees').change(function() {
|
||||
hoaFees = parseInt($(this).val().replace(/,/g, ''));
|
||||
updateCalculations();
|
||||
});
|
||||
|
||||
updateCalculations();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
419
backend/poetry.lock
generated
419
backend/poetry.lock
generated
@ -2,13 +2,13 @@
|
||||
|
||||
[[package]]
|
||||
name = "anthropic"
|
||||
version = "0.18.0"
|
||||
version = "0.18.1"
|
||||
description = "The official Python library for the anthropic API"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "anthropic-0.18.0-py3-none-any.whl", hash = "sha256:af2a65a48ba4661e3902b49f96710d168782af9deb06d58c12f25e53448fb570"},
|
||||
{file = "anthropic-0.18.0.tar.gz", hash = "sha256:80a6134f6562792be8ec30f743e2211476cb1e1f59b01795b901f099ae854825"},
|
||||
{file = "anthropic-0.18.1-py3-none-any.whl", hash = "sha256:b85aee64f619ce1b1964ba733a09adc4053e7bc4e6d4186001229ec191099dcf"},
|
||||
{file = "anthropic-0.18.1.tar.gz", hash = "sha256:f5d1caafd43f6cc933a79753a93531605095f040a384f6a900c3de9c3fb6694e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -26,52 +26,56 @@ vertex = ["google-auth (>=2,<3)"]
|
||||
|
||||
[[package]]
|
||||
name = "anyio"
|
||||
version = "3.7.1"
|
||||
version = "4.3.0"
|
||||
description = "High level compatibility layer for multiple asynchronous event loop implementations"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"},
|
||||
{file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"},
|
||||
{file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"},
|
||||
{file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
|
||||
exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""}
|
||||
idna = ">=2.8"
|
||||
sniffio = ">=1.1"
|
||||
typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[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)"]
|
||||
doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
|
||||
test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
|
||||
trio = ["trio (>=0.23)"]
|
||||
|
||||
[[package]]
|
||||
name = "beautifulsoup4"
|
||||
version = "4.12.2"
|
||||
version = "4.12.3"
|
||||
description = "Screen-scraping library"
|
||||
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"},
|
||||
{file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"},
|
||||
{file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
soupsieve = ">1.2"
|
||||
|
||||
[package.extras]
|
||||
cchardet = ["cchardet"]
|
||||
chardet = ["chardet"]
|
||||
charset-normalizer = ["charset-normalizer"]
|
||||
html5lib = ["html5lib"]
|
||||
lxml = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2023.11.17"
|
||||
version = "2024.2.2"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
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"},
|
||||
{file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"},
|
||||
{file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -209,6 +213,17 @@ files = [
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "decorator"
|
||||
version = "4.4.2"
|
||||
description = "Decorators for Humans"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*"
|
||||
files = [
|
||||
{file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"},
|
||||
{file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "distlib"
|
||||
version = "0.3.8"
|
||||
@ -222,13 +237,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "distro"
|
||||
version = "1.8.0"
|
||||
version = "1.9.0"
|
||||
description = "Distro - an OS platform information API"
|
||||
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"},
|
||||
{file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"},
|
||||
{file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -330,13 +345,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "httpcore"
|
||||
version = "1.0.2"
|
||||
version = "1.0.4"
|
||||
description = "A minimal low-level HTTP client."
|
||||
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"},
|
||||
{file = "httpcore-1.0.4-py3-none-any.whl", hash = "sha256:ac418c1db41bade2ad53ae2f3834a3a0f5ae76b56cf5aa497d2d033384fc7d73"},
|
||||
{file = "httpcore-1.0.4.tar.gz", hash = "sha256:cb2839ccfcba0d2d3c1131d3c3e26dfc327326fbe7a5dc0dbfe9f6c9151bb022"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -347,7 +362,7 @@ h11 = ">=0.13,<0.15"
|
||||
asyncio = ["anyio (>=4.0,<5.0)"]
|
||||
http2 = ["h2 (>=3,<5)"]
|
||||
socks = ["socksio (==1.*)"]
|
||||
trio = ["trio (>=0.22.0,<0.23.0)"]
|
||||
trio = ["trio (>=0.22.0,<0.25.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
@ -375,13 +390,13 @@ socks = ["socksio (==1.*)"]
|
||||
|
||||
[[package]]
|
||||
name = "huggingface-hub"
|
||||
version = "0.21.3"
|
||||
version = "0.21.4"
|
||||
description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
|
||||
optional = false
|
||||
python-versions = ">=3.8.0"
|
||||
files = [
|
||||
{file = "huggingface_hub-0.21.3-py3-none-any.whl", hash = "sha256:b183144336fdf2810a8c109822e0bb6ef1fd61c65da6fb60e8c3f658b7144016"},
|
||||
{file = "huggingface_hub-0.21.3.tar.gz", hash = "sha256:26a15b604e4fc7bad37c467b76456543ec849386cbca9cd7e1e135f53e500423"},
|
||||
{file = "huggingface_hub-0.21.4-py3-none-any.whl", hash = "sha256:df37c2c37fc6c82163cdd8a67ede261687d80d1e262526d6c0ce73b6b3630a7b"},
|
||||
{file = "huggingface_hub-0.21.4.tar.gz", hash = "sha256:e1f4968c93726565a80edf6dc309763c7b546d0cfe79aa221206034d50155531"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -431,6 +446,56 @@ files = [
|
||||
{file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imageio"
|
||||
version = "2.34.0"
|
||||
description = "Library for reading and writing a wide range of image, video, scientific, and volumetric data formats."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "imageio-2.34.0-py3-none-any.whl", hash = "sha256:08082bf47ccb54843d9c73fe9fc8f3a88c72452ab676b58aca74f36167e8ccba"},
|
||||
{file = "imageio-2.34.0.tar.gz", hash = "sha256:ae9732e10acf807a22c389aef193f42215718e16bd06eed0c5bb57e1034a4d53"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
numpy = "*"
|
||||
pillow = ">=8.3.2"
|
||||
|
||||
[package.extras]
|
||||
all-plugins = ["astropy", "av", "imageio-ffmpeg", "pillow-heif", "psutil", "tifffile"]
|
||||
all-plugins-pypy = ["av", "imageio-ffmpeg", "pillow-heif", "psutil", "tifffile"]
|
||||
build = ["wheel"]
|
||||
dev = ["black", "flake8", "fsspec[github]", "pytest", "pytest-cov"]
|
||||
docs = ["numpydoc", "pydata-sphinx-theme", "sphinx (<6)"]
|
||||
ffmpeg = ["imageio-ffmpeg", "psutil"]
|
||||
fits = ["astropy"]
|
||||
full = ["astropy", "av", "black", "flake8", "fsspec[github]", "gdal", "imageio-ffmpeg", "itk", "numpydoc", "pillow-heif", "psutil", "pydata-sphinx-theme", "pytest", "pytest-cov", "sphinx (<6)", "tifffile", "wheel"]
|
||||
gdal = ["gdal"]
|
||||
itk = ["itk"]
|
||||
linting = ["black", "flake8"]
|
||||
pillow-heif = ["pillow-heif"]
|
||||
pyav = ["av"]
|
||||
test = ["fsspec[github]", "pytest", "pytest-cov"]
|
||||
tifffile = ["tifffile"]
|
||||
|
||||
[[package]]
|
||||
name = "imageio-ffmpeg"
|
||||
version = "0.4.9"
|
||||
description = "FFMPEG wrapper for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "imageio-ffmpeg-0.4.9.tar.gz", hash = "sha256:39bcd1660118ef360fa4047456501071364661aa9d9021d3d26c58f1ee2081f5"},
|
||||
{file = "imageio_ffmpeg-0.4.9-py3-none-macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:24095e882a126a0d217197b86265f821b4bb3cf9004104f67c1384a2b4b49168"},
|
||||
{file = "imageio_ffmpeg-0.4.9-py3-none-manylinux2010_x86_64.whl", hash = "sha256:2996c64af3e5489227096580269317719ea1a8121d207f2e28d6c24ebc4a253e"},
|
||||
{file = "imageio_ffmpeg-0.4.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7eead662d2f46d748c0ab446b68f423eb63d2b54d0a8ef96f80607245540866d"},
|
||||
{file = "imageio_ffmpeg-0.4.9-py3-none-win32.whl", hash = "sha256:b6de1e18911687c538d5585d8287ab1a23624ca9dc2044fcc4607de667bcf11e"},
|
||||
{file = "imageio_ffmpeg-0.4.9-py3-none-win_amd64.whl", hash = "sha256:7e900c695c6541b1cb17feb1baacd4009b30a53a45b81c23d53a67ab13ffb766"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
setuptools = "*"
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.0.0"
|
||||
@ -442,6 +507,30 @@ files = [
|
||||
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moviepy"
|
||||
version = "1.0.3"
|
||||
description = "Video editing with Python"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "moviepy-1.0.3.tar.gz", hash = "sha256:2884e35d1788077db3ff89e763c5ba7bfddbd7ae9108c9bc809e7ba58fa433f5"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
decorator = ">=4.0.2,<5.0"
|
||||
imageio = {version = ">=2.5,<3.0", markers = "python_version >= \"3.4\""}
|
||||
imageio_ffmpeg = {version = ">=0.2.0", markers = "python_version >= \"3.4\""}
|
||||
numpy = {version = ">=1.17.3", markers = "python_version > \"2.7\""}
|
||||
proglog = "<=1.0.0"
|
||||
requests = ">=2.8.1,<3.0"
|
||||
tqdm = ">=4.11.2,<5.0"
|
||||
|
||||
[package.extras]
|
||||
doc = ["Sphinx (>=1.5.2,<2.0)", "numpydoc (>=0.6.0,<1.0)", "pygame (>=1.9.3,<2.0)", "sphinx_rtd_theme (>=0.1.10b0,<1.0)"]
|
||||
optional = ["matplotlib (>=2.0.0,<3.0)", "opencv-python (>=3.0,<4.0)", "scikit-image (>=0.13.0,<1.0)", "scikit-learn", "scipy (>=0.19.0,<1.5)", "youtube_dl"]
|
||||
test = ["coverage (<5.0)", "coveralls (>=1.1,<2.0)", "pytest (>=3.0.0,<4.0)", "pytest-cov (>=2.5.1,<3.0)", "requests (>=2.8.1,<3.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "nodeenv"
|
||||
version = "1.8.0"
|
||||
@ -456,25 +545,70 @@ files = [
|
||||
[package.dependencies]
|
||||
setuptools = "*"
|
||||
|
||||
[[package]]
|
||||
name = "numpy"
|
||||
version = "1.26.4"
|
||||
description = "Fundamental package for array computing in Python"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"},
|
||||
{file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"},
|
||||
{file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"},
|
||||
{file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"},
|
||||
{file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"},
|
||||
{file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"},
|
||||
{file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"},
|
||||
{file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"},
|
||||
{file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"},
|
||||
{file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"},
|
||||
{file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"},
|
||||
{file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"},
|
||||
{file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"},
|
||||
{file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"},
|
||||
{file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"},
|
||||
{file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"},
|
||||
{file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"},
|
||||
{file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"},
|
||||
{file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"},
|
||||
{file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"},
|
||||
{file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"},
|
||||
{file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"},
|
||||
{file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"},
|
||||
{file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"},
|
||||
{file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"},
|
||||
{file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"},
|
||||
{file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"},
|
||||
{file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"},
|
||||
{file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"},
|
||||
{file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"},
|
||||
{file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"},
|
||||
{file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"},
|
||||
{file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"},
|
||||
{file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"},
|
||||
{file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"},
|
||||
{file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "1.3.7"
|
||||
version = "1.13.3"
|
||||
description = "The official Python library for the openai API"
|
||||
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"},
|
||||
{file = "openai-1.13.3-py3-none-any.whl", hash = "sha256:5769b62abd02f350a8dd1a3a242d8972c947860654466171d60fb0972ae0a41c"},
|
||||
{file = "openai-1.13.3.tar.gz", hash = "sha256:ff6c6b3bc7327e715e4b3592a923a5a1c7519ff5dd764a83d69f633d49e77a7b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
anyio = ">=3.5.0,<4"
|
||||
anyio = ">=3.5.0,<5"
|
||||
distro = ">=1.7.0,<2"
|
||||
httpx = ">=0.23.0,<1"
|
||||
pydantic = ">=1.9.0,<3"
|
||||
sniffio = "*"
|
||||
tqdm = ">4"
|
||||
typing-extensions = ">=4.5,<5"
|
||||
typing-extensions = ">=4.7,<5"
|
||||
|
||||
[package.extras]
|
||||
datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"]
|
||||
@ -490,6 +624,91 @@ files = [
|
||||
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "10.2.0"
|
||||
description = "Python Imaging Library (Fork)"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pillow-10.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e"},
|
||||
{file = "pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588"},
|
||||
{file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452"},
|
||||
{file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4"},
|
||||
{file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563"},
|
||||
{file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2"},
|
||||
{file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c"},
|
||||
{file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0"},
|
||||
{file = "pillow-10.2.0-cp310-cp310-win32.whl", hash = "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023"},
|
||||
{file = "pillow-10.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72"},
|
||||
{file = "pillow-10.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad"},
|
||||
{file = "pillow-10.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5"},
|
||||
{file = "pillow-10.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67"},
|
||||
{file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61"},
|
||||
{file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e"},
|
||||
{file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f"},
|
||||
{file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311"},
|
||||
{file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1"},
|
||||
{file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757"},
|
||||
{file = "pillow-10.2.0-cp311-cp311-win32.whl", hash = "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068"},
|
||||
{file = "pillow-10.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56"},
|
||||
{file = "pillow-10.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1"},
|
||||
{file = "pillow-10.2.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef"},
|
||||
{file = "pillow-10.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac"},
|
||||
{file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c"},
|
||||
{file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa"},
|
||||
{file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2"},
|
||||
{file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04"},
|
||||
{file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f"},
|
||||
{file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb"},
|
||||
{file = "pillow-10.2.0-cp312-cp312-win32.whl", hash = "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f"},
|
||||
{file = "pillow-10.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9"},
|
||||
{file = "pillow-10.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48"},
|
||||
{file = "pillow-10.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9"},
|
||||
{file = "pillow-10.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483"},
|
||||
{file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129"},
|
||||
{file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e"},
|
||||
{file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213"},
|
||||
{file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d"},
|
||||
{file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6"},
|
||||
{file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe"},
|
||||
{file = "pillow-10.2.0-cp38-cp38-win32.whl", hash = "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e"},
|
||||
{file = "pillow-10.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39"},
|
||||
{file = "pillow-10.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67"},
|
||||
{file = "pillow-10.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364"},
|
||||
{file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb"},
|
||||
{file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e"},
|
||||
{file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01"},
|
||||
{file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13"},
|
||||
{file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7"},
|
||||
{file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591"},
|
||||
{file = "pillow-10.2.0-cp39-cp39-win32.whl", hash = "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516"},
|
||||
{file = "pillow-10.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8"},
|
||||
{file = "pillow-10.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869"},
|
||||
{file = "pillow-10.2.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a"},
|
||||
{file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2"},
|
||||
{file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04"},
|
||||
{file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2"},
|
||||
{file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a"},
|
||||
{file = "pillow-10.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6"},
|
||||
{file = "pillow-10.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7"},
|
||||
{file = "pillow-10.2.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f"},
|
||||
{file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e"},
|
||||
{file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5"},
|
||||
{file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b"},
|
||||
{file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a"},
|
||||
{file = "pillow-10.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"},
|
||||
{file = "pillow-10.2.0.tar.gz", hash = "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"]
|
||||
fpx = ["olefile"]
|
||||
mic = ["olefile"]
|
||||
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
|
||||
typing = ["typing-extensions"]
|
||||
xmp = ["defusedxml"]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.2.0"
|
||||
@ -507,13 +726,13 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
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"},
|
||||
{file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
|
||||
{file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@ -538,49 +757,63 @@ nodeenv = ">=0.11.1"
|
||||
pyyaml = ">=5.1"
|
||||
virtualenv = ">=20.10.0"
|
||||
|
||||
[[package]]
|
||||
name = "proglog"
|
||||
version = "0.1.10"
|
||||
description = "Log and progress bar manager for console, notebooks, web..."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "proglog-0.1.10-py3-none-any.whl", hash = "sha256:19d5da037e8c813da480b741e3fa71fb1ac0a5b02bf21c41577c7f327485ec50"},
|
||||
{file = "proglog-0.1.10.tar.gz", hash = "sha256:658c28c9c82e4caeb2f25f488fff9ceace22f8d69b15d0c1c86d64275e4ddab4"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
tqdm = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "1.10.13"
|
||||
version = "1.10.14"
|
||||
description = "Data validation and settings management using python type hints"
|
||||
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"},
|
||||
{file = "pydantic-1.10.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f4fcec873f90537c382840f330b90f4715eebc2bc9925f04cb92de593eae054"},
|
||||
{file = "pydantic-1.10.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e3a76f571970fcd3c43ad982daf936ae39b3e90b8a2e96c04113a369869dc87"},
|
||||
{file = "pydantic-1.10.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d886bd3c3fbeaa963692ef6b643159ccb4b4cefaf7ff1617720cbead04fd1d"},
|
||||
{file = "pydantic-1.10.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:798a3d05ee3b71967844a1164fd5bdb8c22c6d674f26274e78b9f29d81770c4e"},
|
||||
{file = "pydantic-1.10.14-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:23d47a4b57a38e8652bcab15a658fdb13c785b9ce217cc3a729504ab4e1d6bc9"},
|
||||
{file = "pydantic-1.10.14-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f9f674b5c3bebc2eba401de64f29948ae1e646ba2735f884d1594c5f675d6f2a"},
|
||||
{file = "pydantic-1.10.14-cp310-cp310-win_amd64.whl", hash = "sha256:24a7679fab2e0eeedb5a8924fc4a694b3bcaac7d305aeeac72dd7d4e05ecbebf"},
|
||||
{file = "pydantic-1.10.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d578ac4bf7fdf10ce14caba6f734c178379bd35c486c6deb6f49006e1ba78a7"},
|
||||
{file = "pydantic-1.10.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa7790e94c60f809c95602a26d906eba01a0abee9cc24150e4ce2189352deb1b"},
|
||||
{file = "pydantic-1.10.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad4e10efa5474ed1a611b6d7f0d130f4aafadceb73c11d9e72823e8f508e663"},
|
||||
{file = "pydantic-1.10.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1245f4f61f467cb3dfeced2b119afef3db386aec3d24a22a1de08c65038b255f"},
|
||||
{file = "pydantic-1.10.14-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:21efacc678a11114c765eb52ec0db62edffa89e9a562a94cbf8fa10b5db5c046"},
|
||||
{file = "pydantic-1.10.14-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:412ab4a3f6dbd2bf18aefa9f79c7cca23744846b31f1d6555c2ee2b05a2e14ca"},
|
||||
{file = "pydantic-1.10.14-cp311-cp311-win_amd64.whl", hash = "sha256:e897c9f35281f7889873a3e6d6b69aa1447ceb024e8495a5f0d02ecd17742a7f"},
|
||||
{file = "pydantic-1.10.14-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d604be0f0b44d473e54fdcb12302495fe0467c56509a2f80483476f3ba92b33c"},
|
||||
{file = "pydantic-1.10.14-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a42c7d17706911199798d4c464b352e640cab4351efe69c2267823d619a937e5"},
|
||||
{file = "pydantic-1.10.14-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:596f12a1085e38dbda5cbb874d0973303e34227b400b6414782bf205cc14940c"},
|
||||
{file = "pydantic-1.10.14-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bfb113860e9288d0886e3b9e49d9cf4a9d48b441f52ded7d96db7819028514cc"},
|
||||
{file = "pydantic-1.10.14-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bc3ed06ab13660b565eed80887fcfbc0070f0aa0691fbb351657041d3e874efe"},
|
||||
{file = "pydantic-1.10.14-cp37-cp37m-win_amd64.whl", hash = "sha256:ad8c2bc677ae5f6dbd3cf92f2c7dc613507eafe8f71719727cbc0a7dec9a8c01"},
|
||||
{file = "pydantic-1.10.14-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c37c28449752bb1f47975d22ef2882d70513c546f8f37201e0fec3a97b816eee"},
|
||||
{file = "pydantic-1.10.14-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49a46a0994dd551ec051986806122767cf144b9702e31d47f6d493c336462597"},
|
||||
{file = "pydantic-1.10.14-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53e3819bd20a42470d6dd0fe7fc1c121c92247bca104ce608e609b59bc7a77ee"},
|
||||
{file = "pydantic-1.10.14-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbb503bbbbab0c588ed3cd21975a1d0d4163b87e360fec17a792f7d8c4ff29f"},
|
||||
{file = "pydantic-1.10.14-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:336709883c15c050b9c55a63d6c7ff09be883dbc17805d2b063395dd9d9d0022"},
|
||||
{file = "pydantic-1.10.14-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4ae57b4d8e3312d486e2498d42aed3ece7b51848336964e43abbf9671584e67f"},
|
||||
{file = "pydantic-1.10.14-cp38-cp38-win_amd64.whl", hash = "sha256:dba49d52500c35cfec0b28aa8b3ea5c37c9df183ffc7210b10ff2a415c125c4a"},
|
||||
{file = "pydantic-1.10.14-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c66609e138c31cba607d8e2a7b6a5dc38979a06c900815495b2d90ce6ded35b4"},
|
||||
{file = "pydantic-1.10.14-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d986e115e0b39604b9eee3507987368ff8148222da213cd38c359f6f57b3b347"},
|
||||
{file = "pydantic-1.10.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:646b2b12df4295b4c3148850c85bff29ef6d0d9621a8d091e98094871a62e5c7"},
|
||||
{file = "pydantic-1.10.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282613a5969c47c83a8710cc8bfd1e70c9223feb76566f74683af889faadc0ea"},
|
||||
{file = "pydantic-1.10.14-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:466669501d08ad8eb3c4fecd991c5e793c4e0bbd62299d05111d4f827cded64f"},
|
||||
{file = "pydantic-1.10.14-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:13e86a19dca96373dcf3190fcb8797d40a6f12f154a244a8d1e8e03b8f280593"},
|
||||
{file = "pydantic-1.10.14-cp39-cp39-win_amd64.whl", hash = "sha256:08b6ec0917c30861e3fe71a93be1648a2aa4f62f866142ba21670b24444d7fd8"},
|
||||
{file = "pydantic-1.10.14-py3-none-any.whl", hash = "sha256:8ee853cd12ac2ddbf0ecbac1c289f95882b2d4482258048079d13be700aa114c"},
|
||||
{file = "pydantic-1.10.14.tar.gz", hash = "sha256:46f17b832fe27de7850896f3afee50ea682220dd218f7e9c88d436788419dca6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -610,13 +843,13 @@ dev = ["twine (>=3.4.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "7.4.3"
|
||||
version = "7.4.4"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
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"},
|
||||
{file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
|
||||
{file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -632,13 +865,13 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no
|
||||
|
||||
[[package]]
|
||||
name = "python-dotenv"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
description = "Read key-value pairs from a .env file and set them as environment variables"
|
||||
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"},
|
||||
{file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
|
||||
{file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@ -726,29 +959,29 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "69.0.3"
|
||||
version = "69.1.1"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"},
|
||||
{file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"},
|
||||
{file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"},
|
||||
{file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
|
||||
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
|
||||
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.3.0"
|
||||
version = "1.3.1"
|
||||
description = "Sniff out which async library your code is running under"
|
||||
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"},
|
||||
{file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
|
||||
{file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -919,13 +1152,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "tqdm"
|
||||
version = "4.66.1"
|
||||
version = "4.66.2"
|
||||
description = "Fast, Extensible Progress Meter"
|
||||
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"},
|
||||
{file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"},
|
||||
{file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -939,13 +1172,13 @@ telegram = ["requests"]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.8.0"
|
||||
version = "4.10.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.8+"
|
||||
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"},
|
||||
{file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"},
|
||||
{file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1088,4 +1321,4 @@ files = [
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "17d8bb6d9e4a392f2512e4265df564708d59c1d83b26146f23e2595b6764c711"
|
||||
content-hash = "274fed55cf4a2f4e9954b3d196c103b72225409c6050759a939f0ca197ae3f79"
|
||||
|
||||
@ -16,6 +16,7 @@ beautifulsoup4 = "^4.12.2"
|
||||
httpx = "^0.25.1"
|
||||
pre-commit = "^3.6.2"
|
||||
anthropic = "^0.18.0"
|
||||
moviepy = "^1.0.3"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^7.4.3"
|
||||
|
||||
@ -3,9 +3,12 @@ import traceback
|
||||
from fastapi import APIRouter, WebSocket
|
||||
import openai
|
||||
from config import ANTHROPIC_API_KEY, IS_PROD, SHOULD_MOCK_AI_RESPONSE
|
||||
from custom_types import InputMode
|
||||
from llm import (
|
||||
CODE_GENERATION_MODELS,
|
||||
MODEL_CLAUDE_OPUS,
|
||||
stream_claude_response,
|
||||
stream_claude_response_native,
|
||||
stream_openai_response,
|
||||
)
|
||||
from openai.types.chat import ChatCompletionMessageParam
|
||||
@ -16,9 +19,11 @@ from prompts import assemble_imported_code_prompt, assemble_prompt
|
||||
from access_token import validate_access_token
|
||||
from datetime import datetime
|
||||
import json
|
||||
from prompts.claude_prompts import VIDEO_PROMPT
|
||||
from prompts.types import Stack
|
||||
|
||||
from utils import pprint_prompt # type: ignore
|
||||
# from utils import pprint_prompt
|
||||
from video.utils import extract_tag_content, assemble_claude_prompt_video # type: ignore
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
@ -64,6 +69,19 @@ async def stream_code(websocket: WebSocket):
|
||||
generated_code_config = ""
|
||||
if "generatedCodeConfig" in params and params["generatedCodeConfig"]:
|
||||
generated_code_config = params["generatedCodeConfig"]
|
||||
if not generated_code_config in get_args(Stack):
|
||||
await throw_error(f"Invalid generated code config: {generated_code_config}")
|
||||
return
|
||||
# Cast the variable to the Stack type
|
||||
valid_stack = cast(Stack, generated_code_config)
|
||||
|
||||
# Validate the input mode
|
||||
input_mode = params.get("inputMode")
|
||||
if not input_mode in get_args(InputMode):
|
||||
await throw_error(f"Invalid input mode: {input_mode}")
|
||||
raise Exception(f"Invalid input mode: {input_mode}")
|
||||
# Cast the variable to the right type
|
||||
validated_input_mode = cast(InputMode, input_mode)
|
||||
|
||||
# Read the model from the request. Fall back to default if not provided.
|
||||
code_generation_model = params.get("codeGenerationModel", "gpt_4_vision")
|
||||
@ -72,7 +90,7 @@ async def stream_code(websocket: WebSocket):
|
||||
raise Exception(f"Invalid model: {code_generation_model}")
|
||||
|
||||
print(
|
||||
f"Generating {generated_code_config} code using {code_generation_model} model..."
|
||||
f"Generating {generated_code_config} code for uploaded {input_mode} using {code_generation_model} model..."
|
||||
)
|
||||
|
||||
# Get the OpenAI API key from the request. Fall back to environment variable if not provided.
|
||||
@ -110,13 +128,6 @@ async def stream_code(websocket: WebSocket):
|
||||
)
|
||||
return
|
||||
|
||||
# Validate the generated code config
|
||||
if not generated_code_config in get_args(Stack):
|
||||
await throw_error(f"Invalid generated code config: {generated_code_config}")
|
||||
return
|
||||
# Cast the variable to the Stack type
|
||||
valid_stack = cast(Stack, generated_code_config)
|
||||
|
||||
# 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
|
||||
@ -203,13 +214,34 @@ async def stream_code(websocket: WebSocket):
|
||||
|
||||
image_cache = create_alt_url_mapping(params["history"][-2])
|
||||
|
||||
pprint_prompt(prompt_messages)
|
||||
if validated_input_mode == "video":
|
||||
video_data_url = params["image"]
|
||||
prompt_messages = await assemble_claude_prompt_video(video_data_url)
|
||||
|
||||
# pprint_prompt(prompt_messages) # type: ignore
|
||||
|
||||
if SHOULD_MOCK_AI_RESPONSE:
|
||||
completion = await mock_completion(process_chunk)
|
||||
completion = await mock_completion(
|
||||
process_chunk, input_mode=validated_input_mode
|
||||
)
|
||||
else:
|
||||
try:
|
||||
if code_generation_model == "claude_3_sonnet":
|
||||
if validated_input_mode == "video":
|
||||
if not ANTHROPIC_API_KEY:
|
||||
await throw_error(
|
||||
"No Anthropic API key found. Please add the environment variable ANTHROPIC_API_KEY to backend/.env"
|
||||
)
|
||||
raise Exception("No Anthropic key")
|
||||
|
||||
completion = await stream_claude_response_native(
|
||||
system_prompt=VIDEO_PROMPT,
|
||||
messages=prompt_messages, # type: ignore
|
||||
api_key=ANTHROPIC_API_KEY,
|
||||
callback=lambda x: process_chunk(x),
|
||||
model=MODEL_CLAUDE_OPUS,
|
||||
include_thinking=True,
|
||||
)
|
||||
elif code_generation_model == "claude_3_sonnet":
|
||||
if not ANTHROPIC_API_KEY:
|
||||
await throw_error(
|
||||
"No Anthropic API key found. Please add the environment variable ANTHROPIC_API_KEY to backend/.env"
|
||||
@ -217,13 +249,13 @@ async def stream_code(websocket: WebSocket):
|
||||
raise Exception("No Anthropic key")
|
||||
|
||||
completion = await stream_claude_response(
|
||||
prompt_messages,
|
||||
prompt_messages, # type: ignore
|
||||
api_key=ANTHROPIC_API_KEY,
|
||||
callback=lambda x: process_chunk(x),
|
||||
)
|
||||
else:
|
||||
completion = await stream_openai_response(
|
||||
prompt_messages,
|
||||
prompt_messages, # type: ignore
|
||||
api_key=openai_api_key,
|
||||
base_url=openai_base_url,
|
||||
callback=lambda x: process_chunk(x),
|
||||
@ -263,8 +295,11 @@ async def stream_code(websocket: WebSocket):
|
||||
)
|
||||
return await throw_error(error_message)
|
||||
|
||||
if validated_input_mode == "video":
|
||||
completion = extract_tag_content("html", completion)
|
||||
|
||||
# Write the messages dict into a log so that we can debug later
|
||||
write_logs(prompt_messages, completion)
|
||||
write_logs(prompt_messages, completion) # type: ignore
|
||||
|
||||
try:
|
||||
if should_generate_images:
|
||||
|
||||
134
backend/video/utils.py
Normal file
134
backend/video/utils.py
Normal file
@ -0,0 +1,134 @@
|
||||
# Extract HTML content from the completion string
|
||||
import base64
|
||||
import io
|
||||
import mimetypes
|
||||
import os
|
||||
import tempfile
|
||||
import uuid
|
||||
from typing import Union, cast
|
||||
from moviepy.editor import VideoFileClip # type: ignore
|
||||
from PIL import Image
|
||||
import math
|
||||
|
||||
|
||||
DEBUG = True
|
||||
TARGET_NUM_SCREENSHOTS = (
|
||||
20 # Should be max that Claude supports (20) - reduce to save tokens on testing
|
||||
)
|
||||
|
||||
|
||||
async def assemble_claude_prompt_video(video_data_url: str):
|
||||
images = split_video_into_screenshots(video_data_url)
|
||||
|
||||
# Save images to tmp if we're debugging
|
||||
if DEBUG:
|
||||
save_images_to_tmp(images)
|
||||
|
||||
# Validate number of images
|
||||
print(f"Number of frames extracted from video: {len(images)}")
|
||||
if len(images) > 20:
|
||||
print(f"Too many screenshots: {len(images)}")
|
||||
return
|
||||
|
||||
# Convert images to the message format for Claude
|
||||
content_messages: list[dict[str, Union[dict[str, str], str]]] = []
|
||||
for image in images:
|
||||
|
||||
# Convert Image to buffer
|
||||
buffered = io.BytesIO()
|
||||
image.save(buffered, format="JPEG")
|
||||
|
||||
# Encode bytes as base64
|
||||
base64_data = base64.b64encode(buffered.getvalue()).decode("utf-8")
|
||||
media_type = "image/jpeg"
|
||||
|
||||
content_messages.append(
|
||||
{
|
||||
"type": "image",
|
||||
"source": {
|
||||
"type": "base64",
|
||||
"media_type": media_type,
|
||||
"data": base64_data,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
return [
|
||||
{
|
||||
"role": "user",
|
||||
"content": content_messages,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Returns a list of images/frame (RGB format)
|
||||
def split_video_into_screenshots(video_data_url: str) -> list[Image.Image]:
|
||||
target_num_screenshots = TARGET_NUM_SCREENSHOTS
|
||||
|
||||
# Decode the base64 URL to get the video bytes
|
||||
video_encoded_data = video_data_url.split(",")[1]
|
||||
video_bytes = base64.b64decode(video_encoded_data)
|
||||
|
||||
mime_type = video_data_url.split(";")[0].split(":")[1]
|
||||
suffix = mimetypes.guess_extension(mime_type)
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=suffix, delete=True) as temp_video_file:
|
||||
print(temp_video_file.name)
|
||||
temp_video_file.write(video_bytes)
|
||||
temp_video_file.flush()
|
||||
clip = VideoFileClip(temp_video_file.name)
|
||||
images: list[Image.Image] = []
|
||||
total_frames = cast(int, clip.reader.nframes) # type: ignore
|
||||
|
||||
# Calculate frame skip interval by dividing total frames by the target number of screenshots
|
||||
# Ensuring a minimum skip of 1 frame
|
||||
frame_skip = max(1, math.ceil(total_frames / target_num_screenshots))
|
||||
|
||||
# Iterate over each frame in the clip
|
||||
for i, frame in enumerate(clip.iter_frames()):
|
||||
# Save every nth frame
|
||||
if i % frame_skip == 0:
|
||||
frame_image = Image.fromarray(frame) # type: ignore
|
||||
images.append(frame_image)
|
||||
# Ensure that we don't capture more than the desired number of frames
|
||||
if len(images) >= target_num_screenshots:
|
||||
break
|
||||
|
||||
# Close the video file to release resources
|
||||
clip.close()
|
||||
|
||||
return images
|
||||
|
||||
|
||||
# Save a list of PIL images to a random temporary directory
|
||||
def save_images_to_tmp(images: list[Image.Image]):
|
||||
|
||||
# Create a unique temporary directory
|
||||
unique_dir_name = f"screenshots_{uuid.uuid4()}"
|
||||
tmp_screenshots_dir = os.path.join(tempfile.gettempdir(), unique_dir_name)
|
||||
os.makedirs(tmp_screenshots_dir, exist_ok=True)
|
||||
|
||||
for idx, image in enumerate(images):
|
||||
# Generate a unique image filename using index
|
||||
image_filename = f"screenshot_{idx}.jpg"
|
||||
tmp_filepath = os.path.join(tmp_screenshots_dir, image_filename)
|
||||
image.save(tmp_filepath, format="JPEG")
|
||||
|
||||
print("Saved to " + tmp_screenshots_dir)
|
||||
|
||||
|
||||
def extract_tag_content(tag: str, text: str) -> str:
|
||||
"""
|
||||
Extracts content for a given tag from the provided text.
|
||||
|
||||
:param tag: The tag to search for.
|
||||
:param text: The text to search within.
|
||||
:return: The content found within the tag, if any.
|
||||
"""
|
||||
tag_start = f"<{tag}>"
|
||||
tag_end = f"</{tag}>"
|
||||
start_idx = text.find(tag_start)
|
||||
end_idx = text.find(tag_end, start_idx)
|
||||
if start_idx != -1 and end_idx != -1:
|
||||
return text[start_idx : end_idx + len(tag_end)]
|
||||
return ""
|
||||
@ -1,19 +1,21 @@
|
||||
# Load environment variables first
|
||||
import base64
|
||||
import shutil
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
import base64
|
||||
import mimetypes
|
||||
import time
|
||||
import subprocess
|
||||
import os
|
||||
from typing import Union
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from prompts.claude_prompts import VIDEO_PROMPT, VIDEO_PROMPT_ALPINE_JS
|
||||
from prompts.claude_prompts import VIDEO_PROMPT
|
||||
from utils import pprint_prompt
|
||||
from config import ANTHROPIC_API_KEY
|
||||
from video.utils import extract_tag_content, assemble_claude_prompt_video
|
||||
from llm import (
|
||||
MODEL_CLAUDE_OPUS,
|
||||
# MODEL_CLAUDE_SONNET,
|
||||
@ -28,11 +30,12 @@ OUTPUTS_DIR = "./video_evals/outputs"
|
||||
|
||||
|
||||
async def main():
|
||||
|
||||
video_filename = "mortgage-calculator.mov"
|
||||
screenshot_interval = 850
|
||||
video_filename = "shortest.mov"
|
||||
is_followup = False
|
||||
|
||||
if not ANTHROPIC_API_KEY:
|
||||
raise ValueError("ANTHROPIC_API_KEY is not set")
|
||||
|
||||
# Get previous HTML
|
||||
previous_html = ""
|
||||
if is_followup:
|
||||
@ -44,72 +47,22 @@ async def main():
|
||||
],
|
||||
key=os.path.getctime,
|
||||
)
|
||||
print(previous_html_file)
|
||||
with open(previous_html_file, "r") as file:
|
||||
previous_html = file.read()
|
||||
|
||||
if not ANTHROPIC_API_KEY:
|
||||
raise ValueError("ANTHROPIC_API_KEY is not set")
|
||||
|
||||
# Create the SCREENSHOTS_DIR if it doesn't exist
|
||||
if not os.path.exists(SCREENSHOTS_DIR):
|
||||
os.makedirs(SCREENSHOTS_DIR)
|
||||
|
||||
# Clear out the SCREENSHOTS_DIR before generating new screenshots
|
||||
for filename in os.listdir(SCREENSHOTS_DIR):
|
||||
file_path = os.path.join(SCREENSHOTS_DIR, filename)
|
||||
try:
|
||||
if os.path.isfile(file_path) or os.path.islink(file_path):
|
||||
os.unlink(file_path)
|
||||
elif os.path.isdir(file_path):
|
||||
shutil.rmtree(file_path)
|
||||
except Exception as e:
|
||||
print(f"Failed to delete {file_path}. Reason: {e}")
|
||||
|
||||
# Split the video into screenshots
|
||||
split_video_into_screenshots(
|
||||
os.path.join(VIDEO_DIR, video_filename), SCREENSHOTS_DIR, screenshot_interval
|
||||
video_file = os.path.join(VIDEO_DIR, video_filename)
|
||||
mime_type = mimetypes.guess_type(video_file)[0]
|
||||
with open(video_file, "rb") as file:
|
||||
video_content = file.read()
|
||||
video_data_url = (
|
||||
f"data:{mime_type};base64,{base64.b64encode(video_content).decode('utf-8')}"
|
||||
)
|
||||
|
||||
# Get all the screenshots in the directory
|
||||
screenshots = [f for f in os.listdir(SCREENSHOTS_DIR) if f.endswith(".jpg")]
|
||||
prompt_messages = await assemble_claude_prompt_video(video_data_url)
|
||||
|
||||
if len(screenshots) > 20:
|
||||
print(f"Too many screenshots: {len(screenshots)}")
|
||||
return
|
||||
|
||||
input_image_urls: list[str] = []
|
||||
sorted_screenshots = sorted(screenshots, key=lambda x: int(x.split(".")[0]))
|
||||
for filename in sorted_screenshots:
|
||||
filepath = os.path.join(SCREENSHOTS_DIR, filename)
|
||||
data_url = await image_to_data_url(filepath)
|
||||
print(filename)
|
||||
input_image_urls.append(data_url)
|
||||
|
||||
# Convert images to the message format for Claude
|
||||
content_messages: list[dict[str, Union[dict[str, str], str]]] = []
|
||||
for url in input_image_urls:
|
||||
media_type = url.split(";")[0].split(":")[1]
|
||||
base64_data = url.split(",")[1]
|
||||
content_messages.append(
|
||||
{
|
||||
"type": "image",
|
||||
"source": {
|
||||
"type": "base64",
|
||||
"media_type": media_type,
|
||||
"data": base64_data,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
prompt_messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": content_messages,
|
||||
},
|
||||
# {"role": "assistant", "content": SECOND_MESSAGE},
|
||||
# {"role": "user", "content": "continue"},
|
||||
]
|
||||
# Tell the model to continue
|
||||
# {"role": "assistant", "content": SECOND_MESSAGE},
|
||||
# {"role": "user", "content": "continue"},
|
||||
|
||||
if is_followup:
|
||||
prompt_messages += [
|
||||
@ -161,66 +114,10 @@ async def main():
|
||||
with open(output_path, "w") as file:
|
||||
file.write(html_content)
|
||||
|
||||
print(f"Output file path: {output_path}")
|
||||
|
||||
# Show a notification
|
||||
subprocess.run(["osascript", "-e", 'display notification "Coding Complete"'])
|
||||
|
||||
|
||||
# Extract HTML content from the completion string
|
||||
def extract_tag_content(tag: str, text: str) -> str:
|
||||
"""
|
||||
Extracts content for a given tag from the provided text.
|
||||
|
||||
:param tag: The tag to search for.
|
||||
:param text: The text to search within.
|
||||
:return: The content found within the tag, if any.
|
||||
"""
|
||||
tag_start = f"<{tag}>"
|
||||
tag_end = f"</{tag}>"
|
||||
start_idx = text.find(tag_start)
|
||||
end_idx = text.find(tag_end, start_idx)
|
||||
if start_idx != -1 and end_idx != -1:
|
||||
return text[start_idx : end_idx + len(tag_end)]
|
||||
return ""
|
||||
|
||||
|
||||
def split_video_into_screenshots(video_path: str, output_dir: str, interval: int):
|
||||
# Create the output directory if it doesn't exist
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# Calculate the number of zeros needed for padding
|
||||
# duration = float(
|
||||
# subprocess.check_output(
|
||||
# [
|
||||
# "ffprobe",
|
||||
# "-v",
|
||||
# "error",
|
||||
# "-show_entries",
|
||||
# "format=duration",
|
||||
# "-of",
|
||||
# "default=noprint_wrappers=1:nokey=1",
|
||||
# video_path,
|
||||
# ]
|
||||
# )
|
||||
# )
|
||||
|
||||
# Run the ffmpeg command to extract screenshots
|
||||
subprocess.call(
|
||||
[
|
||||
"ffmpeg",
|
||||
"-i",
|
||||
video_path,
|
||||
"-vf",
|
||||
f"fps=1/{interval/1000}",
|
||||
f"{output_dir}/%d.jpg",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
# TODO: Don't hard-code the media type
|
||||
async def image_to_data_url(filepath: str):
|
||||
with open(filepath, "rb") as image_file:
|
||||
encoded_string = base64.b64encode(image_file.read()).decode()
|
||||
return f"data:image/jpeg;base64,{encoded_string}"
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
@ -44,6 +44,8 @@ function App() {
|
||||
const [appState, setAppState] = useState<AppState>(AppState.INITIAL);
|
||||
const [generatedCode, setGeneratedCode] = useState<string>("");
|
||||
|
||||
const [inputMode, setInputMode] = useState<"image" | "video">("image");
|
||||
|
||||
const [referenceImages, setReferenceImages] = useState<string[]>([]);
|
||||
const [executionConsole, setExecutionConsole] = useState<string[]>([]);
|
||||
const [updateInstruction, setUpdateInstruction] = useState("");
|
||||
@ -140,6 +142,10 @@ function App() {
|
||||
cancelCodeGenerationAndReset();
|
||||
};
|
||||
|
||||
const shouldDisablePreview = inputMode === "video";
|
||||
const previewCode =
|
||||
shouldDisablePreview && appState === AppState.CODING ? "" : generatedCode;
|
||||
|
||||
const cancelCodeGenerationAndReset = () => {
|
||||
// When this is the first version, reset the entire app state
|
||||
if (currentVersion === null) {
|
||||
@ -219,16 +225,18 @@ function App() {
|
||||
}
|
||||
|
||||
// Initial version creation
|
||||
function doCreate(referenceImages: string[]) {
|
||||
function doCreate(referenceImages: string[], inputMode: "image" | "video") {
|
||||
// Reset any existing state
|
||||
reset();
|
||||
|
||||
setReferenceImages(referenceImages);
|
||||
setInputMode(inputMode);
|
||||
if (referenceImages.length > 0) {
|
||||
doGenerateCode(
|
||||
{
|
||||
generationType: "create",
|
||||
image: referenceImages[0],
|
||||
inputMode,
|
||||
},
|
||||
currentVersion
|
||||
);
|
||||
@ -261,6 +269,7 @@ function App() {
|
||||
doGenerateCode(
|
||||
{
|
||||
generationType: "update",
|
||||
inputMode,
|
||||
image: referenceImages[0],
|
||||
resultImage: resultImage,
|
||||
history: updatedHistory,
|
||||
@ -272,6 +281,7 @@ function App() {
|
||||
doGenerateCode(
|
||||
{
|
||||
generationType: "update",
|
||||
inputMode,
|
||||
image: referenceImages[0],
|
||||
history: updatedHistory,
|
||||
isImportedFromCode,
|
||||
@ -519,14 +529,14 @@ function App() {
|
||||
</TabsList>
|
||||
</div>
|
||||
<TabsContent value="desktop">
|
||||
<Preview code={generatedCode} device="desktop" />
|
||||
<Preview code={previewCode} device="desktop" />
|
||||
</TabsContent>
|
||||
<TabsContent value="mobile">
|
||||
<Preview code={generatedCode} device="mobile" />
|
||||
<Preview code={previewCode} device="mobile" />
|
||||
</TabsContent>
|
||||
<TabsContent value="code">
|
||||
<CodeTab
|
||||
code={generatedCode}
|
||||
code={previewCode}
|
||||
setCode={setGeneratedCode}
|
||||
settings={settings}
|
||||
/>
|
||||
|
||||
@ -51,7 +51,10 @@ type FileWithPreview = {
|
||||
} & File;
|
||||
|
||||
interface Props {
|
||||
setReferenceImages: (referenceImages: string[]) => void;
|
||||
setReferenceImages: (
|
||||
referenceImages: string[],
|
||||
inputMode: "image" | "video"
|
||||
) => void;
|
||||
}
|
||||
|
||||
function ImageUpload({ setReferenceImages }: Props) {
|
||||
@ -59,11 +62,13 @@ function ImageUpload({ setReferenceImages }: Props) {
|
||||
const { getRootProps, getInputProps, isFocused, isDragAccept, isDragReject } =
|
||||
useDropzone({
|
||||
maxFiles: 1,
|
||||
maxSize: 1024 * 1024 * 5, // 5 MB
|
||||
maxSize: 1024 * 1024 * 20, // 20 MB
|
||||
accept: {
|
||||
"image/png": [".png"],
|
||||
"image/jpeg": [".jpeg"],
|
||||
"image/jpg": [".jpg"],
|
||||
"video/quicktime": [".mov"],
|
||||
"video/mp4": [".mp4"],
|
||||
},
|
||||
onDrop: (acceptedFiles) => {
|
||||
// Set up the preview thumbnail images
|
||||
@ -78,7 +83,14 @@ function ImageUpload({ setReferenceImages }: Props) {
|
||||
// Convert images to data URLs and set the prompt images state
|
||||
Promise.all(acceptedFiles.map((file) => fileToDataURL(file)))
|
||||
.then((dataUrls) => {
|
||||
setReferenceImages(dataUrls.map((dataUrl) => dataUrl as string));
|
||||
if (dataUrls.length > 0) {
|
||||
setReferenceImages(
|
||||
dataUrls.map((dataUrl) => dataUrl as string),
|
||||
(dataUrls[0] as string).startsWith("data:video")
|
||||
? "video"
|
||||
: "image"
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toast.error("Error reading files" + error);
|
||||
|
||||
@ -6,7 +6,7 @@ import { toast } from "react-hot-toast";
|
||||
|
||||
interface Props {
|
||||
screenshotOneApiKey: string | null;
|
||||
doCreate: (urls: string[]) => void;
|
||||
doCreate: (urls: string[], inputMode: "image" | "video") => void;
|
||||
}
|
||||
|
||||
export function UrlInputSection({ doCreate, screenshotOneApiKey }: Props) {
|
||||
@ -46,7 +46,7 @@ export function UrlInputSection({ doCreate, screenshotOneApiKey }: Props) {
|
||||
}
|
||||
|
||||
const res = await response.json();
|
||||
doCreate([res.url]);
|
||||
doCreate([res.url], "image");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error(
|
||||
|
||||
@ -27,6 +27,7 @@ export enum AppState {
|
||||
|
||||
export interface CodeGenerationParams {
|
||||
generationType: "create" | "update";
|
||||
inputMode: "image" | "video";
|
||||
image: string;
|
||||
resultImage?: string;
|
||||
history?: string[];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user