Spaces:
Sleeping
Sleeping
rsm-roguchi commited on
Commit ·
7b68ff7
1
Parent(s): 3f587d8
update blogs
Browse files- Dockerfile +18 -19
- pyproject.toml +2 -0
- server/blog.py +210 -118
- ui/blog.py +5 -3
- uv.lock +83 -0
Dockerfile
CHANGED
|
@@ -1,30 +1,29 @@
|
|
| 1 |
-
#
|
| 2 |
-
|
| 3 |
|
| 4 |
-
#
|
| 5 |
-
|
| 6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
# Install UV package manager
|
| 9 |
-
# UV is a fast, reliable Python package installer written in Rust
|
| 10 |
-
# It's much faster than pip for installing dependencies
|
| 11 |
RUN pip install uv
|
| 12 |
|
| 13 |
-
|
| 14 |
-
# Copy all files from our project into the container
|
| 15 |
-
# This includes app.py, pyproject.toml, and other necessary files
|
| 16 |
COPY . .
|
| 17 |
|
| 18 |
-
#
|
| 19 |
-
# This ensures we have the same packages as in local development
|
| 20 |
RUN uv sync
|
| 21 |
|
| 22 |
-
#
|
| 23 |
-
|
|
|
|
|
|
|
| 24 |
EXPOSE 7860
|
| 25 |
|
| 26 |
-
#
|
| 27 |
-
|
| 28 |
-
# - host 0.0.0.0 allows connections from outside the container
|
| 29 |
-
# - port 7860 matches our EXPOSE setting above
|
| 30 |
-
CMD ["/.venv/bin/shiny", "run", "app.py", "--host", "0.0.0.0", "--port", "7860"]
|
|
|
|
| 1 |
+
# Start with Python 3.12 base image
|
| 2 |
+
FROM python:3.12-slim
|
| 3 |
|
| 4 |
+
# Install system dependencies required by Playwright + Chromium
|
| 5 |
+
RUN apt-get update && apt-get install -y \
|
| 6 |
+
wget curl gnupg ca-certificates fonts-liberation libappindicator3-1 \
|
| 7 |
+
libasound2 libatk-bridge2.0-0 libatk1.0-0 libcups2 \
|
| 8 |
+
libdbus-1-3 libgdk-pixbuf2.0-0 libnspr4 libnss3 libx11-xcb1 \
|
| 9 |
+
libxcomposite1 libxdamage1 libxrandr2 xdg-utils libgbm1 libxshmfence1 \
|
| 10 |
+
libxss1 libxtst6 libdrm2 libgtk-3-0 \
|
| 11 |
+
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
| 12 |
|
| 13 |
# Install UV package manager
|
|
|
|
|
|
|
| 14 |
RUN pip install uv
|
| 15 |
|
| 16 |
+
# Copy project files
|
|
|
|
|
|
|
| 17 |
COPY . .
|
| 18 |
|
| 19 |
+
# Install Python dependencies
|
|
|
|
| 20 |
RUN uv sync
|
| 21 |
|
| 22 |
+
# Install Playwright and Chromium
|
| 23 |
+
RUN playwright install --with-deps
|
| 24 |
+
|
| 25 |
+
# Expose Shiny port
|
| 26 |
EXPOSE 7860
|
| 27 |
|
| 28 |
+
# Run the Shiny app
|
| 29 |
+
CMD ["/.venv/bin/shiny", "run", "app.py", "--host", "0.0.0.0", "--port", "7860"]
|
|
|
|
|
|
|
|
|
pyproject.toml
CHANGED
|
@@ -22,4 +22,6 @@ dependencies = [
|
|
| 22 |
"pytrends>=4.9.2",
|
| 23 |
"requests>=2.32.4",
|
| 24 |
"tweepy>=4.16.0",
|
|
|
|
|
|
|
| 25 |
]
|
|
|
|
| 22 |
"pytrends>=4.9.2",
|
| 23 |
"requests>=2.32.4",
|
| 24 |
"tweepy>=4.16.0",
|
| 25 |
+
"bs4>=0.0.2",
|
| 26 |
+
"playwright>=1.53.0",
|
| 27 |
]
|
server/blog.py
CHANGED
|
@@ -1,115 +1,195 @@
|
|
| 1 |
from shiny import reactive, render, ui
|
| 2 |
-
import
|
| 3 |
-
from
|
| 4 |
-
import requests
|
| 5 |
-
import warnings
|
| 6 |
from pytrends.request import TrendReq
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
|
|
|
| 8 |
load_dotenv()
|
| 9 |
-
warnings.filterwarnings("ignore", category=FutureWarning)
|
| 10 |
|
| 11 |
SHOPIFY_STORE = "ultima-supply.myshopify.com"
|
| 12 |
SHOPIFY_TOKEN = os.getenv("SHOPIFY_TOKEN")
|
| 13 |
SHOPIFY_API_VERSION = "2024-04"
|
| 14 |
BLOG_ID = "73667707064"
|
| 15 |
-
BRAVE_API_KEY = os.getenv("BRAVE_API_KEY")
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
-
# ===
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
}
|
| 37 |
-
params = {
|
| 38 |
-
"q": query,
|
| 39 |
-
"count": min(num_results, 30),
|
| 40 |
-
"search_lang": "en"
|
| 41 |
-
}
|
| 42 |
|
| 43 |
-
|
| 44 |
-
response = requests.get(url, headers=headers, params=params)
|
| 45 |
-
if response.status_code == 422:
|
| 46 |
-
print(f"[ERROR] Brave rejected the query: {query}")
|
| 47 |
-
print(f"[DETAIL] Response: {response.text}")
|
| 48 |
-
return []
|
| 49 |
-
|
| 50 |
-
response.raise_for_status()
|
| 51 |
-
data = response.json()
|
| 52 |
-
return [
|
| 53 |
-
r.get("description", "")
|
| 54 |
-
for r in data.get("web", {}).get("results", [])
|
| 55 |
-
if "description" in r
|
| 56 |
-
]
|
| 57 |
except Exception as e:
|
| 58 |
-
print(f"[
|
| 59 |
-
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
)
|
| 77 |
|
| 78 |
-
pytrends.build_payload([topic], geo="US", timeframe="today 1-m")
|
| 79 |
-
related = pytrends.related_queries()
|
| 80 |
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
|
|
|
| 95 |
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
template=lambda x: x,
|
| 99 |
-
llm="gemini",
|
| 100 |
-
md=False,
|
| 101 |
-
temperature=0.7,
|
| 102 |
-
max_tokens=1000
|
| 103 |
-
)
|
| 104 |
|
| 105 |
-
llm_keywords = [kw.strip() for kw in llm_keywords_raw.split(",") if kw.strip()]
|
| 106 |
|
| 107 |
-
|
| 108 |
-
all_keywords = list(dict.fromkeys(pytrend_keywords + llm_keywords))[:30]
|
| 109 |
|
| 110 |
-
return all_keywords, research_context
|
| 111 |
|
| 112 |
|
|
|
|
| 113 |
def publish_blog_post(title: str, html_body: str, blog_id: str = BLOG_ID):
|
| 114 |
url = f"https://{SHOPIFY_STORE}/admin/api/{SHOPIFY_API_VERSION}/blogs/{blog_id}/articles.json"
|
| 115 |
headers = {
|
|
@@ -129,60 +209,64 @@ def publish_blog_post(title: str, html_body: str, blog_id: str = BLOG_ID):
|
|
| 129 |
else:
|
| 130 |
return False, response.text
|
| 131 |
|
| 132 |
-
# === SERVER ===
|
| 133 |
-
|
| 134 |
def server(input, output, session):
|
| 135 |
related_keywords = reactive.Value([])
|
| 136 |
-
generated_blog = reactive.Value(("", "")) # (
|
| 137 |
|
| 138 |
@output
|
| 139 |
@render.ui
|
| 140 |
@reactive.event(input.generate_btn)
|
| 141 |
-
def blog_result():
|
| 142 |
-
|
| 143 |
-
if not
|
| 144 |
-
return ui.HTML("<p><strong>⚠️ Please enter a
|
| 145 |
|
| 146 |
-
keywords,
|
| 147 |
related_keywords.set(keywords)
|
| 148 |
keyword_str = ", ".join(keywords)
|
| 149 |
|
| 150 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
prompt = (
|
| 152 |
-
f"You are a
|
| 153 |
-
f"
|
| 154 |
-
f"
|
| 155 |
-
f"
|
| 156 |
-
f"
|
| 157 |
-
f"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
)
|
| 159 |
|
| 160 |
blog_html = get_response(
|
| 161 |
input=prompt,
|
| 162 |
-
template=lambda x: x,
|
| 163 |
llm="gemini",
|
| 164 |
md=False,
|
| 165 |
-
temperature=0.
|
| 166 |
max_tokens=5000
|
| 167 |
)
|
| 168 |
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
f"You are an SEO expert helping a blog called 'Ultima Supply'.\n"
|
| 172 |
-
f"Given the following topic: {topic}\n"
|
| 173 |
-
f"And these SEO keywords: {keyword_str}\n"
|
| 174 |
-
f"Generate a short, engaging, SEO-optimized blog title (max 70 characters).\n"
|
| 175 |
-
f"Return only the title."
|
| 176 |
-
)
|
| 177 |
|
| 178 |
-
seo_title = get_response(
|
| 179 |
-
input=title_prompt,
|
| 180 |
-
template=lambda x: x.strip().replace('"', ''),
|
| 181 |
-
llm="gemini",
|
| 182 |
-
md=False,
|
| 183 |
-
temperature=0.5,
|
| 184 |
-
max_tokens=20
|
| 185 |
-
)
|
| 186 |
|
| 187 |
generated_blog.set((seo_title, blog_html))
|
| 188 |
|
|
@@ -192,10 +276,18 @@ def server(input, output, session):
|
|
| 192 |
)
|
| 193 |
|
| 194 |
@output
|
| 195 |
-
@render.
|
| 196 |
def keywords_used():
|
| 197 |
kws = related_keywords()
|
| 198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
|
| 200 |
@reactive.effect
|
| 201 |
@reactive.event(input.post_btn)
|
|
|
|
| 1 |
from shiny import reactive, render, ui
|
| 2 |
+
import os, sys
|
| 3 |
+
from bs4 import BeautifulSoup
|
|
|
|
|
|
|
| 4 |
from pytrends.request import TrendReq
|
| 5 |
+
from playwright.async_api import async_playwright
|
| 6 |
+
import requests
|
| 7 |
+
import re, ast
|
| 8 |
+
import time
|
| 9 |
+
|
| 10 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "code")))
|
| 11 |
+
from llm_connect import get_response
|
| 12 |
|
| 13 |
+
from dotenv import load_dotenv
|
| 14 |
load_dotenv()
|
|
|
|
| 15 |
|
| 16 |
SHOPIFY_STORE = "ultima-supply.myshopify.com"
|
| 17 |
SHOPIFY_TOKEN = os.getenv("SHOPIFY_TOKEN")
|
| 18 |
SHOPIFY_API_VERSION = "2024-04"
|
| 19 |
BLOG_ID = "73667707064"
|
|
|
|
| 20 |
|
| 21 |
+
# === Async JS-rendered scraping ===
|
| 22 |
+
async def scrape_div_content_from_url(url: str) -> str:
|
| 23 |
+
try:
|
| 24 |
+
async with async_playwright() as p:
|
| 25 |
+
browser = await p.chromium.launch(headless=True)
|
| 26 |
+
page = await browser.new_page()
|
| 27 |
+
await page.goto(url, wait_until="networkidle")
|
| 28 |
+
html = await page.content()
|
| 29 |
+
await browser.close()
|
| 30 |
+
|
| 31 |
+
soup = BeautifulSoup(html, "html.parser")
|
| 32 |
+
divs = soup.find_all("div", class_="article-body")
|
| 33 |
+
if not divs:
|
| 34 |
+
print("[WARN] No <div class='article-body'> found.")
|
| 35 |
+
return ""
|
| 36 |
+
|
| 37 |
+
texts = [div.get_text(separator=" ", strip=True) for div in divs]
|
| 38 |
+
return "\n\n".join(texts)
|
| 39 |
+
except Exception as e:
|
| 40 |
+
print(f"[ERROR] Failed to render or scrape: {e}")
|
| 41 |
+
return ""
|
| 42 |
|
| 43 |
+
# === Async keyword + scrape + fallback logic ===
|
| 44 |
+
async def get_keywords_and_content(url: str, top_n=5, llm_n=25):
|
| 45 |
+
scraped_text = await scrape_div_content_from_url(url)
|
| 46 |
+
if not scraped_text:
|
| 47 |
+
print("[ERROR] No scraped content. Cannot proceed.")
|
| 48 |
+
return [], ""
|
| 49 |
|
| 50 |
+
# === Step 1: Extract condensed topic keywords ===
|
| 51 |
+
try:
|
| 52 |
+
condensed_prompt = (
|
| 53 |
+
"From the content below, extract 5 to 7 mid-specific Google search phrases that reflect real user intent. "
|
| 54 |
+
"They should describe product types, use cases, or collector topics — not brand names alone. "
|
| 55 |
+
"Avoid single-word topics and overly broad terms like 'pokemon'. Each phrase should be 2–5 words, lowercase, and ASCII only.\n\n"
|
| 56 |
+
"You MUST return ONLY a valid Python list of strings. Do not use bullet points, newlines, or any explanation. "
|
| 57 |
+
"Your response must look exactly like this format:\n"
|
| 58 |
+
"['phrase one', 'phrase two', 'phrase three']\n\n"
|
| 59 |
+
f"Content:\n{scraped_text}"
|
| 60 |
+
)
|
| 61 |
|
| 62 |
+
condensed_topic_raw = get_response(
|
| 63 |
+
input=condensed_prompt,
|
| 64 |
+
template=lambda x: x.strip(),
|
| 65 |
+
llm="gemini",
|
| 66 |
+
md=False,
|
| 67 |
+
temperature=0.6,
|
| 68 |
+
max_tokens=100,
|
| 69 |
+
model_name="gemini-2.0-flash-lite"
|
| 70 |
+
)
|
| 71 |
+
print(condensed_topic_raw)
|
| 72 |
|
| 73 |
+
match = re.search(r"\[.*?\]", condensed_topic_raw, re.DOTALL)
|
| 74 |
+
condensed_topic = ast.literal_eval(match.group(0)) if match else []
|
| 75 |
+
if not condensed_topic:
|
| 76 |
+
condensed_topic = ["trading cards"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
+
print(f"[INFO] Condensed topic keywords: {condensed_topic}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
except Exception as e:
|
| 80 |
+
print(f"[WARN] Could not infer topics: {e}")
|
| 81 |
+
condensed_topic = ["trading cards"]
|
| 82 |
|
| 83 |
+
# === Step 2: Pull suggestions from PyTrends ===
|
| 84 |
+
all_suggestions = set()
|
| 85 |
+
try:
|
| 86 |
+
pytrends = TrendReq(hl="en-US", tz=360, timeout=10)
|
| 87 |
+
for topic in condensed_topic:
|
| 88 |
+
time.sleep(5)
|
| 89 |
+
suggestions = pytrends.suggestions(keyword=topic)
|
| 90 |
+
if suggestions:
|
| 91 |
+
titles = [s["title"] for s in suggestions]
|
| 92 |
+
all_suggestions.update(titles)
|
| 93 |
+
print(f"[INFO] Suggestions for '{topic}': {titles[:3]}")
|
| 94 |
+
except Exception as e:
|
| 95 |
+
print(f"[WARN] PyTrends suggestions failed: {e}")
|
| 96 |
+
|
| 97 |
+
all_suggestions = list(all_suggestions)
|
| 98 |
+
|
| 99 |
+
# === Step 3: Let Gemini filter suggestions for relevance ===
|
| 100 |
+
filtered_keywords = []
|
| 101 |
+
if all_suggestions:
|
| 102 |
+
filter_prompt = (
|
| 103 |
+
f"The following article was scraped:\n\n{scraped_text[:1500]}\n\n"
|
| 104 |
+
f"Here is a list of keyword suggestions:\n{all_suggestions}\n\n"
|
| 105 |
+
"Return only the keywords that are clearly relevant to the article topic. "
|
| 106 |
+
"Return a valid Python list of strings only. No explanation, bullets, or formatting."
|
| 107 |
+
)
|
| 108 |
|
| 109 |
+
raw_filtered = get_response(
|
| 110 |
+
input=filter_prompt,
|
| 111 |
+
template=lambda x: x.strip(),
|
| 112 |
+
llm="gemini",
|
| 113 |
+
md=False,
|
| 114 |
+
temperature=0.3,
|
| 115 |
+
max_tokens=200
|
| 116 |
+
)
|
| 117 |
|
| 118 |
+
match = re.search(r"\[.*?\]", raw_filtered)
|
| 119 |
+
if match:
|
| 120 |
+
try:
|
| 121 |
+
filtered_keywords = ast.literal_eval(match.group(0))
|
| 122 |
+
except:
|
| 123 |
+
filtered_keywords = []
|
| 124 |
+
|
| 125 |
+
# === Step 4: Fallback to Gemini keyword generation if needed ===
|
| 126 |
+
if not filtered_keywords:
|
| 127 |
+
fallback_prompt = (
|
| 128 |
+
f"You are an SEO expert. Generate {llm_n} niche-relevant SEO keywords "
|
| 129 |
+
f"based on this content:\n\n{scraped_text}\n\n"
|
| 130 |
+
"Return a comma-separated list of lowercase 2–5 word search phrases. No formatting."
|
| 131 |
+
)
|
| 132 |
+
fallback_keywords_raw = get_response(
|
| 133 |
+
input=fallback_prompt,
|
| 134 |
+
template=lambda x: x.strip(),
|
| 135 |
+
llm="gemini",
|
| 136 |
+
md=False,
|
| 137 |
+
temperature=0.7,
|
| 138 |
+
max_tokens=400
|
| 139 |
+
)
|
| 140 |
+
filtered_keywords = [kw.strip() for kw in fallback_keywords_raw.split(",") if kw.strip()]
|
| 141 |
+
print(f"[INFO] Fallback keywords used: {filtered_keywords[:top_n]}")
|
| 142 |
+
|
| 143 |
+
# === Step 5: Enforce minimum of 30 keywords ===
|
| 144 |
+
combined_keywords = list(dict.fromkeys(filtered_keywords)) # remove duplicates
|
| 145 |
+
if len(combined_keywords) < 30:
|
| 146 |
+
needed = 30 - len(combined_keywords)
|
| 147 |
+
print(f"[INFO] Need {needed} more keywords to reach 30. Using Gemini to pad.")
|
| 148 |
+
|
| 149 |
+
pad_prompt = (
|
| 150 |
+
f"The following article content is missing SEO keyword coverage:\n\n"
|
| 151 |
+
f"{scraped_text}\n\n"
|
| 152 |
+
f"Generate exactly {needed} additional SEO keyword phrases. "
|
| 153 |
+
"Each keyword must be:\n"
|
| 154 |
+
"- 2 to 5 words long\n"
|
| 155 |
+
"- lowercase only\n"
|
| 156 |
+
"- written in ASCII (no symbols or accents)\n"
|
| 157 |
+
"- clearly relevant to the article\n"
|
| 158 |
+
"- not overlapping with any common generic terms like 'pokemon'\n\n"
|
| 159 |
+
"You MUST return a valid Python list of strings. DO NOT include any explanation, extra text, markdown, or formatting.\n"
|
| 160 |
+
"Format example:\n"
|
| 161 |
+
"['keyword one', 'keyword two', 'keyword three']"
|
| 162 |
)
|
| 163 |
|
|
|
|
|
|
|
| 164 |
|
| 165 |
+
pad_raw = get_response(
|
| 166 |
+
input=pad_prompt,
|
| 167 |
+
template=lambda x: x.strip(),
|
| 168 |
+
llm="gemini",
|
| 169 |
+
md=False,
|
| 170 |
+
temperature=0.7,
|
| 171 |
+
max_tokens=200
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
pad_keywords = []
|
| 175 |
|
| 176 |
+
pad_match = re.search(r"\[[^\]]+\]", pad_raw) # greedy, non-linebreaking
|
| 177 |
+
if pad_match:
|
| 178 |
+
try:
|
| 179 |
+
pad_keywords = ast.literal_eval(pad_match.group(0))
|
| 180 |
+
except Exception as e:
|
| 181 |
+
print(f"[WARN] ast.literal_eval failed: {e}")
|
| 182 |
+
pad_keywords = []
|
| 183 |
|
| 184 |
+
combined_keywords = list(dict.fromkeys(combined_keywords + pad_keywords))
|
| 185 |
+
print(f"[INFO] Padded {len(pad_keywords)} keywords:", pad_keywords)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
|
|
|
|
| 187 |
|
| 188 |
+
return combined_keywords[:30], scraped_text
|
|
|
|
| 189 |
|
|
|
|
| 190 |
|
| 191 |
|
| 192 |
+
# === Shopify publisher ===
|
| 193 |
def publish_blog_post(title: str, html_body: str, blog_id: str = BLOG_ID):
|
| 194 |
url = f"https://{SHOPIFY_STORE}/admin/api/{SHOPIFY_API_VERSION}/blogs/{blog_id}/articles.json"
|
| 195 |
headers = {
|
|
|
|
| 209 |
else:
|
| 210 |
return False, response.text
|
| 211 |
|
| 212 |
+
# === SHINY SERVER ===
|
|
|
|
| 213 |
def server(input, output, session):
|
| 214 |
related_keywords = reactive.Value([])
|
| 215 |
+
generated_blog = reactive.Value(("", "")) # (title, html_content)
|
| 216 |
|
| 217 |
@output
|
| 218 |
@render.ui
|
| 219 |
@reactive.event(input.generate_btn)
|
| 220 |
+
async def blog_result():
|
| 221 |
+
url = input.url()
|
| 222 |
+
if not url:
|
| 223 |
+
return ui.HTML("<p><strong>⚠️ Please enter a URL.</strong></p>")
|
| 224 |
|
| 225 |
+
keywords, scraped = await get_keywords_and_content(url)
|
| 226 |
related_keywords.set(keywords)
|
| 227 |
keyword_str = ", ".join(keywords)
|
| 228 |
|
| 229 |
+
# Title generation from scraped text
|
| 230 |
+
infer_topic_prompt = (
|
| 231 |
+
f"Based on the following article content:\n\n{scraped[:2000]}\n\n"
|
| 232 |
+
f"Return a short, descriptive blog post title (max 70 characters)."
|
| 233 |
+
f"Return ONLY the TITLE"
|
| 234 |
+
)
|
| 235 |
+
seo_title = get_response(
|
| 236 |
+
input=infer_topic_prompt,
|
| 237 |
+
template=lambda x: x.strip().replace('"', ''),
|
| 238 |
+
llm="gemini",
|
| 239 |
+
md=False,
|
| 240 |
+
temperature=0.5,
|
| 241 |
+
max_tokens=20
|
| 242 |
+
)
|
| 243 |
+
|
| 244 |
+
# Blog generation with injected SEO
|
| 245 |
prompt = (
|
| 246 |
+
f"You are a content writer for a collectibles brand called 'Ultima Supply'.\n"
|
| 247 |
+
f"Given the following scraped content:\n\n{scraped}\n\n"
|
| 248 |
+
f"Adapt this into an engaging, original, and heavily detailed SEO-optimized blog post.\n"
|
| 249 |
+
f"Inject the following SEO keywords naturally and organically throughout the content:\n{keyword_str}\n\n"
|
| 250 |
+
f"Use proper HTML structure: <h1> for the title, <h2> for section headers, and <p> for all paragraphs.\n"
|
| 251 |
+
f"Do NOT include any markdown, code blocks, or triple backticks. Do NOT use ```html or any formatting fences.\n"
|
| 252 |
+
f"Just return the raw HTML.\n\n"
|
| 253 |
+
f"DO NOT include any hyperlinks or images inside the body of the blog post.\n"
|
| 254 |
+
f"At the very end, add a single call-to-action in a new <p> tag:\n"
|
| 255 |
+
f"Visit <a href='https://ultima-supply.myshopify.com'>Ultima Supply</a> to explore more collectibles."
|
| 256 |
)
|
| 257 |
|
| 258 |
blog_html = get_response(
|
| 259 |
input=prompt,
|
| 260 |
+
template=lambda x: x.strip(),
|
| 261 |
llm="gemini",
|
| 262 |
md=False,
|
| 263 |
+
temperature=0.9,
|
| 264 |
max_tokens=5000
|
| 265 |
)
|
| 266 |
|
| 267 |
+
blog_html = re.sub(r"```[a-zA-Z]*\n?", "", blog_html).strip()
|
| 268 |
+
blog_html = blog_html.replace("```", "").strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 270 |
|
| 271 |
generated_blog.set((seo_title, blog_html))
|
| 272 |
|
|
|
|
| 276 |
)
|
| 277 |
|
| 278 |
@output
|
| 279 |
+
@render.ui
|
| 280 |
def keywords_used():
|
| 281 |
kws = related_keywords()
|
| 282 |
+
if not kws:
|
| 283 |
+
return ui.HTML("<p><strong>No SEO keywords retrieved yet.</strong></p>")
|
| 284 |
+
|
| 285 |
+
return ui.HTML(
|
| 286 |
+
f"<p><strong>✅ SEO Keywords Injected ({len(kws)}):</strong></p><ul>"
|
| 287 |
+
+ "".join(f"<li>{kw}</li>" for kw in kws) +
|
| 288 |
+
"</ul>"
|
| 289 |
+
)
|
| 290 |
+
|
| 291 |
|
| 292 |
@reactive.effect
|
| 293 |
@reactive.event(input.post_btn)
|
ui/blog.py
CHANGED
|
@@ -2,8 +2,10 @@ from shiny import ui
|
|
| 2 |
|
| 3 |
ui = ui.nav_panel(
|
| 4 |
"Blog Writer",
|
| 5 |
-
|
| 6 |
-
|
|
|
|
|
|
|
| 7 |
ui.div(
|
| 8 |
ui.input_action_button("generate_btn", "Generate Blog"),
|
| 9 |
ui.input_action_button("post_btn", "Post to Shopify", class_="ms-3"),
|
|
@@ -11,5 +13,5 @@ ui = ui.nav_panel(
|
|
| 11 |
),
|
| 12 |
|
| 13 |
ui.output_ui("blog_result"),
|
| 14 |
-
ui.
|
| 15 |
)
|
|
|
|
| 2 |
|
| 3 |
ui = ui.nav_panel(
|
| 4 |
"Blog Writer",
|
| 5 |
+
|
| 6 |
+
# Only need URL input now
|
| 7 |
+
ui.input_text("url", "Enter Article URL", placeholder="e.g. https://example.com/some-article"),
|
| 8 |
+
|
| 9 |
ui.div(
|
| 10 |
ui.input_action_button("generate_btn", "Generate Blog"),
|
| 11 |
ui.input_action_button("post_btn", "Post to Shopify", class_="ms-3"),
|
|
|
|
| 13 |
),
|
| 14 |
|
| 15 |
ui.output_ui("blog_result"),
|
| 16 |
+
ui.output_ui("keywords_used")
|
| 17 |
)
|
uv.lock
CHANGED
|
@@ -173,6 +173,19 @@ wheels = [
|
|
| 173 |
{ url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 },
|
| 174 |
]
|
| 175 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 176 |
[[package]]
|
| 177 |
name = "black"
|
| 178 |
version = "25.1.0"
|
|
@@ -225,6 +238,18 @@ wheels = [
|
|
| 225 |
{ url = "https://files.pythonhosted.org/packages/01/b6/dcd0fd188cc28d772e0df23a31ce50af4d358ef31bfee969dc5a033482a5/botocore-1.39.0-py3-none-any.whl", hash = "sha256:d8e72850d3450aeca355b654efb32c8370bf824c1945a61cad2395dc2688581e", size = 13753356 },
|
| 226 |
]
|
| 227 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
[[package]]
|
| 229 |
name = "cachetools"
|
| 230 |
version = "5.5.2"
|
|
@@ -1532,10 +1557,12 @@ wheels = [
|
|
| 1532 |
{ url = "https://files.pythonhosted.org/packages/9a/55/2cb24ea48aa30c99f805921c1c7860c1f45c0e811e44ee4e6a155668de06/lxml-6.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:219e0431ea8006e15005767f0351e3f7f9143e793e58519dc97fe9e07fae5563", size = 4952289 },
|
| 1533 |
{ url = "https://files.pythonhosted.org/packages/31/c0/b25d9528df296b9a3306ba21ff982fc5b698c45ab78b94d18c2d6ae71fd9/lxml-6.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bd5913b4972681ffc9718bc2d4c53cde39ef81415e1671ff93e9aa30b46595e7", size = 5111310 },
|
| 1534 |
{ url = "https://files.pythonhosted.org/packages/e9/af/681a8b3e4f668bea6e6514cbcb297beb6de2b641e70f09d3d78655f4f44c/lxml-6.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:390240baeb9f415a82eefc2e13285016f9c8b5ad71ec80574ae8fa9605093cd7", size = 5025457 },
|
|
|
|
| 1535 |
{ url = "https://files.pythonhosted.org/packages/69/f8/693b1a10a891197143c0673fcce5b75fc69132afa81a36e4568c12c8faba/lxml-6.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ca50bd612438258a91b5b3788c6621c1f05c8c478e7951899f492be42defc0da", size = 5257565 },
|
| 1536 |
{ url = "https://files.pythonhosted.org/packages/a8/96/e08ff98f2c6426c98c8964513c5dab8d6eb81dadcd0af6f0c538ada78d33/lxml-6.0.0-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:c24b8efd9c0f62bad0439283c2c795ef916c5a6b75f03c17799775c7ae3c0c9e", size = 4713390 },
|
| 1537 |
{ url = "https://files.pythonhosted.org/packages/a8/83/6184aba6cc94d7413959f6f8f54807dc318fdcd4985c347fe3ea6937f772/lxml-6.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:afd27d8629ae94c5d863e32ab0e1d5590371d296b87dae0a751fb22bf3685741", size = 5066103 },
|
| 1538 |
{ url = "https://files.pythonhosted.org/packages/ee/01/8bf1f4035852d0ff2e36a4d9aacdbcc57e93a6cd35a54e05fa984cdf73ab/lxml-6.0.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:54c4855eabd9fc29707d30141be99e5cd1102e7d2258d2892314cf4c110726c3", size = 4791428 },
|
|
|
|
| 1539 |
{ url = "https://files.pythonhosted.org/packages/5c/f7/5495829a864bc5f8b0798d2b52a807c89966523140f3d6fa3a58ab6720ea/lxml-6.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36531f81c8214e293097cd2b7873f178997dae33d3667caaae8bdfb9666b76c0", size = 5281290 },
|
| 1540 |
{ url = "https://files.pythonhosted.org/packages/79/56/6b8edb79d9ed294ccc4e881f4db1023af56ba451909b9ce79f2a2cd7c532/lxml-6.0.0-cp312-cp312-win32.whl", hash = "sha256:690b20e3388a7ec98e899fd54c924e50ba6693874aa65ef9cb53de7f7de9d64a", size = 3613495 },
|
| 1541 |
{ url = "https://files.pythonhosted.org/packages/0b/1e/cc32034b40ad6af80b6fd9b66301fc0f180f300002e5c3eb5a6110a93317/lxml-6.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:310b719b695b3dd442cdfbbe64936b2f2e231bb91d998e99e6f0daf991a3eba3", size = 4014711 },
|
|
@@ -1546,10 +1573,12 @@ wheels = [
|
|
| 1546 |
{ url = "https://files.pythonhosted.org/packages/52/46/3572761efc1bd45fcafb44a63b3b0feeb5b3f0066886821e94b0254f9253/lxml-6.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d18a25b19ca7307045581b18b3ec9ead2b1db5ccd8719c291f0cd0a5cec6cb81", size = 4947559 },
|
| 1547 |
{ url = "https://files.pythonhosted.org/packages/94/8a/5e40de920e67c4f2eef9151097deb9b52d86c95762d8ee238134aff2125d/lxml-6.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d4f0c66df4386b75d2ab1e20a489f30dc7fd9a06a896d64980541506086be1f1", size = 5102143 },
|
| 1548 |
{ url = "https://files.pythonhosted.org/packages/7c/4b/20555bdd75d57945bdabfbc45fdb1a36a1a0ff9eae4653e951b2b79c9209/lxml-6.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f4b481b6cc3a897adb4279216695150bbe7a44c03daba3c894f49d2037e0a24", size = 5021931 },
|
|
|
|
| 1549 |
{ url = "https://files.pythonhosted.org/packages/d4/dd/39c8507c16db6031f8c1ddf70ed95dbb0a6d466a40002a3522c128aba472/lxml-6.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae06fbab4f1bb7db4f7c8ca9897dc8db4447d1a2b9bee78474ad403437bcc29", size = 5247467 },
|
| 1550 |
{ url = "https://files.pythonhosted.org/packages/4d/56/732d49def0631ad633844cfb2664563c830173a98d5efd9b172e89a4800d/lxml-6.0.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:1fa377b827ca2023244a06554c6e7dc6828a10aaf74ca41965c5d8a4925aebb4", size = 4720601 },
|
| 1551 |
{ url = "https://files.pythonhosted.org/packages/8f/7f/6b956fab95fa73462bca25d1ea7fc8274ddf68fb8e60b78d56c03b65278e/lxml-6.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1676b56d48048a62ef77a250428d1f31f610763636e0784ba67a9740823988ca", size = 5060227 },
|
| 1552 |
{ url = "https://files.pythonhosted.org/packages/97/06/e851ac2924447e8b15a294855caf3d543424364a143c001014d22c8ca94c/lxml-6.0.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:0e32698462aacc5c1cf6bdfebc9c781821b7e74c79f13e5ffc8bfe27c42b1abf", size = 4790637 },
|
|
|
|
| 1553 |
{ url = "https://files.pythonhosted.org/packages/52/03/0e764ce00b95e008d76b99d432f1807f3574fb2945b496a17807a1645dbd/lxml-6.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7488a43033c958637b1a08cddc9188eb06d3ad36582cebc7d4815980b47e27ef", size = 5272430 },
|
| 1554 |
{ url = "https://files.pythonhosted.org/packages/5f/01/d48cc141bc47bc1644d20fe97bbd5e8afb30415ec94f146f2f76d0d9d098/lxml-6.0.0-cp313-cp313-win32.whl", hash = "sha256:5fcd7d3b1d8ecb91445bd71b9c88bdbeae528fefee4f379895becfc72298d181", size = 3612896 },
|
| 1555 |
{ url = "https://files.pythonhosted.org/packages/f4/87/6456b9541d186ee7d4cb53bf1b9a0d7f3b1068532676940fdd594ac90865/lxml-6.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:2f34687222b78fff795feeb799a7d44eca2477c3d9d3a46ce17d51a4f383e32e", size = 4013132 },
|
|
@@ -1974,6 +2003,8 @@ sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3
|
|
| 1974 |
wheels = [
|
| 1975 |
{ url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800 },
|
| 1976 |
{ url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296 },
|
|
|
|
|
|
|
| 1977 |
{ url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787 },
|
| 1978 |
{ url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236 },
|
| 1979 |
{ url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950 },
|
|
@@ -1986,6 +2017,8 @@ wheels = [
|
|
| 1986 |
{ url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443 },
|
| 1987 |
{ url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474 },
|
| 1988 |
{ url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038 },
|
|
|
|
|
|
|
| 1989 |
{ url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503 },
|
| 1990 |
{ url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574 },
|
| 1991 |
{ url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060 },
|
|
@@ -1995,6 +2028,8 @@ wheels = [
|
|
| 1995 |
{ url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055 },
|
| 1996 |
{ url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110 },
|
| 1997 |
{ url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547 },
|
|
|
|
|
|
|
| 1998 |
{ url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001 },
|
| 1999 |
{ url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814 },
|
| 2000 |
{ url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124 },
|
|
@@ -2004,6 +2039,8 @@ wheels = [
|
|
| 2004 |
{ url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803 },
|
| 2005 |
{ url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520 },
|
| 2006 |
{ url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116 },
|
|
|
|
|
|
|
| 2007 |
{ url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336 },
|
| 2008 |
{ url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699 },
|
| 2009 |
{ url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789 },
|
|
@@ -2013,6 +2050,8 @@ wheels = [
|
|
| 2013 |
{ url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385 },
|
| 2014 |
{ url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129 },
|
| 2015 |
{ url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580 },
|
|
|
|
|
|
|
| 2016 |
{ url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888 },
|
| 2017 |
{ url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330 },
|
| 2018 |
{ url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089 },
|
|
@@ -2031,6 +2070,25 @@ wheels = [
|
|
| 2031 |
{ url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567 },
|
| 2032 |
]
|
| 2033 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2034 |
[[package]]
|
| 2035 |
name = "polars"
|
| 2036 |
version = "1.31.0"
|
|
@@ -2412,6 +2470,18 @@ wheels = [
|
|
| 2412 |
{ url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235 },
|
| 2413 |
]
|
| 2414 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2415 |
[[package]]
|
| 2416 |
name = "pygments"
|
| 2417 |
version = "2.19.2"
|
|
@@ -2955,6 +3025,15 @@ wheels = [
|
|
| 2955 |
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
|
| 2956 |
]
|
| 2957 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2958 |
[[package]]
|
| 2959 |
name = "sqlalchemy"
|
| 2960 |
version = "2.0.41"
|
|
@@ -3241,6 +3320,7 @@ version = "0.1.0"
|
|
| 3241 |
source = { virtual = "." }
|
| 3242 |
dependencies = [
|
| 3243 |
{ name = "anthropic" },
|
|
|
|
| 3244 |
{ name = "google-generativeai" },
|
| 3245 |
{ name = "ipywidgets" },
|
| 3246 |
{ name = "langchain" },
|
|
@@ -3250,6 +3330,7 @@ dependencies = [
|
|
| 3250 |
{ name = "langchain-google-genai" },
|
| 3251 |
{ name = "langchain-openai" },
|
| 3252 |
{ name = "openai" },
|
|
|
|
| 3253 |
{ name = "pydantic" },
|
| 3254 |
{ name = "pydantic-ai" },
|
| 3255 |
{ name = "pyrsm" },
|
|
@@ -3263,6 +3344,7 @@ dependencies = [
|
|
| 3263 |
[package.metadata]
|
| 3264 |
requires-dist = [
|
| 3265 |
{ name = "anthropic", specifier = ">=0.55.0" },
|
|
|
|
| 3266 |
{ name = "google-generativeai", specifier = ">=0.8.5" },
|
| 3267 |
{ name = "ipywidgets", specifier = ">=8.1.7" },
|
| 3268 |
{ name = "langchain", specifier = ">=0.3.26" },
|
|
@@ -3271,6 +3353,7 @@ requires-dist = [
|
|
| 3271 |
{ name = "langchain-google-genai", specifier = ">=2.0.10" },
|
| 3272 |
{ name = "langchain-openai", specifier = ">=0.3.27" },
|
| 3273 |
{ name = "openai", specifier = ">=1.93.0" },
|
|
|
|
| 3274 |
{ name = "pydantic", specifier = ">=2.11.7" },
|
| 3275 |
{ name = "pydantic-ai", specifier = ">=0.3.5" },
|
| 3276 |
{ name = "pyrsm", specifier = ">=1.6.0" },
|
|
|
|
| 173 |
{ url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 },
|
| 174 |
]
|
| 175 |
|
| 176 |
+
[[package]]
|
| 177 |
+
name = "beautifulsoup4"
|
| 178 |
+
version = "4.13.4"
|
| 179 |
+
source = { registry = "https://pypi.org/simple" }
|
| 180 |
+
dependencies = [
|
| 181 |
+
{ name = "soupsieve" },
|
| 182 |
+
{ name = "typing-extensions" },
|
| 183 |
+
]
|
| 184 |
+
sdist = { url = "https://files.pythonhosted.org/packages/d8/e4/0c4c39e18fd76d6a628d4dd8da40543d136ce2d1752bd6eeeab0791f4d6b/beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195", size = 621067 }
|
| 185 |
+
wheels = [
|
| 186 |
+
{ url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285 },
|
| 187 |
+
]
|
| 188 |
+
|
| 189 |
[[package]]
|
| 190 |
name = "black"
|
| 191 |
version = "25.1.0"
|
|
|
|
| 238 |
{ url = "https://files.pythonhosted.org/packages/01/b6/dcd0fd188cc28d772e0df23a31ce50af4d358ef31bfee969dc5a033482a5/botocore-1.39.0-py3-none-any.whl", hash = "sha256:d8e72850d3450aeca355b654efb32c8370bf824c1945a61cad2395dc2688581e", size = 13753356 },
|
| 239 |
]
|
| 240 |
|
| 241 |
+
[[package]]
|
| 242 |
+
name = "bs4"
|
| 243 |
+
version = "0.0.2"
|
| 244 |
+
source = { registry = "https://pypi.org/simple" }
|
| 245 |
+
dependencies = [
|
| 246 |
+
{ name = "beautifulsoup4" },
|
| 247 |
+
]
|
| 248 |
+
sdist = { url = "https://files.pythonhosted.org/packages/c9/aa/4acaf814ff901145da37332e05bb510452ebed97bc9602695059dd46ef39/bs4-0.0.2.tar.gz", hash = "sha256:a48685c58f50fe127722417bae83fe6badf500d54b55f7e39ffe43b798653925", size = 698 }
|
| 249 |
+
wheels = [
|
| 250 |
+
{ url = "https://files.pythonhosted.org/packages/51/bb/bf7aab772a159614954d84aa832c129624ba6c32faa559dfb200a534e50b/bs4-0.0.2-py2.py3-none-any.whl", hash = "sha256:abf8742c0805ef7f662dce4b51cca104cffe52b835238afc169142ab9b3fbccc", size = 1189 },
|
| 251 |
+
]
|
| 252 |
+
|
| 253 |
[[package]]
|
| 254 |
name = "cachetools"
|
| 255 |
version = "5.5.2"
|
|
|
|
| 1557 |
{ url = "https://files.pythonhosted.org/packages/9a/55/2cb24ea48aa30c99f805921c1c7860c1f45c0e811e44ee4e6a155668de06/lxml-6.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:219e0431ea8006e15005767f0351e3f7f9143e793e58519dc97fe9e07fae5563", size = 4952289 },
|
| 1558 |
{ url = "https://files.pythonhosted.org/packages/31/c0/b25d9528df296b9a3306ba21ff982fc5b698c45ab78b94d18c2d6ae71fd9/lxml-6.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bd5913b4972681ffc9718bc2d4c53cde39ef81415e1671ff93e9aa30b46595e7", size = 5111310 },
|
| 1559 |
{ url = "https://files.pythonhosted.org/packages/e9/af/681a8b3e4f668bea6e6514cbcb297beb6de2b641e70f09d3d78655f4f44c/lxml-6.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:390240baeb9f415a82eefc2e13285016f9c8b5ad71ec80574ae8fa9605093cd7", size = 5025457 },
|
| 1560 |
+
{ url = "https://files.pythonhosted.org/packages/99/b6/3a7971aa05b7be7dfebc7ab57262ec527775c2c3c5b2f43675cac0458cad/lxml-6.0.0-cp312-cp312-manylinux_2_27_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d6e200909a119626744dd81bae409fc44134389e03fbf1d68ed2a55a2fb10991", size = 5657016 },
|
| 1561 |
{ url = "https://files.pythonhosted.org/packages/69/f8/693b1a10a891197143c0673fcce5b75fc69132afa81a36e4568c12c8faba/lxml-6.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ca50bd612438258a91b5b3788c6621c1f05c8c478e7951899f492be42defc0da", size = 5257565 },
|
| 1562 |
{ url = "https://files.pythonhosted.org/packages/a8/96/e08ff98f2c6426c98c8964513c5dab8d6eb81dadcd0af6f0c538ada78d33/lxml-6.0.0-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:c24b8efd9c0f62bad0439283c2c795ef916c5a6b75f03c17799775c7ae3c0c9e", size = 4713390 },
|
| 1563 |
{ url = "https://files.pythonhosted.org/packages/a8/83/6184aba6cc94d7413959f6f8f54807dc318fdcd4985c347fe3ea6937f772/lxml-6.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:afd27d8629ae94c5d863e32ab0e1d5590371d296b87dae0a751fb22bf3685741", size = 5066103 },
|
| 1564 |
{ url = "https://files.pythonhosted.org/packages/ee/01/8bf1f4035852d0ff2e36a4d9aacdbcc57e93a6cd35a54e05fa984cdf73ab/lxml-6.0.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:54c4855eabd9fc29707d30141be99e5cd1102e7d2258d2892314cf4c110726c3", size = 4791428 },
|
| 1565 |
+
{ url = "https://files.pythonhosted.org/packages/29/31/c0267d03b16954a85ed6b065116b621d37f559553d9339c7dcc4943a76f1/lxml-6.0.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c907516d49f77f6cd8ead1322198bdfd902003c3c330c77a1c5f3cc32a0e4d16", size = 5678523 },
|
| 1566 |
{ url = "https://files.pythonhosted.org/packages/5c/f7/5495829a864bc5f8b0798d2b52a807c89966523140f3d6fa3a58ab6720ea/lxml-6.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36531f81c8214e293097cd2b7873f178997dae33d3667caaae8bdfb9666b76c0", size = 5281290 },
|
| 1567 |
{ url = "https://files.pythonhosted.org/packages/79/56/6b8edb79d9ed294ccc4e881f4db1023af56ba451909b9ce79f2a2cd7c532/lxml-6.0.0-cp312-cp312-win32.whl", hash = "sha256:690b20e3388a7ec98e899fd54c924e50ba6693874aa65ef9cb53de7f7de9d64a", size = 3613495 },
|
| 1568 |
{ url = "https://files.pythonhosted.org/packages/0b/1e/cc32034b40ad6af80b6fd9b66301fc0f180f300002e5c3eb5a6110a93317/lxml-6.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:310b719b695b3dd442cdfbbe64936b2f2e231bb91d998e99e6f0daf991a3eba3", size = 4014711 },
|
|
|
|
| 1573 |
{ url = "https://files.pythonhosted.org/packages/52/46/3572761efc1bd45fcafb44a63b3b0feeb5b3f0066886821e94b0254f9253/lxml-6.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d18a25b19ca7307045581b18b3ec9ead2b1db5ccd8719c291f0cd0a5cec6cb81", size = 4947559 },
|
| 1574 |
{ url = "https://files.pythonhosted.org/packages/94/8a/5e40de920e67c4f2eef9151097deb9b52d86c95762d8ee238134aff2125d/lxml-6.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d4f0c66df4386b75d2ab1e20a489f30dc7fd9a06a896d64980541506086be1f1", size = 5102143 },
|
| 1575 |
{ url = "https://files.pythonhosted.org/packages/7c/4b/20555bdd75d57945bdabfbc45fdb1a36a1a0ff9eae4653e951b2b79c9209/lxml-6.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f4b481b6cc3a897adb4279216695150bbe7a44c03daba3c894f49d2037e0a24", size = 5021931 },
|
| 1576 |
+
{ url = "https://files.pythonhosted.org/packages/b6/6e/cf03b412f3763d4ca23b25e70c96a74cfece64cec3addf1c4ec639586b13/lxml-6.0.0-cp313-cp313-manylinux_2_27_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a78d6c9168f5bcb20971bf3329c2b83078611fbe1f807baadc64afc70523b3a", size = 5645469 },
|
| 1577 |
{ url = "https://files.pythonhosted.org/packages/d4/dd/39c8507c16db6031f8c1ddf70ed95dbb0a6d466a40002a3522c128aba472/lxml-6.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae06fbab4f1bb7db4f7c8ca9897dc8db4447d1a2b9bee78474ad403437bcc29", size = 5247467 },
|
| 1578 |
{ url = "https://files.pythonhosted.org/packages/4d/56/732d49def0631ad633844cfb2664563c830173a98d5efd9b172e89a4800d/lxml-6.0.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:1fa377b827ca2023244a06554c6e7dc6828a10aaf74ca41965c5d8a4925aebb4", size = 4720601 },
|
| 1579 |
{ url = "https://files.pythonhosted.org/packages/8f/7f/6b956fab95fa73462bca25d1ea7fc8274ddf68fb8e60b78d56c03b65278e/lxml-6.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1676b56d48048a62ef77a250428d1f31f610763636e0784ba67a9740823988ca", size = 5060227 },
|
| 1580 |
{ url = "https://files.pythonhosted.org/packages/97/06/e851ac2924447e8b15a294855caf3d543424364a143c001014d22c8ca94c/lxml-6.0.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:0e32698462aacc5c1cf6bdfebc9c781821b7e74c79f13e5ffc8bfe27c42b1abf", size = 4790637 },
|
| 1581 |
+
{ url = "https://files.pythonhosted.org/packages/06/d4/fd216f3cd6625022c25b336c7570d11f4a43adbaf0a56106d3d496f727a7/lxml-6.0.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4d6036c3a296707357efb375cfc24bb64cd955b9ec731abf11ebb1e40063949f", size = 5662049 },
|
| 1582 |
{ url = "https://files.pythonhosted.org/packages/52/03/0e764ce00b95e008d76b99d432f1807f3574fb2945b496a17807a1645dbd/lxml-6.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7488a43033c958637b1a08cddc9188eb06d3ad36582cebc7d4815980b47e27ef", size = 5272430 },
|
| 1583 |
{ url = "https://files.pythonhosted.org/packages/5f/01/d48cc141bc47bc1644d20fe97bbd5e8afb30415ec94f146f2f76d0d9d098/lxml-6.0.0-cp313-cp313-win32.whl", hash = "sha256:5fcd7d3b1d8ecb91445bd71b9c88bdbeae528fefee4f379895becfc72298d181", size = 3612896 },
|
| 1584 |
{ url = "https://files.pythonhosted.org/packages/f4/87/6456b9541d186ee7d4cb53bf1b9a0d7f3b1068532676940fdd594ac90865/lxml-6.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:2f34687222b78fff795feeb799a7d44eca2477c3d9d3a46ce17d51a4f383e32e", size = 4013132 },
|
|
|
|
| 2003 |
wheels = [
|
| 2004 |
{ url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800 },
|
| 2005 |
{ url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296 },
|
| 2006 |
+
{ url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726 },
|
| 2007 |
+
{ url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652 },
|
| 2008 |
{ url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787 },
|
| 2009 |
{ url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236 },
|
| 2010 |
{ url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950 },
|
|
|
|
| 2017 |
{ url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443 },
|
| 2018 |
{ url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474 },
|
| 2019 |
{ url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038 },
|
| 2020 |
+
{ url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407 },
|
| 2021 |
+
{ url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094 },
|
| 2022 |
{ url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503 },
|
| 2023 |
{ url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574 },
|
| 2024 |
{ url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060 },
|
|
|
|
| 2028 |
{ url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055 },
|
| 2029 |
{ url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110 },
|
| 2030 |
{ url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547 },
|
| 2031 |
+
{ url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554 },
|
| 2032 |
+
{ url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132 },
|
| 2033 |
{ url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001 },
|
| 2034 |
{ url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814 },
|
| 2035 |
{ url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124 },
|
|
|
|
| 2039 |
{ url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803 },
|
| 2040 |
{ url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520 },
|
| 2041 |
{ url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116 },
|
| 2042 |
+
{ url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597 },
|
| 2043 |
+
{ url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246 },
|
| 2044 |
{ url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336 },
|
| 2045 |
{ url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699 },
|
| 2046 |
{ url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789 },
|
|
|
|
| 2050 |
{ url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385 },
|
| 2051 |
{ url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129 },
|
| 2052 |
{ url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580 },
|
| 2053 |
+
{ url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860 },
|
| 2054 |
+
{ url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694 },
|
| 2055 |
{ url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888 },
|
| 2056 |
{ url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330 },
|
| 2057 |
{ url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089 },
|
|
|
|
| 2070 |
{ url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567 },
|
| 2071 |
]
|
| 2072 |
|
| 2073 |
+
[[package]]
|
| 2074 |
+
name = "playwright"
|
| 2075 |
+
version = "1.53.0"
|
| 2076 |
+
source = { registry = "https://pypi.org/simple" }
|
| 2077 |
+
dependencies = [
|
| 2078 |
+
{ name = "greenlet" },
|
| 2079 |
+
{ name = "pyee" },
|
| 2080 |
+
]
|
| 2081 |
+
wheels = [
|
| 2082 |
+
{ url = "https://files.pythonhosted.org/packages/f5/e2/2f107be74419280749723bd1197c99351f4b8a0a25e974b9764affb940b2/playwright-1.53.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:48a1a15ce810f0ffe512b6050de9871ea193b41dd3cc1bbed87b8431012419ba", size = 40392498 },
|
| 2083 |
+
{ url = "https://files.pythonhosted.org/packages/ac/d5/e8c57a4f6fd46059fb2d51da2d22b47afc886b42400f06b742cd4a9ba131/playwright-1.53.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a701f9498a5b87e3f929ec01cea3109fbde75821b19c7ba4bba54f6127b94f76", size = 38647035 },
|
| 2084 |
+
{ url = "https://files.pythonhosted.org/packages/4d/f3/da18cd7c22398531316e58fd131243fd9156fe7765aae239ae542a5d07d2/playwright-1.53.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:f765498341c4037b4c01e742ae32dd335622f249488ccd77ca32d301d7c82c61", size = 40392502 },
|
| 2085 |
+
{ url = "https://files.pythonhosted.org/packages/92/32/5d871c3753fbee5113eefc511b9e44c0006a27f2301b4c6bffa4346fbd94/playwright-1.53.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:db19cb5b58f3b15cad3e2419f4910c053e889202fc202461ee183f1530d1db60", size = 45848364 },
|
| 2086 |
+
{ url = "https://files.pythonhosted.org/packages/dc/6b/9942f86661ff41332f9299db4950623123e60ca71e4fb6e6942fc0212624/playwright-1.53.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9276c9c935fc062f51f4f5107e56420afd6d9a524348dc437793dc2e34c742e3", size = 45235174 },
|
| 2087 |
+
{ url = "https://files.pythonhosted.org/packages/51/63/28b3f2d36e6a95e88f033d2aa7af06083f6f4aa0d9764759d96033cd053e/playwright-1.53.0-py3-none-win32.whl", hash = "sha256:36eedec101724ff5a000cddab87dd9a72a39f9b3e65a687169c465484e667c06", size = 35415131 },
|
| 2088 |
+
{ url = "https://files.pythonhosted.org/packages/a9/b5/4ca25974a90d16cfd4a9a953ee5a666cf484a0bdacb4eed484e5cab49e66/playwright-1.53.0-py3-none-win_amd64.whl", hash = "sha256:d68975807a0fd997433537f1dcf2893cda95884a39dc23c6f591b8d5f691e9e8", size = 35415138 },
|
| 2089 |
+
{ url = "https://files.pythonhosted.org/packages/9a/81/b42ff2116df5d07ccad2dc4eeb20af92c975a1fbc7cd3ed37b678468b813/playwright-1.53.0-py3-none-win_arm64.whl", hash = "sha256:fcfd481f76568d7b011571160e801b47034edd9e2383c43d83a5fb3f35c67885", size = 31188568 },
|
| 2090 |
+
]
|
| 2091 |
+
|
| 2092 |
[[package]]
|
| 2093 |
name = "polars"
|
| 2094 |
version = "1.31.0"
|
|
|
|
| 2470 |
{ url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235 },
|
| 2471 |
]
|
| 2472 |
|
| 2473 |
+
[[package]]
|
| 2474 |
+
name = "pyee"
|
| 2475 |
+
version = "13.0.0"
|
| 2476 |
+
source = { registry = "https://pypi.org/simple" }
|
| 2477 |
+
dependencies = [
|
| 2478 |
+
{ name = "typing-extensions" },
|
| 2479 |
+
]
|
| 2480 |
+
sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250 }
|
| 2481 |
+
wheels = [
|
| 2482 |
+
{ url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730 },
|
| 2483 |
+
]
|
| 2484 |
+
|
| 2485 |
[[package]]
|
| 2486 |
name = "pygments"
|
| 2487 |
version = "2.19.2"
|
|
|
|
| 3025 |
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
|
| 3026 |
]
|
| 3027 |
|
| 3028 |
+
[[package]]
|
| 3029 |
+
name = "soupsieve"
|
| 3030 |
+
version = "2.7"
|
| 3031 |
+
source = { registry = "https://pypi.org/simple" }
|
| 3032 |
+
sdist = { url = "https://files.pythonhosted.org/packages/3f/f4/4a80cd6ef364b2e8b65b15816a843c0980f7a5a2b4dc701fc574952aa19f/soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a", size = 103418 }
|
| 3033 |
+
wheels = [
|
| 3034 |
+
{ url = "https://files.pythonhosted.org/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", size = 36677 },
|
| 3035 |
+
]
|
| 3036 |
+
|
| 3037 |
[[package]]
|
| 3038 |
name = "sqlalchemy"
|
| 3039 |
version = "2.0.41"
|
|
|
|
| 3320 |
source = { virtual = "." }
|
| 3321 |
dependencies = [
|
| 3322 |
{ name = "anthropic" },
|
| 3323 |
+
{ name = "bs4" },
|
| 3324 |
{ name = "google-generativeai" },
|
| 3325 |
{ name = "ipywidgets" },
|
| 3326 |
{ name = "langchain" },
|
|
|
|
| 3330 |
{ name = "langchain-google-genai" },
|
| 3331 |
{ name = "langchain-openai" },
|
| 3332 |
{ name = "openai" },
|
| 3333 |
+
{ name = "playwright" },
|
| 3334 |
{ name = "pydantic" },
|
| 3335 |
{ name = "pydantic-ai" },
|
| 3336 |
{ name = "pyrsm" },
|
|
|
|
| 3344 |
[package.metadata]
|
| 3345 |
requires-dist = [
|
| 3346 |
{ name = "anthropic", specifier = ">=0.55.0" },
|
| 3347 |
+
{ name = "bs4", specifier = ">=0.0.2" },
|
| 3348 |
{ name = "google-generativeai", specifier = ">=0.8.5" },
|
| 3349 |
{ name = "ipywidgets", specifier = ">=8.1.7" },
|
| 3350 |
{ name = "langchain", specifier = ">=0.3.26" },
|
|
|
|
| 3353 |
{ name = "langchain-google-genai", specifier = ">=2.0.10" },
|
| 3354 |
{ name = "langchain-openai", specifier = ">=0.3.27" },
|
| 3355 |
{ name = "openai", specifier = ">=1.93.0" },
|
| 3356 |
+
{ name = "playwright", specifier = ">=1.53.0" },
|
| 3357 |
{ name = "pydantic", specifier = ">=2.11.7" },
|
| 3358 |
{ name = "pydantic-ai", specifier = ">=0.3.5" },
|
| 3359 |
{ name = "pyrsm", specifier = ">=1.6.0" },
|