From 11e071d0e5994a60465ee9a71831ddf525dda414 Mon Sep 17 00:00:00 2001
From: Rasul Kireev
Date: Sat, 27 Dec 2025 23:35:09 +0300
Subject: [PATCH 1/3] init
---
README.md | 3 +-
core/agents/blog_post_outline_agent.py | 98 +
core/agents/research_link_summary_agent.py | 90 +
core/agents/schemas.py | 13 +
core/content_generator/__init__.py | 7 +
core/content_generator/pipeline.py | 598 ++++++
core/content_generator/tasks.py | 91 +
core/content_generator/utils.py | 15 +
...eratedblogpostresearchquestion_and_more.py | 95 +
...edblogpostresearchlink_content_and_more.py | 44 +
core/models.py | 177 +-
core/tasks.py | 12 +
poetry.lock | 1871 ++++++++++-------
pyproject.toml | 1 +
requirements.txt | 162 +-
.../inspect_blog_post_title_suggestion.py | 168 ++
tuxseo/settings.py | 3 +
17 files changed, 2541 insertions(+), 907 deletions(-)
create mode 100644 core/agents/blog_post_outline_agent.py
create mode 100644 core/agents/research_link_summary_agent.py
create mode 100644 core/content_generator/__init__.py
create mode 100644 core/content_generator/pipeline.py
create mode 100644 core/content_generator/tasks.py
create mode 100644 core/content_generator/utils.py
create mode 100644 core/migrations/0050_backlink_generatedblogpostresearchquestion_and_more.py
create mode 100644 core/migrations/0051_rename_markdown_content_generatedblogpostresearchlink_content_and_more.py
create mode 100644 snippets/inspect_blog_post_title_suggestion.py
diff --git a/README.md b/README.md
index 6b77932..3c8fae6 100644
--- a/README.md
+++ b/README.md
@@ -39,9 +39,8 @@
[](https://render.com/deploy?repo=https://github.com/rasulkireev/tuxseo)
The only required env vars are:
-- OPENAI_API_KEY
-- TAVILY_API_KEY
- GEMINI_API_KEY
+- EXA_API_KEY
- PERPLEXITY_API_KEY
- JINA_READER_API_KEY
- KEYWORDS_EVERYWHERE_API_KEY
diff --git a/core/agents/blog_post_outline_agent.py b/core/agents/blog_post_outline_agent.py
new file mode 100644
index 0000000..1f15e44
--- /dev/null
+++ b/core/agents/blog_post_outline_agent.py
@@ -0,0 +1,98 @@
+from __future__ import annotations
+
+from pydantic import BaseModel, Field
+from pydantic_ai import Agent
+
+from core.agents.schemas import BlogPostGenerationContext
+from core.agents.system_prompts import (
+ add_language_specification,
+ add_project_details,
+ add_target_keywords,
+ add_title_details,
+ add_todays_date,
+)
+from core.choices import get_default_ai_model
+
+
+class BlogPostOutlineSection(BaseModel):
+ title: str = Field(description="Section title (use plain text, no markdown prefixes)")
+
+
+class BlogPostOutline(BaseModel):
+ sections: list[BlogPostOutlineSection] = Field(
+ description=(
+ "Ordered list of 4-8 section titles that will be used as H2 (##) headers in the blog post." # noqa: E501
+ )
+ )
+
+
+BLOG_POST_OUTLINE_SYSTEM_PROMPT = """
+You are an expert content strategist.
+
+Your task: propose only the middle-section outline for the blog post.
+
+Requirements:
+- Generate 4-8 main topics that will be used as H2 (##) sections.
+- Do NOT include markdown symbols in section titles (no leading #, ##, -, etc.).
+- Keep titles short and descriptive.
+- Do NOT include 'Introduction' or 'Conclusion' yet.
+
+Output must be a structured list of section titles only.
+"""
+
+
+def create_blog_post_outline_agent(model: str | None = None) -> Agent:
+ agent = Agent(
+ model or get_default_ai_model(),
+ output_type=BlogPostOutline,
+ deps_type=BlogPostGenerationContext,
+ system_prompt=BLOG_POST_OUTLINE_SYSTEM_PROMPT,
+ retries=2,
+ model_settings={"temperature": 0.7},
+ )
+
+ agent.system_prompt(add_project_details)
+ agent.system_prompt(add_title_details)
+ agent.system_prompt(add_todays_date)
+ agent.system_prompt(add_language_specification)
+ agent.system_prompt(add_target_keywords)
+
+ return agent
+
+
+class BlogPostSectionResearchQuestions(BaseModel):
+ questions: list[str] = Field(
+ default_factory=list,
+ description="3-6 concrete research questions for a single section",
+ )
+
+
+BLOG_POST_SECTION_QUESTIONS_SYSTEM_PROMPT = """
+You are an expert content researcher.
+
+Given a blog post section title, generate 3-6 specific research questions to investigate.
+
+Requirements:
+- Questions should be specific and searchable.
+- Prefer questions that lead to concrete examples, comparisons, metrics, pitfalls, and best practices.
+- Avoid vague or overly broad questions.
+""" # noqa: E501
+
+
+def create_blog_post_section_research_questions_agent(model: str | None = None) -> Agent:
+ agent = Agent(
+ model or get_default_ai_model(),
+ output_type=BlogPostSectionResearchQuestions,
+ deps_type=BlogPostGenerationContext,
+ system_prompt=BLOG_POST_SECTION_QUESTIONS_SYSTEM_PROMPT,
+ retries=2,
+ model_settings={"temperature": 0.7},
+ )
+
+ agent.system_prompt(add_project_details)
+ agent.system_prompt(add_title_details)
+ agent.system_prompt(add_todays_date)
+ agent.system_prompt(add_language_specification)
+ agent.system_prompt(add_target_keywords)
+
+ return agent
diff --git a/core/agents/research_link_summary_agent.py b/core/agents/research_link_summary_agent.py
new file mode 100644
index 0000000..e38bfbe
--- /dev/null
+++ b/core/agents/research_link_summary_agent.py
@@ -0,0 +1,90 @@
+from django.utils import timezone
+from pydantic_ai import Agent, RunContext
+
+from core.agents.schemas import (
+ ResearchLinkContextualSummaryContext,
+ TextSummary,
+ WebPageContent,
+)
+from core.choices import get_default_ai_model
+
+
+def _add_webpage_content_from_web_page_content(ctx: RunContext[WebPageContent]) -> str:
+ return (
+ "Web page content:\n"
+ f"Title: {ctx.deps.title}\n"
+ f"Description: {ctx.deps.description}\n"
+ f"Content: {ctx.deps.markdown_content}\n"
+ )
+
+
+def _add_webpage_content_from_contextual_deps(
+ ctx: RunContext[ResearchLinkContextualSummaryContext],
+) -> str:
+ web_page_content = ctx.deps.web_page_content
+ return (
+ "Web page content:\n"
+ f"URL: {ctx.deps.url}\n"
+ f"Title: {web_page_content.title}\n"
+ f"Description: {web_page_content.description}\n"
+ f"Content: {web_page_content.markdown_content}\n"
+ )
+
+
+def _add_blog_post_research_context(ctx: RunContext[ResearchLinkContextualSummaryContext]) -> str:
+ blog_post_generation_context = ctx.deps.blog_post_generation_context
+ project_details = blog_post_generation_context.project_details
+ title_suggestion = blog_post_generation_context.title_suggestion
+ target_keywords = title_suggestion.target_keywords or []
+
+ return (
+ "Context for why we are summarizing this page:\n"
+ f"- Today's date: {timezone.now().strftime('%Y-%m-%d')}\n"
+ f"- Project: {project_details.name}\n"
+ f"- Project summary: {project_details.summary}\n"
+ f"- Blog post title: {ctx.deps.blog_post_title}\n"
+ f"- Blog post section: {ctx.deps.section_title}\n"
+ f"- Research question: {ctx.deps.research_question}\n"
+ f"- Target keywords: {', '.join(target_keywords) if target_keywords else 'None'}\n"
+ "\n"
+ "You must tailor the summary to help the writer answer the research question for that section.\n" # noqa: E501
+ )
+
+
+def create_general_research_link_summary_agent(model=None):
+ agent = Agent(
+ model or get_default_ai_model(),
+ output_type=TextSummary,
+ deps_type=WebPageContent,
+ system_prompt=(
+ "You are an expert content summarizer. Summarize the web page content provided.\n"
+ "Return a concise 2-3 sentence summary that captures the main purpose and key information.\n" # noqa: E501
+ "Focus on what the page is about and its main value proposition.\n"
+ ),
+ retries=2,
+ model_settings={"temperature": 0.4},
+ )
+ agent.system_prompt(_add_webpage_content_from_web_page_content)
+ return agent
+
+
+def create_contextual_research_link_summary_agent(model=None):
+ agent = Agent(
+ model or get_default_ai_model(),
+ output_type=TextSummary,
+ deps_type=ResearchLinkContextualSummaryContext,
+ system_prompt=(
+ "You are a research assistant helping write a blog post.\n"
+ "Summarize the page in a way that is maximally useful for answering the research question.\n" # noqa: E501
+ "Prefer concrete facts, definitions, steps, examples, and any notable stats. If the page is not relevant, say so clearly.\n" # noqa: E501
+ "Output markdown that includes:\n"
+ "- A short paragraph summary\n"
+ "- 'Key takeaways' as 3-7 bullet points\n"
+ "- 'How this helps our section' as 1-3 bullet points\n"
+ ),
+ retries=2,
+ model_settings={"temperature": 0.3},
+ )
+ agent.system_prompt(_add_blog_post_research_context)
+ agent.system_prompt(_add_webpage_content_from_contextual_deps)
+ return agent
diff --git a/core/agents/schemas.py b/core/agents/schemas.py
index 6852bfc..163e66d 100644
--- a/core/agents/schemas.py
+++ b/core/agents/schemas.py
@@ -14,6 +14,10 @@ class WebPageContent(BaseModel):
markdown_content: str
+class TextSummary(BaseModel):
+ summary: str = Field(description="A concise summary of the provided content")
+
+
class ProjectDetails(BaseModel):
name: str = Field(description="Official name of the project or organization")
type: str = Field(
@@ -189,6 +193,15 @@ class BlogPostGenerationContext(BaseModel):
content_type: str = Field(description="Type of content to generate (SEO or SHARING)")
+class ResearchLinkContextualSummaryContext(BaseModel):
+ url: str = Field(description="Source URL of the research page")
+ web_page_content: WebPageContent
+ blog_post_generation_context: BlogPostGenerationContext
+ blog_post_title: str = Field(description="Title of the blog post being written")
+ section_title: str = Field(description="Title of the blog post section being written")
+ research_question: str = Field(description="Research question we are trying to answer")
+
+
class GeneratedBlogPostSchema(BaseModel):
description: str = Field(
description="Meta description (150-160 characters) optimized for search engines"
diff --git a/core/content_generator/__init__.py b/core/content_generator/__init__.py
new file mode 100644
index 0000000..976578a
--- /dev/null
+++ b/core/content_generator/__init__.py
@@ -0,0 +1,7 @@
+"""
+Content generation pipeline package.
+
+This package contains:
+- `pipeline.py`: pipeline "steps" that orchestrate content generation + research.
+- `utils.py`: small reusable helpers for the pipeline.
+"""
diff --git a/core/content_generator/pipeline.py b/core/content_generator/pipeline.py
new file mode 100644
index 0000000..dfc8a6b
--- /dev/null
+++ b/core/content_generator/pipeline.py
@@ -0,0 +1,598 @@
+from __future__ import annotations
+
+from django.conf import settings
+from django.db import transaction
+from django.utils import timezone
+from django.utils.dateparse import parse_datetime
+from django.utils.text import slugify
+from django_q.tasks import async_task
+from exa_py import Exa
+
+from core.agents.blog_post_outline_agent import (
+ create_blog_post_outline_agent,
+ create_blog_post_section_research_questions_agent,
+)
+from core.agents.research_link_summary_agent import (
+ create_contextual_research_link_summary_agent,
+ create_general_research_link_summary_agent,
+)
+from core.agents.schemas import (
+ BlogPostGenerationContext,
+ ResearchLinkContextualSummaryContext,
+ WebPageContent,
+)
+from core.choices import ContentType
+from core.content_generator.utils import get_exa_date_range_iso_strings
+from core.models import (
+ GeneratedBlogPost,
+ GeneratedBlogPostResearchLink,
+ GeneratedBlogPostResearchQuestion,
+ GeneratedBlogPostSection,
+)
+from core.utils import get_markdown_content, run_agent_synchronously
+from tuxseo.utils import get_tuxseo_logger
+
+logger = get_tuxseo_logger(__name__)
+
+
+INTRODUCTION_SECTION_TITLE = "Introduction"
+CONCLUSION_SECTION_TITLE = "Conclusion"
+NON_RESEARCH_SECTION_TITLES = {INTRODUCTION_SECTION_TITLE, CONCLUSION_SECTION_TITLE}
+MAX_RESEARCH_LINK_MARKDOWN_CHARS_FOR_SUMMARY = 25_000
+
+
+def _create_blog_post_generation_context(
+ *, title_suggestion, content_type_to_use: str
+) -> BlogPostGenerationContext:
+ keywords_to_use = title_suggestion.get_blog_post_keywords()
+ return BlogPostGenerationContext(
+ project_details=title_suggestion.project.project_details,
+ title_suggestion=title_suggestion.title_suggestion_schema,
+ project_keywords=keywords_to_use,
+ project_pages=[],
+ content_type=content_type_to_use,
+ )
+
+
+def generate_sections_to_create(*, title_suggestion, content_type: str | None = None) -> list[str]:
+ """
+ Step 1: Generate the section titles we will create (one AI query).
+ """
+ if title_suggestion is None:
+ raise ValueError("title_suggestion is required")
+
+ if not title_suggestion.project_id:
+ raise ValueError("title_suggestion must be associated to a project")
+
+ content_type_to_use = content_type or title_suggestion.content_type or ContentType.SHARING
+ outline_context = _create_blog_post_generation_context(
+ title_suggestion=title_suggestion,
+ content_type_to_use=content_type_to_use,
+ )
+
+ outline_agent = create_blog_post_outline_agent()
+ outline_result = run_agent_synchronously(
+ outline_agent,
+ "Generate the blog post outline sections.",
+ deps=outline_context,
+ function_name="generate_sections_to_create",
+ model_name="GeneratedBlogPost",
+ )
+
+ outline_sections = (
+ outline_result.output.sections if outline_result and outline_result.output else []
+ )
+
+ middle_section_titles = [
+ (section.title or "").strip()
+ for section in outline_sections
+ if (section.title or "").strip()
+ ]
+
+ return [INTRODUCTION_SECTION_TITLE, *middle_section_titles, CONCLUSION_SECTION_TITLE]
+
+
+def create_blog_post_and_sections(
+ *, title_suggestion, section_titles: list[str], content_type: str | None = None
+):
+ """
+ Step 1b: Persist the GeneratedBlogPost + GeneratedBlogPostSection rows.
+ """
+ content_type_to_use = content_type or title_suggestion.content_type or ContentType.SHARING
+ tags = ", ".join(title_suggestion.target_keywords) if title_suggestion.target_keywords else ""
+
+ with transaction.atomic():
+ blog_post = GeneratedBlogPost.objects.create(
+ project=title_suggestion.project,
+ title_suggestion=title_suggestion,
+ title=title_suggestion.title,
+ description=title_suggestion.suggested_meta_description,
+ slug=slugify(title_suggestion.title),
+ tags=tags,
+ content="",
+ )
+
+ for section_order, section_title in enumerate(section_titles):
+ GeneratedBlogPostSection.objects.create(
+ blog_post=blog_post,
+ title=(section_title or "")[:250],
+ content="",
+ order=section_order,
+ )
+
+ logger.info(
+ "[ContentGenerator] Blog post initialized",
+ blog_post_id=blog_post.id,
+ title_suggestion_id=title_suggestion.id,
+ project_id=title_suggestion.project_id,
+ num_sections_created=len(section_titles),
+ content_type=content_type_to_use,
+ )
+
+ return blog_post
+
+
+def queue_research_question_generation_for_sections(*, blog_post_id: int) -> int:
+ """
+ Step 2: Queue one task per (research) section to generate questions.
+ """
+ blog_post = (
+ GeneratedBlogPost.objects.prefetch_related("blog_post_sections")
+ .filter(id=blog_post_id)
+ .first()
+ )
+ if not blog_post:
+ raise ValueError(f"GeneratedBlogPost not found: {blog_post_id}")
+
+ blog_post_sections = list(blog_post.blog_post_sections.all())
+ research_sections = [
+ section
+ for section in blog_post_sections
+ if (section.title or "").strip() not in NON_RESEARCH_SECTION_TITLES
+ ]
+
+ for section in research_sections:
+ async_task(
+ "core.content_generator.tasks.generate_research_questions_for_section_task",
+ section.id,
+ group="Generate Research Questions",
+ )
+
+ logger.info(
+ "[ContentGenerator] Queued research question generation tasks",
+ blog_post_id=blog_post.id,
+ num_sections=len(blog_post_sections),
+ num_research_sections=len(research_sections),
+ )
+
+ return len(research_sections)
+
+
+def init_blog_post_content_generation(title_suggestion, content_type: str | None = None):
+ """
+ Pipeline entrypoint (currently stops after queuing tasks).
+
+ Step 1: generate sections we will create
+ Step 2: queue tasks to generate questions for each section
+ Step 3: (handled by the tasks) queue tasks to fetch Exa links for each generated question
+ Step 4: next steps later
+ """
+ section_titles = generate_sections_to_create(
+ title_suggestion=title_suggestion, content_type=content_type
+ )
+ blog_post = create_blog_post_and_sections(
+ title_suggestion=title_suggestion,
+ section_titles=section_titles,
+ content_type=content_type,
+ )
+ queue_research_question_generation_for_sections(blog_post_id=blog_post.id)
+ return blog_post
+
+
+def populate_research_links_for_question_from_exa(
+ research_question_id: int,
+ num_results_per_question: int = 2,
+ months_back: int = 6,
+):
+ """
+ Step 3: Get links for one question from Exa (called via a task per question).
+ """
+ research_question = (
+ GeneratedBlogPostResearchQuestion.objects.select_related("blog_post")
+ .filter(id=research_question_id)
+ .first()
+ )
+ if not research_question:
+ raise ValueError(f"GeneratedBlogPostResearchQuestion not found: {research_question_id}")
+
+ blog_post = research_question.blog_post
+ if not blog_post:
+ raise ValueError(f"GeneratedBlogPost missing on research question: {research_question_id}")
+
+ research_question_text = (research_question.question or "").strip()
+ if not research_question_text:
+ return 0
+
+ start_date_iso_format, end_date_iso_format = get_exa_date_range_iso_strings(
+ months_back=months_back
+ )
+ exa = Exa(api_key=settings.EXA_API_KEY)
+
+ exa_response = exa.search(
+ research_question_text,
+ end_crawl_date=end_date_iso_format,
+ end_published_date=end_date_iso_format,
+ start_crawl_date=start_date_iso_format,
+ start_published_date=start_date_iso_format,
+ num_results=num_results_per_question,
+ type="auto",
+ )
+
+ exa_results = (
+ exa_response.results
+ if hasattr(exa_response, "results")
+ else (exa_response or {}).get("results", [])
+ )
+ exa_results = exa_results or []
+
+ num_links_upserted = 0
+ num_scrape_tasks_queued = 0
+
+ for result in exa_results:
+ if hasattr(result, "url"):
+ url = getattr(result, "url", "") or ""
+ title = getattr(result, "title", "") or ""
+ author = getattr(result, "author", "") or ""
+ published_date_raw = getattr(result, "publishedDate", None)
+ else:
+ url = (result or {}).get("url", "") or ""
+ title = (result or {}).get("title", "") or ""
+ author = (result or {}).get("author", "") or ""
+ published_date_raw = (result or {}).get("publishedDate") or (result or {}).get(
+ "published_date"
+ )
+
+ url = url.strip()
+ if not url.startswith(("http://", "https://")):
+ continue
+
+ if len(url) > 200:
+ continue
+
+ published_date = parse_datetime(published_date_raw) if published_date_raw else None
+ if published_date and timezone.is_naive(published_date):
+ published_date = timezone.make_aware(
+ published_date, timezone=timezone.get_current_timezone()
+ )
+
+ research_link, _created = GeneratedBlogPostResearchLink.objects.update_or_create(
+ blog_post=blog_post,
+ research_question=research_question,
+ url=url,
+ defaults={
+ "title": title[:500],
+ "author": author[:250],
+ "published_date": published_date,
+ },
+ )
+
+ num_links_upserted += 1
+
+ should_queue_scrape_task = not (research_link.content or "").strip()
+ if should_queue_scrape_task:
+ async_task(
+ "core.content_generator.tasks.scrape_research_link_content_task",
+ research_link.id,
+ group="Scrape Research Links",
+ )
+ num_scrape_tasks_queued += 1
+
+ logger.info(
+ "[ContentGenerator] Exa research link search completed (single question)",
+ blog_post_id=blog_post.id,
+ research_question_id=research_question.id,
+ num_links_upserted=num_links_upserted,
+ num_scrape_tasks_queued=num_scrape_tasks_queued,
+ num_results_per_question=num_results_per_question,
+ months_back=months_back,
+ )
+
+ return num_links_upserted
+
+
+def scrape_research_link_content(*, research_link_id: int) -> bool:
+ """
+ Step 4a: For a single research link, fetch the page content using Jina Reader and store it.
+
+ Returns: True if content is present after the operation, False otherwise.
+ """
+ research_link = (
+ GeneratedBlogPostResearchLink.objects.select_related(
+ "blog_post",
+ "blog_post__title_suggestion",
+ "blog_post__project",
+ "research_question",
+ "research_question__section",
+ )
+ .filter(id=research_link_id)
+ .first()
+ )
+ if not research_link:
+ raise ValueError(f"GeneratedBlogPostResearchLink not found: {research_link_id}")
+
+ url = (research_link.url or "").strip()
+ if not url.startswith(("http://", "https://")):
+ logger.info(
+ "[ContentGenerator] Skipping scrape/summarize for invalid research link url",
+ research_link_id=research_link.id,
+ url=url,
+ )
+ return 0
+
+ blog_post = research_link.blog_post
+ research_question = research_link.research_question
+ if not blog_post or not research_question:
+ raise ValueError(f"Research link missing blog_post/research_question: {research_link_id}")
+
+ should_fetch_page_content = not (research_link.content or "").strip()
+ if not should_fetch_page_content:
+ logger.info(
+ "[ContentGenerator] Research link already scraped; skipping",
+ research_link_id=research_link.id,
+ blog_post_id=blog_post.id,
+ )
+ return True
+
+ page_title = research_link.title
+ page_description = research_link.description
+ page_markdown_content = research_link.content
+
+ scraped_title, scraped_description, scraped_content = get_markdown_content(url)
+ if not scraped_content.strip():
+ logger.warning(
+ "[ContentGenerator] Jina Reader returned empty content for research link",
+ research_link_id=research_link.id,
+ blog_post_id=blog_post.id,
+ url=url,
+ )
+ return False
+
+ page_title = scraped_title or page_title
+ page_description = scraped_description or ""
+ page_markdown_content = scraped_content
+
+ if not (page_markdown_content or "").strip():
+ logger.warning(
+ "[ContentGenerator] Research link has empty content; cannot summarize",
+ research_link_id=research_link.id,
+ blog_post_id=blog_post.id,
+ url=url,
+ )
+ return False
+
+ update_fields: list[str] = []
+
+ research_link.date_scraped = timezone.now()
+ update_fields.append("date_scraped")
+
+ research_link.title = (page_title or "")[:500]
+ update_fields.append("title")
+
+ research_link.description = page_description or ""
+ update_fields.append("description")
+
+ research_link.content = page_markdown_content or ""
+ update_fields.append("content")
+
+ research_link.save(update_fields=list(dict.fromkeys(update_fields)))
+
+ logger.info(
+ "[ContentGenerator] Research link scraped",
+ research_link_id=research_link.id,
+ blog_post_id=blog_post.id,
+ research_question_id=research_question.id,
+ updated_fields=update_fields,
+ url=url,
+ )
+
+ return True
+
+
+def analyze_research_link_content(*, research_link_id: int) -> int:
+ """
+ Step 4b: For a single research link (that already has content), generate:
+ - a general page summary
+ - a blog-post-contextual summary for the research question/section
+
+ Returns: number of fields updated on the research link.
+ """
+ research_link = (
+ GeneratedBlogPostResearchLink.objects.select_related(
+ "blog_post",
+ "blog_post__title_suggestion",
+ "blog_post__project",
+ "research_question",
+ "research_question__section",
+ )
+ .filter(id=research_link_id)
+ .first()
+ )
+ if not research_link:
+ raise ValueError(f"GeneratedBlogPostResearchLink not found: {research_link_id}")
+
+ blog_post = research_link.blog_post
+ research_question = research_link.research_question
+ if not blog_post or not research_question:
+ raise ValueError(f"Research link missing blog_post/research_question: {research_link_id}")
+
+ url = (research_link.url or "").strip()
+ page_markdown_content = (research_link.content or "").strip()
+ if not page_markdown_content:
+ logger.info(
+ "[ContentGenerator] Research link has no content yet; skipping analysis",
+ research_link_id=research_link.id,
+ blog_post_id=blog_post.id,
+ url=url,
+ )
+ return 0
+
+ should_run_general_summary = not (research_link.general_summary or "").strip()
+ should_run_contextual_summary = not (research_link.summary_for_question_research or "").strip()
+ if not should_run_general_summary and not should_run_contextual_summary:
+ logger.info(
+ "[ContentGenerator] Research link already analyzed; skipping",
+ research_link_id=research_link.id,
+ blog_post_id=blog_post.id,
+ )
+ return 0
+
+ webpage_content = WebPageContent(
+ title=(research_link.title or "").strip(),
+ description=(research_link.description or "").strip(),
+ markdown_content=page_markdown_content[:MAX_RESEARCH_LINK_MARKDOWN_CHARS_FOR_SUMMARY],
+ )
+
+ update_fields: list[str] = []
+
+ if should_run_general_summary:
+ general_summary_agent = create_general_research_link_summary_agent()
+ general_summary_result = run_agent_synchronously(
+ general_summary_agent,
+ "Summarize this page.",
+ deps=webpage_content,
+ function_name="analyze_research_link_content.general_summary",
+ model_name="GeneratedBlogPostResearchLink",
+ )
+ research_link.general_summary = (general_summary_result.output.summary or "").strip()
+ update_fields.append("general_summary")
+
+ if should_run_contextual_summary:
+ title_suggestion = blog_post.title_suggestion
+ if not title_suggestion:
+ raise ValueError(f"GeneratedBlogPost missing title_suggestion: {blog_post.id}")
+
+ content_type_to_use = title_suggestion.content_type or ContentType.SHARING
+ blog_post_generation_context = _create_blog_post_generation_context(
+ title_suggestion=title_suggestion,
+ content_type_to_use=content_type_to_use,
+ )
+
+ section_title = (getattr(research_question.section, "title", "") or "").strip()
+ research_question_text = (research_question.question or "").strip()
+
+ contextual_summary_agent = create_contextual_research_link_summary_agent()
+ contextual_summary_deps = ResearchLinkContextualSummaryContext(
+ url=url,
+ web_page_content=webpage_content,
+ blog_post_generation_context=blog_post_generation_context,
+ blog_post_title=(blog_post.title or title_suggestion.title or "").strip(),
+ section_title=section_title,
+ research_question=research_question_text,
+ )
+ contextual_summary_result = run_agent_synchronously(
+ contextual_summary_agent,
+ "Summarize this page specifically to help answer the research question for the blog post section.", # noqa: E501
+ deps=contextual_summary_deps,
+ function_name="analyze_research_link_content.contextual_summary",
+ model_name="GeneratedBlogPostResearchLink",
+ )
+ research_link.summary_for_question_research = (
+ contextual_summary_result.output.summary or ""
+ ).strip()
+ update_fields.append("summary_for_question_research")
+
+ research_link.date_analyzed = timezone.now()
+ update_fields.append("date_analyzed")
+
+ research_link.save(update_fields=list(dict.fromkeys(update_fields)))
+
+ logger.info(
+ "[ContentGenerator] Research link analyzed",
+ research_link_id=research_link.id,
+ blog_post_id=blog_post.id,
+ research_question_id=research_question.id,
+ updated_fields=update_fields,
+ url=url,
+ )
+
+ return len(set(update_fields))
+
+
+def generate_research_questions_for_section(*, section_id: int) -> list[int]:
+ """
+ Step 2 (task): Generate research questions for a single section.
+
+ Returns: list of created GeneratedBlogPostResearchQuestion IDs.
+ """
+ section = (
+ GeneratedBlogPostSection.objects.select_related(
+ "blog_post",
+ "blog_post__title_suggestion",
+ "blog_post__project",
+ )
+ .filter(id=section_id)
+ .first()
+ )
+ if not section:
+ raise ValueError(f"GeneratedBlogPostSection not found: {section_id}")
+
+ section_title = (section.title or "").strip()
+ if section_title in NON_RESEARCH_SECTION_TITLES:
+ logger.info(
+ "[ContentGenerator] Skipping research question generation for non-research section",
+ section_id=section.id,
+ section_title=section_title,
+ blog_post_id=section.blog_post_id,
+ )
+ return []
+
+ blog_post = section.blog_post
+ if not blog_post or not blog_post.title_suggestion_id:
+ raise ValueError(f"Section is missing blog_post/title_suggestion: {section_id}")
+
+ title_suggestion = blog_post.title_suggestion
+ content_type_to_use = title_suggestion.content_type or ContentType.SHARING
+ outline_context = _create_blog_post_generation_context(
+ title_suggestion=title_suggestion,
+ content_type_to_use=content_type_to_use,
+ )
+
+ research_questions_agent = create_blog_post_section_research_questions_agent()
+ questions_result = run_agent_synchronously(
+ research_questions_agent,
+ f"Generate research questions for section: {section_title}",
+ deps=outline_context,
+ function_name="generate_research_questions_for_section",
+ model_name="GeneratedBlogPost",
+ )
+
+ questions = (
+ questions_result.output.questions if questions_result and questions_result.output else []
+ )
+
+ questions_to_create = []
+ for question in questions:
+ research_question_text = (question or "").strip()
+ if not research_question_text:
+ continue
+ questions_to_create.append(
+ GeneratedBlogPostResearchQuestion(
+ blog_post=blog_post,
+ section=section,
+ question=research_question_text[:250],
+ )
+ )
+
+ created_questions = GeneratedBlogPostResearchQuestion.objects.bulk_create(questions_to_create)
+ created_question_ids = [
+ created_question.id for created_question in created_questions if created_question.id
+ ]
+
+ logger.info(
+ "[ContentGenerator] Research questions generated",
+ section_id=section.id,
+ blog_post_id=blog_post.id,
+ num_questions_created=len(created_question_ids),
+ )
+
+ return created_question_ids
diff --git a/core/content_generator/tasks.py b/core/content_generator/tasks.py
new file mode 100644
index 0000000..ca432a6
--- /dev/null
+++ b/core/content_generator/tasks.py
@@ -0,0 +1,91 @@
+from __future__ import annotations
+
+from django_q.tasks import async_task
+
+from core.content_generator.pipeline import (
+ analyze_research_link_content,
+ generate_research_questions_for_section,
+ populate_research_links_for_question_from_exa,
+ scrape_research_link_content,
+)
+from tuxseo.utils import get_tuxseo_logger
+
+logger = get_tuxseo_logger(__name__)
+
+
+def populate_research_links_for_question_from_exa_task(
+ research_question_id: int,
+ num_results_per_question: int = 2,
+ months_back: int = 6,
+):
+ """
+ Populate Exa research links for one research question.
+ """
+ num_links = populate_research_links_for_question_from_exa(
+ research_question_id=research_question_id,
+ num_results_per_question=num_results_per_question,
+ months_back=months_back,
+ )
+ logger.info(
+ "[ContentGenerator Tasks] Populated Exa research links for question",
+ research_question_id=research_question_id,
+ num_links_upserted=num_links,
+ )
+ return f"Populated {num_links} research links for research question {research_question_id}"
+
+
+def scrape_research_link_content_task(research_link_id: int):
+ """
+ Fetch research link content using Jina Reader.
+ If content is successfully fetched, queue the analysis task.
+ """
+ did_fetch_content = scrape_research_link_content(research_link_id=research_link_id)
+ logger.info(
+ "[ContentGenerator Tasks] Scraped research link",
+ research_link_id=research_link_id,
+ did_fetch_content=did_fetch_content,
+ )
+ if did_fetch_content:
+ async_task(
+ "core.content_generator.tasks.analyze_research_link_content_task",
+ research_link_id,
+ group="Analyze Research Links",
+ )
+ return f"Scraped research link {research_link_id} (did_fetch_content={did_fetch_content})"
+
+
+def analyze_research_link_content_task(research_link_id: int):
+ """
+ Analyze a research link that has already been scraped:
+ - generate general summary
+ - generate blog-post contextual summary for the research question/section
+ """
+ num_fields_updated = analyze_research_link_content(research_link_id=research_link_id)
+ logger.info(
+ "[ContentGenerator Tasks] Analyzed research link",
+ research_link_id=research_link_id,
+ num_fields_updated=num_fields_updated,
+ )
+ return f"Analyzed research link {research_link_id} (updated_fields={num_fields_updated})"
+
+
+def generate_research_questions_for_section_task(section_id: int):
+ """
+ Generate research questions for one section, then queue Exa research link tasks for each
+ created question.
+ """
+ created_research_question_ids = generate_research_questions_for_section(section_id=section_id)
+
+ for research_question_id in created_research_question_ids:
+ async_task(
+ "core.content_generator.tasks.populate_research_links_for_question_from_exa_task",
+ research_question_id,
+ group="Populate Research Links",
+ )
+
+ logger.info(
+ "[ContentGenerator Tasks] Generated research questions for section",
+ section_id=section_id,
+ num_questions_created=len(created_research_question_ids),
+ )
+ return f"Generated {len(created_research_question_ids)} research questions for section {section_id}" # noqa: E501
diff --git a/core/content_generator/utils.py b/core/content_generator/utils.py
new file mode 100644
index 0000000..c637adb
--- /dev/null
+++ b/core/content_generator/utils.py
@@ -0,0 +1,15 @@
+from __future__ import annotations
+
+from datetime import timedelta
+
+from django.utils import timezone
+
+
+def get_exa_date_range_iso_strings(*, months_back: int) -> tuple[str, str]:
+ """
+ Exa expects date filters as strings (YYYY-MM-DD).
+ """
+ current_datetime = timezone.now()
+ end_date_iso_format = current_datetime.date().isoformat()
+ start_date_iso_format = (current_datetime - timedelta(days=months_back * 30)).date().isoformat()
+ return start_date_iso_format, end_date_iso_format
diff --git a/core/migrations/0050_backlink_generatedblogpostresearchquestion_and_more.py b/core/migrations/0050_backlink_generatedblogpostresearchquestion_and_more.py
new file mode 100644
index 0000000..8e12c27
--- /dev/null
+++ b/core/migrations/0050_backlink_generatedblogpostresearchquestion_and_more.py
@@ -0,0 +1,95 @@
+# Generated by Django 5.2.8 on 2025-12-26 21:43
+
+import django.db.models.deletion
+import pgvector.django.vector
+import uuid
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('core', '0049_alter_emailsent_email_type'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Backlink',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('uuid', models.UUIDField(default=uuid.uuid4, editable=False)),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ('updated_at', models.DateTimeField(auto_now=True)),
+ ('deleted_at', models.DateTimeField(blank=True, null=True)),
+ ('linked_from_project_page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='backlinks_from', to='core.project')),
+ ('linked_to_project_page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='backlinks_to', to='core.project')),
+ ('linking_from_blog_post', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='backlinks', to='core.generatedblogpost')),
+ ('linkning_to_project_page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='backlinks', to='core.projectpage')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='GeneratedBlogPostResearchQuestion',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('uuid', models.UUIDField(default=uuid.uuid4, editable=False)),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ('updated_at', models.DateTimeField(auto_now=True)),
+ ('deleted_at', models.DateTimeField(blank=True, null=True)),
+ ('question', models.CharField(max_length=250)),
+ ('blog_post', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='research_questions', to='core.generatedblogpost')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='GeneratedBlogPostResearchLink',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('uuid', models.UUIDField(default=uuid.uuid4, editable=False)),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ('updated_at', models.DateTimeField(auto_now=True)),
+ ('deleted_at', models.DateTimeField(blank=True, null=True)),
+ ('url', models.URLField()),
+ ('title', models.CharField(blank=True, default='', max_length=500)),
+ ('author', models.CharField(blank=True, default='', max_length=250)),
+ ('description', models.TextField(blank=True, default='')),
+ ('markdown_content', models.TextField(blank=True, default='')),
+ ('question', models.CharField(max_length=250)),
+ ('date_scraped', models.DateTimeField(blank=True, null=True)),
+ ('date_analyzed', models.DateTimeField(blank=True, null=True)),
+ ('summary', models.TextField(blank=True)),
+ ('embedding', pgvector.django.vector.VectorField(blank=True, default=None, dimensions=1024, null=True)),
+ ('blog_post', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='research_links', to='core.generatedblogpost')),
+ ('research_question', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='research_links', to='core.generatedblogpostresearchquestion')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='GeneratedBlogPostSection',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('uuid', models.UUIDField(default=uuid.uuid4, editable=False)),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ('updated_at', models.DateTimeField(auto_now=True)),
+ ('deleted_at', models.DateTimeField(blank=True, null=True)),
+ ('title', models.CharField(max_length=250)),
+ ('content', models.TextField(blank=True, default='')),
+ ('order', models.IntegerField(default=0)),
+ ('blog_post', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='blog_post_sections', to='core.generatedblogpost')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.AddField(
+ model_name='generatedblogpostresearchquestion',
+ name='section',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='research_questions', to='core.generatedblogpostsection'),
+ ),
+ ]
diff --git a/core/migrations/0051_rename_markdown_content_generatedblogpostresearchlink_content_and_more.py b/core/migrations/0051_rename_markdown_content_generatedblogpostresearchlink_content_and_more.py
new file mode 100644
index 0000000..dde01ab
--- /dev/null
+++ b/core/migrations/0051_rename_markdown_content_generatedblogpostresearchlink_content_and_more.py
@@ -0,0 +1,44 @@
+# Generated by Django 5.2.9 on 2025-12-27 12:14
+
+import django.utils.timezone
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('core', '0050_backlink_generatedblogpostresearchquestion_and_more'),
+ ]
+
+ operations = [
+ migrations.RenameField(
+ model_name='generatedblogpostresearchlink',
+ old_name='markdown_content',
+ new_name='content',
+ ),
+ migrations.RenameField(
+ model_name='generatedblogpostresearchlink',
+ old_name='summary',
+ new_name='general_summary',
+ ),
+ migrations.RemoveField(
+ model_name='generatedblogpostresearchlink',
+ name='question',
+ ),
+ migrations.AddField(
+ model_name='generatedblogpostresearchlink',
+ name='published_date',
+ field=models.DateTimeField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='generatedblogpostresearchlink',
+ name='summary_for_question_research',
+ field=models.TextField(blank=True, default=''),
+ ),
+ migrations.AlterField(
+ model_name='generatedblogpostresearchlink',
+ name='date_scraped',
+ field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
+ preserve_default=False,
+ ),
+ ]
diff --git a/core/models.py b/core/models.py
index c23e23f..8ac9f57 100644
--- a/core/models.py
+++ b/core/models.py
@@ -10,9 +10,7 @@
from django.db import models, transaction
from django.urls import reverse
from django.utils import timezone
-from django.utils.text import slugify
from django_q.tasks import async_task
-from gpt_researcher import GPTResearcher
from pgvector.django import HnswIndex, VectorField
from core.agents import (
@@ -60,9 +58,7 @@
get_og_image_prompt,
get_relevant_external_pages_for_blog_post,
get_relevant_pages_for_blog_post,
- process_generated_blog_content,
run_agent_synchronously,
- run_gptr_synchronously,
)
from tuxseo.utils import get_tuxseo_logger
@@ -805,85 +801,19 @@ def get_blog_post_keywords(self):
return keywords_to_use
def generate_content(self, content_type=ContentType.SHARING):
- # query defines the research question researcher will analyze
- # custom_prompt controls how the research findings are presented
-
- # Suggestion Instructions
- query = "Write a post from the following suggestion:\n"
- query += f"{self.title_suggestion_string_for_ai}\n\n"
-
- # Get keywords to use in the blog post
- project_keywords = list(
- self.project.project_keywords.filter(use=True).select_related("keyword")
- )
- project_keyword_texts = [keyword.keyword.keyword_text for keyword in project_keywords]
- post_suggestion_keywords = self.target_keywords or []
- keywords_to_use = list(set(project_keyword_texts + post_suggestion_keywords))
- newline_separator = "\n"
- keywords_list = newline_separator.join([f"- {keyword}" for keyword in keywords_to_use])
- query += "The following keywords should be used (organically) in the blog post:\n"
- query += keywords_list
- query += "\n\n"
-
- query += "Quick reminder. You are writing a blog post for this company."
- query += self.project.project_desctiption_string_for_ai
- query += ". Make it look good, as the best solution for anyone reading the post."
- query += "\n\n"
-
- # # Writing Instructions
- # query += GENERATE_CONTENT_SYSTEM_PROMPTS[content_type]
- # query += "\n"
- query += GeneratedBlogPost.blog_post_structure_rules()
-
- agent = GPTResearcher(
- query,
- report_type="deep",
- tone="Simple (written for young readers, using basic vocabulary and clear explanations)", # noqa: E501
- report_format="markdown",
- )
-
- result = run_gptr_synchronously(agent)
+ """
+ Backward-compatible wrapper around the content generation pipeline.
- # Create blog post with raw content first
- slug = slugify(self.title)
- tags = ", ".join(self.target_keywords) if self.target_keywords else ""
+ Historically, this method created the blog post content directly. It is now kept
+ as a thin wrapper to preserve existing call sites while the pipeline evolves.
+ """
+ from core.content_generator.pipeline import init_blog_post_content_generation
- blog_post = GeneratedBlogPost.objects.create(
- project=self.project,
+ return init_blog_post_content_generation(
title_suggestion=self,
- title=self.title, # Temporary title, will be updated after processing
- description=self.suggested_meta_description,
- slug=slug,
- tags=tags,
- content=result, # Raw content from GPTResearcher
+ content_type=content_type,
)
- # Insert links into the blog post content
- blog_post.insert_links_into_post()
-
- # Process content after link insertion (extract title, clean up sections)
- blog_post_title, blog_post_content = process_generated_blog_content(
- generated_content=blog_post.content, # Use content after link insertion
- fallback_title=self.title,
- title_suggestion_id=self.id,
- project_id=self.project.id,
- )
-
- # Update blog post with processed content and extracted title
- blog_post.title = blog_post_title
- blog_post.slug = slugify(blog_post_title)
- blog_post.content = blog_post_content
- blog_post.save(update_fields=["title", "slug", "content"])
-
- if self.project.enable_automatic_og_image_generation:
- async_task(
- "core.tasks.generate_og_image_for_blog_post",
- blog_post.id,
- group="Generate OG Image",
- )
-
- return blog_post
-
class AutoSubmissionSetting(BaseModel):
project = models.ForeignKey(
@@ -930,6 +860,8 @@ class GeneratedBlogPost(BaseModel):
on_delete=models.CASCADE,
related_name="generated_blog_posts",
)
+
+ # Final Output Items
title = models.CharField(max_length=250)
description = models.TextField(blank=True)
slug = models.SlugField(max_length=250)
@@ -938,6 +870,10 @@ class GeneratedBlogPost(BaseModel):
icon = models.ImageField(upload_to="generated_blog_post_icons/", blank=True)
image = models.ImageField(upload_to="generated_blog_post_images/", blank=True)
+ # Preparation
+ # GeneratedBlogPostSection model
+
+ # Other
posted = models.BooleanField(default=False)
date_posted = models.DateTimeField(null=True, blank=True)
@@ -1250,6 +1186,72 @@ def insert_links_into_post(self, max_pages=4, max_external_pages=3):
return content_with_links
+class GeneratedBlogPostSection(BaseModel):
+ blog_post = models.ForeignKey(
+ GeneratedBlogPost,
+ null=True,
+ blank=True,
+ on_delete=models.CASCADE,
+ related_name="blog_post_sections",
+ )
+ title = models.CharField(max_length=250)
+ content = models.TextField(blank=True, default="")
+ order = models.IntegerField(default=0)
+ # GeneratedBlogPostResearchQuestion model
+
+
+class GeneratedBlogPostResearchQuestion(BaseModel):
+ blog_post = models.ForeignKey(
+ GeneratedBlogPost,
+ null=True,
+ blank=True,
+ on_delete=models.CASCADE,
+ related_name="research_questions",
+ )
+ section = models.ForeignKey(
+ GeneratedBlogPostSection,
+ null=True,
+ blank=True,
+ on_delete=models.CASCADE,
+ related_name="research_questions",
+ )
+ question = models.CharField(max_length=250)
+
+
+class GeneratedBlogPostResearchLink(BaseModel):
+ blog_post = models.ForeignKey(
+ GeneratedBlogPost,
+ null=True,
+ blank=True,
+ on_delete=models.CASCADE,
+ related_name="research_links",
+ )
+ research_question = models.ForeignKey(
+ GeneratedBlogPostResearchQuestion,
+ null=True,
+ blank=True,
+ on_delete=models.CASCADE,
+ related_name="research_links",
+ )
+
+ # initial data
+ url = models.URLField(max_length=200)
+ title = models.CharField(max_length=500, blank=True, default="")
+ author = models.CharField(max_length=250, blank=True, default="")
+ published_date = models.DateTimeField(null=True, blank=True)
+
+ # jina augmentation
+ date_scraped = models.DateTimeField(auto_now_add=True)
+ content = models.TextField(blank=True, default="")
+ description = models.TextField(blank=True, default="")
+
+ # ai augmentation
+ date_analyzed = models.DateTimeField(null=True, blank=True)
+ summary_for_question_research = models.TextField(blank=True, default="")
+ general_summary = models.TextField(blank=True)
+ embedding = VectorField(dimensions=1024, default=None, null=True, blank=True)
+
+
class ProjectPage(BaseModel):
project = models.ForeignKey(
Project, null=True, blank=True, on_delete=models.CASCADE, related_name="project_pages"
@@ -1998,3 +2000,22 @@ class Meta:
def __str__(self):
return f"{self.email_type} to {self.email_address}"
+
+
+class Backlink(BaseModel):
+ linked_to_project_page = models.ForeignKey(
+ Project, null=True, blank=True, on_delete=models.CASCADE, related_name="backlinks_to"
+ )
+ linkning_to_project_page = models.ForeignKey(
+ ProjectPage, null=True, blank=True, on_delete=models.CASCADE, related_name="backlinks"
+ )
+
+ linked_from_project_page = models.ForeignKey(
+ Project, null=True, blank=True, on_delete=models.CASCADE, related_name="backlinks_from"
+ )
+ linking_from_blog_post = models.ForeignKey(
+ GeneratedBlogPost, null=True, blank=True, on_delete=models.CASCADE, related_name="backlinks"
+ )
+
+ def __str__(self):
+ return f"{self.linking_from_blog_post.title} -> {self.linked_to_project_page.url}"
diff --git a/core/tasks.py b/core/tasks.py
index a5e3a70..9a56118 100644
--- a/core/tasks.py
+++ b/core/tasks.py
@@ -1780,3 +1780,15 @@ def generate_blog_post_content(suggestion_id: int, send_email: bool = True):
project_id=suggestion.project.id if suggestion.project else None,
)
return f"Unexpected error: {str(error)}"
+
+
+def generate_research_questions_for_section_task(section_id: int):
+ """
+ Generate research questions for one blog post section, then queue Exa research link tasks for
+ each created question.
+ """
+ from core.content_generator.tasks import (
+ generate_research_questions_for_section_task as delegated_task,
+ )
+
+ return delegated_task(section_id=section_id)
diff --git a/poetry.lock b/poetry.lock
index 6bc27a9..ba872be 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -250,23 +250,22 @@ vertex = ["google-auth[requests] (>=2,<3)"]
[[package]]
name = "anyio"
-version = "4.11.0"
+version = "4.12.0"
description = "High-level concurrency and networking framework on top of asyncio or Trio"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"},
- {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"},
+ {file = "anyio-4.12.0-py3-none-any.whl", hash = "sha256:dad2376a628f98eeca4881fc56cd06affd18f659b17a747d3ff0307ced94b1bb"},
+ {file = "anyio-4.12.0.tar.gz", hash = "sha256:73c693b567b0c55130c104d0b43a9baf3aa6a31fc6110116509f27bf75e21ec0"},
]
[package.dependencies]
idna = ">=2.8"
-sniffio = ">=1.1"
typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""}
[package.extras]
-trio = ["trio (>=0.31.0)"]
+trio = ["trio (>=0.31.0) ; python_version < \"3.10\"", "trio (>=0.32.0) ; python_version >= \"3.10\""]
[[package]]
name = "argcomplete"
@@ -377,14 +376,14 @@ files = [
[[package]]
name = "authlib"
-version = "1.6.5"
+version = "1.6.6"
description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "authlib-1.6.5-py2.py3-none-any.whl", hash = "sha256:3e0e0507807f842b02175507bdee8957a1d5707fd4afb17c32fb43fee90b6e3a"},
- {file = "authlib-1.6.5.tar.gz", hash = "sha256:6aaf9c79b7cc96c900f0b284061691c5d4e61221640a948fe690b556a6d6d10b"},
+ {file = "authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd"},
+ {file = "authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e"},
]
[package.dependencies]
@@ -421,38 +420,38 @@ testing = ["jaraco.test", "pytest (!=8.0.*)", "pytest (>=6,!=8.1.*)", "pytest-ch
[[package]]
name = "beartype"
-version = "0.22.6"
+version = "0.22.9"
description = "Unbearably fast near-real-time pure-Python runtime-static type-checker."
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "beartype-0.22.6-py3-none-any.whl", hash = "sha256:0584bc46a2ea2a871509679278cda992eadde676c01356ab0ac77421f3c9a093"},
- {file = "beartype-0.22.6.tar.gz", hash = "sha256:97fbda69c20b48c5780ac2ca60ce3c1bb9af29b3a1a0216898ffabdd523e48f4"},
+ {file = "beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2"},
+ {file = "beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f"},
]
[package.extras]
-dev = ["autoapi (>=0.9.0)", "celery", "click", "coverage (>=5.5)", "docutils (>=0.22.0)", "equinox ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "fastmcp ; python_version < \"3.14.0\"", "jax[cpu] ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "jaxtyping ; sys_platform == \"linux\"", "langchain ; python_version < \"3.14.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "mypy (>=0.800) ; platform_python_implementation != \"PyPy\"", "nuitka (>=1.2.6) ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "numba ; python_version < \"3.14.0\"", "numpy ; python_version < \"3.15.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "pandera (>=0.26.0) ; python_version < \"3.14.0\"", "poetry", "polars ; python_version < \"3.14.0\"", "pydata-sphinx-theme (<=0.7.2)", "pygments", "pyright (>=1.1.370)", "pytest (>=6.2.0)", "redis", "rich-click", "setuptools", "sphinx", "sphinx (>=4.2.0,<6.0.0)", "sphinxext-opengraph (>=0.7.5)", "sqlalchemy", "torch ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "tox (>=3.20.1)", "typer", "typing-extensions (>=3.10.0.0)", "xarray ; python_version < \"3.15.0\""]
+dev = ["autoapi (>=0.9.0)", "celery", "click", "coverage (>=5.5)", "docutils (>=0.22.0)", "equinox ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "fastmcp ; python_version < \"3.14.0\"", "jax[cpu] ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "jaxtyping ; sys_platform == \"linux\"", "langchain ; python_version < \"3.14.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "mypy (>=0.800) ; platform_python_implementation != \"PyPy\"", "nuitka (>=1.2.6) ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "numba ; python_version < \"3.14.0\"", "numpy ; python_version < \"3.15.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "pandera (>=0.26.0) ; python_version < \"3.14.0\"", "poetry", "polars ; python_version < \"3.14.0\"", "pydata-sphinx-theme (<=0.7.2)", "pygments", "pyinstaller", "pyright (>=1.1.370)", "pytest (>=6.2.0)", "redis", "rich-click", "setuptools", "sphinx", "sphinx (>=4.2.0,<6.0.0)", "sphinxext-opengraph (>=0.7.5)", "sqlalchemy", "torch ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "tox (>=3.20.1)", "typer", "typing-extensions (>=3.10.0.0)", "xarray ; python_version < \"3.15.0\""]
doc-ghp = ["mkdocs-material[imaging] (>=9.6.0)", "mkdocstrings-python (>=1.16.0)", "mkdocstrings-python-xref (>=1.16.0)"]
doc-rtd = ["autoapi (>=0.9.0)", "pydata-sphinx-theme (<=0.7.2)", "setuptools", "sphinx (>=4.2.0,<6.0.0)", "sphinxext-opengraph (>=0.7.5)"]
-test = ["celery", "click", "coverage (>=5.5)", "docutils (>=0.22.0)", "equinox ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "fastmcp ; python_version < \"3.14.0\"", "jax[cpu] ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "jaxtyping ; sys_platform == \"linux\"", "langchain ; python_version < \"3.14.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "mypy (>=0.800) ; platform_python_implementation != \"PyPy\"", "nuitka (>=1.2.6) ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "numba ; python_version < \"3.14.0\"", "numpy ; python_version < \"3.15.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "pandera (>=0.26.0) ; python_version < \"3.14.0\"", "poetry", "polars ; python_version < \"3.14.0\"", "pygments", "pyright (>=1.1.370)", "pytest (>=6.2.0)", "redis", "rich-click", "sphinx", "sqlalchemy", "torch ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "tox (>=3.20.1)", "typer", "typing-extensions (>=3.10.0.0)", "xarray ; python_version < \"3.15.0\""]
-test-tox = ["celery", "click", "docutils (>=0.22.0)", "equinox ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "fastmcp ; python_version < \"3.14.0\"", "jax[cpu] ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "jaxtyping ; sys_platform == \"linux\"", "langchain ; python_version < \"3.14.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "mypy (>=0.800) ; platform_python_implementation != \"PyPy\"", "nuitka (>=1.2.6) ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "numba ; python_version < \"3.14.0\"", "numpy ; python_version < \"3.15.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "pandera (>=0.26.0) ; python_version < \"3.14.0\"", "poetry", "polars ; python_version < \"3.14.0\"", "pygments", "pyright (>=1.1.370)", "pytest (>=6.2.0)", "redis", "rich-click", "sphinx", "sqlalchemy", "torch ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "typer", "typing-extensions (>=3.10.0.0)", "xarray ; python_version < \"3.15.0\""]
+test = ["celery", "click", "coverage (>=5.5)", "docutils (>=0.22.0)", "equinox ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "fastmcp ; python_version < \"3.14.0\"", "jax[cpu] ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "jaxtyping ; sys_platform == \"linux\"", "langchain ; python_version < \"3.14.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "mypy (>=0.800) ; platform_python_implementation != \"PyPy\"", "nuitka (>=1.2.6) ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "numba ; python_version < \"3.14.0\"", "numpy ; python_version < \"3.15.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "pandera (>=0.26.0) ; python_version < \"3.14.0\"", "poetry", "polars ; python_version < \"3.14.0\"", "pygments", "pyinstaller", "pyright (>=1.1.370)", "pytest (>=6.2.0)", "redis", "rich-click", "sphinx", "sqlalchemy", "torch ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "tox (>=3.20.1)", "typer", "typing-extensions (>=3.10.0.0)", "xarray ; python_version < \"3.15.0\""]
+test-tox = ["celery", "click", "docutils (>=0.22.0)", "equinox ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "fastmcp ; python_version < \"3.14.0\"", "jax[cpu] ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "jaxtyping ; sys_platform == \"linux\"", "langchain ; python_version < \"3.14.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "mypy (>=0.800) ; platform_python_implementation != \"PyPy\"", "nuitka (>=1.2.6) ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "numba ; python_version < \"3.14.0\"", "numpy ; python_version < \"3.15.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "pandera (>=0.26.0) ; python_version < \"3.14.0\"", "poetry", "polars ; python_version < \"3.14.0\"", "pygments", "pyinstaller", "pyright (>=1.1.370)", "pytest (>=6.2.0)", "redis", "rich-click", "sphinx", "sqlalchemy", "torch ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "typer", "typing-extensions (>=3.10.0.0)", "xarray ; python_version < \"3.15.0\""]
test-tox-coverage = ["coverage (>=5.5)"]
[[package]]
name = "beautifulsoup4"
-version = "4.14.2"
+version = "4.14.3"
description = "Screen-scraping library"
optional = false
python-versions = ">=3.7.0"
groups = ["main"]
files = [
- {file = "beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515"},
- {file = "beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e"},
+ {file = "beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb"},
+ {file = "beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86"},
]
[package.dependencies]
-soupsieve = ">1.2"
+soupsieve = ">=1.6.1"
typing-extensions = ">=4.0.0"
[package.extras]
@@ -479,34 +478,34 @@ chardet = ">=3.0.2"
[[package]]
name = "boto3"
-version = "1.41.5"
+version = "1.42.16"
description = "The AWS SDK for Python"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "boto3-1.41.5-py3-none-any.whl", hash = "sha256:bb278111bfb4c33dca8342bda49c9db7685e43debbfa00cc2a5eb854dd54b745"},
- {file = "boto3-1.41.5.tar.gz", hash = "sha256:bc7806bee681dfdff2fe2b74967b107a56274f1e66ebe4d20dc8eee1ea408d17"},
+ {file = "boto3-1.42.16-py3-none-any.whl", hash = "sha256:37a43d42aebd06a8f93ee801ea1b7b5181ac42a30869ef403c9dadc160a748e5"},
+ {file = "boto3-1.42.16.tar.gz", hash = "sha256:811391611db88c8a061f6e6fabbd7ca784ad9de04490a879f091cbaa9de7de74"},
]
[package.dependencies]
-botocore = ">=1.41.5,<1.42.0"
+botocore = ">=1.42.16,<1.43.0"
jmespath = ">=0.7.1,<2.0.0"
-s3transfer = ">=0.15.0,<0.16.0"
+s3transfer = ">=0.16.0,<0.17.0"
[package.extras]
crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
[[package]]
name = "botocore"
-version = "1.41.5"
+version = "1.42.16"
description = "Low-level, data-driven core of boto 3."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "botocore-1.41.5-py3-none-any.whl", hash = "sha256:3fef7fcda30c82c27202d232cfdbd6782cb27f20f8e7e21b20606483e66ee73a"},
- {file = "botocore-1.41.5.tar.gz", hash = "sha256:0367622b811597d183bfcaab4a350f0d3ede712031ce792ef183cabdee80d3bf"},
+ {file = "botocore-1.42.16-py3-none-any.whl", hash = "sha256:b1f584a0f8645c12e07bf6ec9c18e05221a789f2a9b2d3c6291deb42f8c1c542"},
+ {file = "botocore-1.42.16.tar.gz", hash = "sha256:29ee8555cd5d5023350405387cedcf3fe1c7f02fcb8060bf9e01602487482c25"},
]
[package.dependencies]
@@ -515,7 +514,7 @@ python-dateutil = ">=2.1,<3.0.0"
urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""}
[package.extras]
-crt = ["awscrt (==0.29.0)"]
+crt = ["awscrt (==0.29.2)"]
[[package]]
name = "brotli"
@@ -656,14 +655,14 @@ cffi = [
[[package]]
name = "cachetools"
-version = "6.2.2"
+version = "6.2.4"
description = "Extensible memoizing collections and decorators"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "cachetools-6.2.2-py3-none-any.whl", hash = "sha256:6c09c98183bf58560c97b2abfcedcbaf6a896a490f534b031b661d3723b45ace"},
- {file = "cachetools-6.2.2.tar.gz", hash = "sha256:8e6d266b25e539df852251cfd6f990b4bc3a141db73b939058d809ebd2590fc6"},
+ {file = "cachetools-6.2.4-py3-none-any.whl", hash = "sha256:69a7a52634fed8b8bf6e24a050fb60bff1c9bd8f6d24572b99c32d4e71e62a51"},
+ {file = "cachetools-6.2.4.tar.gz", hash = "sha256:82c5c05585e70b6ba2d3ae09ea60b79548872185d2f24ae1f2709d37299fd607"},
]
[[package]]
@@ -937,25 +936,36 @@ files = [
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
+[[package]]
+name = "cloudpickle"
+version = "3.1.2"
+description = "Pickler class to extend the standard pickle.Pickler functionality"
+optional = false
+python-versions = ">=3.8"
+groups = ["main"]
+files = [
+ {file = "cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a"},
+ {file = "cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414"},
+]
+
[[package]]
name = "cohere"
-version = "5.20.0"
+version = "5.20.1"
description = ""
optional = false
python-versions = "<4.0,>=3.9"
groups = ["main"]
markers = "platform_system != \"Emscripten\""
files = [
- {file = "cohere-5.20.0-py3-none-any.whl", hash = "sha256:a95f17ed22be3f978363703beb6008b55000ce0e85124ddb976fa5b688014fea"},
- {file = "cohere-5.20.0.tar.gz", hash = "sha256:fb5ad5afa47447dd7eb090ad29bdb3a8181b0e758a3b03ba6ed8ca48d68d11a7"},
+ {file = "cohere-5.20.1-py3-none-any.whl", hash = "sha256:d230fd13d95ba92ae927fce3dd497599b169883afc7954fe29b39fb8d5df5fc7"},
+ {file = "cohere-5.20.1.tar.gz", hash = "sha256:50973f63d2c6138ff52ce37d8d6f78ccc539af4e8c43865e960d68e0bf835b6f"},
]
[package.dependencies]
fastavro = ">=1.9.4,<2.0.0"
httpx = ">=0.21.2"
-httpx-sse = "0.4.0"
pydantic = ">=1.9.2"
-pydantic-core = ">=2.18.2,<3.0.0"
+pydantic-core = ">=2.18.2"
requests = ">=2.0.0,<3.0.0"
tokenizers = ">=0.15,<1"
types-requests = ">=2.0.0,<3.0.0"
@@ -1111,14 +1121,14 @@ test = ["pytest", "ruff"]
[[package]]
name = "cyclopts"
-version = "4.3.0"
+version = "4.4.1"
description = "Intuitive, easy CLIs based on type hints."
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "cyclopts-4.3.0-py3-none-any.whl", hash = "sha256:91a30b69faf128ada7cfeaefd7d9649dc222e8b2a8697f1fc99e4ee7b7ca44f3"},
- {file = "cyclopts-4.3.0.tar.gz", hash = "sha256:e95179cd0a959ce250ecfb2f0262a5996a92c1f9467bccad2f3d829e6833cef5"},
+ {file = "cyclopts-4.4.1-py3-none-any.whl", hash = "sha256:67500e9fde90f335fddbf9c452d2e7c4f58209dffe52e7abb1e272796a963bde"},
+ {file = "cyclopts-4.4.1.tar.gz", hash = "sha256:368a404926b46a49dc328a33ccd7e55ba879296a28e64a42afe2f6667704cecf"},
]
[package.dependencies]
@@ -1129,8 +1139,9 @@ rich-rst = ">=1.3.1,<2.0.0"
[package.extras]
debug = ["ipdb (>=0.13.9)", "line-profiler (>=3.5.1)"]
-dev = ["coverage[toml] (>=5.1)", "pre-commit (>=2.16.0)", "pydantic (>=2.11.2,<3.0.0)", "pytest (>=8.2.0)", "pytest-cov (>=3.0.0)", "pytest-mock (>=3.7.0)", "pyyaml (>=6.0.1)", "toml (>=0.10.2,<1.0.0)", "trio (>=0.10.0)"]
+dev = ["coverage[toml] (>=5.1)", "mkdocs (>=1.4.0)", "pre-commit (>=2.16.0)", "pydantic (>=2.11.2,<3.0.0)", "pytest (>=8.2.0)", "pytest-cov (>=3.0.0)", "pytest-mock (>=3.7.0)", "pyyaml (>=6.0.1)", "syrupy (>=4.0.0)", "toml (>=0.10.2,<1.0.0)", "trio (>=0.10.0)"]
docs = ["gitpython (>=3.1.31)", "myst-parser[linkify] (>=3.0.1,<5.0.0)", "sphinx (>=7.4.7,<8.2.0)", "sphinx-autodoc-typehints (>=1.25.2,<4.0.0)", "sphinx-copybutton (>=0.5,<1.0)", "sphinx-rtd-dark-mode (>=1.3.0,<2.0.0)", "sphinx-rtd-theme (>=3.0.0,<4.0.0)"]
+mkdocs = ["markdown (>=3.3)", "mkdocs (>=1.4.0)", "pymdown-extensions (>=10.0)"]
toml = ["tomli (>=2.0.0) ; python_version < \"3.11\""]
trio = ["trio (>=0.10.0)"]
yaml = ["pyyaml (>=6.0.1)"]
@@ -1221,14 +1232,14 @@ postgres = ["psycopg (>=3.2.3,<4.0.0)"]
[[package]]
name = "django"
-version = "5.2.8"
+version = "5.2.9"
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "django-5.2.8-py3-none-any.whl", hash = "sha256:37e687f7bd73ddf043e2b6b97cfe02fcbb11f2dbb3adccc6a2b18c6daa054d7f"},
- {file = "django-5.2.8.tar.gz", hash = "sha256:23254866a5bb9a2cfa6004e8b809ec6246eba4b58a7589bc2772f1bcc8456c7f"},
+ {file = "django-5.2.9-py3-none-any.whl", hash = "sha256:3a4ea88a70370557ab1930b332fd2887a9f48654261cdffda663fef5976bb00a"},
+ {file = "django-5.2.9.tar.gz", hash = "sha256:16b5ccfc5e8c27e6c0561af551d2ea32852d7352c67d452ae3e76b4f6b2ca495"},
]
[package.dependencies]
@@ -1335,36 +1346,36 @@ python-ipware = ">=2.0.3"
[[package]]
name = "django-mjml"
-version = "1.4"
+version = "1.5"
description = "Use MJML in Django templates"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "django_mjml-1.4-py3-none-any.whl", hash = "sha256:09dc440fcec64f3656b17646439336e6c15c5987103dfb6fe23eb1f1bf734d11"},
- {file = "django_mjml-1.4.tar.gz", hash = "sha256:a805b3721de92a4972bb6bd4a3bffa91579420e48297a55f3ffef6d8e926b0b4"},
+ {file = "django_mjml-1.5-py3-none-any.whl", hash = "sha256:216faa121e6fdf6eee55061e63a6958a6ab04c6094323ebb8db42385c583ff2d"},
+ {file = "django_mjml-1.5.tar.gz", hash = "sha256:3a6048859f2f9023ffdef95a63595ad66a572d9c8f4be06d3b2f6d84d412be8b"},
]
[package.dependencies]
-django = ">=2.2,<5.3"
+django = ">=2.2,<6.1"
[package.extras]
requests = ["requests (>=2.24)"]
[[package]]
name = "django-ninja"
-version = "1.5.0"
+version = "1.5.1"
description = "Django Ninja - Fast Django REST framework"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
- {file = "django_ninja-1.5.0-py3-none-any.whl", hash = "sha256:e305fc42588406a202d4479263b501813ec863a3a4e11cb091fb905ecdf9bc62"},
- {file = "django_ninja-1.5.0.tar.gz", hash = "sha256:181bc8266a684be8c4cdd8f555d8ccbbc72e9fad86a5928ddccdd13faf73a1c5"},
+ {file = "django_ninja-1.5.1-py3-none-any.whl", hash = "sha256:135aaa1117dce8dfd7a1e80b4487a8cccee3a4182c3c8b562d08ea94e4d2cbdf"},
+ {file = "django_ninja-1.5.1.tar.gz", hash = "sha256:6acda68a64d60934c6fdccb4d97c3ac7f02cfefd78a5d87ae053effe081b17c7"},
]
[package.dependencies]
-Django = ">=3.1,<6.0"
+Django = ">=3.1,<6.1"
pydantic = ">=2.0,<3.0.0"
[package.extras]
@@ -1407,18 +1418,18 @@ sentry-sdk = ">=1.5.5"
[[package]]
name = "django-q2"
-version = "1.8.0"
+version = "1.9.0"
description = "A multiprocessing distributed task queue for Django"
optional = false
python-versions = "<4,>=3.9"
groups = ["main"]
files = [
- {file = "django_q2-1.8.0-py3-none-any.whl", hash = "sha256:78aaaf18dff1ad3e35bcf6556666f2c26494120f0b75961c13206e37d180dfaa"},
- {file = "django_q2-1.8.0.tar.gz", hash = "sha256:e86b9625c0ce57a5ae31ca8fd7e798d63b9ef91a227c52f8b47536ba50b2b284"},
+ {file = "django_q2-1.9.0-py3-none-any.whl", hash = "sha256:4eded27644b0ffb291839c9f9c12fea6c0dec63ebd891fa6881b0b446098a49d"},
+ {file = "django_q2-1.9.0.tar.gz", hash = "sha256:ef7facca96fae9c11ddf2c5252d3817975c7a9a6d989fa0d65487d8823d57799"},
]
[package.dependencies]
-django = ">=4.2,<6"
+django = ">=4.2"
django-picklefield = ">=3.1,<4.0"
django-q-sentry = {version = ">=0.1", optional = true, markers = "extra == \"sentry\""}
@@ -1580,14 +1591,14 @@ test = ["pytest"]
[[package]]
name = "docutils"
-version = "0.22.3"
+version = "0.22.4"
description = "Docutils -- Python Documentation Utilities"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "docutils-0.22.3-py3-none-any.whl", hash = "sha256:bd772e4aca73aff037958d44f2be5229ded4c09927fcf8690c577b66234d6ceb"},
- {file = "docutils-0.22.3.tar.gz", hash = "sha256:21486ae730e4ca9f622677b1412b879af1791efcfba517e4c6f60be543fc8cdd"},
+ {file = "docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de"},
+ {file = "docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968"},
]
[[package]]
@@ -1658,19 +1669,36 @@ dev = ["coverage", "pytest (>=7.4.4)"]
[[package]]
name = "eval-type-backport"
-version = "0.3.0"
+version = "0.3.1"
description = "Like `typing._eval_type`, but lets older Python versions use newer typing features."
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.7"
groups = ["main"]
files = [
- {file = "eval_type_backport-0.3.0-py3-none-any.whl", hash = "sha256:975a10a0fe333c8b6260d7fdb637698c9a16c3a9e3b6eb943fee6a6f67a37fe8"},
- {file = "eval_type_backport-0.3.0.tar.gz", hash = "sha256:1638210401e184ff17f877e9a2fa076b60b5838790f4532a21761cc2be67aea1"},
+ {file = "eval_type_backport-0.3.1-py3-none-any.whl", hash = "sha256:279ab641905e9f11129f56a8a78f493518515b83402b860f6f06dd7c011fdfa8"},
+ {file = "eval_type_backport-0.3.1.tar.gz", hash = "sha256:57e993f7b5b69d271e37482e62f74e76a0276c82490cf8e4f0dffeb6b332d5ed"},
]
[package.extras]
tests = ["pytest"]
+[[package]]
+name = "exa-py"
+version = "2.0.2"
+description = "Python SDK for Exa API."
+optional = false
+python-versions = "*"
+groups = ["main"]
+files = [
+ {file = "exa_py-2.0.2-py3-none-any.whl", hash = "sha256:6b326d558d618dc72a9b048d9228197f6d63dfb890a7d9b6dbdcfdfd63e4040a"},
+ {file = "exa_py-2.0.2.tar.gz", hash = "sha256:229210ddaddd3161319226306165605c97faa86c50c3ab8adcb9b7efce0b0f89"},
+]
+
+[package.dependencies]
+openai = ">=1.10.0"
+requests = "*"
+typing-extensions = "*"
+
[[package]]
name = "exceptiongroup"
version = "1.3.1"
@@ -1704,28 +1732,53 @@ files = [
[package.extras]
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""]
+[[package]]
+name = "fakeredis"
+version = "2.33.0"
+description = "Python implementation of redis API, can be used for testing purposes."
+optional = false
+python-versions = ">=3.7"
+groups = ["main"]
+files = [
+ {file = "fakeredis-2.33.0-py3-none-any.whl", hash = "sha256:de535f3f9ccde1c56672ab2fdd6a8efbc4f2619fc2f1acc87b8737177d71c965"},
+ {file = "fakeredis-2.33.0.tar.gz", hash = "sha256:d7bc9a69d21df108a6451bbffee23b3eba432c21a654afc7ff2d295428ec5770"},
+]
+
+[package.dependencies]
+lupa = {version = ">=2.1", optional = true, markers = "extra == \"lua\""}
+redis = {version = ">=4.3", markers = "python_version > \"3.8\""}
+sortedcontainers = ">=2"
+
+[package.extras]
+bf = ["pyprobables (>=0.6)"]
+cf = ["pyprobables (>=0.6)"]
+json = ["jsonpath-ng (>=1.6)"]
+lua = ["lupa (>=2.1)"]
+probabilistic = ["pyprobables (>=0.6)"]
+valkey = ["valkey (>=6) ; python_version >= \"3.8\""]
+
[[package]]
name = "fastapi"
-version = "0.122.0"
+version = "0.127.1"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "fastapi-0.122.0-py3-none-any.whl", hash = "sha256:a456e8915dfc6c8914a50d9651133bd47ec96d331c5b44600baa635538a30d67"},
- {file = "fastapi-0.122.0.tar.gz", hash = "sha256:cd9b5352031f93773228af8b4c443eedc2ac2aa74b27780387b853c3726fb94b"},
+ {file = "fastapi-0.127.1-py3-none-any.whl", hash = "sha256:31d670a4f9373cc6d7994420f98e4dc46ea693145207abc39696746c83a44430"},
+ {file = "fastapi-0.127.1.tar.gz", hash = "sha256:946a87ee5d931883b562b6bada787d6c8178becee2683cb3f9b980d593206359"},
]
[package.dependencies]
annotated-doc = ">=0.0.2"
-pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
+pydantic = ">=2.7.0"
starlette = ">=0.40.0,<0.51.0"
typing-extensions = ">=4.8.0"
[package.extras]
all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
-standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
-standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[standard-no-fastapi-cloud-cli] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
+standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
+standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[standard-no-fastapi-cloud-cli] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
[[package]]
name = "fastavro"
@@ -1792,14 +1845,14 @@ zstandard = ["zstandard"]
[[package]]
name = "fastmcp"
-version = "2.13.1"
+version = "2.14.0"
description = "The fast, Pythonic way to build MCP servers and clients."
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "fastmcp-2.13.1-py3-none-any.whl", hash = "sha256:7a78b19785c4ec04a758d920c312769a497e3f6ab4c80feed504df1ed7de9f3c"},
- {file = "fastmcp-2.13.1.tar.gz", hash = "sha256:b9c664c51f1ff47c698225e7304267ae29a51913f681bd49e442b8682f9a5f90"},
+ {file = "fastmcp-2.14.0-py3-none-any.whl", hash = "sha256:7b374c0bcaf1ef1ef46b9255ea84c607f354291eaf647ff56a47c69f5ec0c204"},
+ {file = "fastmcp-2.14.0.tar.gz", hash = "sha256:c1f487b36a3e4b043dbf3330e588830047df2e06f8ef0920d62dfb34d0905727"},
]
[package.dependencies]
@@ -1808,11 +1861,12 @@ cyclopts = ">=4.0.0"
exceptiongroup = ">=1.2.2"
httpx = ">=0.28.1"
jsonschema-path = ">=0.3.4"
-mcp = ">=1.19.0,<1.21.1 || >1.21.1,<2.0.0"
+mcp = ">=1.23.1"
openapi-pydantic = ">=0.5.1"
platformdirs = ">=4.0.0"
-py-key-value-aio = {version = ">=0.2.8,<0.3.0", extras = ["disk", "keyring", "memory"]}
+py-key-value-aio = {version = ">=0.3.0,<0.4.0", extras = ["disk", "keyring", "memory"]}
pydantic = {version = ">=2.11.7", extras = ["email"]}
+pydocket = ">=0.15.2"
pyperclip = ">=1.9.0"
python-dotenv = ">=1.1.0"
rich = ">=13.9.4"
@@ -1927,14 +1981,14 @@ sgmllib3k = "*"
[[package]]
name = "filelock"
-version = "3.20.0"
+version = "3.20.1"
description = "A platform independent file lock."
optional = false
python-versions = ">=3.10"
groups = ["main", "dev"]
files = [
- {file = "filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2"},
- {file = "filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4"},
+ {file = "filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a"},
+ {file = "filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c"},
]
[[package]]
@@ -1951,70 +2005,62 @@ files = [
[[package]]
name = "fonttools"
-version = "4.60.1"
+version = "4.61.1"
description = "Tools to manipulate font files"
optional = false
-python-versions = ">=3.9"
+python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "fonttools-4.60.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9a52f254ce051e196b8fe2af4634c2d2f02c981756c6464dc192f1b6050b4e28"},
- {file = "fonttools-4.60.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7420a2696a44650120cdd269a5d2e56a477e2bfa9d95e86229059beb1c19e15"},
- {file = "fonttools-4.60.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee0c0b3b35b34f782afc673d503167157094a16f442ace7c6c5e0ca80b08f50c"},
- {file = "fonttools-4.60.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:282dafa55f9659e8999110bd8ed422ebe1c8aecd0dc396550b038e6c9a08b8ea"},
- {file = "fonttools-4.60.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4ba4bd646e86de16160f0fb72e31c3b9b7d0721c3e5b26b9fa2fc931dfdb2652"},
- {file = "fonttools-4.60.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0b0835ed15dd5b40d726bb61c846a688f5b4ce2208ec68779bc81860adb5851a"},
- {file = "fonttools-4.60.1-cp310-cp310-win32.whl", hash = "sha256:1525796c3ffe27bb6268ed2a1bb0dcf214d561dfaf04728abf01489eb5339dce"},
- {file = "fonttools-4.60.1-cp310-cp310-win_amd64.whl", hash = "sha256:268ecda8ca6cb5c4f044b1fb9b3b376e8cd1b361cef275082429dc4174907038"},
- {file = "fonttools-4.60.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7b4c32e232a71f63a5d00259ca3d88345ce2a43295bb049d21061f338124246f"},
- {file = "fonttools-4.60.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3630e86c484263eaac71d117085d509cbcf7b18f677906824e4bace598fb70d2"},
- {file = "fonttools-4.60.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5c1015318e4fec75dd4943ad5f6a206d9727adf97410d58b7e32ab644a807914"},
- {file = "fonttools-4.60.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e6c58beb17380f7c2ea181ea11e7db8c0ceb474c9dd45f48e71e2cb577d146a1"},
- {file = "fonttools-4.60.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec3681a0cb34c255d76dd9d865a55f260164adb9fa02628415cdc2d43ee2c05d"},
- {file = "fonttools-4.60.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f4b5c37a5f40e4d733d3bbaaef082149bee5a5ea3156a785ff64d949bd1353fa"},
- {file = "fonttools-4.60.1-cp311-cp311-win32.whl", hash = "sha256:398447f3d8c0c786cbf1209711e79080a40761eb44b27cdafffb48f52bcec258"},
- {file = "fonttools-4.60.1-cp311-cp311-win_amd64.whl", hash = "sha256:d066ea419f719ed87bc2c99a4a4bfd77c2e5949cb724588b9dd58f3fd90b92bf"},
- {file = "fonttools-4.60.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7b0c6d57ab00dae9529f3faf187f2254ea0aa1e04215cf2f1a8ec277c96661bc"},
- {file = "fonttools-4.60.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:839565cbf14645952d933853e8ade66a463684ed6ed6c9345d0faf1f0e868877"},
- {file = "fonttools-4.60.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8177ec9676ea6e1793c8a084a90b65a9f778771998eb919d05db6d4b1c0b114c"},
- {file = "fonttools-4.60.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:996a4d1834524adbb423385d5a629b868ef9d774670856c63c9a0408a3063401"},
- {file = "fonttools-4.60.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a46b2f450bc79e06ef3b6394f0c68660529ed51692606ad7f953fc2e448bc903"},
- {file = "fonttools-4.60.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6ec722ee589e89a89f5b7574f5c45604030aa6ae24cb2c751e2707193b466fed"},
- {file = "fonttools-4.60.1-cp312-cp312-win32.whl", hash = "sha256:b2cf105cee600d2de04ca3cfa1f74f1127f8455b71dbad02b9da6ec266e116d6"},
- {file = "fonttools-4.60.1-cp312-cp312-win_amd64.whl", hash = "sha256:992775c9fbe2cf794786fa0ffca7f09f564ba3499b8fe9f2f80bd7197db60383"},
- {file = "fonttools-4.60.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f68576bb4bbf6060c7ab047b1574a1ebe5c50a17de62830079967b211059ebb"},
- {file = "fonttools-4.60.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:eedacb5c5d22b7097482fa834bda0dafa3d914a4e829ec83cdea2a01f8c813c4"},
- {file = "fonttools-4.60.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b33a7884fabd72bdf5f910d0cf46be50dce86a0362a65cfc746a4168c67eb96c"},
- {file = "fonttools-4.60.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2409d5fb7b55fd70f715e6d34e7a6e4f7511b8ad29a49d6df225ee76da76dd77"},
- {file = "fonttools-4.60.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8651e0d4b3bdeda6602b85fdc2abbefc1b41e573ecb37b6779c4ca50753a199"},
- {file = "fonttools-4.60.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:145daa14bf24824b677b9357c5e44fd8895c2a8f53596e1b9ea3496081dc692c"},
- {file = "fonttools-4.60.1-cp313-cp313-win32.whl", hash = "sha256:2299df884c11162617a66b7c316957d74a18e3758c0274762d2cc87df7bc0272"},
- {file = "fonttools-4.60.1-cp313-cp313-win_amd64.whl", hash = "sha256:a3db56f153bd4c5c2b619ab02c5db5192e222150ce5a1bc10f16164714bc39ac"},
- {file = "fonttools-4.60.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:a884aef09d45ba1206712c7dbda5829562d3fea7726935d3289d343232ecb0d3"},
- {file = "fonttools-4.60.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8a44788d9d91df72d1a5eac49b31aeb887a5f4aab761b4cffc4196c74907ea85"},
- {file = "fonttools-4.60.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e852d9dda9f93ad3651ae1e3bb770eac544ec93c3807888798eccddf84596537"},
- {file = "fonttools-4.60.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:154cb6ee417e417bf5f7c42fe25858c9140c26f647c7347c06f0cc2d47eff003"},
- {file = "fonttools-4.60.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5664fd1a9ea7f244487ac8f10340c4e37664675e8667d6fee420766e0fb3cf08"},
- {file = "fonttools-4.60.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:583b7f8e3c49486e4d489ad1deacfb8d5be54a8ef34d6df824f6a171f8511d99"},
- {file = "fonttools-4.60.1-cp314-cp314-win32.whl", hash = "sha256:66929e2ea2810c6533a5184f938502cfdaea4bc3efb7130d8cc02e1c1b4108d6"},
- {file = "fonttools-4.60.1-cp314-cp314-win_amd64.whl", hash = "sha256:f3d5be054c461d6a2268831f04091dc82753176f6ea06dc6047a5e168265a987"},
- {file = "fonttools-4.60.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:b6379e7546ba4ae4b18f8ae2b9bc5960936007a1c0e30b342f662577e8bc3299"},
- {file = "fonttools-4.60.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9d0ced62b59e0430b3690dbc5373df1c2aa7585e9a8ce38eff87f0fd993c5b01"},
- {file = "fonttools-4.60.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:875cb7764708b3132637f6c5fb385b16eeba0f7ac9fa45a69d35e09b47045801"},
- {file = "fonttools-4.60.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a184b2ea57b13680ab6d5fbde99ccef152c95c06746cb7718c583abd8f945ccc"},
- {file = "fonttools-4.60.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:026290e4ec76583881763fac284aca67365e0be9f13a7fb137257096114cb3bc"},
- {file = "fonttools-4.60.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f0e8817c7d1a0c2eedebf57ef9a9896f3ea23324769a9a2061a80fe8852705ed"},
- {file = "fonttools-4.60.1-cp314-cp314t-win32.whl", hash = "sha256:1410155d0e764a4615774e5c2c6fc516259fe3eca5882f034eb9bfdbee056259"},
- {file = "fonttools-4.60.1-cp314-cp314t-win_amd64.whl", hash = "sha256:022beaea4b73a70295b688f817ddc24ed3e3418b5036ffcd5658141184ef0d0c"},
- {file = "fonttools-4.60.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:122e1a8ada290423c493491d002f622b1992b1ab0b488c68e31c413390dc7eb2"},
- {file = "fonttools-4.60.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a140761c4ff63d0cb9256ac752f230460ee225ccef4ad8f68affc723c88e2036"},
- {file = "fonttools-4.60.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0eae96373e4b7c9e45d099d7a523444e3554360927225c1cdae221a58a45b856"},
- {file = "fonttools-4.60.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:596ecaca36367027d525b3b426d8a8208169d09edcf8c7506aceb3a38bfb55c7"},
- {file = "fonttools-4.60.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2ee06fc57512144d8b0445194c2da9f190f61ad51e230f14836286470c99f854"},
- {file = "fonttools-4.60.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b42d86938e8dda1cd9a1a87a6d82f1818eaf933348429653559a458d027446da"},
- {file = "fonttools-4.60.1-cp39-cp39-win32.whl", hash = "sha256:8b4eb332f9501cb1cd3d4d099374a1e1306783ff95489a1026bde9eb02ccc34a"},
- {file = "fonttools-4.60.1-cp39-cp39-win_amd64.whl", hash = "sha256:7473a8ed9ed09aeaa191301244a5a9dbe46fe0bf54f9d6cd21d83044c3321217"},
- {file = "fonttools-4.60.1-py3-none-any.whl", hash = "sha256:906306ac7afe2156fcf0042173d6ebbb05416af70f6b370967b47f8f00103bbb"},
- {file = "fonttools-4.60.1.tar.gz", hash = "sha256:ef00af0439ebfee806b25f24c8f92109157ff3fac5731dc7867957812e87b8d9"},
+ {file = "fonttools-4.61.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c7db70d57e5e1089a274cbb2b1fd635c9a24de809a231b154965d415d6c6d24"},
+ {file = "fonttools-4.61.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5fe9fd43882620017add5eabb781ebfbc6998ee49b35bd7f8f79af1f9f99a958"},
+ {file = "fonttools-4.61.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8db08051fc9e7d8bc622f2112511b8107d8f27cd89e2f64ec45e9825e8288da"},
+ {file = "fonttools-4.61.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a76d4cb80f41ba94a6691264be76435e5f72f2cb3cab0b092a6212855f71c2f6"},
+ {file = "fonttools-4.61.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a13fc8aeb24bad755eea8f7f9d409438eb94e82cf86b08fe77a03fbc8f6a96b1"},
+ {file = "fonttools-4.61.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b846a1fcf8beadeb9ea4f44ec5bdde393e2f1569e17d700bfc49cd69bde75881"},
+ {file = "fonttools-4.61.1-cp310-cp310-win32.whl", hash = "sha256:78a7d3ab09dc47ac1a363a493e6112d8cabed7ba7caad5f54dbe2f08676d1b47"},
+ {file = "fonttools-4.61.1-cp310-cp310-win_amd64.whl", hash = "sha256:eff1ac3cc66c2ac7cda1e64b4e2f3ffef474b7335f92fc3833fc632d595fcee6"},
+ {file = "fonttools-4.61.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c6604b735bb12fef8e0efd5578c9fb5d3d8532d5001ea13a19cddf295673ee09"},
+ {file = "fonttools-4.61.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5ce02f38a754f207f2f06557523cd39a06438ba3aafc0639c477ac409fc64e37"},
+ {file = "fonttools-4.61.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77efb033d8d7ff233385f30c62c7c79271c8885d5c9657d967ede124671bbdfb"},
+ {file = "fonttools-4.61.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:75c1a6dfac6abd407634420c93864a1e274ebc1c7531346d9254c0d8f6ca00f9"},
+ {file = "fonttools-4.61.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0de30bfe7745c0d1ffa2b0b7048fb7123ad0d71107e10ee090fa0b16b9452e87"},
+ {file = "fonttools-4.61.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:58b0ee0ab5b1fc9921eccfe11d1435added19d6494dde14e323f25ad2bc30c56"},
+ {file = "fonttools-4.61.1-cp311-cp311-win32.whl", hash = "sha256:f79b168428351d11e10c5aeb61a74e1851ec221081299f4cf56036a95431c43a"},
+ {file = "fonttools-4.61.1-cp311-cp311-win_amd64.whl", hash = "sha256:fe2efccb324948a11dd09d22136fe2ac8a97d6c1347cf0b58a911dcd529f66b7"},
+ {file = "fonttools-4.61.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f3cb4a569029b9f291f88aafc927dd53683757e640081ca8c412781ea144565e"},
+ {file = "fonttools-4.61.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41a7170d042e8c0024703ed13b71893519a1a6d6e18e933e3ec7507a2c26a4b2"},
+ {file = "fonttools-4.61.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10d88e55330e092940584774ee5e8a6971b01fc2f4d3466a1d6c158230880796"},
+ {file = "fonttools-4.61.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15acc09befd16a0fb8a8f62bc147e1a82817542d72184acca9ce6e0aeda9fa6d"},
+ {file = "fonttools-4.61.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e6bcdf33aec38d16508ce61fd81838f24c83c90a1d1b8c68982857038673d6b8"},
+ {file = "fonttools-4.61.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5fade934607a523614726119164ff621e8c30e8fa1ffffbbd358662056ba69f0"},
+ {file = "fonttools-4.61.1-cp312-cp312-win32.whl", hash = "sha256:75da8f28eff26defba42c52986de97b22106cb8f26515b7c22443ebc9c2d3261"},
+ {file = "fonttools-4.61.1-cp312-cp312-win_amd64.whl", hash = "sha256:497c31ce314219888c0e2fce5ad9178ca83fe5230b01a5006726cdf3ac9f24d9"},
+ {file = "fonttools-4.61.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c56c488ab471628ff3bfa80964372fc13504ece601e0d97a78ee74126b2045c"},
+ {file = "fonttools-4.61.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc492779501fa723b04d0ab1f5be046797fee17d27700476edc7ee9ae535a61e"},
+ {file = "fonttools-4.61.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:64102ca87e84261419c3747a0d20f396eb024bdbeb04c2bfb37e2891f5fadcb5"},
+ {file = "fonttools-4.61.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c1b526c8d3f615a7b1867f38a9410849c8f4aef078535742198e942fba0e9bd"},
+ {file = "fonttools-4.61.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:41ed4b5ec103bd306bb68f81dc166e77409e5209443e5773cb4ed837bcc9b0d3"},
+ {file = "fonttools-4.61.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b501c862d4901792adaec7c25b1ecc749e2662543f68bb194c42ba18d6eec98d"},
+ {file = "fonttools-4.61.1-cp313-cp313-win32.whl", hash = "sha256:4d7092bb38c53bbc78e9255a59158b150bcdc115a1e3b3ce0b5f267dc35dd63c"},
+ {file = "fonttools-4.61.1-cp313-cp313-win_amd64.whl", hash = "sha256:21e7c8d76f62ab13c9472ccf74515ca5b9a761d1bde3265152a6dc58700d895b"},
+ {file = "fonttools-4.61.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fff4f534200a04b4a36e7ae3cb74493afe807b517a09e99cb4faa89a34ed6ecd"},
+ {file = "fonttools-4.61.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d9203500f7c63545b4ce3799319fe4d9feb1a1b89b28d3cb5abd11b9dd64147e"},
+ {file = "fonttools-4.61.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa646ecec9528bef693415c79a86e733c70a4965dd938e9a226b0fc64c9d2e6c"},
+ {file = "fonttools-4.61.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f35ad7805edba3aac1a3710d104592df59f4b957e30108ae0ba6c10b11dd75"},
+ {file = "fonttools-4.61.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b931ae8f62db78861b0ff1ac017851764602288575d65b8e8ff1963fed419063"},
+ {file = "fonttools-4.61.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b148b56f5de675ee16d45e769e69f87623a4944f7443850bf9a9376e628a89d2"},
+ {file = "fonttools-4.61.1-cp314-cp314-win32.whl", hash = "sha256:9b666a475a65f4e839d3d10473fad6d47e0a9db14a2f4a224029c5bfde58ad2c"},
+ {file = "fonttools-4.61.1-cp314-cp314-win_amd64.whl", hash = "sha256:4f5686e1fe5fce75d82d93c47a438a25bf0d1319d2843a926f741140b2b16e0c"},
+ {file = "fonttools-4.61.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:e76ce097e3c57c4bcb67c5aa24a0ecdbd9f74ea9219997a707a4061fbe2707aa"},
+ {file = "fonttools-4.61.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9cfef3ab326780c04d6646f68d4b4742aae222e8b8ea1d627c74e38afcbc9d91"},
+ {file = "fonttools-4.61.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a75c301f96db737e1c5ed5fd7d77d9c34466de16095a266509e13da09751bd19"},
+ {file = "fonttools-4.61.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:91669ccac46bbc1d09e9273546181919064e8df73488ea087dcac3e2968df9ba"},
+ {file = "fonttools-4.61.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c33ab3ca9d3ccd581d58e989d67554e42d8d4ded94ab3ade3508455fe70e65f7"},
+ {file = "fonttools-4.61.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:664c5a68ec406f6b1547946683008576ef8b38275608e1cee6c061828171c118"},
+ {file = "fonttools-4.61.1-cp314-cp314t-win32.whl", hash = "sha256:aed04cabe26f30c1647ef0e8fbb207516fd40fe9472e9439695f5c6998e60ac5"},
+ {file = "fonttools-4.61.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2180f14c141d2f0f3da43f3a81bc8aa4684860f6b0e6f9e165a4831f24e6a23b"},
+ {file = "fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371"},
+ {file = "fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69"},
]
[package.dependencies]
@@ -2023,16 +2069,16 @@ brotlicffi = {version = ">=0.8.0", optional = true, markers = "platform_python_i
zopfli = {version = ">=0.1.4", optional = true, markers = "extra == \"woff\""}
[package.extras]
-all = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\"", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0) ; python_version <= \"3.12\"", "xattr ; sys_platform == \"darwin\"", "zopfli (>=0.1.4)"]
+all = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\"", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.45.0)", "unicodedata2 (>=17.0.0) ; python_version <= \"3.14\"", "xattr ; sys_platform == \"darwin\"", "zopfli (>=0.1.4)"]
graphite = ["lz4 (>=1.7.4.2)"]
interpolatable = ["munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\""]
lxml = ["lxml (>=4.0)"]
pathops = ["skia-pathops (>=0.5.0)"]
plot = ["matplotlib"]
-repacker = ["uharfbuzz (>=0.23.0)"]
+repacker = ["uharfbuzz (>=0.45.0)"]
symfont = ["sympy"]
type1 = ["xattr ; sys_platform == \"darwin\""]
-unicode = ["unicodedata2 (>=15.1.0) ; python_version <= \"3.12\""]
+unicode = ["unicodedata2 (>=17.0.0) ; python_version <= \"3.14\""]
woff = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "zopfli (>=0.1.4)"]
[[package]]
@@ -2177,14 +2223,14 @@ files = [
[[package]]
name = "fsspec"
-version = "2025.10.0"
+version = "2025.12.0"
description = "File-system specification"
optional = false
-python-versions = ">=3.9"
+python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d"},
- {file = "fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59"},
+ {file = "fsspec-2025.12.0-py3-none-any.whl", hash = "sha256:8bf1fe301b7d8acfa6e8571e3b1c3d158f909666642431cc78a1b7b4dbc5ec5b"},
+ {file = "fsspec-2025.12.0.tar.gz", hash = "sha256:c505de011584597b1060ff778bb664c1bc022e87921b0e4f10cc9c44f9635973"},
]
[package.extras]
@@ -2217,14 +2263,14 @@ tqdm = ["tqdm"]
[[package]]
name = "genai-prices"
-version = "0.0.47"
+version = "0.0.49"
description = "Calculate prices for calling LLM inference APIs."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "genai_prices-0.0.47-py3-none-any.whl", hash = "sha256:735e45950d2299276f2c00cd18075b77a124cd24ee58243f236ee29af3210594"},
- {file = "genai_prices-0.0.47.tar.gz", hash = "sha256:3b8c514f0ce5818b3944a371861586ed9bfe10d02598e62c350b5bd2916d03c2"},
+ {file = "genai_prices-0.0.49-py3-none-any.whl", hash = "sha256:dd3efbebcd865d89cd849793530729e7f7e1ca59d2b17a091ad1aa6aa76daf0d"},
+ {file = "genai_prices-0.0.49.tar.gz", hash = "sha256:a7f98f1537e6f89ed54f1cd8f560806e187033dcb42554fbecd4d635567120c5"},
]
[package.dependencies]
@@ -2233,23 +2279,25 @@ pydantic = ">=2.10"
[[package]]
name = "google-auth"
-version = "2.43.0"
+version = "2.45.0"
description = "Google Authentication Library"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
- {file = "google_auth-2.43.0-py2.py3-none-any.whl", hash = "sha256:af628ba6fa493f75c7e9dbe9373d148ca9f4399b5ea29976519e0a3848eddd16"},
- {file = "google_auth-2.43.0.tar.gz", hash = "sha256:88228eee5fc21b62a1b5fe773ca15e67778cb07dc8363adcb4a8827b52d81483"},
+ {file = "google_auth-2.45.0-py2.py3-none-any.whl", hash = "sha256:82344e86dc00410ef5382d99be677c6043d72e502b625aa4f4afa0bdacca0f36"},
+ {file = "google_auth-2.45.0.tar.gz", hash = "sha256:90d3f41b6b72ea72dd9811e765699ee491ab24139f34ebf1ca2b9cc0c38708f3"},
]
[package.dependencies]
cachetools = ">=2.0.0,<7.0"
pyasn1-modules = ">=0.2.1"
+requests = {version = ">=2.20.0,<3.0.0", optional = true, markers = "extra == \"requests\""}
rsa = ">=3.1.4,<5"
[package.extras]
aiohttp = ["aiohttp (>=3.6.2,<4.0.0)", "requests (>=2.20.0,<3.0.0)"]
+cryptography = ["cryptography (<39.0.0) ; python_version < \"3.8\"", "cryptography (>=38.0.3)"]
enterprise-cert = ["cryptography", "pyopenssl"]
pyjwt = ["cryptography (<39.0.0) ; python_version < \"3.8\"", "cryptography (>=38.0.3)", "pyjwt (>=2.0)"]
pyopenssl = ["cryptography (<39.0.0) ; python_version < \"3.8\"", "cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"]
@@ -2260,22 +2308,24 @@ urllib3 = ["packaging", "urllib3"]
[[package]]
name = "google-genai"
-version = "1.52.0"
+version = "1.56.0"
description = "GenAI Python SDK"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "google_genai-1.52.0-py3-none-any.whl", hash = "sha256:c8352b9f065ae14b9322b949c7debab8562982f03bf71d44130cd2b798c20743"},
- {file = "google_genai-1.52.0.tar.gz", hash = "sha256:a74e8a4b3025f23aa98d6a0f84783119012ca6c336fd68f73c5d2b11465d7fc5"},
+ {file = "google_genai-1.56.0-py3-none-any.whl", hash = "sha256:9e6b11e0c105ead229368cb5849a480e4d0185519f8d9f538d61ecfcf193b052"},
+ {file = "google_genai-1.56.0.tar.gz", hash = "sha256:0491af33c375f099777ae207d9621f044e27091fafad4c50e617eba32165e82f"},
]
[package.dependencies]
anyio = ">=4.8.0,<5.0.0"
-google-auth = ">=2.14.1,<3.0.0"
+distro = ">=1.7.0,<2"
+google-auth = {version = ">=2.45.0,<3.0.0", extras = ["requests"]}
httpx = ">=0.28.1,<1.0.0"
pydantic = ">=2.9.0,<3.0.0"
requests = ">=2.28.1,<3.0.0"
+sniffio = "*"
tenacity = ">=8.2.3,<9.2.0"
typing-extensions = ">=4.11.0,<5.0.0"
websockets = ">=13.0.0,<15.1.0"
@@ -2461,66 +2511,60 @@ requirements-txt = ["arxiv-client", "azure-storage-blob", "duckduckgo-search", "
[[package]]
name = "greenlet"
-version = "3.2.4"
+version = "3.3.0"
description = "Lightweight in-process concurrent programming"
optional = false
-python-versions = ">=3.9"
+python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c"},
- {file = "greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590"},
- {file = "greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c"},
- {file = "greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b"},
- {file = "greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31"},
- {file = "greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d"},
- {file = "greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5"},
- {file = "greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f"},
- {file = "greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c"},
- {file = "greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2"},
- {file = "greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246"},
- {file = "greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3"},
- {file = "greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633"},
- {file = "greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079"},
- {file = "greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8"},
- {file = "greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52"},
- {file = "greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa"},
- {file = "greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9"},
- {file = "greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd"},
- {file = "greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb"},
- {file = "greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968"},
- {file = "greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9"},
- {file = "greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6"},
- {file = "greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0"},
- {file = "greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0"},
- {file = "greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f"},
- {file = "greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02"},
- {file = "greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31"},
- {file = "greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945"},
- {file = "greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc"},
- {file = "greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a"},
- {file = "greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504"},
- {file = "greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671"},
- {file = "greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b"},
- {file = "greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae"},
- {file = "greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b"},
- {file = "greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0"},
- {file = "greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f"},
- {file = "greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5"},
- {file = "greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1"},
- {file = "greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735"},
- {file = "greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337"},
- {file = "greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01"},
- {file = "greenlet-3.2.4-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:b6a7c19cf0d2742d0809a4c05975db036fdff50cd294a93632d6a310bf9ac02c"},
- {file = "greenlet-3.2.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:27890167f55d2387576d1f41d9487ef171849ea0359ce1510ca6e06c8bece11d"},
- {file = "greenlet-3.2.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:18d9260df2b5fbf41ae5139e1be4e796d99655f023a636cd0e11e6406cca7d58"},
- {file = "greenlet-3.2.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:671df96c1f23c4a0d4077a325483c1503c96a1b7d9db26592ae770daa41233d4"},
- {file = "greenlet-3.2.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:16458c245a38991aa19676900d48bd1a6f2ce3e16595051a4db9d012154e8433"},
- {file = "greenlet-3.2.4-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9913f1a30e4526f432991f89ae263459b1c64d1608c0d22a5c79c287b3c70df"},
- {file = "greenlet-3.2.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b90654e092f928f110e0007f572007c9727b5265f7632c2fa7415b4689351594"},
- {file = "greenlet-3.2.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81701fd84f26330f0d5f4944d4e92e61afe6319dcd9775e39396e39d7c3e5f98"},
- {file = "greenlet-3.2.4-cp39-cp39-win32.whl", hash = "sha256:65458b409c1ed459ea899e939f0e1cdb14f58dbc803f2f93c5eab5694d32671b"},
- {file = "greenlet-3.2.4-cp39-cp39-win_amd64.whl", hash = "sha256:d2e685ade4dafd447ede19c31277a224a239a0a1a4eca4e6390efedf20260cfb"},
- {file = "greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d"},
+ {file = "greenlet-3.3.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f8496d434d5cb2dce025773ba5597f71f5410ae499d5dd9533e0653258cdb3d"},
+ {file = "greenlet-3.3.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b96dc7eef78fd404e022e165ec55327f935b9b52ff355b067eb4a0267fc1cffb"},
+ {file = "greenlet-3.3.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73631cd5cccbcfe63e3f9492aaa664d278fda0ce5c3d43aeda8e77317e38efbd"},
+ {file = "greenlet-3.3.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b299a0cb979f5d7197442dccc3aee67fce53500cd88951b7e6c35575701c980b"},
+ {file = "greenlet-3.3.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dee147740789a4632cace364816046e43310b59ff8fb79833ab043aefa72fd5"},
+ {file = "greenlet-3.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:39b28e339fc3c348427560494e28d8a6f3561c8d2bcf7d706e1c624ed8d822b9"},
+ {file = "greenlet-3.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3c374782c2935cc63b2a27ba8708471de4ad1abaa862ffdb1ef45a643ddbb7d"},
+ {file = "greenlet-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:b49e7ed51876b459bd645d83db257f0180e345d3f768a35a85437a24d5a49082"},
+ {file = "greenlet-3.3.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e29f3018580e8412d6aaf5641bb7745d38c85228dacf51a73bd4e26ddf2a6a8e"},
+ {file = "greenlet-3.3.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a687205fb22794e838f947e2194c0566d3812966b41c78709554aa883183fb62"},
+ {file = "greenlet-3.3.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4243050a88ba61842186cb9e63c7dfa677ec146160b0efd73b855a3d9c7fcf32"},
+ {file = "greenlet-3.3.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:670d0f94cd302d81796e37299bcd04b95d62403883b24225c6b5271466612f45"},
+ {file = "greenlet-3.3.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb3a8ec3db4a3b0eb8a3c25436c2d49e3505821802074969db017b87bc6a948"},
+ {file = "greenlet-3.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2de5a0b09eab81fc6a382791b995b1ccf2b172a9fec934747a7a23d2ff291794"},
+ {file = "greenlet-3.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4449a736606bd30f27f8e1ff4678ee193bc47f6ca810d705981cfffd6ce0d8c5"},
+ {file = "greenlet-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:7652ee180d16d447a683c04e4c5f6441bae7ba7b17ffd9f6b3aff4605e9e6f71"},
+ {file = "greenlet-3.3.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:b01548f6e0b9e9784a2c99c5651e5dc89ffcbe870bc5fb2e5ef864e9cc6b5dcb"},
+ {file = "greenlet-3.3.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:349345b770dc88f81506c6861d22a6ccd422207829d2c854ae2af8025af303e3"},
+ {file = "greenlet-3.3.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e8e18ed6995e9e2c0b4ed264d2cf89260ab3ac7e13555b8032b25a74c6d18655"},
+ {file = "greenlet-3.3.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c024b1e5696626890038e34f76140ed1daf858e37496d33f2af57f06189e70d7"},
+ {file = "greenlet-3.3.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:047ab3df20ede6a57c35c14bf5200fcf04039d50f908270d3f9a7a82064f543b"},
+ {file = "greenlet-3.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2d9ad37fc657b1102ec880e637cccf20191581f75c64087a549e66c57e1ceb53"},
+ {file = "greenlet-3.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83cd0e36932e0e7f36a64b732a6f60c2fc2df28c351bae79fbaf4f8092fe7614"},
+ {file = "greenlet-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7a34b13d43a6b78abf828a6d0e87d3385680eaf830cd60d20d52f249faabf39"},
+ {file = "greenlet-3.3.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:a1e41a81c7e2825822f4e068c48cb2196002362619e2d70b148f20a831c00739"},
+ {file = "greenlet-3.3.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f515a47d02da4d30caaa85b69474cec77b7929b2e936ff7fb853d42f4bf8808"},
+ {file = "greenlet-3.3.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d2d9fd66bfadf230b385fdc90426fcd6eb64db54b40c495b72ac0feb5766c54"},
+ {file = "greenlet-3.3.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30a6e28487a790417d036088b3bcb3f3ac7d8babaa7d0139edbaddebf3af9492"},
+ {file = "greenlet-3.3.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:087ea5e004437321508a8d6f20efc4cfec5e3c30118e1417ea96ed1d93950527"},
+ {file = "greenlet-3.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab97cf74045343f6c60a39913fa59710e4bd26a536ce7ab2397adf8b27e67c39"},
+ {file = "greenlet-3.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5375d2e23184629112ca1ea89a53389dddbffcf417dad40125713d88eb5f96e8"},
+ {file = "greenlet-3.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:9ee1942ea19550094033c35d25d20726e4f1c40d59545815e1128ac58d416d38"},
+ {file = "greenlet-3.3.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:60c2ef0f578afb3c8d92ea07ad327f9a062547137afe91f38408f08aacab667f"},
+ {file = "greenlet-3.3.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a5d554d0712ba1de0a6c94c640f7aeba3f85b3a6e1f2899c11c2c0428da9365"},
+ {file = "greenlet-3.3.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3a898b1e9c5f7307ebbde4102908e6cbfcb9ea16284a3abe15cab996bee8b9b3"},
+ {file = "greenlet-3.3.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dcd2bdbd444ff340e8d6bdf54d2f206ccddbb3ccfdcd3c25bf4afaa7b8f0cf45"},
+ {file = "greenlet-3.3.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5773edda4dc00e173820722711d043799d3adb4f01731f40619e07ea2750b955"},
+ {file = "greenlet-3.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ac0549373982b36d5fd5d30beb8a7a33ee541ff98d2b502714a09f1169f31b55"},
+ {file = "greenlet-3.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d198d2d977460358c3b3a4dc844f875d1adb33817f0613f663a656f463764ccc"},
+ {file = "greenlet-3.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:73f51dd0e0bdb596fb0417e475fa3c5e32d4c83638296e560086b8d7da7c4170"},
+ {file = "greenlet-3.3.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d6ed6f85fae6cdfdb9ce04c9bf7a08d666cfcfb914e7d006f44f840b46741931"},
+ {file = "greenlet-3.3.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9125050fcf24554e69c4cacb086b87b3b55dc395a8b3ebe6487b045b2614388"},
+ {file = "greenlet-3.3.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:87e63ccfa13c0a0f6234ed0add552af24cc67dd886731f2261e46e241608bee3"},
+ {file = "greenlet-3.3.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2662433acbca297c9153a4023fe2161c8dcfdcc91f10433171cf7e7d94ba2221"},
+ {file = "greenlet-3.3.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c6e9b9c1527a78520357de498b0e709fb9e2f49c3a513afd5a249007261911b"},
+ {file = "greenlet-3.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:286d093f95ec98fdd92fcb955003b8a3d054b4e2cab3e2707a5039e7b50520fd"},
+ {file = "greenlet-3.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c10513330af5b8ae16f023e8ddbfb486ab355d04467c4679c5cfe4659975dd9"},
+ {file = "greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb"},
]
[package.extras]
@@ -2547,14 +2591,14 @@ pypi = ["pip (>=24.0)", "platformdirs (>=4.2)", "wheel (>=0.42)"]
[[package]]
name = "groq"
-version = "0.36.0"
+version = "1.0.0"
description = "The official Python library for the groq API"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "groq-0.36.0-py3-none-any.whl", hash = "sha256:ac7eeae31a5c2e76d30ea678f0b1a9168ff906c4440f5ec3a42ac74d5b4fdb3c"},
- {file = "groq-0.36.0.tar.gz", hash = "sha256:766fa1ae34918552c6488767b16d4bd0ee3f0fb7809cd9f7760c90d13f32c63a"},
+ {file = "groq-1.0.0-py3-none-any.whl", hash = "sha256:6e22bf92ffad988f01d2d4df7729add66b8fd5dbfb2154b5bbf3af245b72c731"},
+ {file = "groq-1.0.0.tar.gz", hash = "sha256:66cb7bb729e6eb644daac7ce8efe945e99e4eb33657f733ee6f13059ef0c25a9"},
]
[package.dependencies]
@@ -2575,6 +2619,7 @@ description = "HTTP/2-based RPC framework"
optional = false
python-versions = ">=3.8"
groups = ["main"]
+markers = "python_version <= \"3.13\""
files = [
{file = "grpcio-1.67.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:8b0341d66a57f8a3119b77ab32207072be60c9bf79760fa609c5609f2deb1f3f"},
{file = "grpcio-1.67.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:f5a27dddefe0e2357d3e617b9079b4bfdc91341a91565111a21ed6ebbc51b22d"},
@@ -2636,6 +2681,84 @@ files = [
[package.extras]
protobuf = ["grpcio-tools (>=1.67.1)"]
+[[package]]
+name = "grpcio"
+version = "1.76.0"
+description = "HTTP/2-based RPC framework"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version >= \"3.14\""
+files = [
+ {file = "grpcio-1.76.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:65a20de41e85648e00305c1bb09a3598f840422e522277641145a32d42dcefcc"},
+ {file = "grpcio-1.76.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:40ad3afe81676fd9ec6d9d406eda00933f218038433980aa19d401490e46ecde"},
+ {file = "grpcio-1.76.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:035d90bc79eaa4bed83f524331d55e35820725c9fbb00ffa1904d5550ed7ede3"},
+ {file = "grpcio-1.76.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4215d3a102bd95e2e11b5395c78562967959824156af11fa93d18fdd18050990"},
+ {file = "grpcio-1.76.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:49ce47231818806067aea3324d4bf13825b658ad662d3b25fada0bdad9b8a6af"},
+ {file = "grpcio-1.76.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8cc3309d8e08fd79089e13ed4819d0af72aa935dd8f435a195fd152796752ff2"},
+ {file = "grpcio-1.76.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:971fd5a1d6e62e00d945423a567e42eb1fa678ba89072832185ca836a94daaa6"},
+ {file = "grpcio-1.76.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d9adda641db7207e800a7f089068f6f645959f2df27e870ee81d44701dd9db3"},
+ {file = "grpcio-1.76.0-cp310-cp310-win32.whl", hash = "sha256:063065249d9e7e0782d03d2bca50787f53bd0fb89a67de9a7b521c4a01f1989b"},
+ {file = "grpcio-1.76.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6ae758eb08088d36812dd5d9af7a9859c05b1e0f714470ea243694b49278e7b"},
+ {file = "grpcio-1.76.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2e1743fbd7f5fa713a1b0a8ac8ebabf0ec980b5d8809ec358d488e273b9cf02a"},
+ {file = "grpcio-1.76.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:a8c2cf1209497cf659a667d7dea88985e834c24b7c3b605e6254cbb5076d985c"},
+ {file = "grpcio-1.76.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:08caea849a9d3c71a542827d6df9d5a69067b0a1efbea8a855633ff5d9571465"},
+ {file = "grpcio-1.76.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f0e34c2079d47ae9f6188211db9e777c619a21d4faba6977774e8fa43b085e48"},
+ {file = "grpcio-1.76.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8843114c0cfce61b40ad48df65abcfc00d4dba82eae8718fab5352390848c5da"},
+ {file = "grpcio-1.76.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8eddfb4d203a237da6f3cc8a540dad0517d274b5a1e9e636fd8d2c79b5c1d397"},
+ {file = "grpcio-1.76.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:32483fe2aab2c3794101c2a159070584e5db11d0aa091b2c0ea9c4fc43d0d749"},
+ {file = "grpcio-1.76.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dcfe41187da8992c5f40aa8c5ec086fa3672834d2be57a32384c08d5a05b4c00"},
+ {file = "grpcio-1.76.0-cp311-cp311-win32.whl", hash = "sha256:2107b0c024d1b35f4083f11245c0e23846ae64d02f40b2b226684840260ed054"},
+ {file = "grpcio-1.76.0-cp311-cp311-win_amd64.whl", hash = "sha256:522175aba7af9113c48ec10cc471b9b9bd4f6ceb36aeb4544a8e2c80ed9d252d"},
+ {file = "grpcio-1.76.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:81fd9652b37b36f16138611c7e884eb82e0cec137c40d3ef7c3f9b3ed00f6ed8"},
+ {file = "grpcio-1.76.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:04bbe1bfe3a68bbfd4e52402ab7d4eb59d72d02647ae2042204326cf4bbad280"},
+ {file = "grpcio-1.76.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d388087771c837cdb6515539f43b9d4bf0b0f23593a24054ac16f7a960be16f4"},
+ {file = "grpcio-1.76.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f8f757bebaaea112c00dba718fc0d3260052ce714e25804a03f93f5d1c6cc11"},
+ {file = "grpcio-1.76.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:980a846182ce88c4f2f7e2c22c56aefd515daeb36149d1c897f83cf57999e0b6"},
+ {file = "grpcio-1.76.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f92f88e6c033db65a5ae3d97905c8fea9c725b63e28d5a75cb73b49bda5024d8"},
+ {file = "grpcio-1.76.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4baf3cbe2f0be3289eb68ac8ae771156971848bb8aaff60bad42005539431980"},
+ {file = "grpcio-1.76.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:615ba64c208aaceb5ec83bfdce7728b80bfeb8be97562944836a7a0a9647d882"},
+ {file = "grpcio-1.76.0-cp312-cp312-win32.whl", hash = "sha256:45d59a649a82df5718fd9527ce775fd66d1af35e6d31abdcdc906a49c6822958"},
+ {file = "grpcio-1.76.0-cp312-cp312-win_amd64.whl", hash = "sha256:c088e7a90b6017307f423efbb9d1ba97a22aa2170876223f9709e9d1de0b5347"},
+ {file = "grpcio-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:26ef06c73eb53267c2b319f43e6634c7556ea37672029241a056629af27c10e2"},
+ {file = "grpcio-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:45e0111e73f43f735d70786557dc38141185072d7ff8dc1829d6a77ac1471468"},
+ {file = "grpcio-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83d57312a58dcfe2a3a0f9d1389b299438909a02db60e2f2ea2ae2d8034909d3"},
+ {file = "grpcio-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3e2a27c89eb9ac3d81ec8835e12414d73536c6e620355d65102503064a4ed6eb"},
+ {file = "grpcio-1.76.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61f69297cba3950a524f61c7c8ee12e55c486cb5f7db47ff9dcee33da6f0d3ae"},
+ {file = "grpcio-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a15c17af8839b6801d554263c546c69c4d7718ad4321e3166175b37eaacca77"},
+ {file = "grpcio-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25a18e9810fbc7e7f03ec2516addc116a957f8cbb8cbc95ccc80faa072743d03"},
+ {file = "grpcio-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:931091142fd8cc14edccc0845a79248bc155425eee9a98b2db2ea4f00a235a42"},
+ {file = "grpcio-1.76.0-cp313-cp313-win32.whl", hash = "sha256:5e8571632780e08526f118f74170ad8d50fb0a48c23a746bef2a6ebade3abd6f"},
+ {file = "grpcio-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:f9f7bd5faab55f47231ad8dba7787866b69f5e93bc306e3915606779bbfb4ba8"},
+ {file = "grpcio-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:ff8a59ea85a1f2191a0ffcc61298c571bc566332f82e5f5be1b83c9d8e668a62"},
+ {file = "grpcio-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06c3d6b076e7b593905d04fdba6a0525711b3466f43b3400266f04ff735de0cd"},
+ {file = "grpcio-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd5ef5932f6475c436c4a55e4336ebbe47bd3272be04964a03d316bbf4afbcbc"},
+ {file = "grpcio-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b331680e46239e090f5b3cead313cc772f6caa7d0fc8de349337563125361a4a"},
+ {file = "grpcio-1.76.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2229ae655ec4e8999599469559e97630185fdd53ae1e8997d147b7c9b2b72cba"},
+ {file = "grpcio-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:490fa6d203992c47c7b9e4a9d39003a0c2bcc1c9aa3c058730884bbbb0ee9f09"},
+ {file = "grpcio-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:479496325ce554792dba6548fae3df31a72cef7bad71ca2e12b0e58f9b336bfc"},
+ {file = "grpcio-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c9b93f79f48b03ada57ea24725d83a30284a012ec27eab2cf7e50a550cbbbcc"},
+ {file = "grpcio-1.76.0-cp314-cp314-win32.whl", hash = "sha256:747fa73efa9b8b1488a95d0ba1039c8e2dca0f741612d80415b1e1c560febf4e"},
+ {file = "grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e"},
+ {file = "grpcio-1.76.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:8ebe63ee5f8fa4296b1b8cfc743f870d10e902ca18afc65c68cf46fd39bb0783"},
+ {file = "grpcio-1.76.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:3bf0f392c0b806905ed174dcd8bdd5e418a40d5567a05615a030a5aeddea692d"},
+ {file = "grpcio-1.76.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b7604868b38c1bfd5cf72d768aedd7db41d78cb6a4a18585e33fb0f9f2363fd"},
+ {file = "grpcio-1.76.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e6d1db20594d9daba22f90da738b1a0441a7427552cc6e2e3d1297aeddc00378"},
+ {file = "grpcio-1.76.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d099566accf23d21037f18a2a63d323075bebace807742e4b0ac210971d4dd70"},
+ {file = "grpcio-1.76.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ebea5cc3aa8ea72e04df9913492f9a96d9348db876f9dda3ad729cfedf7ac416"},
+ {file = "grpcio-1.76.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0c37db8606c258e2ee0c56b78c62fc9dee0e901b5dbdcf816c2dd4ad652b8b0c"},
+ {file = "grpcio-1.76.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ebebf83299b0cb1721a8859ea98f3a77811e35dce7609c5c963b9ad90728f886"},
+ {file = "grpcio-1.76.0-cp39-cp39-win32.whl", hash = "sha256:0aaa82d0813fd4c8e589fac9b65d7dd88702555f702fb10417f96e2a2a6d4c0f"},
+ {file = "grpcio-1.76.0-cp39-cp39-win_amd64.whl", hash = "sha256:acab0277c40eff7143c2323190ea57b9ee5fd353d8190ee9652369fae735668a"},
+ {file = "grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73"},
+]
+
+[package.dependencies]
+typing-extensions = ">=4.12,<5.0"
+
+[package.extras]
+protobuf = ["grpcio-tools (>=1.76.0)"]
+
[[package]]
name = "gunicorn"
version = "23.0.0"
@@ -2793,14 +2916,14 @@ zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "httpx-aiohttp"
-version = "0.1.9"
+version = "0.1.12"
description = "Aiohttp transport for HTTPX"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
- {file = "httpx_aiohttp-0.1.9-py3-none-any.whl", hash = "sha256:3dc2845568b07742588710fcf3d72db2cbcdf2acc93376edf85f789c4d8e5fda"},
- {file = "httpx_aiohttp-0.1.9.tar.gz", hash = "sha256:4ee8b22e6f2e7c80cd03be29eff98bfe7d89bd77f021ce0b578ee76b73b4bfe6"},
+ {file = "httpx_aiohttp-0.1.12-py3-none-any.whl", hash = "sha256:5b0eac39a7f360fa7867a60bcb46bb1024eada9c01cbfecdb54dc1edb3fb7141"},
+ {file = "httpx_aiohttp-0.1.12.tar.gz", hash = "sha256:81feec51fd82c0ecfa0e9aaf1b1a6c2591260d5e2bcbeb7eb0277a78e610df2c"},
]
[package.dependencies]
@@ -2809,26 +2932,26 @@ httpx = ">=0.27.0"
[[package]]
name = "httpx-sse"
-version = "0.4.0"
+version = "0.4.3"
description = "Consume Server-Sent Event (SSE) messages with HTTPX."
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721"},
- {file = "httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f"},
+ {file = "httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc"},
+ {file = "httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d"},
]
[[package]]
name = "huggingface-hub"
-version = "1.1.5"
+version = "1.2.3"
description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
optional = false
python-versions = ">=3.9.0"
groups = ["main"]
files = [
- {file = "huggingface_hub-1.1.5-py3-none-any.whl", hash = "sha256:e88ecc129011f37b868586bbcfae6c56868cae80cd56a79d61575426a3aa0d7d"},
- {file = "huggingface_hub-1.1.5.tar.gz", hash = "sha256:40ba5c9a08792d888fde6088920a0a71ab3cd9d5e6617c81a797c657f1fd9968"},
+ {file = "huggingface_hub-1.2.3-py3-none-any.whl", hash = "sha256:c9b7a91a9eedaa2149cdc12bdd8f5a11780e10de1f1024718becf9e41e5a4642"},
+ {file = "huggingface_hub-1.2.3.tar.gz", hash = "sha256:4ba57f17004fd27bb176a6b7107df579865d4cde015112db59184c51f5602ba7"},
]
[package.dependencies]
@@ -2887,14 +3010,14 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2
[[package]]
name = "importlib-metadata"
-version = "8.7.0"
+version = "8.7.1"
description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"},
- {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"},
+ {file = "importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151"},
+ {file = "importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb"},
]
[package.dependencies]
@@ -2904,10 +3027,10 @@ zipp = ">=3.20"
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-enabler = ["pytest-enabler (>=2.2)"]
+enabler = ["pytest-enabler (>=3.4)"]
perf = ["ipython"]
-test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
-type = ["pytest-mypy"]
+test = ["flufl.flake8", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
+type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"]
[[package]]
name = "iniconfig"
@@ -2992,33 +3115,37 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-ena
[[package]]
name = "jaraco-context"
-version = "6.0.1"
+version = "6.0.2"
description = "Useful decorators and context managers"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4"},
- {file = "jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3"},
+ {file = "jaraco_context-6.0.2-py3-none-any.whl", hash = "sha256:55fc21af4b4f9ca94aa643b6ee7fe13b1e4c01abf3aeb98ca4ad9c80b741c786"},
+ {file = "jaraco_context-6.0.2.tar.gz", hash = "sha256:953ae8dddb57b1d791bf72ea1009b32088840a7dd19b9ba16443f62be919ee57"},
]
[package.dependencies]
"backports.tarfile" = {version = "*", markers = "python_version < \"3.12\""}
[package.extras]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
+cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
+enabler = ["pytest-enabler (>=3.4)"]
+test = ["jaraco.test (>=5.6.0)", "portend", "pytest (>=6,!=8.1.*)"]
+type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"]
[[package]]
name = "jaraco-functools"
-version = "4.3.0"
+version = "4.4.0"
description = "Functools like those found in stdlib"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "jaraco_functools-4.3.0-py3-none-any.whl", hash = "sha256:227ff8ed6f7b8f62c56deff101545fa7543cf2c8e7b82a7c2116e672f29c26e8"},
- {file = "jaraco_functools-4.3.0.tar.gz", hash = "sha256:cfd13ad0dd2c47a3600b439ef72d8615d482cedcff1632930d6f28924d92f294"},
+ {file = "jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176"},
+ {file = "jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb"},
]
[package.dependencies]
@@ -3028,9 +3155,9 @@ more_itertools = "*"
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-enabler = ["pytest-enabler (>=2.2)"]
+enabler = ["pytest-enabler (>=3.4)"]
test = ["jaraco.classes", "pytest (>=6,!=8.1.*)"]
-type = ["pytest-mypy"]
+type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"]
[[package]]
name = "jedi"
@@ -3213,14 +3340,14 @@ files = [
[[package]]
name = "joblib"
-version = "1.5.2"
+version = "1.5.3"
description = "Lightweight pipelining with Python functions"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "joblib-1.5.2-py3-none-any.whl", hash = "sha256:4e1f0bdbb987e6d843c70cf43714cb276623def372df3c22fe5266b2670bc241"},
- {file = "joblib-1.5.2.tar.gz", hash = "sha256:3faa5c39054b2f03ca547da9b2f52fde67c06240c31853f306aea97f13647b55"},
+ {file = "joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713"},
+ {file = "joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3"},
]
[[package]]
@@ -3241,14 +3368,14 @@ six = ">=1.13.0"
[[package]]
name = "json-repair"
-version = "0.54.2"
+version = "0.54.3"
description = "A package to repair broken json strings"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "json_repair-0.54.2-py3-none-any.whl", hash = "sha256:be51cce5dca97e0c24ebdf61a1ede2449a8a7666012de99467bb7b0afb35179b"},
- {file = "json_repair-0.54.2.tar.gz", hash = "sha256:4b6b62ce17f1a505b220fa4aadba1fc37dc9c221544f158471efe3775620bad6"},
+ {file = "json_repair-0.54.3-py3-none-any.whl", hash = "sha256:4cdc132ee27d4780576f71bf27a113877046224a808bfc17392e079cb344fb81"},
+ {file = "json_repair-0.54.3.tar.gz", hash = "sha256:e50feec9725e52ac91f12184609754684ac1656119dfbd31de09bdaf9a1d8bf6"},
]
[[package]]
@@ -3491,18 +3618,18 @@ files = [
[[package]]
name = "langchain"
-version = "1.1.0"
+version = "1.2.0"
description = "Building applications with LLMs through composability"
optional = false
python-versions = "<4.0.0,>=3.10.0"
groups = ["main"]
files = [
- {file = "langchain-1.1.0-py3-none-any.whl", hash = "sha256:af080f3a4a779bfa5925de7aacb6dfab83249d4aab9a08f7aa7b9bec3766d8ea"},
- {file = "langchain-1.1.0.tar.gz", hash = "sha256:583c892f59873c0329dbe04169fb3234ac794c50780e7c6fb62a61c7b86a981b"},
+ {file = "langchain-1.2.0-py3-none-any.whl", hash = "sha256:82f0d17aa4fbb11560b30e1e7d4aeb75e3ad71ce09b85c90ab208b181a24ffac"},
+ {file = "langchain-1.2.0.tar.gz", hash = "sha256:a087d1e2b2969819e29a91a6d5f98302aafe31bd49ba377ecee3bf5a5dcfe14a"},
]
[package.dependencies]
-langchain-core = ">=1.1.0,<2.0.0"
+langchain-core = ">=1.2.1,<2.0.0"
langgraph = ">=1.0.2,<1.1.0"
pydantic = ">=2.7.4,<3.0.0"
@@ -3526,19 +3653,19 @@ xai = ["langchain-xai"]
[[package]]
name = "langchain-classic"
-version = "1.0.0"
+version = "1.0.1"
description = "Building applications with LLMs through composability"
optional = false
python-versions = "<4.0.0,>=3.10.0"
groups = ["main"]
files = [
- {file = "langchain_classic-1.0.0-py3-none-any.whl", hash = "sha256:97f71f150c10123f5511c08873f030e35ede52311d729a7688c721b4e1e01f33"},
- {file = "langchain_classic-1.0.0.tar.gz", hash = "sha256:a63655609254ebc36d660eb5ad7c06c778b2e6733c615ffdac3eac4fbe2b12c5"},
+ {file = "langchain_classic-1.0.1-py3-none-any.whl", hash = "sha256:131d83a02bb80044c68fedc1ab4ae885d5b8f8c2c742d8ab9e7534ad9cda8e80"},
+ {file = "langchain_classic-1.0.1.tar.gz", hash = "sha256:40a499684df36b005a1213735dc7f8dca8f5eb67978d6ec763e7a49780864fdc"},
]
[package.dependencies]
-langchain-core = ">=1.0.0,<2.0.0"
-langchain-text-splitters = ">=1.0.0,<2.0.0"
+langchain-core = ">=1.2.5,<2.0.0"
+langchain-text-splitters = ">=1.1.0,<2.0.0"
langsmith = ">=0.1.17,<1.0.0"
pydantic = ">=2.7.4,<3.0.0"
pyyaml = ">=5.3.0,<7.0.0"
@@ -3553,6 +3680,7 @@ fireworks = ["langchain-fireworks"]
google-genai = ["langchain-google-genai"]
google-vertexai = ["langchain-google-vertexai"]
groq = ["langchain-groq"]
+huggingface = ["langchain-huggingface"]
mistralai = ["langchain-mistralai"]
ollama = ["langchain-ollama"]
openai = ["langchain-openai"]
@@ -3591,14 +3719,14 @@ tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<10.0.0"
[[package]]
name = "langchain-core"
-version = "1.1.0"
+version = "1.2.5"
description = "Building applications with LLMs through composability"
optional = false
python-versions = "<4.0.0,>=3.10.0"
groups = ["main"]
files = [
- {file = "langchain_core-1.1.0-py3-none-any.whl", hash = "sha256:2c9f27dadc6d21ed4aa46506a37a56e6a7e2d2f9141922dc5c251ba921822ee6"},
- {file = "langchain_core-1.1.0.tar.gz", hash = "sha256:2b76a82d427922c8bc51c08404af4fc2a29e9f161dfe2297cb05091e810201e7"},
+ {file = "langchain_core-1.2.5-py3-none-any.whl", hash = "sha256:3255944ef4e21b2551facb319bfc426057a40247c0a05de5bd6f2fc021fbfa34"},
+ {file = "langchain_core-1.2.5.tar.gz", hash = "sha256:d674f6df42f07e846859b9d3afe547cad333d6bf9763e92c88eb4f8aaedcd3cc"},
]
[package.dependencies]
@@ -3609,17 +3737,18 @@ pydantic = ">=2.7.4,<3.0.0"
pyyaml = ">=5.3.0,<7.0.0"
tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<10.0.0"
typing-extensions = ">=4.7.0,<5.0.0"
+uuid-utils = ">=0.12.0,<1.0"
[[package]]
name = "langchain-ollama"
-version = "1.0.0"
+version = "1.0.1"
description = "An integration package connecting Ollama and LangChain"
optional = false
python-versions = "<4.0.0,>=3.10.0"
groups = ["main"]
files = [
- {file = "langchain_ollama-1.0.0-py3-none-any.whl", hash = "sha256:5828523fcbd137847490841110a6aedf96b68534e7fe2735715ecf3e835b2391"},
- {file = "langchain_ollama-1.0.0.tar.gz", hash = "sha256:2ea9ad1b0f0ab319d600b9193d1124a8925523a3b943d75a967718e24ec09a8a"},
+ {file = "langchain_ollama-1.0.1-py3-none-any.whl", hash = "sha256:37eb939a4718a0255fe31e19fbb0def044746c717b01b97d397606ebc3e9b440"},
+ {file = "langchain_ollama-1.0.1.tar.gz", hash = "sha256:e37880c2f41cdb0895e863b1cfd0c2c840a117868b3f32e44fef42569e367443"},
]
[package.dependencies]
@@ -3628,35 +3757,35 @@ ollama = ">=0.6.0,<1.0.0"
[[package]]
name = "langchain-openai"
-version = "1.1.0"
+version = "1.1.6"
description = "An integration package connecting OpenAI and LangChain"
optional = false
python-versions = "<4.0.0,>=3.10.0"
groups = ["main"]
files = [
- {file = "langchain_openai-1.1.0-py3-none-any.whl", hash = "sha256:243bb345d0260ea1326c2b6ac2237ec29f082ab457c59e9306bac349df4577e8"},
- {file = "langchain_openai-1.1.0.tar.gz", hash = "sha256:9a33280c2e8315d013d64e6b15e583be347beb0d0f281755c335ae504ad0c184"},
+ {file = "langchain_openai-1.1.6-py3-none-any.whl", hash = "sha256:c42d04a67a85cee1d994afe400800d2b09ebf714721345f0b651eb06a02c3948"},
+ {file = "langchain_openai-1.1.6.tar.gz", hash = "sha256:e306612654330ae36fb6bbe36db91c98534312afade19e140c3061fe4208dac8"},
]
[package.dependencies]
-langchain-core = ">=1.1.0,<2.0.0"
+langchain-core = ">=1.2.2,<2.0.0"
openai = ">=1.109.1,<3.0.0"
tiktoken = ">=0.7.0,<1.0.0"
[[package]]
name = "langchain-text-splitters"
-version = "1.0.0"
+version = "1.1.0"
description = "LangChain text splitting utilities"
optional = false
python-versions = "<4.0.0,>=3.10.0"
groups = ["main"]
files = [
- {file = "langchain_text_splitters-1.0.0-py3-none-any.whl", hash = "sha256:f00c8219d3468f2c5bd951b708b6a7dd9bc3c62d0cfb83124c377f7170f33b2e"},
- {file = "langchain_text_splitters-1.0.0.tar.gz", hash = "sha256:d8580a20ad7ed10b432feb273e5758b2cc0902d094919629cec0e1ad691a6744"},
+ {file = "langchain_text_splitters-1.1.0-py3-none-any.whl", hash = "sha256:f00341fe883358786104a5f881375ac830a4dd40253ecd42b4c10536c6e4693f"},
+ {file = "langchain_text_splitters-1.1.0.tar.gz", hash = "sha256:75e58acb7585dc9508f3cd9d9809cb14751283226c2d6e21fb3a9ae57582ca22"},
]
[package.dependencies]
-langchain-core = ">=1.0.0,<2.0.0"
+langchain-core = ">=1.2.0,<2.0.0"
[[package]]
name = "langdetect"
@@ -3675,21 +3804,21 @@ six = "*"
[[package]]
name = "langgraph"
-version = "1.0.4"
+version = "1.0.5"
description = "Building stateful, multi-actor applications with LLMs"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "langgraph-1.0.4-py3-none-any.whl", hash = "sha256:b1a835ceb0a8d69b9db48075e1939e28b1ad70ee23fa3fa8f90149904778bacf"},
- {file = "langgraph-1.0.4.tar.gz", hash = "sha256:86d08e25d7244340f59c5200fa69fdd11066aa999b3164b531e2a20036fac156"},
+ {file = "langgraph-1.0.5-py3-none-any.whl", hash = "sha256:b4cfd173dca3c389735b47228ad8b295e6f7b3df779aba3a1e0c23871f81281e"},
+ {file = "langgraph-1.0.5.tar.gz", hash = "sha256:7f6ae59622386b60fe9fa0ad4c53f42016b668455ed604329e7dc7904adbf3f8"},
]
[package.dependencies]
langchain-core = ">=0.1"
langgraph-checkpoint = ">=2.1.0,<4.0.0"
langgraph-prebuilt = ">=1.0.2,<1.1.0"
-langgraph-sdk = ">=0.2.2,<0.3.0"
+langgraph-sdk = ">=0.3.0,<0.4.0"
pydantic = ">=2.7.4"
xxhash = ">=3.5.0"
@@ -3711,14 +3840,14 @@ ormsgpack = ">=1.12.0"
[[package]]
name = "langgraph-cli"
-version = "0.4.7"
+version = "0.4.11"
description = "CLI for interacting with LangGraph API"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "langgraph_cli-0.4.7-py3-none-any.whl", hash = "sha256:c24e1593c2cdffb658841999eccf71d8bd73025105c727ebcad7fafe35c983ab"},
- {file = "langgraph_cli-0.4.7.tar.gz", hash = "sha256:51dc5c7bfd0ce957162facea5ef93ffe9778e8d9ec993354f19aec9dd0161470"},
+ {file = "langgraph_cli-0.4.11-py3-none-any.whl", hash = "sha256:807cd6e8c5d4fd7160bd44be67f3b5621cb34ec309658072f75852b639b41ca0"},
+ {file = "langgraph_cli-0.4.11.tar.gz", hash = "sha256:c38c531510ace1c2d90f8a15f4bb5b874ca9d07c0564cbda7590730da2b0dff3"},
]
[package.dependencies]
@@ -3726,7 +3855,7 @@ click = ">=8.1.7"
langgraph-sdk = {version = ">=0.1.0", markers = "python_version >= \"3.11\""}
[package.extras]
-inmem = ["langgraph-api (>=0.4,<0.6.0) ; python_version >= \"3.11\"", "langgraph-runtime-inmem (>=0.7) ; python_version >= \"3.11\"", "python-dotenv (>=0.8.0)"]
+inmem = ["langgraph-api (>=0.5.35,<0.7.0) ; python_version >= \"3.11\"", "langgraph-runtime-inmem (>=0.7) ; python_version >= \"3.11\"", "python-dotenv (>=0.8.0)"]
[[package]]
name = "langgraph-prebuilt"
@@ -3746,14 +3875,14 @@ langgraph-checkpoint = ">=2.1.0,<4.0.0"
[[package]]
name = "langgraph-sdk"
-version = "0.2.10"
+version = "0.3.1"
description = "SDK for interacting with LangGraph API"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "langgraph_sdk-0.2.10-py3-none-any.whl", hash = "sha256:9aef403663726085de6851e4e50459df9562069bd316dd0261eb359f776fd0ef"},
- {file = "langgraph_sdk-0.2.10.tar.gz", hash = "sha256:ab58331504fbea28e6322037aa362929799b4e9106663ac1dbd7c5ac44558933"},
+ {file = "langgraph_sdk-0.3.1-py3-none-any.whl", hash = "sha256:0b856923bfd20bf3441ce9d03bef488aa333fb610e972618799a9d584436acad"},
+ {file = "langgraph_sdk-0.3.1.tar.gz", hash = "sha256:f6dadfd2444eeff3e01405a9005c95fb3a028d4bd954ebec80ea6150084f92bb"},
]
[package.dependencies]
@@ -3762,14 +3891,14 @@ orjson = ">=3.10.1"
[[package]]
name = "langsmith"
-version = "0.4.49"
+version = "0.5.1"
description = "Client library to connect to the LangSmith Observability and Evaluation Platform."
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "langsmith-0.4.49-py3-none-any.whl", hash = "sha256:95f84edcd8e74ed658e4a3eb7355b530f35cb08a9a8865dbfde6740e4b18323c"},
- {file = "langsmith-0.4.49.tar.gz", hash = "sha256:4a16ef6f3a9b20c5471884991a12ff37d81f2c13a50660cfe27fa79a7ca2c1b0"},
+ {file = "langsmith-0.5.1-py3-none-any.whl", hash = "sha256:70aa2a4c75add3f723c3bbac80dbb8adc575077834d3a733ee1ec133206ff351"},
+ {file = "langsmith-0.5.1.tar.gz", hash = "sha256:6a10b38cb4ce58941b7f1dbdf41a461868605dd0162bf05d17690f2e4b6e50e7"},
]
[package.dependencies]
@@ -3779,6 +3908,7 @@ packaging = ">=23.2"
pydantic = ">=1,<3"
requests = ">=2.0.0"
requests-toolbelt = ">=1.0.0"
+uuid-utils = ">=0.12.0,<1.0"
zstandard = ">=0.23.0"
[package.extras]
@@ -3791,25 +3921,28 @@ vcr = ["vcrpy (>=7.0.0)"]
[[package]]
name = "litellm"
-version = "1.80.7"
+version = "1.80.11"
description = "Library to easily interface with LLM API providers"
optional = false
python-versions = "<4.0,>=3.9"
groups = ["main"]
files = [
- {file = "litellm-1.80.7-py3-none-any.whl", hash = "sha256:f7d993f78c1e0e4e1202b2a925cc6540b55b6e5fb055dd342d88b145ab3102ed"},
- {file = "litellm-1.80.7.tar.gz", hash = "sha256:3977a8d195aef842d01c18bf9e22984829363c6a4b54daf9a43c9dd9f190b42c"},
+ {file = "litellm-1.80.11-py3-none-any.whl", hash = "sha256:406283d66ead77dc7ff0e0b2559c80e9e497d8e7c2257efb1cb9210a20d09d54"},
+ {file = "litellm-1.80.11.tar.gz", hash = "sha256:c9fc63e7acb6360363238fe291bcff1488c59ff66020416d8376c0ee56414a19"},
]
[package.dependencies]
aiohttp = ">=3.10"
click = "*"
fastuuid = ">=0.13.0"
-grpcio = ">=1.62.3,<1.68.0"
+grpcio = [
+ {version = ">=1.62.3,<1.68.0", markers = "python_version < \"3.14\""},
+ {version = ">=1.75.0", markers = "python_version >= \"3.14\""},
+]
httpx = ">=0.23.0"
importlib-metadata = ">=6.8.0"
jinja2 = ">=3.1.2,<4.0.0"
-jsonschema = ">=4.22.0,<5.0.0"
+jsonschema = ">=4.23.0,<5.0.0"
openai = ">=2.8.0"
pydantic = ">=2.5.0,<3.0.0"
python-dotenv = ">=0.2.0"
@@ -3820,7 +3953,7 @@ tokenizers = "*"
caching = ["diskcache (>=5.6.1,<6.0.0)"]
extra-proxy = ["azure-identity (>=1.15.0,<2.0.0) ; python_version >= \"3.9\"", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-iam (>=2.19.1,<3.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "redisvl (>=0.4.1,<0.5.0) ; python_version >= \"3.9\" and python_version < \"3.14\"", "resend (>=0.8.0)"]
mlflow = ["mlflow (>3.1.4) ; python_version >= \"3.10\""]
-proxy = ["PyJWT (>=2.10.1,<3.0.0) ; python_version >= \"3.9\"", "apscheduler (>=3.10.4,<4.0.0)", "azure-identity (>=1.15.0,<2.0.0) ; python_version >= \"3.9\"", "azure-storage-blob (>=12.25.1,<13.0.0)", "backoff", "boto3 (==1.36.0)", "cryptography", "fastapi (>=0.120.1)", "fastapi-sso (>=0.16.0,<0.17.0)", "gunicorn (>=23.0.0,<24.0.0)", "litellm-enterprise (==0.1.22)", "litellm-proxy-extras (==0.4.9)", "mcp (>=1.21.2,<2.0.0) ; python_version >= \"3.10\"", "orjson (>=3.9.7,<4.0.0)", "polars (>=1.31.0,<2.0.0) ; python_version >= \"3.10\"", "pynacl (>=1.5.0,<2.0.0)", "python-multipart (>=0.0.18,<0.0.19)", "pyyaml (>=6.0.1,<7.0.0)", "rich (==13.7.1)", "rq", "soundfile (>=0.12.1,<0.13.0)", "uvicorn (>=0.31.1,<0.32.0)", "uvloop (>=0.21.0,<0.22.0) ; sys_platform != \"win32\"", "websockets (>=15.0.1,<16.0.0)"]
+proxy = ["PyJWT (>=2.10.1,<3.0.0) ; python_version >= \"3.9\"", "apscheduler (>=3.10.4,<4.0.0)", "azure-identity (>=1.15.0,<2.0.0) ; python_version >= \"3.9\"", "azure-storage-blob (>=12.25.1,<13.0.0)", "backoff", "boto3 (==1.36.0)", "cryptography", "fastapi (>=0.120.1)", "fastapi-sso (>=0.16.0,<0.17.0)", "gunicorn (>=23.0.0,<24.0.0)", "litellm-enterprise (==0.1.27)", "litellm-proxy-extras (==0.4.16)", "mcp (>=1.21.2,<2.0.0) ; python_version >= \"3.10\"", "orjson (>=3.9.7,<4.0.0)", "polars (>=1.31.0,<2.0.0) ; python_version >= \"3.10\"", "pynacl (>=1.5.0,<2.0.0)", "python-multipart (>=0.0.18,<0.0.19)", "pyyaml (>=6.0.1,<7.0.0)", "rich (==13.7.1)", "rq", "soundfile (>=0.12.1,<0.13.0)", "uvicorn (>=0.31.1,<0.32.0)", "uvloop (>=0.21.0,<0.22.0) ; sys_platform != \"win32\"", "websockets (>=15.0.1,<16.0.0)"]
semantic-router = ["semantic-router (>=0.1.12) ; python_version >= \"3.9\" and python_version < \"3.14\""]
utils = ["numpydoc"]
@@ -3872,14 +4005,14 @@ wsgi = ["opentelemetry-instrumentation-wsgi (>=0.42b0)"]
[[package]]
name = "logfire-api"
-version = "4.15.1"
+version = "4.16.0"
description = "Shim for the Logfire SDK which does nothing unless Logfire is installed"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
- {file = "logfire_api-4.15.1-py3-none-any.whl", hash = "sha256:a88b5c4b6e4acbf6f35a3e992a63f271cf2797aefd21e1cfc93d52b21ade65f6"},
- {file = "logfire_api-4.15.1.tar.gz", hash = "sha256:3fbafc5593f4a16a038a3d23c67a7a7ee9da8be9e3b148fa73069d32e1ed4e8e"},
+ {file = "logfire_api-4.16.0-py3-none-any.whl", hash = "sha256:7351153c35cb61f0f89d2d4123ebf99b5469d70ef34c613a5ce56f85bf1b14fb"},
+ {file = "logfire_api-4.16.0.tar.gz", hash = "sha256:0efa62f5e73abdea670b5e9384c841b544474207110a089536a0fa8704f9e386"},
]
[[package]]
@@ -3901,6 +4034,104 @@ win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
[package.extras]
dev = ["Sphinx (==8.1.3) ; python_version >= \"3.11\"", "build (==1.2.2) ; python_version >= \"3.11\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.5.0) ; python_version >= \"3.8\"", "mypy (==v0.910) ; python_version < \"3.6\"", "mypy (==v0.971) ; python_version == \"3.6\"", "mypy (==v1.13.0) ; python_version >= \"3.8\"", "mypy (==v1.4.1) ; python_version == \"3.7\"", "myst-parser (==4.0.0) ; python_version >= \"3.11\"", "pre-commit (==4.0.1) ; python_version >= \"3.9\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==8.3.2) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==5.0.0) ; python_version == \"3.8\"", "pytest-cov (==6.0.0) ; python_version >= \"3.9\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.1.0) ; python_version >= \"3.8\"", "sphinx-rtd-theme (==3.0.2) ; python_version >= \"3.11\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.23.2) ; python_version >= \"3.8\"", "twine (==6.0.1) ; python_version >= \"3.11\""]
+[[package]]
+name = "lupa"
+version = "2.6"
+description = "Python wrapper around Lua and LuaJIT"
+optional = false
+python-versions = "*"
+groups = ["main"]
+files = [
+ {file = "lupa-2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6b3dabda836317e63c5ad052826e156610f356a04b3003dfa0dbe66b5d54d671"},
+ {file = "lupa-2.6-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8726d1c123bbe9fbb974ce29825e94121824e66003038ff4532c14cc2ed0c51c"},
+ {file = "lupa-2.6-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:f4e159e7d814171199b246f9235ca8961f6461ea8c1165ab428afa13c9289a94"},
+ {file = "lupa-2.6-cp310-cp310-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:202160e80dbfddfb79316692a563d843b767e0f6787bbd1c455f9d54052efa6c"},
+ {file = "lupa-2.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5deede7c5b36ab64f869dae4831720428b67955b0bb186c8349cf6ea121c852b"},
+ {file = "lupa-2.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86f04901f920bbf7c0cac56807dc9597e42347123e6f1f3ca920f15f54188ce5"},
+ {file = "lupa-2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6deef8f851d6afb965c84849aa5b8c38856942df54597a811ce0369ced678610"},
+ {file = "lupa-2.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:21f2b5549681c2a13b1170a26159d30875d367d28f0247b81ca347222c755038"},
+ {file = "lupa-2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:66eea57630eab5e6f49fdc5d7811c0a2a41f2011be4ea56a087ea76112011eb7"},
+ {file = "lupa-2.6-cp310-cp310-win32.whl", hash = "sha256:60a403de8cab262a4fe813085dd77010effa6e2eb1886db2181df803140533b1"},
+ {file = "lupa-2.6-cp310-cp310-win_amd64.whl", hash = "sha256:e4656a39d93dfa947cf3db56dc16c7916cb0cc8024acd3a952071263f675df64"},
+ {file = "lupa-2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6d988c0f9331b9f2a5a55186701a25444ab10a1432a1021ee58011499ecbbdd5"},
+ {file = "lupa-2.6-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:ebe1bbf48259382c72a6fe363dea61a0fd6fe19eab95e2ae881e20f3654587bf"},
+ {file = "lupa-2.6-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:a8fcee258487cf77cdd41560046843bb38c2e18989cd19671dd1e2596f798306"},
+ {file = "lupa-2.6-cp311-cp311-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:561a8e3be800827884e767a694727ed8482d066e0d6edfcbf423b05e63b05535"},
+ {file = "lupa-2.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af880a62d47991cae78b8e9905c008cbfdc4a3a9723a66310c2634fc7644578c"},
+ {file = "lupa-2.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80b22923aa4023c86c0097b235615f89d469a0c4eee0489699c494d3367c4c85"},
+ {file = "lupa-2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:153d2cc6b643f7efb9cfc0c6bb55ec784d5bac1a3660cfc5b958a7b8f38f4a75"},
+ {file = "lupa-2.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3fa8777e16f3ded50b72967dc17e23f5a08e4f1e2c9456aff2ebdb57f5b2869f"},
+ {file = "lupa-2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8dbdcbe818c02a2f56f5ab5ce2de374dab03e84b25266cfbaef237829bc09b3f"},
+ {file = "lupa-2.6-cp311-cp311-win32.whl", hash = "sha256:defaf188fde8f7a1e5ce3a5e6d945e533b8b8d547c11e43b96c9b7fe527f56dc"},
+ {file = "lupa-2.6-cp311-cp311-win_amd64.whl", hash = "sha256:9505ae600b5c14f3e17e70f87f88d333717f60411faca1ddc6f3e61dce85fa9e"},
+ {file = "lupa-2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47ce718817ef1cc0c40d87c3d5ae56a800d61af00fbc0fad1ca9be12df2f3b56"},
+ {file = "lupa-2.6-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:7aba985b15b101495aa4b07112cdc08baa0c545390d560ad5cfde2e9e34f4d58"},
+ {file = "lupa-2.6-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:b766f62f95b2739f2248977d29b0722e589dcf4f0ccfa827ccbd29f0148bd2e5"},
+ {file = "lupa-2.6-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:00a934c23331f94cb51760097ebfab14b005d55a6b30a2b480e3c53dd2fa290d"},
+ {file = "lupa-2.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21de9f38bd475303e34a042b7081aabdf50bd9bafd36ce4faea2f90fd9f15c31"},
+ {file = "lupa-2.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf3bda96d3fc41237e964a69c23647d50d4e28421111360274d4799832c560e9"},
+ {file = "lupa-2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a76ead245da54801a81053794aa3975f213221f6542d14ec4b859ee2e7e0323"},
+ {file = "lupa-2.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8dd0861741caa20886ddbda0a121d8e52fb9b5bb153d82fa9bba796962bf30e8"},
+ {file = "lupa-2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:239e63948b0b23023f81d9a19a395e768ed3da6a299f84e7963b8f813f6e3f9c"},
+ {file = "lupa-2.6-cp312-cp312-win32.whl", hash = "sha256:325894e1099499e7a6f9c351147661a2011887603c71086d36fe0f964d52d1ce"},
+ {file = "lupa-2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c735a1ce8ee60edb0fe71d665f1e6b7c55c6021f1d340eb8c865952c602cd36f"},
+ {file = "lupa-2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:663a6e58a0f60e7d212017d6678639ac8df0119bc13c2145029dcba084391310"},
+ {file = "lupa-2.6-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:d1f5afda5c20b1f3217a80e9bc1b77037f8a6eb11612fd3ada19065303c8f380"},
+ {file = "lupa-2.6-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:26f2b3c085fe76e9119e48c1013c1cccdc1f51585d456858290475aa38e7089e"},
+ {file = "lupa-2.6-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:60d2f902c7b96fb8ab98493dcff315e7bb4d0b44dc9dd76eb37de575025d5685"},
+ {file = "lupa-2.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a02d25dee3a3250967c36590128d9220ae02f2eda166a24279da0b481519cbff"},
+ {file = "lupa-2.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eae1ee16b886b8914ff292dbefbf2f48abfbdee94b33a88d1d5475e02423203"},
+ {file = "lupa-2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0edd5073a4ee74ab36f74fe61450148e6044f3952b8d21248581f3c5d1a58be"},
+ {file = "lupa-2.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c53ee9f22a8a17e7d4266ad48e86f43771951797042dd51d1494aaa4f5f3f0a"},
+ {file = "lupa-2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de7c0f157a9064a400d828789191a96da7f4ce889969a588b87ec80de9b14772"},
+ {file = "lupa-2.6-cp313-cp313-win32.whl", hash = "sha256:ee9523941ae0a87b5b703417720c5d78f72d2f5bc23883a2ea80a949a3ed9e75"},
+ {file = "lupa-2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b1335a5835b0a25ebdbc75cf0bda195e54d133e4d994877ef025e218c2e59db9"},
+ {file = "lupa-2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dcb6d0a3264873e1653bc188499f48c1fb4b41a779e315eba45256cfe7bc33c1"},
+ {file = "lupa-2.6-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:a37e01f2128f8c36106726cb9d360bac087d58c54b4522b033cc5691c584db18"},
+ {file = "lupa-2.6-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:458bd7e9ff3c150b245b0fcfbb9bd2593d1152ea7f0a7b91c1d185846da033fe"},
+ {file = "lupa-2.6-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:052ee82cac5206a02df77119c325339acbc09f5ce66967f66a2e12a0f3211cad"},
+ {file = "lupa-2.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96594eca3c87dd07938009e95e591e43d554c1dbd0385be03c100367141db5a8"},
+ {file = "lupa-2.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8faddd9d198688c8884091173a088a8e920ecc96cda2ffed576a23574c4b3f6"},
+ {file = "lupa-2.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:daebb3a6b58095c917e76ba727ab37b27477fb926957c825205fbda431552134"},
+ {file = "lupa-2.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f3154e68972befe0f81564e37d8142b5d5d79931a18309226a04ec92487d4ea3"},
+ {file = "lupa-2.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e4dadf77b9fedc0bfa53417cc28dc2278a26d4cbd95c29f8927ad4d8fe0a7ef9"},
+ {file = "lupa-2.6-cp314-cp314-win32.whl", hash = "sha256:cb34169c6fa3bab3e8ac58ca21b8a7102f6a94b6a5d08d3636312f3f02fafd8f"},
+ {file = "lupa-2.6-cp314-cp314-win_amd64.whl", hash = "sha256:b74f944fe46c421e25d0f8692aef1e842192f6f7f68034201382ac440ef9ea67"},
+ {file = "lupa-2.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0e21b716408a21ab65723f8841cf7f2f37a844b7a965eeabb785e27fca4099cf"},
+ {file = "lupa-2.6-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:589db872a141bfff828340079bbdf3e9a31f2689f4ca0d88f97d9e8c2eae6142"},
+ {file = "lupa-2.6-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:cd852a91a4a9d4dcbb9a58100f820a75a425703ec3e3f049055f60b8533b7953"},
+ {file = "lupa-2.6-cp314-cp314t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:0334753be028358922415ca97a64a3048e4ed155413fc4eaf87dd0a7e2752983"},
+ {file = "lupa-2.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:661d895cd38c87658a34780fac54a690ec036ead743e41b74c3fb81a9e65a6aa"},
+ {file = "lupa-2.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aa58454ccc13878cc177c62529a2056be734da16369e451987ff92784994ca7"},
+ {file = "lupa-2.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1425017264e470c98022bba8cff5bd46d054a827f5df6b80274f9cc71dafd24f"},
+ {file = "lupa-2.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:224af0532d216e3105f0a127410f12320f7c5f1aa0300bdf9646b8d9afb0048c"},
+ {file = "lupa-2.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9abb98d5a8fd27c8285302e82199f0e56e463066f88f619d6594a450bf269d80"},
+ {file = "lupa-2.6-cp314-cp314t-win32.whl", hash = "sha256:1849efeba7a8f6fb8aa2c13790bee988fd242ae404bd459509640eeea3d1e291"},
+ {file = "lupa-2.6-cp314-cp314t-win_amd64.whl", hash = "sha256:fc1498d1a4fc028bc521c26d0fad4ca00ed63b952e32fb95949bda76a04bad52"},
+ {file = "lupa-2.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9591700991e333b70dd92b48f152eb4731b8b24af671a9f6f721b74d68ed4499"},
+ {file = "lupa-2.6-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:ef8dfa7fe08bc3f4591411b8945bbeb15af8512c3e7ad5e9b1e3a9036cdbbce7"},
+ {file = "lupa-2.6-cp38-cp38-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:728c466e91174dad238f8a9c1cbdb8e69ffe559df85f87ee76edac3395300949"},
+ {file = "lupa-2.6-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c781170bc7134704ae317a66204d30688b41d3e471e17e659987ea4947e11f20"},
+ {file = "lupa-2.6-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241f4ddab33b9a686fc76667241bebc39a06b74ec40d79ec222f5add9000fe57"},
+ {file = "lupa-2.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:c17f6b6193ced33cc7ca0b2b08b319a1b3501b014a3a3f9999c01cafc04c40f5"},
+ {file = "lupa-2.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:fa6c1379e83d4104065c151736250a09f3a99e368423c7a20f9c59b15945e9fc"},
+ {file = "lupa-2.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aef1a8bc10c50695e1a33a07dbef803b93eb97fc150fdb19858d704a603a67dd"},
+ {file = "lupa-2.6-cp38-cp38-win32.whl", hash = "sha256:10c191bc1d5565e4360d884bea58320975ddb33270cdf9a9f55d1a1efe79aa03"},
+ {file = "lupa-2.6-cp38-cp38-win_amd64.whl", hash = "sha256:05681f8ffb41f0c7fbb9ca859cc3a7e4006e9c6350d25358b535c5295c6a9928"},
+ {file = "lupa-2.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8897dc6c3249786b2cdf2f83324febb436193d4581b6a71dea49f77bf8b19bb0"},
+ {file = "lupa-2.6-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:4446396ca3830be0c106c70db4b4f622c37b2d447874c07952cafb9c57949a4a"},
+ {file = "lupa-2.6-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:5826e687c89995a6eaafeae242071ba16448eec1a9ee8e17ed48551b5d1e21c2"},
+ {file = "lupa-2.6-cp39-cp39-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:5871935cb36d1d22f9c04ac0db75c06751bd95edcfa0d9309f732de908e297a9"},
+ {file = "lupa-2.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:43eb6e43ea8512d0d65b995d36dd9d77aa02598035e25b84c23a1b58700c9fb2"},
+ {file = "lupa-2.6-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:559714053018d9885cc8c36a33c5b7eb9aad30fb6357719cac3ce4dc6b39157e"},
+ {file = "lupa-2.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:57ac88a00ce59bd9d4ddcd4fca8e02564765725f5068786b011c9d1be3de20c5"},
+ {file = "lupa-2.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:b683fbd867c2e54c44a686361b75eee7e7a790da55afdbe89f1f23b106de0274"},
+ {file = "lupa-2.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d2f656903a2ed2e074bf2b7d300968028dfa327a45b055be8e3b51ef0b82f9bf"},
+ {file = "lupa-2.6-cp39-cp39-win32.whl", hash = "sha256:bf28f68ae231b72008523ab5ac23835ba0f76e0e99ec38b59766080a84eb596a"},
+ {file = "lupa-2.6-cp39-cp39-win_amd64.whl", hash = "sha256:b4b2e9b3795a9897cf6cfcc58d08210fdc0d13ab47c9a0e13858c68932d8353c"},
+ {file = "lupa-2.6.tar.gz", hash = "sha256:9a770a6e89576be3447668d7ced312cd6fd41d3c13c2462c9dc2c2ab570e45d9"},
+]
+
[[package]]
name = "lxml"
version = "6.0.2"
@@ -4216,14 +4447,14 @@ files = [
[[package]]
name = "marshmallow"
-version = "3.26.1"
+version = "3.26.2"
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"},
- {file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"},
+ {file = "marshmallow-3.26.2-py3-none-any.whl", hash = "sha256:013fa8a3c4c276c24d26d84ce934dc964e2aa794345a0f8c7e5a7191482c8a73"},
+ {file = "marshmallow-3.26.2.tar.gz", hash = "sha256:bbe2adb5a03e6e3571b573f42527c6fe926e17467833660bebd11593ab8dfd57"},
]
[package.dependencies]
@@ -4254,14 +4485,14 @@ test = ["flake8", "nbdime", "nbval", "notebook", "pytest"]
[[package]]
name = "mcp"
-version = "1.22.0"
+version = "1.25.0"
description = "Model Context Protocol SDK"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "mcp-1.22.0-py3-none-any.whl", hash = "sha256:bed758e24df1ed6846989c909ba4e3df339a27b4f30f1b8b627862a4bade4e98"},
- {file = "mcp-1.22.0.tar.gz", hash = "sha256:769b9ac90ed42134375b19e777a2858ca300f95f2e800982b3e2be62dfc0ba01"},
+ {file = "mcp-1.25.0-py3-none-any.whl", hash = "sha256:b37c38144a666add0862614cc79ec276e97d72aa8ca26d622818d4e278b9721a"},
+ {file = "mcp-1.25.0.tar.gz", hash = "sha256:56310361ebf0364e2d438e5b45f7668cbb124e158bb358333cd06e49e83a6802"},
]
[package.dependencies]
@@ -4340,14 +4571,14 @@ gcp = ["google-auth (>=2.27.0)", "requests (>=2.32.3)"]
[[package]]
name = "mistune"
-version = "3.1.4"
+version = "3.2.0"
description = "A sane and fast Markdown parser with useful plugins and renderers"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
- {file = "mistune-3.1.4-py3-none-any.whl", hash = "sha256:93691da911e5d9d2e23bc54472892aff676df27a75274962ff9edc210364266d"},
- {file = "mistune-3.1.4.tar.gz", hash = "sha256:b5a7f801d389f724ec702840c11d8fc48f2b33519102fc7ee739e8177b672164"},
+ {file = "mistune-3.2.0-py3-none-any.whl", hash = "sha256:febdc629a3c78616b94393c6580551e0e34cc289987ec6c35ed3f4be42d0eee1"},
+ {file = "mistune-3.2.0.tar.gz", hash = "sha256:708487c8a8cdd99c9d90eb3ed4c3ed961246ff78ac82f03418f5183ab70e398a"},
]
[[package]]
@@ -4585,14 +4816,14 @@ twitter = ["twython"]
[[package]]
name = "nodeenv"
-version = "1.9.1"
+version = "1.10.0"
description = "Node.js virtual environment builder"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
groups = ["dev"]
files = [
- {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
- {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
+ {file = "nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827"},
+ {file = "nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb"},
]
[[package]]
@@ -4710,14 +4941,14 @@ pydantic = ">=2.9"
[[package]]
name = "openai"
-version = "2.8.1"
+version = "2.14.0"
description = "The official Python library for the openai API"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "openai-2.8.1-py3-none-any.whl", hash = "sha256:c6c3b5a04994734386e8dad3c00a393f56d3b68a27cd2e8acae91a59e4122463"},
- {file = "openai-2.8.1.tar.gz", hash = "sha256:cb1b79eef6e809f6da326a7ef6038719e35aa944c42d081807bfa1be8060f15f"},
+ {file = "openai-2.14.0-py3-none-any.whl", hash = "sha256:7ea40aca4ffc4c4a776e77679021b47eec1160e341f42ae086ba949c9dcc9183"},
+ {file = "openai-2.14.0.tar.gz", hash = "sha256:419357bedde9402d23bf8f2ee372fca1985a73348debba94bddff06f19459952"},
]
[package.dependencies]
@@ -4803,6 +5034,23 @@ opentelemetry-sdk = ">=1.35.0,<1.36.0"
requests = ">=2.7,<3.0"
typing-extensions = ">=4.5.0"
+[[package]]
+name = "opentelemetry-exporter-prometheus"
+version = "0.56b0"
+description = "Prometheus Metric Exporter for OpenTelemetry"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "opentelemetry_exporter_prometheus-0.56b0-py3-none-any.whl", hash = "sha256:0a7d8952e2938f830deb5000505eecf20416ef25fdb591753b9b0cb91d9560ea"},
+ {file = "opentelemetry_exporter_prometheus-0.56b0.tar.gz", hash = "sha256:c783146b66400269783b773c5ccedf7d2ff140271478e2816202930a14c07c6b"},
+]
+
+[package.dependencies]
+opentelemetry-api = ">=1.12,<2.0"
+opentelemetry-sdk = ">=1.35.0,<1.36.0"
+prometheus-client = ">=0.5.0,<1.0.0"
+
[[package]]
name = "opentelemetry-instrumentation"
version = "0.56b0"
@@ -4905,157 +5153,157 @@ files = [
[[package]]
name = "orjson"
-version = "3.11.4"
+version = "3.11.5"
description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "orjson-3.11.4-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e3aa2118a3ece0d25489cbe48498de8a5d580e42e8d9979f65bf47900a15aba1"},
- {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a69ab657a4e6733133a3dca82768f2f8b884043714e8d2b9ba9f52b6efef5c44"},
- {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3740bffd9816fc0326ddc406098a3a8f387e42223f5f455f2a02a9f834ead80c"},
- {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65fd2f5730b1bf7f350c6dc896173d3460d235c4be007af73986d7cd9a2acd23"},
- {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fdc3ae730541086158d549c97852e2eea6820665d4faf0f41bf99df41bc11ea"},
- {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e10b4d65901da88845516ce9f7f9736f9638d19a1d483b3883dc0182e6e5edba"},
- {file = "orjson-3.11.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb6a03a678085f64b97f9d4a9ae69376ce91a3a9e9b56a82b1580d8e1d501aff"},
- {file = "orjson-3.11.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c82e4f0b1c712477317434761fbc28b044c838b6b1240d895607441412371ac"},
- {file = "orjson-3.11.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d58c166a18f44cc9e2bad03a327dc2d1a3d2e85b847133cfbafd6bfc6719bd79"},
- {file = "orjson-3.11.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94f206766bf1ea30e1382e4890f763bd1eefddc580e08fec1ccdc20ddd95c827"},
- {file = "orjson-3.11.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:41bf25fb39a34cf8edb4398818523277ee7096689db352036a9e8437f2f3ee6b"},
- {file = "orjson-3.11.4-cp310-cp310-win32.whl", hash = "sha256:fa9627eba4e82f99ca6d29bc967f09aba446ee2b5a1ea728949ede73d313f5d3"},
- {file = "orjson-3.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:23ef7abc7fca96632d8174ac115e668c1e931b8fe4dde586e92a500bf1914dcc"},
- {file = "orjson-3.11.4-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5e59d23cd93ada23ec59a96f215139753fbfe3a4d989549bcb390f8c00370b39"},
- {file = "orjson-3.11.4-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5c3aedecfc1beb988c27c79d52ebefab93b6c3921dbec361167e6559aba2d36d"},
- {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da9e5301f1c2caa2a9a4a303480d79c9ad73560b2e7761de742ab39fe59d9175"},
- {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8873812c164a90a79f65368f8f96817e59e35d0cc02786a5356f0e2abed78040"},
- {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d7feb0741ebb15204e748f26c9638e6665a5fa93c37a2c73d64f1669b0ddc63"},
- {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ee5487fefee21e6910da4c2ee9eef005bee568a0879834df86f888d2ffbdd9"},
- {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d40d46f348c0321df01507f92b95a377240c4ec31985225a6668f10e2676f9a"},
- {file = "orjson-3.11.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95713e5fc8af84d8edc75b785d2386f653b63d62b16d681687746734b4dfc0be"},
- {file = "orjson-3.11.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad73ede24f9083614d6c4ca9a85fe70e33be7bf047ec586ee2363bc7418fe4d7"},
- {file = "orjson-3.11.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:842289889de515421f3f224ef9c1f1efb199a32d76d8d2ca2706fa8afe749549"},
- {file = "orjson-3.11.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3b2427ed5791619851c52a1261b45c233930977e7de8cf36de05636c708fa905"},
- {file = "orjson-3.11.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c36e524af1d29982e9b190573677ea02781456b2e537d5840e4538a5ec41907"},
- {file = "orjson-3.11.4-cp311-cp311-win32.whl", hash = "sha256:87255b88756eab4a68ec61837ca754e5d10fa8bc47dc57f75cedfeaec358d54c"},
- {file = "orjson-3.11.4-cp311-cp311-win_amd64.whl", hash = "sha256:e2d5d5d798aba9a0e1fede8d853fa899ce2cb930ec0857365f700dffc2c7af6a"},
- {file = "orjson-3.11.4-cp311-cp311-win_arm64.whl", hash = "sha256:6bb6bb41b14c95d4f2702bce9975fda4516f1db48e500102fc4d8119032ff045"},
- {file = "orjson-3.11.4-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d4371de39319d05d3f482f372720b841c841b52f5385bd99c61ed69d55d9ab50"},
- {file = "orjson-3.11.4-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e41fd3b3cac850eaae78232f37325ed7d7436e11c471246b87b2cd294ec94853"},
- {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:600e0e9ca042878c7fdf189cf1b028fe2c1418cc9195f6cb9824eb6ed99cb938"},
- {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7bbf9b333f1568ef5da42bc96e18bf30fd7f8d54e9ae066d711056add508e415"},
- {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4806363144bb6e7297b8e95870e78d30a649fdc4e23fc84daa80c8ebd366ce44"},
- {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad355e8308493f527d41154e9053b86a5be892b3b359a5c6d5d95cda23601cb2"},
- {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a7517482667fb9f0ff1b2f16fe5829296ed7a655d04d68cd9711a4d8a4e708"},
- {file = "orjson-3.11.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97eb5942c7395a171cbfecc4ef6701fc3c403e762194683772df4c54cfbb2210"},
- {file = "orjson-3.11.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:149d95d5e018bdd822e3f38c103b1a7c91f88d38a88aada5c4e9b3a73a244241"},
- {file = "orjson-3.11.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:624f3951181eb46fc47dea3d221554e98784c823e7069edb5dbd0dc826ac909b"},
- {file = "orjson-3.11.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:03bfa548cf35e3f8b3a96c4e8e41f753c686ff3d8e182ce275b1751deddab58c"},
- {file = "orjson-3.11.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:525021896afef44a68148f6ed8a8bf8375553d6066c7f48537657f64823565b9"},
- {file = "orjson-3.11.4-cp312-cp312-win32.whl", hash = "sha256:b58430396687ce0f7d9eeb3dd47761ca7d8fda8e9eb92b3077a7a353a75efefa"},
- {file = "orjson-3.11.4-cp312-cp312-win_amd64.whl", hash = "sha256:c6dbf422894e1e3c80a177133c0dda260f81428f9de16d61041949f6a2e5c140"},
- {file = "orjson-3.11.4-cp312-cp312-win_arm64.whl", hash = "sha256:d38d2bc06d6415852224fcc9c0bfa834c25431e466dc319f0edd56cca81aa96e"},
- {file = "orjson-3.11.4-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2d6737d0e616a6e053c8b4acc9eccea6b6cce078533666f32d140e4f85002534"},
- {file = "orjson-3.11.4-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:afb14052690aa328cc118a8e09f07c651d301a72e44920b887c519b313d892ff"},
- {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38aa9e65c591febb1b0aed8da4d469eba239d434c218562df179885c94e1a3ad"},
- {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f2cf4dfaf9163b0728d061bebc1e08631875c51cd30bf47cb9e3293bfbd7dcd5"},
- {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89216ff3dfdde0e4070932e126320a1752c9d9a758d6a32ec54b3b9334991a6a"},
- {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9daa26ca8e97fae0ce8aa5d80606ef8f7914e9b129b6b5df9104266f764ce436"},
- {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c8b2769dc31883c44a9cd126560327767f848eb95f99c36c9932f51090bfce9"},
- {file = "orjson-3.11.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1469d254b9884f984026bd9b0fa5bbab477a4bfe558bba6848086f6d43eb5e73"},
- {file = "orjson-3.11.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:68e44722541983614e37117209a194e8c3ad07838ccb3127d96863c95ec7f1e0"},
- {file = "orjson-3.11.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8e7805fda9672c12be2f22ae124dcd7b03928d6c197544fe12174b86553f3196"},
- {file = "orjson-3.11.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:04b69c14615fb4434ab867bf6f38b2d649f6f300af30a6705397e895f7aec67a"},
- {file = "orjson-3.11.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:639c3735b8ae7f970066930e58cf0ed39a852d417c24acd4a25fc0b3da3c39a6"},
- {file = "orjson-3.11.4-cp313-cp313-win32.whl", hash = "sha256:6c13879c0d2964335491463302a6ca5ad98105fc5db3565499dcb80b1b4bd839"},
- {file = "orjson-3.11.4-cp313-cp313-win_amd64.whl", hash = "sha256:09bf242a4af98732db9f9a1ec57ca2604848e16f132e3f72edfd3c5c96de009a"},
- {file = "orjson-3.11.4-cp313-cp313-win_arm64.whl", hash = "sha256:a85f0adf63319d6c1ba06fb0dbf997fced64a01179cf17939a6caca662bf92de"},
- {file = "orjson-3.11.4-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:42d43a1f552be1a112af0b21c10a5f553983c2a0938d2bbb8ecd8bc9fb572803"},
- {file = "orjson-3.11.4-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:26a20f3fbc6c7ff2cb8e89c4c5897762c9d88cf37330c6a117312365d6781d54"},
- {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e3f20be9048941c7ffa8fc523ccbd17f82e24df1549d1d1fe9317712d19938e"},
- {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aac364c758dc87a52e68e349924d7e4ded348dedff553889e4d9f22f74785316"},
- {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5c54a6d76e3d741dcc3f2707f8eeb9ba2a791d3adbf18f900219b62942803b1"},
- {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f28485bdca8617b79d44627f5fb04336897041dfd9fa66d383a49d09d86798bc"},
- {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bfc2a484cad3585e4ba61985a6062a4c2ed5c7925db6d39f1fa267c9d166487f"},
- {file = "orjson-3.11.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e34dbd508cb91c54f9c9788923daca129fe5b55c5b4eebe713bf5ed3791280cf"},
- {file = "orjson-3.11.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b13c478fa413d4b4ee606ec8e11c3b2e52683a640b006bb586b3041c2ca5f606"},
- {file = "orjson-3.11.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:724ca721ecc8a831b319dcd72cfa370cc380db0bf94537f08f7edd0a7d4e1780"},
- {file = "orjson-3.11.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:977c393f2e44845ce1b540e19a786e9643221b3323dae190668a98672d43fb23"},
- {file = "orjson-3.11.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1e539e382cf46edec157ad66b0b0872a90d829a6b71f17cb633d6c160a223155"},
- {file = "orjson-3.11.4-cp314-cp314-win32.whl", hash = "sha256:d63076d625babab9db5e7836118bdfa086e60f37d8a174194ae720161eb12394"},
- {file = "orjson-3.11.4-cp314-cp314-win_amd64.whl", hash = "sha256:0a54d6635fa3aaa438ae32e8570b9f0de36f3f6562c308d2a2a452e8b0592db1"},
- {file = "orjson-3.11.4-cp314-cp314-win_arm64.whl", hash = "sha256:78b999999039db3cf58f6d230f524f04f75f129ba3d1ca2ed121f8657e575d3d"},
- {file = "orjson-3.11.4-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:405261b0a8c62bcbd8e2931c26fdc08714faf7025f45531541e2b29e544b545b"},
- {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af02ff34059ee9199a3546f123a6ab4c86caf1708c79042caf0820dc290a6d4f"},
- {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b2eba969ea4203c177c7b38b36c69519e6067ee68c34dc37081fac74c796e10"},
- {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0baa0ea43cfa5b008a28d3c07705cf3ada40e5d347f0f44994a64b1b7b4b5350"},
- {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80fd082f5dcc0e94657c144f1b2a3a6479c44ad50be216cf0c244e567f5eae19"},
- {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e3704d35e47d5bee811fb1cbd8599f0b4009b14d451c4c57be5a7e25eb89a13"},
- {file = "orjson-3.11.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:caa447f2b5356779d914658519c874cf3b7629e99e63391ed519c28c8aea4919"},
- {file = "orjson-3.11.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bba5118143373a86f91dadb8df41d9457498226698ebdf8e11cbb54d5b0e802d"},
- {file = "orjson-3.11.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:622463ab81d19ef3e06868b576551587de8e4d518892d1afab71e0fbc1f9cffc"},
- {file = "orjson-3.11.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3e0a700c4b82144b72946b6629968df9762552ee1344bfdb767fecdd634fbd5a"},
- {file = "orjson-3.11.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6e18a5c15e764e5f3fc569b47872450b4bcea24f2a6354c0a0e95ad21045d5a9"},
- {file = "orjson-3.11.4-cp39-cp39-win32.whl", hash = "sha256:fb1c37c71cad991ef4d89c7a634b5ffb4447dbd7ae3ae13e8f5ee7f1775e7ab1"},
- {file = "orjson-3.11.4-cp39-cp39-win_amd64.whl", hash = "sha256:e2985ce8b8c42d00492d0ed79f2bd2b6460d00f2fa671dfde4bf2e02f49bf5c6"},
- {file = "orjson-3.11.4.tar.gz", hash = "sha256:39485f4ab4c9b30a3943cfe99e1a213c4776fb69e8abd68f66b83d5a0b0fdc6d"},
+ {file = "orjson-3.11.5-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:df9eadb2a6386d5ea2bfd81309c505e125cfc9ba2b1b99a97e60985b0b3665d1"},
+ {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc70da619744467d8f1f49a8cadae5ec7bbe054e5232d95f92ed8737f8c5870"},
+ {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:073aab025294c2f6fc0807201c76fdaed86f8fc4be52c440fb78fbb759a1ac09"},
+ {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:835f26fa24ba0bb8c53ae2a9328d1706135b74ec653ed933869b74b6909e63fd"},
+ {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667c132f1f3651c14522a119e4dd631fad98761fa960c55e8e7430bb2a1ba4ac"},
+ {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42e8961196af655bb5e63ce6c60d25e8798cd4dfbc04f4203457fa3869322c2e"},
+ {file = "orjson-3.11.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75412ca06e20904c19170f8a24486c4e6c7887dea591ba18a1ab572f1300ee9f"},
+ {file = "orjson-3.11.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6af8680328c69e15324b5af3ae38abbfcf9cbec37b5346ebfd52339c3d7e8a18"},
+ {file = "orjson-3.11.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a86fe4ff4ea523eac8f4b57fdac319faf037d3c1be12405e6a7e86b3fbc4756a"},
+ {file = "orjson-3.11.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e607b49b1a106ee2086633167033afbd63f76f2999e9236f638b06b112b24ea7"},
+ {file = "orjson-3.11.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7339f41c244d0eea251637727f016b3d20050636695bc78345cce9029b189401"},
+ {file = "orjson-3.11.5-cp310-cp310-win32.whl", hash = "sha256:8be318da8413cdbbce77b8c5fac8d13f6eb0f0db41b30bb598631412619572e8"},
+ {file = "orjson-3.11.5-cp310-cp310-win_amd64.whl", hash = "sha256:b9f86d69ae822cabc2a0f6c099b43e8733dda788405cba2665595b7e8dd8d167"},
+ {file = "orjson-3.11.5-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9c8494625ad60a923af6b2b0bd74107146efe9b55099e20d7740d995f338fcd8"},
+ {file = "orjson-3.11.5-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:7bb2ce0b82bc9fd1168a513ddae7a857994b780b2945a8c51db4ab1c4b751ebc"},
+ {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67394d3becd50b954c4ecd24ac90b5051ee7c903d167459f93e77fc6f5b4c968"},
+ {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:298d2451f375e5f17b897794bcc3e7b821c0f32b4788b9bcae47ada24d7f3cf7"},
+ {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa5e4244063db8e1d87e0f54c3f7522f14b2dc937e65d5241ef0076a096409fd"},
+ {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1db2088b490761976c1b2e956d5d4e6409f3732e9d79cfa69f876c5248d1baf9"},
+ {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2ed66358f32c24e10ceea518e16eb3549e34f33a9d51f99ce23b0251776a1ef"},
+ {file = "orjson-3.11.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2021afda46c1ed64d74b555065dbd4c2558d510d8cec5ea6a53001b3e5e82a9"},
+ {file = "orjson-3.11.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b42ffbed9128e547a1647a3e50bc88ab28ae9daa61713962e0d3dd35e820c125"},
+ {file = "orjson-3.11.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8d5f16195bb671a5dd3d1dbea758918bada8f6cc27de72bd64adfbd748770814"},
+ {file = "orjson-3.11.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c0e5d9f7a0227df2927d343a6e3859bebf9208b427c79bd31949abcc2fa32fa5"},
+ {file = "orjson-3.11.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:23d04c4543e78f724c4dfe656b3791b5f98e4c9253e13b2636f1af5d90e4a880"},
+ {file = "orjson-3.11.5-cp311-cp311-win32.whl", hash = "sha256:c404603df4865f8e0afe981aa3c4b62b406e6d06049564d58934860b62b7f91d"},
+ {file = "orjson-3.11.5-cp311-cp311-win_amd64.whl", hash = "sha256:9645ef655735a74da4990c24ffbd6894828fbfa117bc97c1edd98c282ecb52e1"},
+ {file = "orjson-3.11.5-cp311-cp311-win_arm64.whl", hash = "sha256:1cbf2735722623fcdee8e712cbaaab9e372bbcb0c7924ad711b261c2eccf4a5c"},
+ {file = "orjson-3.11.5-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:334e5b4bff9ad101237c2d799d9fd45737752929753bf4faf4b207335a416b7d"},
+ {file = "orjson-3.11.5-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:ff770589960a86eae279f5d8aa536196ebda8273a2a07db2a54e82b93bc86626"},
+ {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed24250e55efbcb0b35bed7caaec8cedf858ab2f9f2201f17b8938c618c8ca6f"},
+ {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a66d7769e98a08a12a139049aac2f0ca3adae989817f8c43337455fbc7669b85"},
+ {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86cfc555bfd5794d24c6a1903e558b50644e5e68e6471d66502ce5cb5fdef3f9"},
+ {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a230065027bc2a025e944f9d4714976a81e7ecfa940923283bca7bbc1f10f626"},
+ {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b29d36b60e606df01959c4b982729c8845c69d1963f88686608be9ced96dbfaa"},
+ {file = "orjson-3.11.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c74099c6b230d4261fdc3169d50efc09abf38ace1a42ea2f9994b1d79153d477"},
+ {file = "orjson-3.11.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e697d06ad57dd0c7a737771d470eedc18e68dfdefcdd3b7de7f33dfda5b6212e"},
+ {file = "orjson-3.11.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e08ca8a6c851e95aaecc32bc44a5aa75d0ad26af8cdac7c77e4ed93acf3d5b69"},
+ {file = "orjson-3.11.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e8b5f96c05fce7d0218df3fdfeb962d6b8cfff7e3e20264306b46dd8b217c0f3"},
+ {file = "orjson-3.11.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ddbfdb5099b3e6ba6d6ea818f61997bb66de14b411357d24c4612cf1ebad08ca"},
+ {file = "orjson-3.11.5-cp312-cp312-win32.whl", hash = "sha256:9172578c4eb09dbfcf1657d43198de59b6cef4054de385365060ed50c458ac98"},
+ {file = "orjson-3.11.5-cp312-cp312-win_amd64.whl", hash = "sha256:2b91126e7b470ff2e75746f6f6ee32b9ab67b7a93c8ba1d15d3a0caaf16ec875"},
+ {file = "orjson-3.11.5-cp312-cp312-win_arm64.whl", hash = "sha256:acbc5fac7e06777555b0722b8ad5f574739e99ffe99467ed63da98f97f9ca0fe"},
+ {file = "orjson-3.11.5-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3b01799262081a4c47c035dd77c1301d40f568f77cc7ec1bb7db5d63b0a01629"},
+ {file = "orjson-3.11.5-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:61de247948108484779f57a9f406e4c84d636fa5a59e411e6352484985e8a7c3"},
+ {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:894aea2e63d4f24a7f04a1908307c738d0dce992e9249e744b8f4e8dd9197f39"},
+ {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ddc21521598dbe369d83d4d40338e23d4101dad21dae0e79fa20465dbace019f"},
+ {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cce16ae2f5fb2c53c3eafdd1706cb7b6530a67cc1c17abe8ec747f5cd7c0c51"},
+ {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e46c762d9f0e1cfb4ccc8515de7f349abbc95b59cb5a2bd68df5973fdef913f8"},
+ {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d7345c759276b798ccd6d77a87136029e71e66a8bbf2d2755cbdde1d82e78706"},
+ {file = "orjson-3.11.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75bc2e59e6a2ac1dd28901d07115abdebc4563b5b07dd612bf64260a201b1c7f"},
+ {file = "orjson-3.11.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:54aae9b654554c3b4edd61896b978568c6daa16af96fa4681c9b5babd469f863"},
+ {file = "orjson-3.11.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4bdd8d164a871c4ec773f9de0f6fe8769c2d6727879c37a9666ba4183b7f8228"},
+ {file = "orjson-3.11.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a261fef929bcf98a60713bf5e95ad067cea16ae345d9a35034e73c3990e927d2"},
+ {file = "orjson-3.11.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c028a394c766693c5c9909dec76b24f37e6a1b91999e8d0c0d5feecbe93c3e05"},
+ {file = "orjson-3.11.5-cp313-cp313-win32.whl", hash = "sha256:2cc79aaad1dfabe1bd2d50ee09814a1253164b3da4c00a78c458d82d04b3bdef"},
+ {file = "orjson-3.11.5-cp313-cp313-win_amd64.whl", hash = "sha256:ff7877d376add4e16b274e35a3f58b7f37b362abf4aa31863dadacdd20e3a583"},
+ {file = "orjson-3.11.5-cp313-cp313-win_arm64.whl", hash = "sha256:59ac72ea775c88b163ba8d21b0177628bd015c5dd060647bbab6e22da3aad287"},
+ {file = "orjson-3.11.5-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e446a8ea0a4c366ceafc7d97067bfd55292969143b57e3c846d87fc701e797a0"},
+ {file = "orjson-3.11.5-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:53deb5addae9c22bbe3739298f5f2196afa881ea75944e7720681c7080909a81"},
+ {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82cd00d49d6063d2b8791da5d4f9d20539c5951f965e45ccf4e96d33505ce68f"},
+ {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3fd15f9fc8c203aeceff4fda211157fad114dde66e92e24097b3647a08f4ee9e"},
+ {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9df95000fbe6777bf9820ae82ab7578e8662051bb5f83d71a28992f539d2cda7"},
+ {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92a8d676748fca47ade5bc3da7430ed7767afe51b2f8100e3cd65e151c0eaceb"},
+ {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa0f513be38b40234c77975e68805506cad5d57b3dfd8fe3baa7f4f4051e15b4"},
+ {file = "orjson-3.11.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa1863e75b92891f553b7922ce4ee10ed06db061e104f2b7815de80cdcb135ad"},
+ {file = "orjson-3.11.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4be86b58e9ea262617b8ca6251a2f0d63cc132a6da4b5fcc8e0a4128782c829"},
+ {file = "orjson-3.11.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:b923c1c13fa02084eb38c9c065afd860a5cff58026813319a06949c3af5732ac"},
+ {file = "orjson-3.11.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:1b6bd351202b2cd987f35a13b5e16471cf4d952b42a73c391cc537974c43ef6d"},
+ {file = "orjson-3.11.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bb150d529637d541e6af06bbe3d02f5498d628b7f98267ff87647584293ab439"},
+ {file = "orjson-3.11.5-cp314-cp314-win32.whl", hash = "sha256:9cc1e55c884921434a84a0c3dd2699eb9f92e7b441d7f53f3941079ec6ce7499"},
+ {file = "orjson-3.11.5-cp314-cp314-win_amd64.whl", hash = "sha256:a4f3cb2d874e03bc7767c8f88adaa1a9a05cecea3712649c3b58589ec7317310"},
+ {file = "orjson-3.11.5-cp314-cp314-win_arm64.whl", hash = "sha256:38b22f476c351f9a1c43e5b07d8b5a02eb24a6ab8e75f700f7d479d4568346a5"},
+ {file = "orjson-3.11.5-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1b280e2d2d284a6713b0cfec7b08918ebe57df23e3f76b27586197afca3cb1e9"},
+ {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c8d8a112b274fae8c5f0f01954cb0480137072c271f3f4958127b010dfefaec"},
+ {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0a2ae6f09ac7bd47d2d5a5305c1d9ed08ac057cda55bb0a49fa506f0d2da00"},
+ {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c0d87bd1896faac0d10b4f849016db81a63e4ec5df38757ffae84d45ab38aa71"},
+ {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:801a821e8e6099b8c459ac7540b3c32dba6013437c57fdcaec205b169754f38c"},
+ {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69a0f6ac618c98c74b7fbc8c0172ba86f9e01dbf9f62aa0b1776c2231a7bffe5"},
+ {file = "orjson-3.11.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea7339bdd22e6f1060c55ac31b6a755d86a5b2ad3657f2669ec243f8e3b2bdb"},
+ {file = "orjson-3.11.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4dad582bc93cef8f26513e12771e76385a7e6187fd713157e971c784112aad56"},
+ {file = "orjson-3.11.5-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:0522003e9f7fba91982e83a97fec0708f5a714c96c4209db7104e6b9d132f111"},
+ {file = "orjson-3.11.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7403851e430a478440ecc1258bcbacbfbd8175f9ac1e39031a7121dd0de05ff8"},
+ {file = "orjson-3.11.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5f691263425d3177977c8d1dd896cde7b98d93cbf390b2544a090675e83a6a0a"},
+ {file = "orjson-3.11.5-cp39-cp39-win32.whl", hash = "sha256:61026196a1c4b968e1b1e540563e277843082e9e97d78afa03eb89315af531f1"},
+ {file = "orjson-3.11.5-cp39-cp39-win_amd64.whl", hash = "sha256:09b94b947ac08586af635ef922d69dc9bc63321527a3a04647f4986a73f4bd30"},
+ {file = "orjson-3.11.5.tar.gz", hash = "sha256:82393ab47b4fe44ffd0a7659fa9cfaacc717eb617c93cde83795f14af5c2e9d5"},
]
[[package]]
name = "ormsgpack"
-version = "1.12.0"
-description = ""
+version = "1.12.1"
+description = "Fast, correct Python msgpack library supporting dataclasses, datetimes, and numpy"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "ormsgpack-1.12.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e08904c232358b94a682ccfbb680bc47d3fd5c424bb7dccb65974dd20c95e8e1"},
- {file = "ormsgpack-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9ed7a4b0037d69c8ba7e670e03ee65ae8d5c5114a409e73c5770d7fb5e4b895"},
- {file = "ormsgpack-1.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:db2928525b684f3f2af0367aef7ae8d20cde37fc5349c700017129d493a755aa"},
- {file = "ormsgpack-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45f911d9c5b23d11e49ff03fc8f9566745a2b1a7d9033733a1c0a2fa9301cd60"},
- {file = "ormsgpack-1.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:98c54ae6fd682b2aceb264505af9b2255f3df9d84e6e4369bc44d2110f1f311d"},
- {file = "ormsgpack-1.12.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:857ab987c3502de08258cc4baf0e87267cb2c80931601084e13df3c355b1ab9d"},
- {file = "ormsgpack-1.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:27579d45dc502ee736238e1024559cb0a01aa72a3b68827448b8edf6a2dcdc9c"},
- {file = "ormsgpack-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c78379d054760875540cf2e81f28da1bb78d09fda3eabdbeb6c53b3e297158cb"},
- {file = "ormsgpack-1.12.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c40d86d77391b18dd34de5295e3de2b8ad818bcab9c9def4121c8ec5c9714ae4"},
- {file = "ormsgpack-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:777b7fab364dc0f200bb382a98a385c8222ffa6a2333d627d763797326202c86"},
- {file = "ormsgpack-1.12.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b5089ad9dd5b3d3013b245a55e4abaea2f8ad70f4a78e1b002127b02340004"},
- {file = "ormsgpack-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:deaf0c87cace7bc08fbf68c5cc66605b593df6427e9f4de235b2da358787e008"},
- {file = "ormsgpack-1.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f62d476fe28bc5675d9aff30341bfa9f41d7de332c5b63fbbe9aaf6bb7ec74d4"},
- {file = "ormsgpack-1.12.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ded7810095b887e28434f32f5a345d354e88cf851bab3c5435aeb86a718618d2"},
- {file = "ormsgpack-1.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f72a1dea0c4ae7c4101dcfbe8133f274a9d769d0b87fe5188db4fab07ffabaee"},
- {file = "ormsgpack-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:8f479bfef847255d7d0b12c7a198f6a21490155da2da3062e082ba370893d4a1"},
- {file = "ormsgpack-1.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:3583ca410e4502144b2594170542e4bbef7b15643fd1208703ae820f11029036"},
- {file = "ormsgpack-1.12.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e0c1e08b64d99076fee155276097489b82cc56e8d5951c03c721a65a32f44494"},
- {file = "ormsgpack-1.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fd43bcb299131690b8e0677af172020b2ada8e625169034b42ac0c13adf84aa"},
- {file = "ormsgpack-1.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0149d595341e22ead340bf281b2995c4cc7dc8d522a6b5f575fe17aa407604"},
- {file = "ormsgpack-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f19a1b27d169deb553c80fd10b589fc2be1fc14cee779fae79fcaf40db04de2b"},
- {file = "ormsgpack-1.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6f28896942d655064940dfe06118b7ce1e3468d051483148bf02c99ec157483a"},
- {file = "ormsgpack-1.12.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9396efcfa48b4abbc06e44c5dbc3c4574a8381a80cb4cd01eea15d28b38c554e"},
- {file = "ormsgpack-1.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:96586ed537a5fb386a162c4f9f7d8e6f76e07b38a990d50c73f11131e00ff040"},
- {file = "ormsgpack-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e70387112fb3870e4844de090014212cdcf1342f5022047aecca01ec7de05d7a"},
- {file = "ormsgpack-1.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:d71290a23de5d4829610c42665d816c661ecad8979883f3f06b2e3ab9639962e"},
- {file = "ormsgpack-1.12.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:766f2f3b512d85cd375b26a8b1329b99843560b50b93d3880718e634ad4a5de5"},
- {file = "ormsgpack-1.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84b285b1f3f185aad7da45641b873b30acfd13084cf829cf668c4c6480a81583"},
- {file = "ormsgpack-1.12.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e23604fc79fe110292cb365f4c8232e64e63a34f470538be320feae3921f271b"},
- {file = "ormsgpack-1.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc32b156c113a0fae2975051417d8d9a7a5247c34b2d7239410c46b75ce9348a"},
- {file = "ormsgpack-1.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:94ac500dd10c20fa8b8a23bc55606250bfe711bf9716828d9f3d44dfd1f25668"},
- {file = "ormsgpack-1.12.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c5201ff7ec24f721f813a182885a17064cffdbe46b2412685a52e6374a872c8f"},
- {file = "ormsgpack-1.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a9740bb3839c9368aacae1cbcfc474ee6976458f41cc135372b7255d5206c953"},
- {file = "ormsgpack-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ed37f29772432048b58174e920a1d4c4cde0404a5d448d3d8bbcc95d86a6918"},
- {file = "ormsgpack-1.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:b03994bbec5d6d42e03d6604e327863f885bde67aa61e06107ce1fa5bdd3e71d"},
- {file = "ormsgpack-1.12.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0f3981ba3cba80656012090337e548e597799e14b41e3d0b595ab5ab05a23d7f"},
- {file = "ormsgpack-1.12.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:901f6f55184d6776dbd5183cbce14caf05bf7f467eef52faf9b094686980bf71"},
- {file = "ormsgpack-1.12.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e13b15412571422b711b40f45e3fe6d993ea3314b5e97d1a853fe99226c5effc"},
- {file = "ormsgpack-1.12.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91fa8a452553a62e5fb3fbab471e7faf7b3bec3c87a2f355ebf3d7aab290fe4f"},
- {file = "ormsgpack-1.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:74ec101f69624695eec4ce7c953192d97748254abe78fb01b591f06d529e1952"},
- {file = "ormsgpack-1.12.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:9bbf7896580848326c1f9bd7531f264e561f98db7e08e15aa75963d83832c717"},
- {file = "ormsgpack-1.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7567917da613b8f8d591c1674e411fd3404bea41ef2b9a0e0a1e049c0f9406d7"},
- {file = "ormsgpack-1.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e418256c5d8622b8bc92861936f7c6a0131355e7bcad88a42102ae8227f8a1c"},
- {file = "ormsgpack-1.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:433ace29aa02713554f714c62a4e4dcad0c9e32674ba4f66742c91a4c3b1b969"},
- {file = "ormsgpack-1.12.0-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e57164be4ca34b64e210ec515059193280ac84df4d6f31a6fcbfb2fc8436de55"},
- {file = "ormsgpack-1.12.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:904f96289deaa92fc6440b122edc27c5bdc28234edd63717f6d853d88c823a83"},
- {file = "ormsgpack-1.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b291d086e524a1062d57d1b7b5a8bcaaf29caebf0212fec12fd86240bd33633"},
- {file = "ormsgpack-1.12.0.tar.gz", hash = "sha256:94be818fdbb0285945839b88763b269987787cb2f7ef280cad5d6ec815b7e608"},
+ {file = "ormsgpack-1.12.1-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:62e3614cab63fa5aa42f5f0ca3cd12899f0bfc5eb8a5a0ebab09d571c89d427d"},
+ {file = "ormsgpack-1.12.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86d9fbf85c05c69c33c229d2eba7c8c3500a56596cd8348131c918acd040d6af"},
+ {file = "ormsgpack-1.12.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8d246e66f09d8e0f96e770829149ee83206e90ed12f5987998bb7be84aec99fe"},
+ {file = "ormsgpack-1.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfc2c830a1ed2d00de713d08c9e62efa699e8fd29beafa626aaebe466f583ebb"},
+ {file = "ormsgpack-1.12.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc892757d8f9eea5208268a527cf93c98409802f6a9f7c8d71a7b8f9ba5cb944"},
+ {file = "ormsgpack-1.12.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0de1dbcf11ea739ac4a882b43d5c2055e6d99ce64e8d6502e25d6d881700c017"},
+ {file = "ormsgpack-1.12.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d5065dfb9ec4db93241c60847624d9aeef4ccb449c26a018c216b55c69be83c0"},
+ {file = "ormsgpack-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d17103c4726181d7000c61b751c881f1b6f401d146df12da028fc730227df19"},
+ {file = "ormsgpack-1.12.1-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4038f59ae0e19dac5e5d9aae4ec17ff84a79e046342ee73ccdecf3547ecf0d34"},
+ {file = "ormsgpack-1.12.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16c63b0c5a3eec467e4bb33a14dabba076b7d934dff62898297b5c0b5f7c3cb3"},
+ {file = "ormsgpack-1.12.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:74fd6a8e037eb310dda865298e8d122540af00fe5658ec18b97a1d34f4012e4d"},
+ {file = "ormsgpack-1.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58ad60308e233dd824a1859eabb5fe092e123e885eafa4ad5789322329c80fb5"},
+ {file = "ormsgpack-1.12.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:35127464c941c1219acbe1a220e48d55e7933373d12257202f4042f7044b4c90"},
+ {file = "ormsgpack-1.12.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c48d1c50794692d1e6e3f8c3bb65f5c3acfaae9347e506484a65d60b3d91fb50"},
+ {file = "ormsgpack-1.12.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b512b2ad6feaaefdc26e05431ed2843e42483041e354e167c53401afaa83d919"},
+ {file = "ormsgpack-1.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:93f30db95e101a9616323bfc50807ad00e7f6197cea2216d2d24af42afc77d88"},
+ {file = "ormsgpack-1.12.1-cp311-cp311-win_arm64.whl", hash = "sha256:d75b5fa14f6abffce2c392ee03b4731199d8a964c81ee8645c4c79af0e80fd50"},
+ {file = "ormsgpack-1.12.1-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4d7fb0e1b6fbc701d75269f7405a4f79230a6ce0063fb1092e4f6577e312f86d"},
+ {file = "ormsgpack-1.12.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43a9353e2db5b024c91a47d864ef15eaa62d81824cfc7740fed4cef7db738694"},
+ {file = "ormsgpack-1.12.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fc8fe866b7706fc25af0adf1f600bc06ece5b15ca44e34641327198b821e5c3c"},
+ {file = "ormsgpack-1.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:813755b5f598a78242042e05dfd1ada4e769e94b98c9ab82554550f97ff4d641"},
+ {file = "ormsgpack-1.12.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8eea2a13536fae45d78f93f2cc846c9765c7160c85f19cfefecc20873c137cdd"},
+ {file = "ormsgpack-1.12.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7a02ebda1a863cbc604740e76faca8eee1add322db2dcbe6cf32669fffdff65c"},
+ {file = "ormsgpack-1.12.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3c0bd63897c439931cdf29348e5e6e8c330d529830e848d10767615c0f3d1b82"},
+ {file = "ormsgpack-1.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:362f2e812f8d7035dc25a009171e09d7cc97cb30d3c9e75a16aeae00ca3c1dcf"},
+ {file = "ormsgpack-1.12.1-cp312-cp312-win_arm64.whl", hash = "sha256:6190281e381db2ed0045052208f47a995ccf61eed48f1215ae3cce3fbccd59c5"},
+ {file = "ormsgpack-1.12.1-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:9663d6b3ecc917c063d61a99169ce196a80f3852e541ae404206836749459279"},
+ {file = "ormsgpack-1.12.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32e85cfbaf01a94a92520e7fe7851cfcfe21a5698299c28ab86194895f9b9233"},
+ {file = "ormsgpack-1.12.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dabfd2c24b59c7c69870a5ecee480dfae914a42a0c2e7c9d971cf531e2ba471a"},
+ {file = "ormsgpack-1.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51bbf2b64afeded34ccd8e25402e4bca038757913931fa0d693078d75563f6f9"},
+ {file = "ormsgpack-1.12.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9959a71dde1bd0ced84af17facc06a8afada495a34e9cb1bad8e9b20d4c59cef"},
+ {file = "ormsgpack-1.12.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:e9be0e3b62d758f21f5b20e0e06b3a240ec546c4a327bf771f5825462aa74714"},
+ {file = "ormsgpack-1.12.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a29d49ab7fdd77ea787818e60cb4ef491708105b9c4c9b0f919201625eb036b5"},
+ {file = "ormsgpack-1.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:c418390b47a1d367e803f6c187f77e4d67c7ae07ba962e3a4a019001f4b0291a"},
+ {file = "ormsgpack-1.12.1-cp313-cp313-win_arm64.whl", hash = "sha256:cfa22c91cffc10a7fbd43729baff2de7d9c28cef2509085a704168ae31f02568"},
+ {file = "ormsgpack-1.12.1-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b93c91efb1a70751a1902a5b43b27bd8fd38e0ca0365cf2cde2716423c15c3a6"},
+ {file = "ormsgpack-1.12.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf0ea0389167b5fa8d2933dd3f33e887ec4ba68f89c25214d7eec4afd746d22"},
+ {file = "ormsgpack-1.12.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4c29af837f35af3375070689e781161e7cf019eb2f7cd641734ae45cd001c0d"},
+ {file = "ormsgpack-1.12.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:336fc65aa0fe65896a3dabaae31e332a0a98b4a00ad7b0afde21a7505fd23ff3"},
+ {file = "ormsgpack-1.12.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:940f60aabfefe71dd6b82cb33f4ff10b2e7f5fcfa5f103cdb0a23b6aae4c713c"},
+ {file = "ormsgpack-1.12.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:596ad9e1b6d4c95595c54aaf49b1392609ca68f562ce06f4f74a5bc4053bcda4"},
+ {file = "ormsgpack-1.12.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:575210e8fcbc7b0375026ba040a5eef223e9f66a4453d9623fc23282ae09c3c8"},
+ {file = "ormsgpack-1.12.1-cp314-cp314-win_amd64.whl", hash = "sha256:647daa3718572280893456be44c60aea6690b7f2edc54c55648ee66e8f06550f"},
+ {file = "ormsgpack-1.12.1-cp314-cp314-win_arm64.whl", hash = "sha256:a8b3ab762a6deaf1b6490ab46dda0c51528cf8037e0246c40875c6fe9e37b699"},
+ {file = "ormsgpack-1.12.1-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:12087214e436c1f6c28491949571abea759a63111908c4f7266586d78144d7a8"},
+ {file = "ormsgpack-1.12.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6d54c14cf86ef13f10ccade94d1e7de146aa9b17d371e18b16e95f329393b7"},
+ {file = "ormsgpack-1.12.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f3584d07882b7ea2a1a589f795a3af97fe4c2932b739408e6d1d9d286cad862"},
+ {file = "ormsgpack-1.12.1.tar.gz", hash = "sha256:a3877fde1e4f27a39f92681a0aab6385af3a41d0c25375d33590ae20410ea2ac"},
]
[[package]]
@@ -5145,14 +5393,14 @@ ptyprocess = ">=0.5"
[[package]]
name = "pgvector"
-version = "0.4.1"
+version = "0.4.2"
description = "pgvector support for Python"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "pgvector-0.4.1-py3-none-any.whl", hash = "sha256:34bb4e99e1b13d08a2fe82dda9f860f15ddcd0166fbb25bffe15821cbfeb7362"},
- {file = "pgvector-0.4.1.tar.gz", hash = "sha256:83d3a1c044ff0c2f1e95d13dfb625beb0b65506cfec0941bfe81fd0ad44f4003"},
+ {file = "pgvector-0.4.2-py3-none-any.whl", hash = "sha256:549d45f7a18593783d5eec609ea1684a724ba8405c4cb182a0b2b08aeff04e08"},
+ {file = "pgvector-0.4.2.tar.gz", hash = "sha256:322cac0c1dc5d41c9ecf782bd9991b7966685dee3a00bc873631391ed949513a"},
]
[package.dependencies]
@@ -5269,14 +5517,14 @@ xmp = ["defusedxml"]
[[package]]
name = "platformdirs"
-version = "4.5.0"
+version = "4.5.1"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
optional = false
python-versions = ">=3.10"
groups = ["main", "dev"]
files = [
- {file = "platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3"},
- {file = "platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312"},
+ {file = "platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31"},
+ {file = "platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda"},
]
[package.extras]
@@ -5365,6 +5613,21 @@ files = [
[package.extras]
dev = ["certifi", "mypy (>=1.14.1)", "pytest (>=8.1.1)", "pytest-asyncio (>=0.25.3)", "ruff (>=0.9.2)", "typing-extensions ; python_full_version < \"3.12.0\""]
+[[package]]
+name = "prometheus-client"
+version = "0.23.1"
+description = "Python client for the Prometheus monitoring system."
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "prometheus_client-0.23.1-py3-none-any.whl", hash = "sha256:dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99"},
+ {file = "prometheus_client-0.23.1.tar.gz", hash = "sha256:6ae8f9081eaaaf153a2e959d2e6c4f4fb57b12ef76c8c7980202f1e57b48b2ce"},
+]
+
+[package.extras]
+twisted = ["twisted"]
+
[[package]]
name = "prompt-toolkit"
version = "3.0.52"
@@ -5514,56 +5777,58 @@ files = [
[[package]]
name = "protobuf"
-version = "6.33.1"
+version = "6.33.2"
description = ""
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "protobuf-6.33.1-cp310-abi3-win32.whl", hash = "sha256:f8d3fdbc966aaab1d05046d0240dd94d40f2a8c62856d41eaa141ff64a79de6b"},
- {file = "protobuf-6.33.1-cp310-abi3-win_amd64.whl", hash = "sha256:923aa6d27a92bf44394f6abf7ea0500f38769d4b07f4be41cb52bd8b1123b9ed"},
- {file = "protobuf-6.33.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:fe34575f2bdde76ac429ec7b570235bf0c788883e70aee90068e9981806f2490"},
- {file = "protobuf-6.33.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:f8adba2e44cde2d7618996b3fc02341f03f5bc3f2748be72dc7b063319276178"},
- {file = "protobuf-6.33.1-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:0f4cf01222c0d959c2b399142deb526de420be8236f22c71356e2a544e153c53"},
- {file = "protobuf-6.33.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:8fd7d5e0eb08cd5b87fd3df49bc193f5cfd778701f47e11d127d0afc6c39f1d1"},
- {file = "protobuf-6.33.1-cp39-cp39-win32.whl", hash = "sha256:023af8449482fa884d88b4563d85e83accab54138ae098924a985bcbb734a213"},
- {file = "protobuf-6.33.1-cp39-cp39-win_amd64.whl", hash = "sha256:df051de4fd7e5e4371334e234c62ba43763f15ab605579e04c7008c05735cd82"},
- {file = "protobuf-6.33.1-py3-none-any.whl", hash = "sha256:d595a9fd694fdeb061a62fbe10eb039cc1e444df81ec9bb70c7fc59ebcb1eafa"},
- {file = "protobuf-6.33.1.tar.gz", hash = "sha256:97f65757e8d09870de6fd973aeddb92f85435607235d20b2dfed93405d00c85b"},
+ {file = "protobuf-6.33.2-cp310-abi3-win32.whl", hash = "sha256:87eb388bd2d0f78febd8f4c8779c79247b26a5befad525008e49a6955787ff3d"},
+ {file = "protobuf-6.33.2-cp310-abi3-win_amd64.whl", hash = "sha256:fc2a0e8b05b180e5fc0dd1559fe8ebdae21a27e81ac77728fb6c42b12c7419b4"},
+ {file = "protobuf-6.33.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d9b19771ca75935b3a4422957bc518b0cecb978b31d1dd12037b088f6bcc0e43"},
+ {file = "protobuf-6.33.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5d3b5625192214066d99b2b605f5783483575656784de223f00a8d00754fc0e"},
+ {file = "protobuf-6.33.2-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8cd7640aee0b7828b6d03ae518b5b4806fdfc1afe8de82f79c3454f8aef29872"},
+ {file = "protobuf-6.33.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:1f8017c48c07ec5859106533b682260ba3d7c5567b1ca1f24297ce03384d1b4f"},
+ {file = "protobuf-6.33.2-cp39-cp39-win32.whl", hash = "sha256:7109dcc38a680d033ffb8bf896727423528db9163be1b6a02d6a49606dcadbfe"},
+ {file = "protobuf-6.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:2981c58f582f44b6b13173e12bb8656711189c2a70250845f264b877f00b1913"},
+ {file = "protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c"},
+ {file = "protobuf-6.33.2.tar.gz", hash = "sha256:56dc370c91fbb8ac85bc13582c9e373569668a290aa2e66a590c2a0d35ddb9e4"},
]
[[package]]
name = "psutil"
-version = "7.1.3"
+version = "7.2.0"
description = "Cross-platform lib for process and system monitoring."
optional = false
python-versions = ">=3.6"
groups = ["main"]
files = [
- {file = "psutil-7.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0005da714eee687b4b8decd3d6cc7c6db36215c9e74e5ad2264b90c3df7d92dc"},
- {file = "psutil-7.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19644c85dcb987e35eeeaefdc3915d059dac7bd1167cdcdbf27e0ce2df0c08c0"},
- {file = "psutil-7.1.3-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95ef04cf2e5ba0ab9eaafc4a11eaae91b44f4ef5541acd2ee91d9108d00d59a7"},
- {file = "psutil-7.1.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1068c303be3a72f8e18e412c5b2a8f6d31750fb152f9cb106b54090296c9d251"},
- {file = "psutil-7.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:18349c5c24b06ac5612c0428ec2a0331c26443d259e2a0144a9b24b4395b58fa"},
- {file = "psutil-7.1.3-cp313-cp313t-win_arm64.whl", hash = "sha256:c525ffa774fe4496282fb0b1187725793de3e7c6b29e41562733cae9ada151ee"},
- {file = "psutil-7.1.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b403da1df4d6d43973dc004d19cee3b848e998ae3154cc8097d139b77156c353"},
- {file = "psutil-7.1.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ad81425efc5e75da3f39b3e636293360ad8d0b49bed7df824c79764fb4ba9b8b"},
- {file = "psutil-7.1.3-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f33a3702e167783a9213db10ad29650ebf383946e91bc77f28a5eb083496bc9"},
- {file = "psutil-7.1.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fac9cd332c67f4422504297889da5ab7e05fd11e3c4392140f7370f4208ded1f"},
- {file = "psutil-7.1.3-cp314-cp314t-win_amd64.whl", hash = "sha256:3792983e23b69843aea49c8f5b8f115572c5ab64c153bada5270086a2123c7e7"},
- {file = "psutil-7.1.3-cp314-cp314t-win_arm64.whl", hash = "sha256:31d77fcedb7529f27bb3a0472bea9334349f9a04160e8e6e5020f22c59893264"},
- {file = "psutil-7.1.3-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2bdbcd0e58ca14996a42adf3621a6244f1bb2e2e528886959c72cf1e326677ab"},
- {file = "psutil-7.1.3-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:bc31fa00f1fbc3c3802141eede66f3a2d51d89716a194bf2cd6fc68310a19880"},
- {file = "psutil-7.1.3-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3bb428f9f05c1225a558f53e30ccbad9930b11c3fc206836242de1091d3e7dd3"},
- {file = "psutil-7.1.3-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d974e02ca2c8eb4812c3f76c30e28836fffc311d55d979f1465c1feeb2b68b"},
- {file = "psutil-7.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:f39c2c19fe824b47484b96f9692932248a54c43799a84282cfe58d05a6449efd"},
- {file = "psutil-7.1.3-cp37-abi3-win_arm64.whl", hash = "sha256:bd0d69cee829226a761e92f28140bec9a5ee9d5b4fb4b0cc589068dbfff559b1"},
- {file = "psutil-7.1.3.tar.gz", hash = "sha256:6c86281738d77335af7aec228328e944b30930899ea760ecf33a4dba66be5e74"},
+ {file = "psutil-7.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c31e927555539132a00380c971816ea43d089bf4bd5f3e918ed8c16776d68474"},
+ {file = "psutil-7.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:db8e44e766cef86dea47d9a1fa535d38dc76449e5878a92f33683b7dba5bfcb2"},
+ {file = "psutil-7.2.0-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85ef849ac92169dedc59a7ac2fb565f47b3468fbe1524bf748746bc21afb94c7"},
+ {file = "psutil-7.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:26782bdbae2f5c14ce9ebe8ad2411dc2ca870495e0cd90f8910ede7fa5e27117"},
+ {file = "psutil-7.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b7665f612d3b38a583391b95969667a53aaf6c5706dc27a602c9a4874fbf09e4"},
+ {file = "psutil-7.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:4413373c174520ae28a24a8974ad8ce6b21f060d27dde94e25f8c73a7effe57a"},
+ {file = "psutil-7.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2f2f53fd114e7946dfba3afb98c9b7c7f376009447360ca15bfb73f2066f84c7"},
+ {file = "psutil-7.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e65c41d7e60068f60ce43b31a3a7fc90deb0dfd34ffc824a2574c2e5279b377e"},
+ {file = "psutil-7.2.0-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc66d21366850a4261412ce994ae9976bba9852dafb4f2fa60db68ed17ff5281"},
+ {file = "psutil-7.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e025d67b42b8f22b096d5d20f5171de0e0fefb2f0ce983a13c5a1b5ed9872706"},
+ {file = "psutil-7.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:45f6b91f7ad63414d6454fd609e5e3556d0e1038d5d9c75a1368513bdf763f57"},
+ {file = "psutil-7.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87b18a19574139d60a546e88b5f5b9cbad598e26cdc790d204ab95d7024f03ee"},
+ {file = "psutil-7.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:977a2fcd132d15cb05b32b2d85b98d087cad039b0ce435731670ba74da9e6133"},
+ {file = "psutil-7.2.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:24151011c21fadd94214d7139d7c6c54569290d7e553989bdf0eab73b13beb8c"},
+ {file = "psutil-7.2.0-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91f211ba9279e7c61d9d8f84b713cfc38fa161cb0597d5cb3f1ca742f6848254"},
+ {file = "psutil-7.2.0-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f37415188b7ea98faf90fed51131181646c59098b077550246e2e092e127418b"},
+ {file = "psutil-7.2.0-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0d12c7ce6ed1128cd81fd54606afa054ac7dbb9773469ebb58cf2f171c49f2ac"},
+ {file = "psutil-7.2.0-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ca0faef7976530940dcd39bc5382d0d0d5eb023b186a4901ca341bd8d8684151"},
+ {file = "psutil-7.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:abdb74137ca232d20250e9ad471f58d500e7743bc8253ba0bfbf26e570c0e437"},
+ {file = "psutil-7.2.0-cp37-abi3-win_arm64.whl", hash = "sha256:284e71038b3139e7ab3834b63b3eb5aa5565fcd61a681ec746ef9a0a8c457fd2"},
+ {file = "psutil-7.2.0.tar.gz", hash = "sha256:2e4f8e1552f77d14dc96fb0f6240c5b34a37081c0889f0853b3b29a496e5ef64"},
]
[package.extras]
-dev = ["abi3audit", "black", "check-manifest", "colorama ; os_name == \"nt\"", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pyreadline ; os_name == \"nt\"", "pytest", "pytest-cov", "pytest-instafail", "pytest-subtests", "pytest-xdist", "pywin32 ; os_name == \"nt\" and platform_python_implementation != \"PyPy\"", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "validate-pyproject[all]", "virtualenv", "vulture", "wheel", "wheel ; os_name == \"nt\" and platform_python_implementation != \"PyPy\"", "wmi ; os_name == \"nt\" and platform_python_implementation != \"PyPy\""]
-test = ["pytest", "pytest-instafail", "pytest-subtests", "pytest-xdist", "pywin32 ; os_name == \"nt\" and platform_python_implementation != \"PyPy\"", "setuptools", "wheel ; os_name == \"nt\" and platform_python_implementation != \"PyPy\"", "wmi ; os_name == \"nt\" and platform_python_implementation != \"PyPy\""]
+dev = ["abi3audit", "black", "check-manifest", "coverage", "packaging", "psleak", "pylint", "pyperf", "pypinfo", "pytest", "pytest-cov", "pytest-instafail", "pytest-xdist", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "validate-pyproject[all]", "virtualenv", "vulture", "wheel"]
+test = ["psleak", "pytest", "pytest-instafail", "pytest-xdist", "setuptools"]
[[package]]
name = "psycopg2"
@@ -5612,54 +5877,57 @@ tests = ["pytest"]
[[package]]
name = "py-key-value-aio"
-version = "0.2.8"
+version = "0.3.0"
description = "Async Key-Value"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "py_key_value_aio-0.2.8-py3-none-any.whl", hash = "sha256:561565547ce8162128fd2bd0b9d70ce04a5f4586da8500cce79a54dfac78c46a"},
- {file = "py_key_value_aio-0.2.8.tar.gz", hash = "sha256:c0cfbb0bd4e962a3fa1a9fa6db9ba9df812899bd9312fa6368aaea7b26008b36"},
+ {file = "py_key_value_aio-0.3.0-py3-none-any.whl", hash = "sha256:1c781915766078bfd608daa769fefb97e65d1d73746a3dfb640460e322071b64"},
+ {file = "py_key_value_aio-0.3.0.tar.gz", hash = "sha256:858e852fcf6d696d231266da66042d3355a7f9871650415feef9fca7a6cd4155"},
]
[package.dependencies]
-beartype = ">=0.22.2"
-cachetools = {version = ">=6.0.0", optional = true, markers = "extra == \"memory\""}
-diskcache = {version = ">=5.6.0", optional = true, markers = "extra == \"disk\""}
+beartype = ">=0.20.0"
+cachetools = {version = ">=5.0.0", optional = true, markers = "extra == \"memory\""}
+diskcache = {version = ">=5.0.0", optional = true, markers = "extra == \"disk\""}
keyring = {version = ">=25.6.0", optional = true, markers = "extra == \"keyring\""}
pathvalidate = {version = ">=3.3.1", optional = true, markers = "extra == \"disk\""}
-py-key-value-shared = "0.2.8"
+py-key-value-shared = "0.3.0"
+redis = {version = ">=4.3.0", optional = true, markers = "extra == \"redis\""}
[package.extras]
-disk = ["diskcache (>=5.6.0)", "pathvalidate (>=3.3.1)"]
+disk = ["diskcache (>=5.0.0)", "pathvalidate (>=3.3.1)"]
+duckdb = ["duckdb (>=1.1.1)", "pytz (>=2025.2)"]
dynamodb = ["aioboto3 (>=13.3.0)", "types-aiobotocore-dynamodb (>=2.16.0)"]
-elasticsearch = ["aiohttp (>=3.12)", "elasticsearch (>=9.0.0)"]
+elasticsearch = ["aiohttp (>=3.12)", "elasticsearch (>=8.0.0)"]
+filetree = ["aiofile (>=3.5.0)", "anyio (>=4.4.0)"]
keyring = ["keyring (>=25.6.0)"]
keyring-linux = ["dbus-python (>=1.4.0)", "keyring (>=25.6.0)"]
memcached = ["aiomcache (>=0.8.0)"]
-memory = ["cachetools (>=6.0.0)"]
-mongodb = ["pymongo (>=4.15.0)"]
+memory = ["cachetools (>=5.0.0)"]
+mongodb = ["pymongo (>=4.0.0)"]
pydantic = ["pydantic (>=2.11.9)"]
redis = ["redis (>=4.3.0)"]
-rocksdb = ["rocksdict (>=0.3.0)"]
+rocksdb = ["rocksdict (>=0.3.2) ; python_full_version < \"3.12.0\"", "rocksdict (>=0.3.24) ; python_full_version >= \"3.12.0\""]
valkey = ["valkey-glide (>=2.1.0)"]
vault = ["hvac (>=2.3.0)", "types-hvac (>=2.3.0)"]
wrappers-encryption = ["cryptography (>=45.0.0)"]
[[package]]
name = "py-key-value-shared"
-version = "0.2.8"
+version = "0.3.0"
description = "Shared Key-Value"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "py_key_value_shared-0.2.8-py3-none-any.whl", hash = "sha256:aff1bbfd46d065b2d67897d298642e80e5349eae588c6d11b48452b46b8d46ba"},
- {file = "py_key_value_shared-0.2.8.tar.gz", hash = "sha256:703b4d3c61af124f0d528ba85995c3c8d78f8bd3d2b217377bd3278598070cc1"},
+ {file = "py_key_value_shared-0.3.0-py3-none-any.whl", hash = "sha256:5b0efba7ebca08bb158b1e93afc2f07d30b8f40c2fc12ce24a4c0d84f42f9298"},
+ {file = "py_key_value_shared-0.3.0.tar.gz", hash = "sha256:8fdd786cf96c3e900102945f92aa1473138ebe960ef49da1c833790160c28a4b"},
]
[package.dependencies]
-beartype = ">=0.22.2"
+beartype = ">=0.20.0"
typing-extensions = ">=4.15.0"
[[package]]
@@ -6023,20 +6291,45 @@ gcp-secret-manager = ["google-cloud-secret-manager (>=2.23.1)"]
toml = ["tomli (>=2.0.1)"]
yaml = ["pyyaml (>=6.0.1)"]
+[[package]]
+name = "pydocket"
+version = "0.15.4"
+description = "A distributed background task system for Python functions"
+optional = false
+python-versions = ">=3.10"
+groups = ["main"]
+files = [
+ {file = "pydocket-0.15.4-py3-none-any.whl", hash = "sha256:21a961f87cd995b40df3f559fb0bae75d925ec0916c10b9739b6c1638ffde436"},
+ {file = "pydocket-0.15.4.tar.gz", hash = "sha256:31c79bca5eb11dfac8635d3e5b81ec7d7c8949c8c8cfc536ae76753949c147c8"},
+]
+
+[package.dependencies]
+cloudpickle = ">=3.1.1"
+fakeredis = {version = ">=2.32.1", extras = ["lua"]}
+opentelemetry-api = ">=1.30.0"
+opentelemetry-exporter-prometheus = ">=0.51b0"
+prometheus-client = ">=0.21.1"
+py-key-value-aio = {version = ">=0.3.0", extras = ["memory", "redis"]}
+python-json-logger = ">=2.0.7"
+redis = ">=5"
+rich = ">=13.9.4"
+typer = ">=0.15.1"
+typing-extensions = ">=4.12.0"
+
[[package]]
name = "pydyf"
-version = "0.11.0"
+version = "0.12.1"
description = "A low-level PDF generator."
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "pydyf-0.11.0-py3-none-any.whl", hash = "sha256:0aaf9e2ebbe786ec7a78ec3fbffa4cdcecde53fd6f563221d53c6bc1328848a3"},
- {file = "pydyf-0.11.0.tar.gz", hash = "sha256:394dddf619cca9d0c55715e3c55ea121a9bf9cbc780cdc1201a2427917b86b64"},
+ {file = "pydyf-0.12.1-py3-none-any.whl", hash = "sha256:ea25b4e1fe7911195cb57067560daaa266639184e8335365cc3ee5214e7eaadc"},
+ {file = "pydyf-0.12.1.tar.gz", hash = "sha256:fbd7e759541ac725c29c506612003de393249b94310ea78ae44cb1d04b220095"},
]
[package.extras]
-doc = ["sphinx", "sphinx_rtd_theme"]
+doc = ["furo", "sphinx"]
test = ["pillow", "pytest", "ruff"]
[[package]]
@@ -6077,31 +6370,32 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
[[package]]
name = "pymupdf"
-version = "1.26.6"
+version = "1.26.7"
description = "A high performance Python library for data extraction, analysis, conversion & manipulation of PDF (and other) documents."
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "pymupdf-1.26.6-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:e46f320a136ad55e5219e8f0f4061bdf3e4c12b126d2740d5a49f73fae7ea176"},
- {file = "pymupdf-1.26.6-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:6844cd2396553c0fa06de4869d5d5ecb1260e6fc3b9d85abe8fa35f14dd9d688"},
- {file = "pymupdf-1.26.6-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:617ba69e02c44f0da1c0e039ea4a26cf630849fd570e169c71daeb8ac52a81d6"},
- {file = "pymupdf-1.26.6-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:7777d0b7124c2ebc94849536b6a1fb85d158df3b9d873935e63036559391534c"},
- {file = "pymupdf-1.26.6-cp310-abi3-win32.whl", hash = "sha256:8f3ef05befc90ca6bb0f12983200a7048d5bff3e1c1edef1bb3de60b32cb5274"},
- {file = "pymupdf-1.26.6-cp310-abi3-win_amd64.whl", hash = "sha256:ce02ca96ed0d1acfd00331a4d41a34c98584d034155b06fd4ec0f051718de7ba"},
- {file = "pymupdf-1.26.6.tar.gz", hash = "sha256:a2b4531cd4ab36d6f1f794bb6d3c33b49bda22f36d58bb1f3e81cbc10183bd2b"},
+ {file = "pymupdf-1.26.7-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:07085718dfdae5ab83b05eb5eb397f863bcc538fe05135318a01ea353e7a1353"},
+ {file = "pymupdf-1.26.7-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:31aa9c8377ea1eea02934b92f4dcf79fb2abba0bf41f8a46d64c3e31546a3c02"},
+ {file = "pymupdf-1.26.7-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e419b609996434a14a80fa060adec72c434a1cca6a511ec54db9841bc5d51b3c"},
+ {file = "pymupdf-1.26.7-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:69dfc78f206a96e5b3ac22741263ebab945fdf51f0dbe7c5757c3511b23d9d72"},
+ {file = "pymupdf-1.26.7-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1d5106f46e1ca0d64d46bd51892372a4f82076bdc14a9678d33d630702abca36"},
+ {file = "pymupdf-1.26.7-cp310-abi3-win32.whl", hash = "sha256:7c9645b6f5452629c747690190350213d3e5bbdb6b2eca227d82702b327f6eee"},
+ {file = "pymupdf-1.26.7-cp310-abi3-win_amd64.whl", hash = "sha256:425b1befe40d41b72eb0fe211711c7ae334db5eb60307e9dd09066ed060cceba"},
+ {file = "pymupdf-1.26.7.tar.gz", hash = "sha256:71add8bdc8eb1aaa207c69a13400693f06ad9b927bea976f5d5ab9df0bb489c3"},
]
[[package]]
name = "pypdf"
-version = "6.4.0"
+version = "6.5.0"
description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "pypdf-6.4.0-py3-none-any.whl", hash = "sha256:55ab9837ed97fd7fcc5c131d52fcc2223bc5c6b8a1488bbf7c0e27f1f0023a79"},
- {file = "pypdf-6.4.0.tar.gz", hash = "sha256:4769d471f8ddc3341193ecc5d6560fa44cf8cd0abfabf21af4e195cc0c224072"},
+ {file = "pypdf-6.5.0-py3-none-any.whl", hash = "sha256:9cef8002aaedeecf648dfd9ff1ce38f20ae8d88e2534fced6630038906440b25"},
+ {file = "pypdf-6.5.0.tar.gz", hash = "sha256:9e78950906380ae4f2ce1d9039e9008098ba6366a4d9c7423c4bdbd6e6683404"},
]
[package.extras]
@@ -6276,6 +6570,21 @@ files = [
[package.extras]
dev = ["black (==25.11.0)", "build (==1.3.0)", "flake8 (==7.3.0)", "mypy (==1.18.2)", "pytest (==9.0.0)", "requests (==2.32.5)", "twine (==6.2.0)"]
+[[package]]
+name = "python-json-logger"
+version = "4.0.0"
+description = "JSON Log Formatter for the Python Logging Package"
+optional = false
+python-versions = ">=3.8"
+groups = ["main"]
+files = [
+ {file = "python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2"},
+ {file = "python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f"},
+]
+
+[package.extras]
+dev = ["backports.zoneinfo ; python_version < \"3.9\"", "black", "build", "freezegun", "mdx_truly_sane_lists", "mike", "mkdocs", "mkdocs-awesome-pages-plugin", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-material (>=8.5)", "mkdocstrings[python]", "msgspec ; implementation_name != \"pypy\"", "mypy", "orjson ; implementation_name != \"pypy\"", "pylint", "pytest", "tzdata", "validate-pyproject[all]"]
+
[[package]]
name = "python-magic"
version = "0.4.27"
@@ -6290,14 +6599,14 @@ files = [
[[package]]
name = "python-multipart"
-version = "0.0.20"
+version = "0.0.21"
description = "A streaming multipart parser for Python"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104"},
- {file = "python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13"},
+ {file = "python_multipart-0.0.21-py3-none-any.whl", hash = "sha256:cf7a6713e01c87aa35387f4774e812c4361150938d20d232800f75ffcf266090"},
+ {file = "python_multipart-0.0.21.tar.gz", hash = "sha256:7137ebd4d3bbf70ea1622998f902b97a29434a9e8dc40eb203bbcf7c2a2cba92"},
]
[[package]]
@@ -6849,127 +7158,127 @@ docs = ["sphinx"]
[[package]]
name = "rpds-py"
-version = "0.29.0"
+version = "0.30.0"
description = "Python bindings to Rust's persistent data structures (rpds)"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "rpds_py-0.29.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4ae4b88c6617e1b9e5038ab3fccd7bac0842fdda2b703117b2aa99bc85379113"},
- {file = "rpds_py-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7d9128ec9d8cecda6f044001fde4fb71ea7c24325336612ef8179091eb9596b9"},
- {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37812c3da8e06f2bb35b3cf10e4a7b68e776a706c13058997238762b4e07f4f"},
- {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66786c3fb1d8de416a7fa8e1cb1ec6ba0a745b2b0eee42f9b7daa26f1a495545"},
- {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58f5c77f1af888b5fd1876c9a0d9858f6f88a39c9dd7c073a88e57e577da66d"},
- {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:799156ef1f3529ed82c36eb012b5d7a4cf4b6ef556dd7cc192148991d07206ae"},
- {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:453783477aa4f2d9104c4b59b08c871431647cb7af51b549bbf2d9eb9c827756"},
- {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:24a7231493e3c4a4b30138b50cca089a598e52c34cf60b2f35cebf62f274fdea"},
- {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7033c1010b1f57bb44d8067e8c25aa6fa2e944dbf46ccc8c92b25043839c3fd2"},
- {file = "rpds_py-0.29.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0248b19405422573621172ab8e3a1f29141362d13d9f72bafa2e28ea0cdca5a2"},
- {file = "rpds_py-0.29.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f9f436aee28d13b9ad2c764fc273e0457e37c2e61529a07b928346b219fcde3b"},
- {file = "rpds_py-0.29.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24a16cb7163933906c62c272de20ea3c228e4542c8c45c1d7dc2b9913e17369a"},
- {file = "rpds_py-0.29.0-cp310-cp310-win32.whl", hash = "sha256:1a409b0310a566bfd1be82119891fefbdce615ccc8aa558aff7835c27988cbef"},
- {file = "rpds_py-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5523b0009e7c3c1263471b69d8da1c7d41b3ecb4cb62ef72be206b92040a950"},
- {file = "rpds_py-0.29.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9b9c764a11fd637e0322a488560533112837f5334ffeb48b1be20f6d98a7b437"},
- {file = "rpds_py-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fd2164d73812026ce970d44c3ebd51e019d2a26a4425a5dcbdfa93a34abc383"},
- {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a097b7f7f7274164566ae90a221fd725363c0e9d243e2e9ed43d195ccc5495c"},
- {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cdc0490374e31cedefefaa1520d5fe38e82fde8748cbc926e7284574c714d6b"},
- {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89ca2e673ddd5bde9b386da9a0aac0cab0e76f40c8f0aaf0d6311b6bbf2aa311"},
- {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5d9da3ff5af1ca1249b1adb8ef0573b94c76e6ae880ba1852f033bf429d4588"},
- {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8238d1d310283e87376c12f658b61e1ee23a14c0e54c7c0ce953efdbdc72deed"},
- {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2d6fb2ad1c36f91c4646989811e84b1ea5e0c3cf9690b826b6e32b7965853a63"},
- {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:534dc9df211387547267ccdb42253aa30527482acb38dd9b21c5c115d66a96d2"},
- {file = "rpds_py-0.29.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d456e64724a075441e4ed648d7f154dc62e9aabff29bcdf723d0c00e9e1d352f"},
- {file = "rpds_py-0.29.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a738f2da2f565989401bd6fd0b15990a4d1523c6d7fe83f300b7e7d17212feca"},
- {file = "rpds_py-0.29.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a110e14508fd26fd2e472bb541f37c209409876ba601cf57e739e87d8a53cf95"},
- {file = "rpds_py-0.29.0-cp311-cp311-win32.whl", hash = "sha256:923248a56dd8d158389a28934f6f69ebf89f218ef96a6b216a9be6861804d3f4"},
- {file = "rpds_py-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:539eb77eb043afcc45314d1be09ea6d6cafb3addc73e0547c171c6d636957f60"},
- {file = "rpds_py-0.29.0-cp311-cp311-win_arm64.whl", hash = "sha256:bdb67151ea81fcf02d8f494703fb728d4d34d24556cbff5f417d74f6f5792e7c"},
- {file = "rpds_py-0.29.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0891cfd8db43e085c0ab93ab7e9b0c8fee84780d436d3b266b113e51e79f954"},
- {file = "rpds_py-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3897924d3f9a0361472d884051f9a2460358f9a45b1d85a39a158d2f8f1ad71c"},
- {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21deb8e0d1571508c6491ce5ea5e25669b1dd4adf1c9d64b6314842f708b5d"},
- {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9efe71687d6427737a0a2de9ca1c0a216510e6cd08925c44162be23ed7bed2d5"},
- {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:40f65470919dc189c833e86b2c4bd21bd355f98436a2cef9e0a9a92aebc8e57e"},
- {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:def48ff59f181130f1a2cb7c517d16328efac3ec03951cca40c1dc2049747e83"},
- {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7bd570be92695d89285a4b373006930715b78d96449f686af422debb4d3949"},
- {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:5a572911cd053137bbff8e3a52d31c5d2dba51d3a67ad902629c70185f3f2181"},
- {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d583d4403bcbf10cffc3ab5cee23d7643fcc960dff85973fd3c2d6c86e8dbb0c"},
- {file = "rpds_py-0.29.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:070befbb868f257d24c3bb350dbd6e2f645e83731f31264b19d7231dd5c396c7"},
- {file = "rpds_py-0.29.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fc935f6b20b0c9f919a8ff024739174522abd331978f750a74bb68abd117bd19"},
- {file = "rpds_py-0.29.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c5a8ecaa44ce2d8d9d20a68a2483a74c07f05d72e94a4dff88906c8807e77b0"},
- {file = "rpds_py-0.29.0-cp312-cp312-win32.whl", hash = "sha256:ba5e1aeaf8dd6d8f6caba1f5539cddda87d511331714b7b5fc908b6cfc3636b7"},
- {file = "rpds_py-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:b5f6134faf54b3cb83375db0f113506f8b7770785be1f95a631e7e2892101977"},
- {file = "rpds_py-0.29.0-cp312-cp312-win_arm64.whl", hash = "sha256:b016eddf00dca7944721bf0cd85b6af7f6c4efaf83ee0b37c4133bd39757a8c7"},
- {file = "rpds_py-0.29.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1585648d0760b88292eecab5181f5651111a69d90eff35d6b78aa32998886a61"},
- {file = "rpds_py-0.29.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:521807963971a23996ddaf764c682b3e46459b3c58ccd79fefbe16718db43154"},
- {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8896986efaa243ab713c69e6491a4138410f0fe36f2f4c71e18bd5501e8014"},
- {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d24564a700ef41480a984c5ebed62b74e6ce5860429b98b1fede76049e953e6"},
- {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6596b93c010d386ae46c9fba9bfc9fc5965fa8228edeac51576299182c2e31c"},
- {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5cc58aac218826d054c7da7f95821eba94125d88be673ff44267bb89d12a5866"},
- {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de73e40ebc04dd5d9556f50180395322193a78ec247e637e741c1b954810f295"},
- {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:295ce5ac7f0cf69a651ea75c8f76d02a31f98e5698e82a50a5f4d4982fbbae3b"},
- {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ea59b23ea931d494459c8338056fe7d93458c0bf3ecc061cd03916505369d55"},
- {file = "rpds_py-0.29.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f49d41559cebd608042fdcf54ba597a4a7555b49ad5c1c0c03e0af82692661cd"},
- {file = "rpds_py-0.29.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:05a2bd42768ea988294ca328206efbcc66e220d2d9b7836ee5712c07ad6340ea"},
- {file = "rpds_py-0.29.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33ca7bdfedd83339ca55da3a5e1527ee5870d4b8369456b5777b197756f3ca22"},
- {file = "rpds_py-0.29.0-cp313-cp313-win32.whl", hash = "sha256:20c51ae86a0bb9accc9ad4e6cdeec58d5ebb7f1b09dd4466331fc65e1766aae7"},
- {file = "rpds_py-0.29.0-cp313-cp313-win_amd64.whl", hash = "sha256:6410e66f02803600edb0b1889541f4b5cc298a5ccda0ad789cc50ef23b54813e"},
- {file = "rpds_py-0.29.0-cp313-cp313-win_arm64.whl", hash = "sha256:56838e1cd9174dc23c5691ee29f1d1be9eab357f27efef6bded1328b23e1ced2"},
- {file = "rpds_py-0.29.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:37d94eadf764d16b9a04307f2ab1d7af6dc28774bbe0535c9323101e14877b4c"},
- {file = "rpds_py-0.29.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d472cf73efe5726a067dce63eebe8215b14beabea7c12606fd9994267b3cfe2b"},
- {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72fdfd5ff8992e4636621826371e3ac5f3e3b8323e9d0e48378e9c13c3dac9d0"},
- {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2549d833abdf8275c901313b9e8ff8fba57e50f6a495035a2a4e30621a2f7cc4"},
- {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4448dad428f28a6a767c3e3b80cde3446a22a0efbddaa2360f4bb4dc836d0688"},
- {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:115f48170fd4296a33938d8c11f697f5f26e0472e43d28f35624764173a60e4d"},
- {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e5bb73ffc029820f4348e9b66b3027493ae00bca6629129cd433fd7a76308ee"},
- {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:b1581fcde18fcdf42ea2403a16a6b646f8eb1e58d7f90a0ce693da441f76942e"},
- {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16e9da2bda9eb17ea318b4c335ec9ac1818e88922cbe03a5743ea0da9ecf74fb"},
- {file = "rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:28fd300326dd21198f311534bdb6d7e989dd09b3418b3a91d54a0f384c700967"},
- {file = "rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2aba991e041d031c7939e1358f583ae405a7bf04804ca806b97a5c0e0af1ea5e"},
- {file = "rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f437026dbbc3f08c99cc41a5b2570c6e1a1ddbe48ab19a9b814254128d4ea7a"},
- {file = "rpds_py-0.29.0-cp313-cp313t-win32.whl", hash = "sha256:6e97846e9800a5d0fe7be4d008f0c93d0feeb2700da7b1f7528dabafb31dfadb"},
- {file = "rpds_py-0.29.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f49196aec7c4b406495f60e6f947ad71f317a765f956d74bbd83996b9edc0352"},
- {file = "rpds_py-0.29.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:394d27e4453d3b4d82bb85665dc1fcf4b0badc30fc84282defed71643b50e1a1"},
- {file = "rpds_py-0.29.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55d827b2ae95425d3be9bc9a5838b6c29d664924f98146557f7715e331d06df8"},
- {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc31a07ed352e5462d3ee1b22e89285f4ce97d5266f6d1169da1142e78045626"},
- {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c4695dd224212f6105db7ea62197144230b808d6b2bba52238906a2762f1d1e7"},
- {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcae1770b401167f8b9e1e3f566562e6966ffa9ce63639916248a9e25fa8a244"},
- {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:90f30d15f45048448b8da21c41703b31c61119c06c216a1bf8c245812a0f0c17"},
- {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44a91e0ab77bdc0004b43261a4b8cd6d6b451e8d443754cfda830002b5745b32"},
- {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:4aa195e5804d32c682e453b34474f411ca108e4291c6a0f824ebdc30a91c973c"},
- {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7971bdb7bf4ee0f7e6f67fa4c7fbc6019d9850cc977d126904392d363f6f8318"},
- {file = "rpds_py-0.29.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8ae33ad9ce580c7a47452c3b3f7d8a9095ef6208e0a0c7e4e2384f9fc5bf8212"},
- {file = "rpds_py-0.29.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c661132ab2fb4eeede2ef69670fd60da5235209874d001a98f1542f31f2a8a94"},
- {file = "rpds_py-0.29.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bb78b3a0d31ac1bde132c67015a809948db751cb4e92cdb3f0b242e430b6ed0d"},
- {file = "rpds_py-0.29.0-cp314-cp314-win32.whl", hash = "sha256:f475f103488312e9bd4000bc890a95955a07b2d0b6e8884aef4be56132adbbf1"},
- {file = "rpds_py-0.29.0-cp314-cp314-win_amd64.whl", hash = "sha256:b9cf2359a4fca87cfb6801fae83a76aedf66ee1254a7a151f1341632acf67f1b"},
- {file = "rpds_py-0.29.0-cp314-cp314-win_arm64.whl", hash = "sha256:9ba8028597e824854f0f1733d8b964e914ae3003b22a10c2c664cb6927e0feb9"},
- {file = "rpds_py-0.29.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:e71136fd0612556b35c575dc2726ae04a1669e6a6c378f2240312cf5d1a2ab10"},
- {file = "rpds_py-0.29.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:76fe96632d53f3bf0ea31ede2f53bbe3540cc2736d4aec3b3801b0458499ef3a"},
- {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9459a33f077130dbb2c7c3cea72ee9932271fb3126404ba2a2661e4fe9eb7b79"},
- {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5c9546cfdd5d45e562cc0444b6dddc191e625c62e866bf567a2c69487c7ad28a"},
- {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12597d11d97b8f7e376c88929a6e17acb980e234547c92992f9f7c058f1a7310"},
- {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28de03cf48b8a9e6ec10318f2197b83946ed91e2891f651a109611be4106ac4b"},
- {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd7951c964069039acc9d67a8ff1f0a7f34845ae180ca542b17dc1456b1f1808"},
- {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:c07d107b7316088f1ac0177a7661ca0c6670d443f6fe72e836069025e6266761"},
- {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de2345af363d25696969befc0c1688a6cb5e8b1d32b515ef84fc245c6cddba3"},
- {file = "rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:00e56b12d2199ca96068057e1ae7f9998ab6e99cda82431afafd32f3ec98cca9"},
- {file = "rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3919a3bbecee589300ed25000b6944174e07cd20db70552159207b3f4bbb45b8"},
- {file = "rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7fa2ccc312bbd91e43aa5e0869e46bc03278a3dddb8d58833150a18b0f0283a"},
- {file = "rpds_py-0.29.0-cp314-cp314t-win32.whl", hash = "sha256:97c817863ffc397f1e6a6e9d2d89fe5408c0a9922dac0329672fb0f35c867ea5"},
- {file = "rpds_py-0.29.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2023473f444752f0f82a58dfcbee040d0a1b3d1b3c2ec40e884bd25db6d117d2"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:acd82a9e39082dc5f4492d15a6b6c8599aa21db5c35aaf7d6889aea16502c07d"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:715b67eac317bf1c7657508170a3e011a1ea6ccb1c9d5f296e20ba14196be6b3"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3b1b87a237cb2dba4db18bcfaaa44ba4cd5936b91121b62292ff21df577fc43"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c3c3e8101bb06e337c88eb0c0ede3187131f19d97d43ea0e1c5407ea74c0cbf"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8e54d6e61f3ecd3abe032065ce83ea63417a24f437e4a3d73d2f85ce7b7cfe"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fbd4e9aebf110473a420dea85a238b254cf8a15acb04b22a5a6b5ce8925b760"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80fdf53d36e6c72819993e35d1ebeeb8e8fc688d0c6c2b391b55e335b3afba5a"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:ea7173df5d86f625f8dde6d5929629ad811ed8decda3b60ae603903839ac9ac0"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:76054d540061eda273274f3d13a21a4abdde90e13eaefdc205db37c05230efce"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:9f84c549746a5be3bc7415830747a3a0312573afc9f95785eb35228bb17742ec"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:0ea962671af5cb9a260489e311fa22b2e97103e3f9f0caaea6f81390af96a9ed"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:f7728653900035fb7b8d06e1e5900545d8088efc9d5d4545782da7df03ec803f"},
- {file = "rpds_py-0.29.0.tar.gz", hash = "sha256:fe55fe686908f50154d1dc599232016e50c243b438c3b7432f24e2895b0e5359"},
+ {file = "rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288"},
+ {file = "rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00"},
+ {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6"},
+ {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7"},
+ {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324"},
+ {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df"},
+ {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3"},
+ {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221"},
+ {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7"},
+ {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff"},
+ {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7"},
+ {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139"},
+ {file = "rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464"},
+ {file = "rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169"},
+ {file = "rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425"},
+ {file = "rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d"},
+ {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4"},
+ {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f"},
+ {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4"},
+ {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97"},
+ {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89"},
+ {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d"},
+ {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038"},
+ {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7"},
+ {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed"},
+ {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85"},
+ {file = "rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c"},
+ {file = "rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825"},
+ {file = "rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229"},
+ {file = "rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad"},
+ {file = "rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05"},
+ {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28"},
+ {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd"},
+ {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f"},
+ {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1"},
+ {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23"},
+ {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6"},
+ {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51"},
+ {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5"},
+ {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e"},
+ {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394"},
+ {file = "rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf"},
+ {file = "rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b"},
+ {file = "rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e"},
+ {file = "rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2"},
+ {file = "rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8"},
+ {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4"},
+ {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136"},
+ {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7"},
+ {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2"},
+ {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6"},
+ {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e"},
+ {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d"},
+ {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7"},
+ {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31"},
+ {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95"},
+ {file = "rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d"},
+ {file = "rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15"},
+ {file = "rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d"},
+ {file = "rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0"},
+ {file = "rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be"},
+ {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f"},
+ {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f"},
+ {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87"},
+ {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18"},
+ {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad"},
+ {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07"},
+ {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f"},
+ {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65"},
+ {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f"},
+ {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53"},
+ {file = "rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed"},
+ {file = "rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950"},
+ {file = "rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e"},
+ {file = "rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84"},
]
[[package]]
@@ -7018,14 +7327,14 @@ files = [
[[package]]
name = "s3transfer"
-version = "0.15.0"
+version = "0.16.0"
description = "An Amazon S3 Transfer Manager"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "s3transfer-0.15.0-py3-none-any.whl", hash = "sha256:6f8bf5caa31a0865c4081186689db1b2534cef721d104eb26101de4b9d6a5852"},
- {file = "s3transfer-0.15.0.tar.gz", hash = "sha256:d36fac8d0e3603eff9b5bfa4282c7ce6feb0301a633566153cbd0b93d11d8379"},
+ {file = "s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe"},
+ {file = "s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920"},
]
[package.dependencies]
@@ -7053,14 +7362,14 @@ jeepney = ">=0.6"
[[package]]
name = "sentry-sdk"
-version = "2.46.0"
+version = "2.48.0"
description = "Python client for Sentry (https://sentry.io)"
optional = false
python-versions = ">=3.6"
groups = ["main"]
files = [
- {file = "sentry_sdk-2.46.0-py2.py3-none-any.whl", hash = "sha256:4eeeb60198074dff8d066ea153fa6f241fef1668c10900ea53a4200abc8da9b1"},
- {file = "sentry_sdk-2.46.0.tar.gz", hash = "sha256:91821a23460725734b7741523021601593f35731808afc0bb2ba46c27b8acd91"},
+ {file = "sentry_sdk-2.48.0-py2.py3-none-any.whl", hash = "sha256:6b12ac256769d41825d9b7518444e57fa35b5642df4c7c5e322af4d2c8721172"},
+ {file = "sentry_sdk-2.48.0.tar.gz", hash = "sha256:5213190977ff7fdff8a58b722fb807f8d5524a80488626ebeda1b5676c0c1473"},
]
[package.dependencies]
@@ -7162,83 +7471,90 @@ files = [
{file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
]
+[[package]]
+name = "sortedcontainers"
+version = "2.4.0"
+description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
+optional = false
+python-versions = "*"
+groups = ["main"]
+files = [
+ {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"},
+ {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
+]
+
[[package]]
name = "soupsieve"
-version = "2.8"
+version = "2.8.1"
description = "A modern CSS selector implementation for Beautiful Soup."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c"},
- {file = "soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f"},
+ {file = "soupsieve-2.8.1-py3-none-any.whl", hash = "sha256:a11fe2a6f3d76ab3cf2de04eb339c1be5b506a8a47f2ceb6d139803177f85434"},
+ {file = "soupsieve-2.8.1.tar.gz", hash = "sha256:4cf733bc50fa805f5df4b8ef4740fc0e0fa6218cf3006269afd3f9d6d80fd350"},
]
[[package]]
name = "sqlalchemy"
-version = "2.0.44"
+version = "2.0.45"
description = "Database Abstraction Library"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
- {file = "SQLAlchemy-2.0.44-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:471733aabb2e4848d609141a9e9d56a427c0a038f4abf65dd19d7a21fd563632"},
- {file = "SQLAlchemy-2.0.44-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48bf7d383a35e668b984c805470518b635d48b95a3c57cb03f37eaa3551b5f9f"},
- {file = "SQLAlchemy-2.0.44-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bf4bb6b3d6228fcf3a71b50231199fb94d2dd2611b66d33be0578ea3e6c2726"},
- {file = "SQLAlchemy-2.0.44-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:e998cf7c29473bd077704cea3577d23123094311f59bdc4af551923b168332b1"},
- {file = "SQLAlchemy-2.0.44-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:ebac3f0b5732014a126b43c2b7567f2f0e0afea7d9119a3378bde46d3dcad88e"},
- {file = "SQLAlchemy-2.0.44-cp37-cp37m-win32.whl", hash = "sha256:3255d821ee91bdf824795e936642bbf43a4c7cedf5d1aed8d24524e66843aa74"},
- {file = "SQLAlchemy-2.0.44-cp37-cp37m-win_amd64.whl", hash = "sha256:78e6c137ba35476adb5432103ae1534f2f5295605201d946a4198a0dea4b38e7"},
- {file = "sqlalchemy-2.0.44-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c77f3080674fc529b1bd99489378c7f63fcb4ba7f8322b79732e0258f0ea3ce"},
- {file = "sqlalchemy-2.0.44-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26ef74ba842d61635b0152763d057c8d48215d5be9bb8b7604116a059e9985"},
- {file = "sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4a172b31785e2f00780eccab00bc240ccdbfdb8345f1e6063175b3ff12ad1b0"},
- {file = "sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9480c0740aabd8cb29c329b422fb65358049840b34aba0adf63162371d2a96e"},
- {file = "sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:17835885016b9e4d0135720160db3095dc78c583e7b902b6be799fb21035e749"},
- {file = "sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cbe4f85f50c656d753890f39468fcd8190c5f08282caf19219f684225bfd5fd2"},
- {file = "sqlalchemy-2.0.44-cp310-cp310-win32.whl", hash = "sha256:2fcc4901a86ed81dc76703f3b93ff881e08761c63263c46991081fd7f034b165"},
- {file = "sqlalchemy-2.0.44-cp310-cp310-win_amd64.whl", hash = "sha256:9919e77403a483ab81e3423151e8ffc9dd992c20d2603bf17e4a8161111e55f5"},
- {file = "sqlalchemy-2.0.44-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fe3917059c7ab2ee3f35e77757062b1bea10a0b6ca633c58391e3f3c6c488dd"},
- {file = "sqlalchemy-2.0.44-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:de4387a354ff230bc979b46b2207af841dc8bf29847b6c7dbe60af186d97aefa"},
- {file = "sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3678a0fb72c8a6a29422b2732fe423db3ce119c34421b5f9955873eb9b62c1e"},
- {file = "sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cf6872a23601672d61a68f390e44703442639a12ee9dd5a88bbce52a695e46e"},
- {file = "sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:329aa42d1be9929603f406186630135be1e7a42569540577ba2c69952b7cf399"},
- {file = "sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:70e03833faca7166e6a9927fbee7c27e6ecde436774cd0b24bbcc96353bce06b"},
- {file = "sqlalchemy-2.0.44-cp311-cp311-win32.whl", hash = "sha256:253e2f29843fb303eca6b2fc645aca91fa7aa0aa70b38b6950da92d44ff267f3"},
- {file = "sqlalchemy-2.0.44-cp311-cp311-win_amd64.whl", hash = "sha256:7a8694107eb4308a13b425ca8c0e67112f8134c846b6e1f722698708741215d5"},
- {file = "sqlalchemy-2.0.44-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72fea91746b5890f9e5e0997f16cbf3d53550580d76355ba2d998311b17b2250"},
- {file = "sqlalchemy-2.0.44-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:585c0c852a891450edbb1eaca8648408a3cc125f18cf433941fa6babcc359e29"},
- {file = "sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b94843a102efa9ac68a7a30cd46df3ff1ed9c658100d30a725d10d9c60a2f44"},
- {file = "sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:119dc41e7a7defcefc57189cfa0e61b1bf9c228211aba432b53fb71ef367fda1"},
- {file = "sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0765e318ee9179b3718c4fd7ba35c434f4dd20332fbc6857a5e8df17719c24d7"},
- {file = "sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2e7b5b079055e02d06a4308d0481658e4f06bc7ef211567edc8f7d5dce52018d"},
- {file = "sqlalchemy-2.0.44-cp312-cp312-win32.whl", hash = "sha256:846541e58b9a81cce7dee8329f352c318de25aa2f2bbe1e31587eb1f057448b4"},
- {file = "sqlalchemy-2.0.44-cp312-cp312-win_amd64.whl", hash = "sha256:7cbcb47fd66ab294703e1644f78971f6f2f1126424d2b300678f419aa73c7b6e"},
- {file = "sqlalchemy-2.0.44-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ff486e183d151e51b1d694c7aa1695747599bb00b9f5f604092b54b74c64a8e1"},
- {file = "sqlalchemy-2.0.44-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1af8392eb27b372ddb783b317dea0f650241cea5bd29199b22235299ca2e45"},
- {file = "sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b61188657e3a2b9ac4e8f04d6cf8e51046e28175f79464c67f2fd35bceb0976"},
- {file = "sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87e7b91a5d5973dda5f00cd61ef72ad75a1db73a386b62877d4875a8840959c"},
- {file = "sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15f3326f7f0b2bfe406ee562e17f43f36e16167af99c4c0df61db668de20002d"},
- {file = "sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e77faf6ff919aa8cd63f1c4e561cac1d9a454a191bb864d5dd5e545935e5a40"},
- {file = "sqlalchemy-2.0.44-cp313-cp313-win32.whl", hash = "sha256:ee51625c2d51f8baadf2829fae817ad0b66b140573939dd69284d2ba3553ae73"},
- {file = "sqlalchemy-2.0.44-cp313-cp313-win_amd64.whl", hash = "sha256:c1c80faaee1a6c3428cecf40d16a2365bcf56c424c92c2b6f0f9ad204b899e9e"},
- {file = "sqlalchemy-2.0.44-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2fc44e5965ea46909a416fff0af48a219faefd5773ab79e5f8a5fcd5d62b2667"},
- {file = "sqlalchemy-2.0.44-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dc8b3850d2a601ca2320d081874033684e246d28e1c5e89db0864077cfc8f5a9"},
- {file = "sqlalchemy-2.0.44-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d733dec0614bb8f4bcb7c8af88172b974f685a31dc3a65cca0527e3120de5606"},
- {file = "sqlalchemy-2.0.44-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22be14009339b8bc16d6b9dc8780bacaba3402aa7581658e246114abbd2236e3"},
- {file = "sqlalchemy-2.0.44-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:357bade0e46064f88f2c3a99808233e67b0051cdddf82992379559322dfeb183"},
- {file = "sqlalchemy-2.0.44-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4848395d932e93c1595e59a8672aa7400e8922c39bb9b0668ed99ac6fa867822"},
- {file = "sqlalchemy-2.0.44-cp38-cp38-win32.whl", hash = "sha256:2f19644f27c76f07e10603580a47278abb2a70311136a7f8fd27dc2e096b9013"},
- {file = "sqlalchemy-2.0.44-cp38-cp38-win_amd64.whl", hash = "sha256:1df4763760d1de0dfc8192cc96d8aa293eb1a44f8f7a5fbe74caf1b551905c5e"},
- {file = "sqlalchemy-2.0.44-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7027414f2b88992877573ab780c19ecb54d3a536bef3397933573d6b5068be4"},
- {file = "sqlalchemy-2.0.44-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3fe166c7d00912e8c10d3a9a0ce105569a31a3d0db1a6e82c4e0f4bf16d5eca9"},
- {file = "sqlalchemy-2.0.44-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3caef1ff89b1caefc28f0368b3bde21a7e3e630c2eddac16abd9e47bd27cc36a"},
- {file = "sqlalchemy-2.0.44-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc2856d24afa44295735e72f3c75d6ee7fdd4336d8d3a8f3d44de7aa6b766df2"},
- {file = "sqlalchemy-2.0.44-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:11bac86b0deada30b6b5f93382712ff0e911fe8d31cb9bf46e6b149ae175eff0"},
- {file = "sqlalchemy-2.0.44-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4d18cd0e9a0f37c9f4088e50e3839fcb69a380a0ec957408e0b57cff08ee0a26"},
- {file = "sqlalchemy-2.0.44-cp39-cp39-win32.whl", hash = "sha256:9e9018544ab07614d591a26c1bd4293ddf40752cc435caf69196740516af7100"},
- {file = "sqlalchemy-2.0.44-cp39-cp39-win_amd64.whl", hash = "sha256:8e0e4e66fd80f277a8c3de016a81a554e76ccf6b8d881ee0b53200305a8433f6"},
- {file = "sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05"},
- {file = "sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22"},
+ {file = "sqlalchemy-2.0.45-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c64772786d9eee72d4d3784c28f0a636af5b0a29f3fe26ff11f55efe90c0bd85"},
+ {file = "sqlalchemy-2.0.45-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ae64ebf7657395824a19bca98ab10eb9a3ecb026bf09524014f1bb81cb598d4"},
+ {file = "sqlalchemy-2.0.45-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f02325709d1b1a1489f23a39b318e175a171497374149eae74d612634b234c0"},
+ {file = "sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2c3684fca8a05f0ac1d9a21c1f4a266983a7ea9180efb80ffeb03861ecd01a0"},
+ {file = "sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040f6f0545b3b7da6b9317fc3e922c9a98fc7243b2a1b39f78390fc0942f7826"},
+ {file = "sqlalchemy-2.0.45-cp310-cp310-win32.whl", hash = "sha256:830d434d609fe7bfa47c425c445a8b37929f140a7a44cdaf77f6d34df3a7296a"},
+ {file = "sqlalchemy-2.0.45-cp310-cp310-win_amd64.whl", hash = "sha256:0209d9753671b0da74da2cfbb9ecf9c02f72a759e4b018b3ab35f244c91842c7"},
+ {file = "sqlalchemy-2.0.45-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e90a344c644a4fa871eb01809c32096487928bd2038bf10f3e4515cb688cc56"},
+ {file = "sqlalchemy-2.0.45-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8c8b41b97fba5f62349aa285654230296829672fc9939cd7f35aab246d1c08b"},
+ {file = "sqlalchemy-2.0.45-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12c694ed6468333a090d2f60950e4250b928f457e4962389553d6ba5fe9951ac"},
+ {file = "sqlalchemy-2.0.45-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f7d27a1d977a1cfef38a0e2e1ca86f09c4212666ce34e6ae542f3ed0a33bc606"},
+ {file = "sqlalchemy-2.0.45-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d62e47f5d8a50099b17e2bfc1b0c7d7ecd8ba6b46b1507b58cc4f05eefc3bb1c"},
+ {file = "sqlalchemy-2.0.45-cp311-cp311-win32.whl", hash = "sha256:3c5f76216e7b85770d5bb5130ddd11ee89f4d52b11783674a662c7dd57018177"},
+ {file = "sqlalchemy-2.0.45-cp311-cp311-win_amd64.whl", hash = "sha256:a15b98adb7f277316f2c276c090259129ee4afca783495e212048daf846654b2"},
+ {file = "sqlalchemy-2.0.45-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3ee2aac15169fb0d45822983631466d60b762085bc4535cd39e66bea362df5f"},
+ {file = "sqlalchemy-2.0.45-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba547ac0b361ab4f1608afbc8432db669bd0819b3e12e29fb5fa9529a8bba81d"},
+ {file = "sqlalchemy-2.0.45-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:215f0528b914e5c75ef2559f69dca86878a3beeb0c1be7279d77f18e8d180ed4"},
+ {file = "sqlalchemy-2.0.45-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:107029bf4f43d076d4011f1afb74f7c3e2ea029ec82eb23d8527d5e909e97aa6"},
+ {file = "sqlalchemy-2.0.45-cp312-cp312-win32.whl", hash = "sha256:0c9f6ada57b58420a2c0277ff853abe40b9e9449f8d7d231763c6bc30f5c4953"},
+ {file = "sqlalchemy-2.0.45-cp312-cp312-win_amd64.whl", hash = "sha256:8defe5737c6d2179c7997242d6473587c3beb52e557f5ef0187277009f73e5e1"},
+ {file = "sqlalchemy-2.0.45-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe187fc31a54d7fd90352f34e8c008cf3ad5d064d08fedd3de2e8df83eb4a1cf"},
+ {file = "sqlalchemy-2.0.45-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:672c45cae53ba88e0dad74b9027dddd09ef6f441e927786b05bec75d949fbb2e"},
+ {file = "sqlalchemy-2.0.45-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:470daea2c1ce73910f08caf10575676a37159a6d16c4da33d0033546bddebc9b"},
+ {file = "sqlalchemy-2.0.45-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9c6378449e0940476577047150fd09e242529b761dc887c9808a9a937fe990c8"},
+ {file = "sqlalchemy-2.0.45-cp313-cp313-win32.whl", hash = "sha256:4b6bec67ca45bc166c8729910bd2a87f1c0407ee955df110d78948f5b5827e8a"},
+ {file = "sqlalchemy-2.0.45-cp313-cp313-win_amd64.whl", hash = "sha256:afbf47dc4de31fa38fd491f3705cac5307d21d4bb828a4f020ee59af412744ee"},
+ {file = "sqlalchemy-2.0.45-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83d7009f40ce619d483d26ac1b757dfe3167b39921379a8bd1b596cf02dab4a6"},
+ {file = "sqlalchemy-2.0.45-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d8a2ca754e5415cde2b656c27900b19d50ba076aa05ce66e2207623d3fe41f5a"},
+ {file = "sqlalchemy-2.0.45-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f46ec744e7f51275582e6a24326e10c49fbdd3fc99103e01376841213028774"},
+ {file = "sqlalchemy-2.0.45-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:883c600c345123c033c2f6caca18def08f1f7f4c3ebeb591a63b6fceffc95cce"},
+ {file = "sqlalchemy-2.0.45-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2c0b74aa79e2deade948fe8593654c8ef4228c44ba862bb7c9585c8e0db90f33"},
+ {file = "sqlalchemy-2.0.45-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a420169cef179d4c9064365f42d779f1e5895ad26ca0c8b4c0233920973db74"},
+ {file = "sqlalchemy-2.0.45-cp314-cp314-win32.whl", hash = "sha256:e50dcb81a5dfe4b7b4a4aa8f338116d127cb209559124f3694c70d6cd072b68f"},
+ {file = "sqlalchemy-2.0.45-cp314-cp314-win_amd64.whl", hash = "sha256:4748601c8ea959e37e03d13dcda4a44837afcd1b21338e637f7c935b8da06177"},
+ {file = "sqlalchemy-2.0.45-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd337d3526ec5298f67d6a30bbbe4ed7e5e68862f0bf6dd21d289f8d37b7d60b"},
+ {file = "sqlalchemy-2.0.45-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9a62b446b7d86a3909abbcd1cd3cc550a832f99c2bc37c5b22e1925438b9367b"},
+ {file = "sqlalchemy-2.0.45-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5964f832431b7cdfaaa22a660b4c7eb1dfcd6ed41375f67fd3e3440fd95cb3cc"},
+ {file = "sqlalchemy-2.0.45-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee580ab50e748208754ae8980cec79ec205983d8cf8b3f7c39067f3d9f2c8e22"},
+ {file = "sqlalchemy-2.0.45-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13e27397a7810163440c6bfed6b3fe46f1bfb2486eb540315a819abd2c004128"},
+ {file = "sqlalchemy-2.0.45-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ed3635353e55d28e7f4a95c8eda98a5cdc0a0b40b528433fbd41a9ae88f55b3d"},
+ {file = "sqlalchemy-2.0.45-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:db6834900338fb13a9123307f0c2cbb1f890a8656fcd5e5448ae3ad5bbe8d312"},
+ {file = "sqlalchemy-2.0.45-cp38-cp38-win32.whl", hash = "sha256:1d8b4a7a8c9b537509d56d5cd10ecdcfbb95912d72480c8861524efecc6a3fff"},
+ {file = "sqlalchemy-2.0.45-cp38-cp38-win_amd64.whl", hash = "sha256:ebd300afd2b62679203435f596b2601adafe546cb7282d5a0cd3ed99e423720f"},
+ {file = "sqlalchemy-2.0.45-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d29b2b99d527dbc66dd87c3c3248a5dd789d974a507f4653c969999fc7c1191b"},
+ {file = "sqlalchemy-2.0.45-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:59a8b8bd9c6bedf81ad07c8bd5543eedca55fe9b8780b2b628d495ba55f8db1e"},
+ {file = "sqlalchemy-2.0.45-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd93c6f5d65f254ceabe97548c709e073d6da9883343adaa51bf1a913ce93f8e"},
+ {file = "sqlalchemy-2.0.45-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6d0beadc2535157070c9c17ecf25ecec31e13c229a8f69196d7590bde8082bf1"},
+ {file = "sqlalchemy-2.0.45-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e057f928ffe9c9b246a55b469c133b98a426297e1772ad24ce9f0c47d123bd5b"},
+ {file = "sqlalchemy-2.0.45-cp39-cp39-win32.whl", hash = "sha256:c1c2091b1489435ff85728fafeb990f073e64f6f5e81d5cd53059773e8521eb6"},
+ {file = "sqlalchemy-2.0.45-cp39-cp39-win_amd64.whl", hash = "sha256:56ead1f8dfb91a54a28cd1d072c74b3d635bcffbd25e50786533b822d4f2cde2"},
+ {file = "sqlalchemy-2.0.45-py3-none-any.whl", hash = "sha256:5225a288e4c8cc2308dbdd874edad6e7d0fd38eac1e9e5f23503425c8eee20d0"},
+ {file = "sqlalchemy-2.0.45.tar.gz", hash = "sha256:1632a4bda8d2d25703fdad6363058d882541bdaaee0e5e3ddfa0cd3229efce88"},
]
[package.dependencies]
@@ -7272,38 +7588,39 @@ sqlcipher = ["sqlcipher3_binary"]
[[package]]
name = "sqlparse"
-version = "0.5.3"
+version = "0.5.5"
description = "A non-validating SQL parser."
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
- {file = "sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca"},
- {file = "sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272"},
+ {file = "sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba"},
+ {file = "sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e"},
]
[package.extras]
-dev = ["build", "hatch"]
+dev = ["build"]
doc = ["sphinx"]
[[package]]
name = "sse-starlette"
-version = "3.0.3"
+version = "3.1.1"
description = "SSE plugin for Starlette"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "sse_starlette-3.0.3-py3-none-any.whl", hash = "sha256:af5bf5a6f3933df1d9c7f8539633dc8444ca6a97ab2e2a7cd3b6e431ac03a431"},
- {file = "sse_starlette-3.0.3.tar.gz", hash = "sha256:88cfb08747e16200ea990c8ca876b03910a23b547ab3bd764c0d8eb81019b971"},
+ {file = "sse_starlette-3.1.1-py3-none-any.whl", hash = "sha256:bb38f71ae74cfd86b529907a9fda5632195dfa6ae120f214ea4c890c7ee9d436"},
+ {file = "sse_starlette-3.1.1.tar.gz", hash = "sha256:bffa531420c1793ab224f63648c059bcadc412bf9fdb1301ac8de1cf9a67b7fb"},
]
[package.dependencies]
anyio = ">=4.7.0"
+starlette = ">=0.49.1"
[package.extras]
daphne = ["daphne (>=4.2.0)"]
-examples = ["aiosqlite (>=0.21.0)", "fastapi (>=0.115.12)", "sqlalchemy[asyncio] (>=2.0.41)", "starlette (>=0.49.1)", "uvicorn (>=0.34.0)"]
+examples = ["aiosqlite (>=0.21.0)", "fastapi (>=0.115.12)", "sqlalchemy[asyncio] (>=2.0.41)", "uvicorn (>=0.34.0)"]
granian = ["granian (>=2.3.1)"]
uvicorn = ["uvicorn (>=0.34.0)"]
@@ -7636,42 +7953,60 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,
[[package]]
name = "ty"
-version = "0.0.1a28"
+version = "0.0.1a35"
description = "An extremely fast Python type checker, written in Rust."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
- {file = "ty-0.0.1a28-py3-none-linux_armv6l.whl", hash = "sha256:0ea28aaaf35176a75ce85da7a4b7f577f3a3319a1eb4d13c0105629e239a7d95"},
- {file = "ty-0.0.1a28-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:463f8b6bee5c3d338a535c40764a4f209f5465caecbc9f7358ee2a7f8b2d321e"},
- {file = "ty-0.0.1a28-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7d037ea9f896e6e9b96ca066959e2a7600db0da9e4038f1247c9337af253cc8c"},
- {file = "ty-0.0.1a28-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad5099ffaa891391733d6fd85bcdd00ad68042a2da4f80a114b9e7044e6f7460"},
- {file = "ty-0.0.1a28-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:587652aecb8d238adcb45ae7cd12efd27b9778f74b636cbbe5dcc2e938f9af4e"},
- {file = "ty-0.0.1a28-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d9556c87419264ffc3071a249f89d890a29df5d09abd8d216bac850ad2d7ba9"},
- {file = "ty-0.0.1a28-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7481abc03a0aabf966c9e1cccb18c9edbb7cf01ec011568cd24feb1ab45faef7"},
- {file = "ty-0.0.1a28-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fd4926f668b733aeadd09f7d16e63af30cba5438bbba1274f950a1059c8d64"},
- {file = "ty-0.0.1a28-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fb119d7db1a064dd74ccedf78bdc5caae30cf5de421dff972a849bcff411269"},
- {file = "ty-0.0.1a28-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd7f7d744920af9ceaf7fe6db290366abefbcffd7cce54f15e8cef6a86e2df31"},
- {file = "ty-0.0.1a28-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c20c6cf7e786ecf6c8f34892240b4b1ae8b1adce52243868aa400c80b7a9bc1d"},
- {file = "ty-0.0.1a28-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:54c94a06c0236dfd249217e28816b6aedfc40e71d5b5131924efa3b095dfcf1a"},
- {file = "ty-0.0.1a28-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1a15eb2535229ab65aaafbe3fb22c3d289c4e34cda92fb748815573b6d52fe3a"},
- {file = "ty-0.0.1a28-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6c2ebd5314707cd26aabe77b1d664e597b7b29a8d07fed5091f986ebdaa261a9"},
- {file = "ty-0.0.1a28-py3-none-win32.whl", hash = "sha256:ae10abd8575d28744d905979632040222581ba364281abf75baf8f269a10ffc3"},
- {file = "ty-0.0.1a28-py3-none-win_amd64.whl", hash = "sha256:44ef82c1169c050ad9e91b2d76251be097ddd163719735cf7e5a978065f6b87c"},
- {file = "ty-0.0.1a28-py3-none-win_arm64.whl", hash = "sha256:051c1d43df50366fb8e795ae52af8f2015b79d176dbb82cdd45668074847ddf3"},
- {file = "ty-0.0.1a28.tar.gz", hash = "sha256:6454f2bc0d5b716aeaba3e32c4585a14a0d6bfc7e90d5aba64539fa33df824c4"},
+ {file = "ty-0.0.1a35-py3-none-linux_armv6l.whl", hash = "sha256:70dde7bcf9ccf56be0708d1a399c5b796c38f38726dddbc61d5d64d84f83af6b"},
+ {file = "ty-0.0.1a35-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:56a4f81fa980565abe692af82266c1c348bd4eaf4205bff4fcb5bb6ec0a211f4"},
+ {file = "ty-0.0.1a35-py3-none-macosx_11_0_arm64.whl", hash = "sha256:144ab0ca5d0552b4248ae31a7ca89f434d5f16df29bcc95100bef1fdec892b95"},
+ {file = "ty-0.0.1a35-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6db9a9e252ca73b4bae063ae3bac9e0723557aff6298f96ad1696a985b3e623f"},
+ {file = "ty-0.0.1a35-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:081955635f12233433f74eff51acb2f5380ed23091c9c72d09d02494073c9d4f"},
+ {file = "ty-0.0.1a35-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4f59fa2dc7dc4e8f0c9135738670c6691072de0b5b11582c9ca55895f8b92c9"},
+ {file = "ty-0.0.1a35-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:48e466329c16f0dc4d60b54539bb3e50983516f7024e7e260d50f4b4bbbd4c3e"},
+ {file = "ty-0.0.1a35-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87ca20fe7f05dd7335f863cfcff4d51d611816b413be6d14e20c146e4e522471"},
+ {file = "ty-0.0.1a35-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:188c8e2d7f3767d20dcd44b38cc585a0ab4fcee2ebcdb1d4442ca588dbc8c688"},
+ {file = "ty-0.0.1a35-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dba3072078486e4e5ffebe83ac2e2199053fabddecbf5b8b9736021a8258556"},
+ {file = "ty-0.0.1a35-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b9bdef2a44fc1e8e40531bd714f9ec4f208dd13474f307547c0a93055dbaeccc"},
+ {file = "ty-0.0.1a35-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c4d1ed3b9c954ddb258ece43c84bb2dafd569bad3f437467b594de535caf3bde"},
+ {file = "ty-0.0.1a35-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ae9e28d2d549faac040b6b8acd9338b7acebbd7185e89af30b01bb115d68ddd4"},
+ {file = "ty-0.0.1a35-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0cb5f1084023d5bef5afa510db3a5052d4d18160e1708d3c5bf5a23f1d59a792"},
+ {file = "ty-0.0.1a35-py3-none-win32.whl", hash = "sha256:11e3ee5df2a26fd6d0764cf425ac538a225fc077a694ba2c0f4a0fa78b760c07"},
+ {file = "ty-0.0.1a35-py3-none-win_amd64.whl", hash = "sha256:9448a12b34f377eb841141908bfd74861404d25c21d8c934e67415a3655c952b"},
+ {file = "ty-0.0.1a35-py3-none-win_arm64.whl", hash = "sha256:78fbb25e4a516568c993a2e12024285248ac091ad24a5b2f41cc815b8c65935e"},
+ {file = "ty-0.0.1a35.tar.gz", hash = "sha256:b122986b36a7e7482943312026948e67670f8f252bdf1afa5378b1a59372c25e"},
+]
+
+[[package]]
+name = "typer"
+version = "0.21.0"
+description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "typer-0.21.0-py3-none-any.whl", hash = "sha256:c79c01ca6b30af9fd48284058a7056ba0d3bf5cf10d0ff3d0c5b11b68c258ac6"},
+ {file = "typer-0.21.0.tar.gz", hash = "sha256:c87c0d2b6eee3b49c5c64649ec92425492c14488096dfbc8a0c2799b2f6f9c53"},
]
+[package.dependencies]
+click = ">=8.0.0"
+rich = ">=10.11.0"
+shellingham = ">=1.3.0"
+typing-extensions = ">=3.7.4.3"
+
[[package]]
name = "typer-slim"
-version = "0.20.0"
+version = "0.21.0"
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "typer_slim-0.20.0-py3-none-any.whl", hash = "sha256:f42a9b7571a12b97dddf364745d29f12221865acef7a2680065f9bb29c7dc89d"},
- {file = "typer_slim-0.20.0.tar.gz", hash = "sha256:9fc6607b3c6c20f5c33ea9590cbeb17848667c51feee27d9e314a579ab07d1a3"},
+ {file = "typer_slim-0.21.0-py3-none-any.whl", hash = "sha256:92aee2188ac6fc2b2924bd75bb61a340b78bd8cd51fd9735533ce5a856812c8e"},
+ {file = "typer_slim-0.21.0.tar.gz", hash = "sha256:f2dbd150cfa0fead2242e21fa9f654dfc64773763ddf07c6be9a49ad34f79557"},
]
[package.dependencies]
@@ -7683,14 +8018,14 @@ standard = ["rich (>=10.11.0)", "shellingham (>=1.3.0)"]
[[package]]
name = "types-protobuf"
-version = "6.32.1.20251105"
+version = "6.32.1.20251210"
description = "Typing stubs for protobuf"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "types_protobuf-6.32.1.20251105-py3-none-any.whl", hash = "sha256:a15109d38f7cfefd2539ef86d3f93a6a41c7cad53924f8aa1a51eaddbb72a660"},
- {file = "types_protobuf-6.32.1.20251105.tar.gz", hash = "sha256:641002611ff87dd9fedc38a39a29cacb9907ae5ce61489b53e99ca2074bef764"},
+ {file = "types_protobuf-6.32.1.20251210-py3-none-any.whl", hash = "sha256:2641f78f3696822a048cfb8d0ff42ccd85c25f12f871fbebe86da63793692140"},
+ {file = "types_protobuf-6.32.1.20251210.tar.gz", hash = "sha256:c698bb3f020274b1a2798ae09dc773728ce3f75209a35187bd11916ebfde6763"},
]
[[package]]
@@ -7754,14 +8089,14 @@ typing-extensions = ">=4.12.0"
[[package]]
name = "tzdata"
-version = "2025.2"
+version = "2025.3"
description = "Provider of IANA time zone data"
optional = false
python-versions = ">=2"
groups = ["main"]
files = [
- {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"},
- {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"},
+ {file = "tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1"},
+ {file = "tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7"},
]
[[package]]
@@ -7822,14 +8157,14 @@ xlsx = ["msoffcrypto-tool", "networkx", "openpyxl", "pandas", "xlrd"]
[[package]]
name = "unstructured-client"
-version = "0.42.4"
+version = "0.42.6"
description = "Python Client SDK for Unstructured API"
optional = false
python-versions = ">=3.9.2"
groups = ["main"]
files = [
- {file = "unstructured_client-0.42.4-py3-none-any.whl", hash = "sha256:fc6341344dd2f2e2aed793636b5f4e6204cad741ff2253d5a48ff2f2bccb8e9a"},
- {file = "unstructured_client-0.42.4.tar.gz", hash = "sha256:144ecd231a11d091cdc76acf50e79e57889269b8c9d8b9df60e74cf32ac1ba5e"},
+ {file = "unstructured_client-0.42.6-py3-none-any.whl", hash = "sha256:c93b1d9d1b9f63a8e961729d00224b3659ef9ef3e14996ea4e53ddc95df671a9"},
+ {file = "unstructured_client-0.42.6.tar.gz", hash = "sha256:ea54f2c4ca3e7a1330f9e77cbc96f88f829518beeec5e1b797b5352f4d76a73a"},
]
[package.dependencies]
@@ -7843,32 +8178,64 @@ requests-toolbelt = ">=1.0.0"
[[package]]
name = "urllib3"
-version = "2.5.0"
+version = "2.6.2"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"},
- {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"},
+ {file = "urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd"},
+ {file = "urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797"},
]
[package.extras]
-brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
+brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""]
h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
-zstd = ["zstandard (>=0.18.0)"]
+zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""]
+
+[[package]]
+name = "uuid-utils"
+version = "0.12.0"
+description = "Drop-in replacement for Python UUID with bindings in Rust"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+ {file = "uuid_utils-0.12.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3b9b30707659292f207b98f294b0e081f6d77e1fbc760ba5b41331a39045f514"},
+ {file = "uuid_utils-0.12.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:add3d820c7ec14ed37317375bea30249699c5d08ff4ae4dbee9fc9bce3bfbf65"},
+ {file = "uuid_utils-0.12.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b8fce83ecb3b16af29c7809669056c4b6e7cc912cab8c6d07361645de12dd79"},
+ {file = "uuid_utils-0.12.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec921769afcb905035d785582b0791d02304a7850fbd6ce924c1a8976380dfc6"},
+ {file = "uuid_utils-0.12.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f3b060330f5899a92d5c723547dc6a95adef42433e9748f14c66859a7396664"},
+ {file = "uuid_utils-0.12.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:908dfef7f0bfcf98d406e5dc570c25d2f2473e49b376de41792b6e96c1d5d291"},
+ {file = "uuid_utils-0.12.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c6a24148926bd0ca63e8a2dabf4cc9dc329a62325b3ad6578ecd60fbf926506"},
+ {file = "uuid_utils-0.12.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:64a91e632669f059ef605f1771d28490b1d310c26198e46f754e8846dddf12f4"},
+ {file = "uuid_utils-0.12.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:93c082212470bb4603ca3975916c205a9d7ef1443c0acde8fbd1e0f5b36673c7"},
+ {file = "uuid_utils-0.12.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:431b1fb7283ba974811b22abd365f2726f8f821ab33f0f715be389640e18d039"},
+ {file = "uuid_utils-0.12.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2ffd7838c40149100299fa37cbd8bab5ee382372e8e65a148002a37d380df7c8"},
+ {file = "uuid_utils-0.12.0-cp39-abi3-win32.whl", hash = "sha256:487f17c0fee6cbc1d8b90fe811874174a9b1b5683bf2251549e302906a50fed3"},
+ {file = "uuid_utils-0.12.0-cp39-abi3-win_amd64.whl", hash = "sha256:9598e7c9da40357ae8fffc5d6938b1a7017f09a1acbcc95e14af8c65d48c655a"},
+ {file = "uuid_utils-0.12.0-cp39-abi3-win_arm64.whl", hash = "sha256:c9bea7c5b2aa6f57937ebebeee4d4ef2baad10f86f1b97b58a3f6f34c14b4e84"},
+ {file = "uuid_utils-0.12.0-pp311-pypy311_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e2209d361f2996966ab7114f49919eb6aaeabc6041672abbbbf4fdbb8ec1acc0"},
+ {file = "uuid_utils-0.12.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d9636bcdbd6cfcad2b549c352b669412d0d1eb09be72044a2f13e498974863cd"},
+ {file = "uuid_utils-0.12.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8cd8543a3419251fb78e703ce3b15fdfafe1b7c542cf40caf0775e01db7e7674"},
+ {file = "uuid_utils-0.12.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e98db2d8977c052cb307ae1cb5cc37a21715e8d415dbc65863b039397495a013"},
+ {file = "uuid_utils-0.12.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8f2bdf5e4ffeb259ef6d15edae92aed60a1d6f07cbfab465d836f6b12b48da8"},
+ {file = "uuid_utils-0.12.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c3ec53c0cb15e1835870c139317cc5ec06e35aa22843e3ed7d9c74f23f23898"},
+ {file = "uuid_utils-0.12.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:84e5c0eba209356f7f389946a3a47b2cc2effd711b3fc7c7f155ad9f7d45e8a3"},
+ {file = "uuid_utils-0.12.0.tar.gz", hash = "sha256:252bd3d311b5d6b7f5dfce7a5857e27bb4458f222586bb439463231e5a9cbd64"},
+]
[[package]]
name = "uvicorn"
-version = "0.38.0"
+version = "0.40.0"
description = "The lightning-fast ASGI server."
optional = false
-python-versions = ">=3.9"
+python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02"},
- {file = "uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d"},
+ {file = "uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee"},
+ {file = "uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea"},
]
[package.dependencies]
@@ -8604,4 +8971,4 @@ cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and pyt
[metadata]
lock-version = "2.1"
python-versions = "^3.11"
-content-hash = "a53642bd3db034d6eb6f352814ce931b199e7912e418fbc8a5e1016a2b4c998c"
+content-hash = "0d56df14efce74c0a81cde9325cf08237e7aea2c1e17ae89ab2027e4fc04e537"
diff --git a/pyproject.toml b/pyproject.toml
index 6bea657..5d93703 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -42,6 +42,7 @@ replicate = "^1.0.7"
pgvector = "^0.4.1"
gpt-researcher = "^0.14.5"
pillow = "^12.0.0"
+exa-py = "^2.0.2"
[tool.poetry.group.dev.dependencies]
pre-commit = "^3.2.1"
diff --git a/requirements.txt b/requirements.txt
index 3ba9987..2e6aeb7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,7 +6,7 @@ aiosignal==1.4.0 ; python_version >= "3.11" and python_version < "4.0"
annotated-doc==0.0.4 ; python_version >= "3.11" and python_version < "4.0"
annotated-types==0.7.0 ; python_version >= "3.11" and python_version < "4.0"
anthropic==0.75.0 ; python_version >= "3.11" and python_version < "4.0"
-anyio==4.11.0 ; python_version >= "3.11" and python_version < "4.0"
+anyio==4.12.0 ; python_version >= "3.11" and python_version < "4.0"
argcomplete==3.6.3 ; python_version >= "3.11" and python_version < "4.0"
arrow==1.4.0 ; python_version >= "3.11" and python_version < "4.0"
arxiv==2.3.1 ; python_version >= "3.11" and python_version < "4.0"
@@ -14,28 +14,29 @@ asgiref==3.11.0 ; python_version >= "3.11" and python_version < "4.0"
asttokens==3.0.1 ; python_version >= "3.11" and python_version < "4.0"
async-timeout==5.0.1 ; python_version >= "3.11" and python_full_version < "3.11.3"
attrs==25.4.0 ; python_version >= "3.11" and python_version < "4.0"
-authlib==1.6.5 ; python_version >= "3.11" and python_version < "4.0"
+authlib==1.6.6 ; python_version >= "3.11" and python_version < "4.0"
backoff==2.2.1 ; python_version >= "3.11" and python_version < "4.0"
backports-tarfile==1.2.0 ; python_version == "3.11"
-beartype==0.22.6 ; python_version >= "3.11" and python_version < "4.0"
-beautifulsoup4==4.14.2 ; python_version >= "3.11" and python_version < "4.0"
+beartype==0.22.9 ; python_version >= "3.11" and python_version < "4.0"
+beautifulsoup4==4.14.3 ; python_version >= "3.11" and python_version < "4.0"
binaryornot==0.4.4 ; python_version >= "3.11" and python_version < "4.0"
-boto3==1.41.5 ; python_version >= "3.11" and python_version < "4.0"
-botocore==1.41.5 ; python_version >= "3.11" and python_version < "4.0"
+boto3==1.42.16 ; python_version >= "3.11" and python_version < "4.0"
+botocore==1.42.16 ; python_version >= "3.11" and python_version < "4.0"
brotli==1.2.0 ; python_version >= "3.11" and python_version < "4.0"
brotlicffi==1.2.0.0 ; python_version >= "3.11" and python_version < "4.0" and platform_python_implementation != "CPython"
-cachetools==6.2.2 ; python_version >= "3.11" and python_version < "4.0"
+cachetools==6.2.4 ; python_version >= "3.11" and python_version < "4.0"
certifi==2025.11.12 ; python_version >= "3.11" and python_version < "4.0"
cffi==2.0.0 ; python_version >= "3.11" and python_version < "4.0"
chardet==5.2.0 ; python_version >= "3.11" and python_version < "4.0"
charset-normalizer==3.4.4 ; python_version >= "3.11" and python_version < "4.0"
click==8.3.1 ; python_version >= "3.11" and python_version < "4.0"
-cohere==5.20.0 ; python_version >= "3.11" and python_version < "4.0" and platform_system != "Emscripten"
+cloudpickle==3.1.2 ; python_version >= "3.11" and python_version < "4.0"
+cohere==5.20.1 ; python_version >= "3.11" and python_version < "4.0" and platform_system != "Emscripten"
colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0"
cookiecutter==2.6.0 ; python_version >= "3.11" and python_version < "4.0"
cryptography==46.0.3 ; python_version >= "3.11" and python_version < "4.0"
cssselect2==0.8.0 ; python_version >= "3.11" and python_version < "4.0"
-cyclopts==4.3.0 ; python_version >= "3.11" and python_version < "4.0"
+cyclopts==4.4.1 ; python_version >= "3.11" and python_version < "4.0"
dataclasses-json==0.6.7 ; python_version >= "3.11" and python_version < "4.0"
decorator==5.2.1 ; python_version >= "3.11" and python_version < "4.0"
diskcache==5.6.3 ; python_version >= "3.11" and python_version < "4.0"
@@ -46,69 +47,72 @@ django-anymail==12.0 ; python_version >= "3.11" and python_version < "4.0"
django-environ==0.11.2 ; python_version >= "3.11" and python_version < "4.0"
django-extensions==3.2.3 ; python_version >= "3.11" and python_version < "4.0"
django-ipware==7.0.1 ; python_version >= "3.11" and python_version < "4.0"
-django-mjml==1.4 ; python_version >= "3.11" and python_version < "4.0"
-django-ninja==1.5.0 ; python_version >= "3.11" and python_version < "4.0"
+django-mjml==1.5 ; python_version >= "3.11" and python_version < "4.0"
+django-ninja==1.5.1 ; python_version >= "3.11" and python_version < "4.0"
django-picklefield==3.4.0 ; python_version >= "3.11" and python_version < "4.0"
django-q-sentry==0.1.6 ; python_version >= "3.11" and python_version < "4.0"
-django-q2==1.8.0 ; python_version >= "3.11" and python_version < "4.0"
+django-q2==1.9.0 ; python_version >= "3.11" and python_version < "4.0"
django-storages==1.14.6 ; python_version >= "3.11" and python_version < "4.0"
django-structlog==8.1.0 ; python_version >= "3.11" and python_version < "4.0"
django-widget-tweaks==1.5.0 ; python_version >= "3.11" and python_version < "4.0"
-django==5.2.8 ; python_version >= "3.11" and python_version < "4.0"
+django==5.2.9 ; python_version >= "3.11" and python_version < "4.0"
dnspython==2.8.0 ; python_version >= "3.11" and python_version < "4.0"
docopt==0.6.2 ; python_version >= "3.11" and python_version < "4.0"
docstring-parser==0.17.0 ; python_version >= "3.11" and python_version < "4.0"
-docutils==0.22.3 ; python_version >= "3.11" and python_version < "4.0"
+docutils==0.22.4 ; python_version >= "3.11" and python_version < "4.0"
duckduckgo-search==8.1.1 ; python_version >= "3.11" and python_version < "4.0"
email-validator==2.3.0 ; python_version >= "3.11" and python_version < "4.0"
emoji==2.15.0 ; python_version >= "3.11" and python_version < "4.0"
-eval-type-backport==0.3.0 ; python_version >= "3.11" and python_version < "4.0"
+eval-type-backport==0.3.1 ; python_version >= "3.11" and python_version < "4.0"
+exa-py==2.0.2 ; python_version >= "3.11" and python_version < "4.0"
exceptiongroup==1.3.1 ; python_version >= "3.11" and python_version < "4.0"
executing==2.2.1 ; python_version >= "3.11" and python_version < "4.0"
-fastapi==0.122.0 ; python_version >= "3.11" and python_version < "4.0"
+fakeredis==2.33.0 ; python_version >= "3.11" and python_version < "4.0"
+fastapi==0.127.1 ; python_version >= "3.11" and python_version < "4.0"
fastavro==1.12.1 ; python_version >= "3.11" and python_version < "4.0" and platform_system != "Emscripten"
-fastmcp==2.13.1 ; python_version >= "3.11" and python_version < "4.0"
+fastmcp==2.14.0 ; python_version >= "3.11" and python_version < "4.0"
fastuuid==0.14.0 ; python_version >= "3.11" and python_version < "4.0"
feedparser==6.0.12 ; python_version >= "3.11" and python_version < "4.0"
-filelock==3.20.0 ; python_version >= "3.11" and python_version < "4.0"
+filelock==3.20.1 ; python_version >= "3.11" and python_version < "4.0"
filetype==1.2.0 ; python_version >= "3.11" and python_version < "4.0"
-fonttools==4.60.1 ; python_version >= "3.11" and python_version < "4.0"
+fonttools==4.61.1 ; python_version >= "3.11" and python_version < "4.0"
frozenlist==1.8.0 ; python_version >= "3.11" and python_version < "4.0"
-fsspec==2025.10.0 ; python_version >= "3.11" and python_version < "4.0"
-genai-prices==0.0.47 ; python_version >= "3.11" and python_version < "4.0"
-google-auth==2.43.0 ; python_version >= "3.11" and python_version < "4.0"
-google-genai==1.52.0 ; python_version >= "3.11" and python_version < "4.0"
+fsspec==2025.12.0 ; python_version >= "3.11" and python_version < "4.0"
+genai-prices==0.0.49 ; python_version >= "3.11" and python_version < "4.0"
+google-auth==2.45.0 ; python_version >= "3.11" and python_version < "4.0"
+google-genai==1.56.0 ; python_version >= "3.11" and python_version < "4.0"
googleapis-common-protos==1.72.0 ; python_version >= "3.11" and python_version < "4.0"
gpt-researcher==0.14.5 ; python_version >= "3.11" and python_version < "4.0"
-greenlet==3.2.4 ; python_version >= "3.11" and python_version < "4.0"
+greenlet==3.3.0 ; python_version >= "3.11" and python_version < "4.0"
griffe==1.15.0 ; python_version >= "3.11" and python_version < "4.0"
-groq==0.36.0 ; python_version >= "3.11" and python_version < "4.0"
-grpcio==1.67.1 ; python_version >= "3.11" and python_version < "4.0"
+groq==1.0.0 ; python_version >= "3.11" and python_version < "4.0"
+grpcio==1.67.1 ; python_version >= "3.11" and python_version <= "3.13"
+grpcio==1.76.0 ; python_version >= "3.14" and python_version < "4.0"
gunicorn==23.0.0 ; python_version >= "3.11" and python_version < "4.0"
h11==0.16.0 ; python_version >= "3.11" and python_version < "4.0"
hf-xet==1.2.0 ; python_version >= "3.11" and python_version < "4.0" and (platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "arm64" or platform_machine == "aarch64")
html5lib==1.1 ; python_version >= "3.11" and python_version < "4.0"
htmldocx==0.0.6 ; python_version >= "3.11" and python_version < "4.0"
httpcore==1.0.9 ; python_version >= "3.11" and python_version < "4.0"
-httpx-aiohttp==0.1.9 ; python_version >= "3.11" and python_version < "4.0"
-httpx-sse==0.4.0 ; python_version >= "3.11" and python_version < "4.0"
+httpx-aiohttp==0.1.12 ; python_version >= "3.11" and python_version < "4.0"
+httpx-sse==0.4.3 ; python_version >= "3.11" and python_version < "4.0"
httpx==0.28.1 ; python_version >= "3.11" and python_version < "4.0"
-huggingface-hub==1.1.5 ; python_version >= "3.11" and python_version < "4.0"
+huggingface-hub==1.2.3 ; python_version >= "3.11" and python_version < "4.0"
idna==3.11 ; python_version >= "3.11" and python_version < "4.0"
-importlib-metadata==8.7.0 ; python_version >= "3.11" and python_version < "4.0"
+importlib-metadata==8.7.1 ; python_version >= "3.11" and python_version < "4.0"
iniconfig==2.3.0 ; python_version >= "3.11" and python_version < "4.0"
invoke==2.2.1 ; python_version >= "3.11" and python_version < "4.0"
ipython==8.37.0 ; python_version >= "3.11" and python_version < "4.0"
jaraco-classes==3.4.0 ; python_version >= "3.11" and python_version < "4.0"
-jaraco-context==6.0.1 ; python_version >= "3.11" and python_version < "4.0"
-jaraco-functools==4.3.0 ; python_version >= "3.11" and python_version < "4.0"
+jaraco-context==6.0.2 ; python_version >= "3.11" and python_version < "4.0"
+jaraco-functools==4.4.0 ; python_version >= "3.11" and python_version < "4.0"
jedi==0.19.2 ; python_version >= "3.11" and python_version < "4.0"
jeepney==0.9.0 ; python_version >= "3.11" and python_version < "4.0" and sys_platform == "linux"
jinja2==3.1.6 ; python_version >= "3.11" and python_version < "4.0"
jiter==0.12.0 ; python_version >= "3.11" and python_version < "4.0"
jmespath==1.0.1 ; python_version >= "3.11" and python_version < "4.0"
-joblib==1.5.2 ; python_version >= "3.11" and python_version < "4.0"
-json-repair==0.54.2 ; python_version >= "3.11" and python_version < "4.0"
+joblib==1.5.3 ; python_version >= "3.11" and python_version < "4.0"
+json-repair==0.54.3 ; python_version >= "3.11" and python_version < "4.0"
json5==0.12.1 ; python_version >= "3.11" and python_version < "4.0"
jsonpatch==1.33 ; python_version >= "3.11" and python_version < "4.0"
jsonpointer==3.0.0 ; python_version >= "3.11" and python_version < "4.0"
@@ -117,36 +121,37 @@ jsonschema-specifications==2025.9.1 ; python_version >= "3.11" and python_versio
jsonschema==4.25.1 ; python_version >= "3.11" and python_version < "4.0"
keyring==25.7.0 ; python_version >= "3.11" and python_version < "4.0"
kiwisolver==1.4.9 ; python_version >= "3.11" and python_version < "4.0"
-langchain-classic==1.0.0 ; python_version >= "3.11" and python_version < "4.0"
+langchain-classic==1.0.1 ; python_version >= "3.11" and python_version < "4.0"
langchain-community==0.4.1 ; python_version >= "3.11" and python_version < "4.0"
-langchain-core==1.1.0 ; python_version >= "3.11" and python_version < "4.0"
-langchain-ollama==1.0.0 ; python_version >= "3.11" and python_version < "4.0"
-langchain-openai==1.1.0 ; python_version >= "3.11" and python_version < "4.0"
-langchain-text-splitters==1.0.0 ; python_version >= "3.11" and python_version < "4.0"
-langchain==1.1.0 ; python_version >= "3.11" and python_version < "4.0"
+langchain-core==1.2.5 ; python_version >= "3.11" and python_version < "4.0"
+langchain-ollama==1.0.1 ; python_version >= "3.11" and python_version < "4.0"
+langchain-openai==1.1.6 ; python_version >= "3.11" and python_version < "4.0"
+langchain-text-splitters==1.1.0 ; python_version >= "3.11" and python_version < "4.0"
+langchain==1.2.0 ; python_version >= "3.11" and python_version < "4.0"
langdetect==1.0.9 ; python_version >= "3.11" and python_version < "4.0"
langgraph-checkpoint==3.0.1 ; python_version >= "3.11" and python_version < "4.0"
-langgraph-cli==0.4.7 ; python_version >= "3.11" and python_version < "4.0"
+langgraph-cli==0.4.11 ; python_version >= "3.11" and python_version < "4.0"
langgraph-prebuilt==1.0.5 ; python_version >= "3.11" and python_version < "4.0"
-langgraph-sdk==0.2.10 ; python_version >= "3.11" and python_version < "4.0"
-langgraph==1.0.4 ; python_version >= "3.11" and python_version < "4.0"
-langsmith==0.4.49 ; python_version >= "3.11" and python_version < "4.0"
-litellm==1.80.7 ; python_version >= "3.11" and python_version < "4.0"
-logfire-api==4.15.1 ; python_version >= "3.11" and python_version < "4.0"
+langgraph-sdk==0.3.1 ; python_version >= "3.11" and python_version < "4.0"
+langgraph==1.0.5 ; python_version >= "3.11" and python_version < "4.0"
+langsmith==0.5.1 ; python_version >= "3.11" and python_version < "4.0"
+litellm==1.80.11 ; python_version >= "3.11" and python_version < "4.0"
+logfire-api==4.16.0 ; python_version >= "3.11" and python_version < "4.0"
logfire==3.25.0 ; python_version >= "3.11" and python_version < "4.0"
loguru==0.7.3 ; python_version >= "3.11" and python_version < "4.0"
+lupa==2.6 ; python_version >= "3.11" and python_version < "4.0"
lxml==6.0.2 ; python_version >= "3.11" and python_version < "4.0"
markdown-it-py==4.0.0 ; python_version >= "3.11" and python_version < "4.0"
markdown2==2.5.4 ; python_version >= "3.11" and python_version < "4.0"
markdown==3.10 ; python_version >= "3.11" and python_version < "4.0"
markupsafe==3.0.3 ; python_version >= "3.11" and python_version < "4.0"
-marshmallow==3.26.1 ; python_version >= "3.11" and python_version < "4.0"
+marshmallow==3.26.2 ; python_version >= "3.11" and python_version < "4.0"
matplotlib-inline==0.2.1 ; python_version >= "3.11" and python_version < "4.0"
-mcp==1.22.0 ; python_version >= "3.11" and python_version < "4.0"
+mcp==1.25.0 ; python_version >= "3.11" and python_version < "4.0"
md2pdf==1.0.1 ; python_version >= "3.11" and python_version < "4.0"
mdurl==0.1.2 ; python_version >= "3.11" and python_version < "4.0"
mistralai==1.9.11 ; python_version >= "3.11" and python_version < "4.0"
-mistune==3.1.4 ; python_version >= "3.11" and python_version < "4.0"
+mistune==3.2.0 ; python_version >= "3.11" and python_version < "4.0"
more-itertools==10.8.0 ; python_version >= "3.11" and python_version < "4.0"
multidict==6.7.0 ; python_version >= "3.11" and python_version < "4.0"
mypy-extensions==1.1.0 ; python_version >= "3.11" and python_version < "4.0"
@@ -157,39 +162,41 @@ numpy==2.2.6 ; python_version >= "3.11" and python_version < "4.0"
oauthlib==3.3.1 ; python_version >= "3.11" and python_version < "4.0"
olefile==0.47 ; python_version >= "3.11" and python_version < "4.0"
ollama==0.6.1 ; python_version >= "3.11" and python_version < "4.0"
-openai==2.8.1 ; python_version >= "3.11" and python_version < "4.0"
+openai==2.14.0 ; python_version >= "3.11" and python_version < "4.0"
openapi-pydantic==0.5.1 ; python_version >= "3.11" and python_version < "4.0"
opentelemetry-api==1.35.0 ; python_version >= "3.11" and python_version < "4.0"
opentelemetry-exporter-otlp-proto-common==1.35.0 ; python_version >= "3.11" and python_version < "4.0"
opentelemetry-exporter-otlp-proto-http==1.35.0 ; python_version >= "3.11" and python_version < "4.0"
+opentelemetry-exporter-prometheus==0.56b0 ; python_version >= "3.11" and python_version < "4.0"
opentelemetry-instrumentation-httpx==0.56b0 ; python_version >= "3.11" and python_version < "4.0"
opentelemetry-instrumentation==0.56b0 ; python_version >= "3.11" and python_version < "4.0"
opentelemetry-proto==1.35.0 ; python_version >= "3.11" and python_version < "4.0"
opentelemetry-sdk==1.35.0 ; python_version >= "3.11" and python_version < "4.0"
opentelemetry-semantic-conventions==0.56b0 ; python_version >= "3.11" and python_version < "4.0"
opentelemetry-util-http==0.56b0 ; python_version >= "3.11" and python_version < "4.0"
-orjson==3.11.4 ; python_version >= "3.11" and python_version < "4.0"
-ormsgpack==1.12.0 ; python_version >= "3.11" and python_version < "4.0"
+orjson==3.11.5 ; python_version >= "3.11" and python_version < "4.0"
+ormsgpack==1.12.1 ; python_version >= "3.11" and python_version < "4.0"
packaging==25.0 ; python_version >= "3.11" and python_version < "4.0"
parso==0.8.5 ; python_version >= "3.11" and python_version < "4.0"
pathable==0.4.4 ; python_version >= "3.11" and python_version < "4.0"
pathvalidate==3.3.1 ; python_version >= "3.11" and python_version < "4.0"
pexpect==4.9.0 ; python_version >= "3.11" and python_version < "4.0" and sys_platform != "win32" and sys_platform != "emscripten"
-pgvector==0.4.1 ; python_version >= "3.11" and python_version < "4.0"
+pgvector==0.4.2 ; python_version >= "3.11" and python_version < "4.0"
pillow==12.0.0 ; python_version >= "3.11" and python_version < "4.0"
-platformdirs==4.5.0 ; python_version >= "3.11" and python_version < "4.0"
+platformdirs==4.5.1 ; python_version >= "3.11" and python_version < "4.0"
pluggy==1.6.0 ; python_version >= "3.11" and python_version < "4.0"
posthog==5.4.0 ; python_version >= "3.11" and python_version < "4.0"
primp==0.15.0 ; python_version >= "3.11" and python_version < "4.0"
+prometheus-client==0.23.1 ; python_version >= "3.11" and python_version < "4.0"
prompt-toolkit==3.0.52 ; python_version >= "3.11" and python_version < "4.0"
propcache==0.4.1 ; python_version >= "3.11" and python_version < "4.0"
-protobuf==6.33.1 ; python_version >= "3.11" and python_version < "4.0"
-psutil==7.1.3 ; python_version >= "3.11" and python_version < "4.0"
+protobuf==6.33.2 ; python_version >= "3.11" and python_version < "4.0"
+psutil==7.2.0 ; python_version >= "3.11" and python_version < "4.0"
psycopg2==2.9.11 ; python_version >= "3.11" and python_version < "4.0"
ptyprocess==0.7.0 ; python_version >= "3.11" and python_version < "4.0" and sys_platform != "win32" and sys_platform != "emscripten"
pure-eval==0.2.3 ; python_version >= "3.11" and python_version < "4.0"
-py-key-value-aio==0.2.8 ; python_version >= "3.11" and python_version < "4.0"
-py-key-value-shared==0.2.8 ; python_version >= "3.11" and python_version < "4.0"
+py-key-value-aio==0.3.0 ; python_version >= "3.11" and python_version < "4.0"
+py-key-value-shared==0.3.0 ; python_version >= "3.11" and python_version < "4.0"
pyasn1-modules==0.4.2 ; python_version >= "3.11" and python_version < "4.0"
pyasn1==0.6.1 ; python_version >= "3.11" and python_version < "4.0"
pycparser==2.23 ; python_version >= "3.11" and python_version < "4.0"
@@ -200,11 +207,12 @@ pydantic-evals==1.22.0 ; python_version >= "3.11" and python_version < "4.0"
pydantic-graph==1.22.0 ; python_version >= "3.11" and python_version < "4.0"
pydantic-settings==2.12.0 ; python_version >= "3.11" and python_version < "4.0"
pydantic==2.12.5 ; python_version >= "3.11" and python_version < "4.0"
-pydyf==0.11.0 ; python_version >= "3.11" and python_version < "4.0"
+pydocket==0.15.4 ; python_version >= "3.11" and python_version < "4.0"
+pydyf==0.12.1 ; python_version >= "3.11" and python_version < "4.0"
pygments==2.19.2 ; python_version >= "3.11" and python_version < "4.0"
pyjwt==2.10.1 ; python_version >= "3.11" and python_version < "4.0"
-pymupdf==1.26.6 ; python_version >= "3.11" and python_version < "4.0"
-pypdf==6.4.0 ; python_version >= "3.11" and python_version < "4.0"
+pymupdf==1.26.7 ; python_version >= "3.11" and python_version < "4.0"
+pypdf==6.5.0 ; python_version >= "3.11" and python_version < "4.0"
pyperclip==1.11.0 ; python_version >= "3.11" and python_version < "4.0"
pyphen==0.17.2 ; python_version >= "3.11" and python_version < "4.0"
pytest-django==4.11.1 ; python_version >= "3.11" and python_version < "4.0"
@@ -215,8 +223,9 @@ python-dotenv==1.2.1 ; python_version >= "3.11" and python_version < "4.0"
python-frontmatter==1.1.0 ; python_version >= "3.11" and python_version < "4.0"
python-ipware==3.0.0 ; python_version >= "3.11" and python_version < "4.0"
python-iso639==2025.11.16 ; python_version >= "3.11" and python_version < "4.0"
+python-json-logger==4.0.0 ; python_version >= "3.11" and python_version < "4.0"
python-magic==0.4.27 ; python_version >= "3.11" and python_version < "4.0"
-python-multipart==0.0.20 ; python_version >= "3.11" and python_version < "4.0"
+python-multipart==0.0.21 ; python_version >= "3.11" and python_version < "4.0"
python-oxmsg==0.0.2 ; python_version >= "3.11" and python_version < "4.0"
python-slugify==8.0.4 ; python_version >= "3.11" and python_version < "4.0"
python-webpack-boilerplate==1.0.4 ; python_version >= "3.11" and python_version < "4.0"
@@ -233,19 +242,20 @@ requests-toolbelt==1.0.0 ; python_version >= "3.11" and python_version < "4.0"
requests==2.32.5 ; python_version >= "3.11" and python_version < "4.0"
rich-rst==1.3.2 ; python_version >= "3.11" and python_version < "4.0"
rich==14.2.0 ; python_version >= "3.11" and python_version < "4.0"
-rpds-py==0.29.0 ; python_version >= "3.11" and python_version < "4.0"
+rpds-py==0.30.0 ; python_version >= "3.11" and python_version < "4.0"
rsa==4.9.1 ; python_version >= "3.11" and python_version < "4.0"
-s3transfer==0.15.0 ; python_version >= "3.11" and python_version < "4.0"
+s3transfer==0.16.0 ; python_version >= "3.11" and python_version < "4.0"
secretstorage==3.5.0 ; python_version >= "3.11" and python_version < "4.0" and sys_platform == "linux"
-sentry-sdk==2.46.0 ; python_version >= "3.11" and python_version < "4.0"
+sentry-sdk==2.48.0 ; python_version >= "3.11" and python_version < "4.0"
sgmllib3k==1.0.0 ; python_version >= "3.11" and python_version < "4.0"
shellingham==1.5.4 ; python_version >= "3.11" and python_version < "4.0"
six==1.17.0 ; python_version >= "3.11" and python_version < "4.0"
sniffio==1.3.1 ; python_version >= "3.11" and python_version < "4.0"
-soupsieve==2.8 ; python_version >= "3.11" and python_version < "4.0"
-sqlalchemy==2.0.44 ; python_version >= "3.11" and python_version < "4.0"
-sqlparse==0.5.3 ; python_version >= "3.11" and python_version < "4.0"
-sse-starlette==3.0.3 ; python_version >= "3.11" and python_version < "4.0"
+sortedcontainers==2.4.0 ; python_version >= "3.11" and python_version < "4.0"
+soupsieve==2.8.1 ; python_version >= "3.11" and python_version < "4.0"
+sqlalchemy==2.0.45 ; python_version >= "3.11" and python_version < "4.0"
+sqlparse==0.5.5 ; python_version >= "3.11" and python_version < "4.0"
+sse-starlette==3.1.1 ; python_version >= "3.11" and python_version < "4.0"
stack-data==0.6.3 ; python_version >= "3.11" and python_version < "4.0"
starlette==0.50.0 ; python_version >= "3.11" and python_version < "4.0"
stripe==11.6.0 ; python_version >= "3.11" and python_version < "4.0"
@@ -260,17 +270,19 @@ tinyhtml5==2.0.0 ; python_version >= "3.11" and python_version < "4.0"
tokenizers==0.22.1 ; python_version >= "3.11" and python_version < "4.0"
tqdm==4.67.1 ; python_version >= "3.11" and python_version < "4.0"
traitlets==5.14.3 ; python_version >= "3.11" and python_version < "4.0"
-typer-slim==0.20.0 ; python_version >= "3.11" and python_version < "4.0"
-types-protobuf==6.32.1.20251105 ; python_version >= "3.11" and python_version < "4.0"
+typer-slim==0.21.0 ; python_version >= "3.11" and python_version < "4.0"
+typer==0.21.0 ; python_version >= "3.11" and python_version < "4.0"
+types-protobuf==6.32.1.20251210 ; python_version >= "3.11" and python_version < "4.0"
types-requests==2.32.4.20250913 ; python_version >= "3.11" and python_version < "4.0" and platform_system != "Emscripten"
typing-extensions==4.15.0 ; python_version >= "3.11" and python_version < "4.0"
typing-inspect==0.9.0 ; python_version >= "3.11" and python_version < "4.0"
typing-inspection==0.4.2 ; python_version >= "3.11" and python_version < "4.0"
-tzdata==2025.2 ; python_version >= "3.11" and python_version < "4.0"
-unstructured-client==0.42.4 ; python_version >= "3.11" and python_version < "4.0"
+tzdata==2025.3 ; python_version >= "3.11" and python_version < "4.0"
+unstructured-client==0.42.6 ; python_version >= "3.11" and python_version < "4.0"
unstructured==0.18.21 ; python_version >= "3.11" and python_version < "4.0"
-urllib3==2.5.0 ; python_version >= "3.11" and python_version < "4.0"
-uvicorn==0.38.0 ; python_version >= "3.11" and python_version < "4.0"
+urllib3==2.6.2 ; python_version >= "3.11" and python_version < "4.0"
+uuid-utils==0.12.0 ; python_version >= "3.11" and python_version < "4.0"
+uvicorn==0.40.0 ; python_version >= "3.11" and python_version < "4.0"
wcwidth==0.2.14 ; python_version >= "3.11" and python_version < "4.0"
weasyprint==66.0 ; python_version >= "3.11" and python_version < "4.0"
webencodings==0.5.1 ; python_version >= "3.11" and python_version < "4.0"
diff --git a/snippets/inspect_blog_post_title_suggestion.py b/snippets/inspect_blog_post_title_suggestion.py
new file mode 100644
index 0000000..d907aef
--- /dev/null
+++ b/snippets/inspect_blog_post_title_suggestion.py
@@ -0,0 +1,168 @@
+from core.models import (
+ BlogPostTitleSuggestion,
+ GeneratedBlogPost,
+ GeneratedBlogPostResearchLink,
+ GeneratedBlogPostResearchQuestion,
+ GeneratedBlogPostSection,
+)
+from core.utils import get_relevant_external_pages_for_blog_post
+
+
+def format_heading(text: str) -> str:
+ return f"\n{'=' * 16} {text} {'=' * 16}\n"
+
+
+def format_subheading(text: str) -> str:
+ return f"\n{'-' * 10} {text} {'-' * 10}\n"
+
+
+blog_post_title_suggestion_id_raw = input("BlogPostTitleSuggestion id: ").strip()
+blog_post_title_suggestion_id = int(blog_post_title_suggestion_id_raw)
+
+title_suggestion = (
+ BlogPostTitleSuggestion.objects.select_related("project")
+ .filter(id=blog_post_title_suggestion_id)
+ .first()
+)
+
+if not title_suggestion:
+ raise ValueError(f"BlogPostTitleSuggestion not found: {blog_post_title_suggestion_id}")
+
+project = title_suggestion.project
+
+print(format_heading("TITLE SUGGESTION"))
+print(f"id: {title_suggestion.id}")
+print(f"project_id: {title_suggestion.project_id}")
+print(f"title: {title_suggestion.title}")
+print(f"content_type: {title_suggestion.content_type}")
+print(f"category: {title_suggestion.category}")
+print(f"description: {title_suggestion.description}")
+print(f"prompt: {title_suggestion.prompt}")
+print(f"target_keywords: {title_suggestion.target_keywords or []}")
+print(f"suggested_meta_description: {title_suggestion.suggested_meta_description}")
+print(f"user_score: {title_suggestion.user_score}")
+print(f"archived: {title_suggestion.archived}")
+
+print(format_heading("PROJECT"))
+if not project:
+ print("No project associated")
+else:
+ print(f"id: {project.id}")
+ print(f"name: {project.name}")
+ print(f"type: {project.type}")
+ print(f"language: {project.language}")
+ print(f"location: {project.location}")
+ print(f"summary: {project.summary}")
+ print(f"blog_theme: {project.blog_theme}")
+ print(f"founders: {project.founders}")
+ print(f"key_features: {project.key_features}")
+ print(f"target_audience_summary: {project.target_audience_summary}")
+ print(f"pain_points: {project.pain_points}")
+ print(f"product_usage: {project.product_usage}")
+ print(f"proposed_keywords: {project.proposed_keywords}")
+ print(f"links: {project.links}")
+
+print(format_heading("DERIVED INPUTS"))
+keywords_to_use = title_suggestion.get_blog_post_keywords()
+print(f"keywords_to_use ({len(keywords_to_use)}): {keywords_to_use}")
+
+try:
+ internal_links = title_suggestion.get_internal_links(max_pages=2)
+except Exception as error:
+ internal_links = []
+ print(f"get_internal_links error: {error}")
+
+print(f"internal_links ({len(internal_links)}):")
+for project_page in internal_links:
+ print(
+ f"- [{project_page.id}] {project_page.title} | {project_page.url} | always_use={project_page.always_use}" # noqa: E501
+ )
+
+try:
+ external_links = list(
+ get_relevant_external_pages_for_blog_post(
+ meta_description=title_suggestion.suggested_meta_description or "",
+ exclude_project=title_suggestion.project,
+ max_pages=3,
+ )
+ )
+except Exception as error:
+ external_links = []
+ print(f"get_relevant_external_pages_for_blog_post error: {error}")
+
+print(f"external_links ({len(external_links)}):")
+for project_page in external_links:
+ print(
+ f"- [{project_page.id}] {project_page.title} | {project_page.url} | "
+ f"project_id={project_page.project_id} always_use={project_page.always_use}"
+ )
+
+generated_blog_posts = (
+ GeneratedBlogPost.objects.filter(title_suggestion=title_suggestion)
+ .select_related("project", "title_suggestion")
+ .order_by("-id")
+)
+
+print(format_heading("GENERATED BLOG POSTS"))
+print(f"count: {generated_blog_posts.count()}")
+
+for blog_post in generated_blog_posts:
+ print(format_subheading(f"GeneratedBlogPost {blog_post.id}"))
+ print(f"id: {blog_post.id}")
+ print(f"project_id: {blog_post.project_id}")
+ print(f"title_suggestion_id: {blog_post.title_suggestion_id}")
+ print(f"title: {blog_post.title}")
+ print(f"description: {blog_post.description}")
+ print(f"slug: {blog_post.slug}")
+ print(f"tags: {blog_post.tags}")
+ print(f"posted: {blog_post.posted}")
+ print(f"date_posted: {blog_post.date_posted}")
+ print(f"content_length: {len(blog_post.content or '')}")
+
+ sections = GeneratedBlogPostSection.objects.filter(blog_post=blog_post).order_by("order", "id")
+ print(format_subheading(f"Sections ({sections.count()})"))
+
+ for section in sections:
+ print(
+ f"[{section.order}] section_id={section.id} title={section.title} content_length={len(section.content or '')}" # noqa: E501
+ )
+
+ section_questions = GeneratedBlogPostResearchQuestion.objects.filter(
+ section=section
+ ).order_by("id")
+ print(f" research_questions ({section_questions.count()}):")
+ for research_question in section_questions:
+ print(f" - [{research_question.id}] {research_question.question}")
+
+ question_links = (
+ GeneratedBlogPostResearchLink.objects.filter(research_question=research_question)
+ .order_by("id")
+ .values(
+ "id",
+ "url",
+ "title",
+ "author",
+ "published_date",
+ "date_scraped",
+ "date_analyzed",
+ )
+ )
+ question_links_list = list(question_links)
+ print(f" links ({len(question_links_list)}):")
+ for research_link in question_links_list:
+ print(
+ f" - [{research_link['id']}] {research_link['title']} | {research_link['url']} | " # noqa: E501
+ f"author={research_link['author']} published_date={research_link['published_date']} " # noqa: E501
+ f"date_scraped={research_link['date_scraped']} date_analyzed={research_link['date_analyzed']}" # noqa: E501
+ )
+
+ blog_post_questions = GeneratedBlogPostResearchQuestion.objects.filter(
+ blog_post=blog_post,
+ section__isnull=True,
+ ).order_by("id")
+ if blog_post_questions.exists():
+ print(format_subheading(f"Blog-level research questions ({blog_post_questions.count()})"))
+ for research_question in blog_post_questions:
+ print(f"- [{research_question.id}] {research_question.question}")
+
+print(format_heading("DONE"))
diff --git a/tuxseo/settings.py b/tuxseo/settings.py
index accc865..f77a45a 100644
--- a/tuxseo/settings.py
+++ b/tuxseo/settings.py
@@ -549,4 +549,7 @@ def extract_from_record(logger, name, event_dict):
"from django_q.tasks import async_task",
"from core.scheduled_tasks import *",
"from core.tasks import *",
+ "from exa_py import Exa",
]
+
+EXA_API_KEY = env("EXA_API_KEY", default="")
From 01efb03030b0bb95b21ad0cfdca0c3a8b2ce35ec Mon Sep 17 00:00:00 2001
From: Rasul Kireev
Date: Sat, 27 Dec 2025 23:51:27 +0300
Subject: [PATCH 2/3] task
---
core/tasks.py | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/core/tasks.py b/core/tasks.py
index 9a56118..a5e3a70 100644
--- a/core/tasks.py
+++ b/core/tasks.py
@@ -1780,15 +1780,3 @@ def generate_blog_post_content(suggestion_id: int, send_email: bool = True):
project_id=suggestion.project.id if suggestion.project else None,
)
return f"Unexpected error: {str(error)}"
-
-
-def generate_research_questions_for_section_task(section_id: int):
- """
- Generate research questions for one blog post section, then queue Exa research link tasks for
- each created question.
- """
- from core.content_generator.tasks import (
- generate_research_questions_for_section_task as delegated_task,
- )
-
- return delegated_task(section_id=section_id)
From d01bb1ce7babbf433912b01fe3b99c99d86fa391 Mon Sep 17 00:00:00 2001
From: Rasul Kireev
Date: Sun, 28 Dec 2025 14:29:04 +0300
Subject: [PATCH 3/3] dump
---
README.md | 35 +
content_generation/__init__.py | 0
content_generation/admin.py | 1 +
content_generation/apps.py | 6 +
content_generation/migrations/__init__.py | 0
content_generation/models.py | 369 +++++++++
content_generation/tests.py | 1 +
content_generation/views.py | 1 +
...nerate_blog_post_intro_conclusion_agent.py | 100 +++
...enerate_blog_post_section_content_agent.py | 123 +++
core/agents/research_link_summary_agent.py | 33 +
core/agents/schemas.py | 98 +++
core/content_generator/pipeline.py | 749 +++++++++++++++++-
core/content_generator/tasks.py | 74 +-
...eratedblogpostresearchquestion_and_more.py | 95 ---
...edblogpostresearchlink_content_and_more.py | 44 -
core/models.py | 3 +-
core/urls.py | 5 +
core/views.py | 210 +++++
.../blog/blog_post_research_process.html | 336 ++++++++
.../blog/generated_blog_post_detail.html | 14 +
.../components/blog_post_suggestion_card.html | 14 +-
tuxseo/settings.py | 1 +
23 files changed, 2120 insertions(+), 192 deletions(-)
create mode 100644 content_generation/__init__.py
create mode 100644 content_generation/admin.py
create mode 100644 content_generation/apps.py
create mode 100644 content_generation/migrations/__init__.py
create mode 100644 content_generation/models.py
create mode 100644 content_generation/tests.py
create mode 100644 content_generation/views.py
create mode 100644 core/agents/generate_blog_post_intro_conclusion_agent.py
create mode 100644 core/agents/generate_blog_post_section_content_agent.py
delete mode 100644 core/migrations/0050_backlink_generatedblogpostresearchquestion_and_more.py
delete mode 100644 core/migrations/0051_rename_markdown_content_generatedblogpostresearchlink_content_and_more.py
create mode 100644 frontend/templates/blog/blog_post_research_process.html
diff --git a/README.md b/README.md
index 3c8fae6..08fcb51 100644
--- a/README.md
+++ b/README.md
@@ -19,9 +19,44 @@
***
+## Technical Details
+
+### Content Generation Pipeline
+
+```mermaid
+flowchart TD
+ A["BlogPostTitleSuggestion.generate_content()"] --> B["init_blog_post_content_generation()"]
+ B --> C["AI: generate outline section titles
(Introduction + middle sections + Conclusion)"]
+ C --> D["DB: create GeneratedBlogPost + GeneratedBlogPostSection rows"]
+
+ D --> E{"For each middle section"}
+ E --> F["Task: generate research questions for section
(local: 1 question)"]
+
+ F --> G{"For each research question"}
+ G --> H["Task: Exa search for links
(local: 2 links)"]
+
+ H --> I{"For each research link"}
+ I --> J["Task: scrape link with Jina Reader"]
+ J --> K["Task: analyze link (AI)
summary + contextual summary + answer"]
+
+ K --> L{"All links attempted/analyzed?"}
+ L -- no --> K
+ L -- yes --> M["Task: synthesize middle section contents (AI)"]
+
+ M --> N{"All middle sections have content?"}
+ N -- yes --> O["Task: generate Introduction + Conclusion (AI)"]
+ N -- no --> M
+
+ O --> P{"All sections (incl. intro/conclusion) have content?"}
+ P -- yes --> Q["Task: populate GeneratedBlogPost.content
(code: combine sections into final markdown)"]
+ P -- no --> O
+```
+
## TOC
- [Overview](#overview)
+- [Technical Details](#technical-details)
+ - [Content Generation Pipeline](#content-generation-pipeline)
- [TOC](#toc)
- [Deployment](#deployment)
- [Render](#render)
diff --git a/content_generation/__init__.py b/content_generation/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/content_generation/admin.py b/content_generation/admin.py
new file mode 100644
index 0000000..846f6b4
--- /dev/null
+++ b/content_generation/admin.py
@@ -0,0 +1 @@
+# Register your models here.
diff --git a/content_generation/apps.py b/content_generation/apps.py
new file mode 100644
index 0000000..f88ddd6
--- /dev/null
+++ b/content_generation/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class ContentGenerationConfig(AppConfig):
+ default_auto_field = "django.db.models.BigAutoField"
+ name = "content_generation"
diff --git a/content_generation/migrations/__init__.py b/content_generation/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/content_generation/models.py b/content_generation/models.py
new file mode 100644
index 0000000..4875573
--- /dev/null
+++ b/content_generation/models.py
@@ -0,0 +1,369 @@
+from urllib.request import urlopen
+
+import replicate
+import requests
+from django.conf import settings
+from django.core.files.base import ContentFile
+from django.db import models
+
+from core.agents import (
+ create_insert_links_agent,
+)
+from core.agents.schemas import (
+ GeneratedBlogPostSchema,
+ LinkInsertionContext,
+ ProjectPageContext,
+)
+from core.base_models import BaseModel
+from core.choices import (
+ OGImageStyle,
+)
+from core.models import AutoSubmissionSetting, BlogPostTitleSuggestion, Project
+from core.utils import (
+ get_og_image_prompt,
+ get_relevant_external_pages_for_blog_post,
+)
+from tuxseo.utils import get_tuxseo_logger
+
+logger = get_tuxseo_logger(__name__)
+
+
+class GeneratedBlogPost(BaseModel):
+ project = models.ForeignKey(
+ Project,
+ null=True,
+ blank=True,
+ on_delete=models.CASCADE,
+ related_name="generated_blog_posts",
+ )
+ title_suggestion = models.ForeignKey(
+ BlogPostTitleSuggestion,
+ null=True,
+ blank=True,
+ on_delete=models.CASCADE,
+ related_name="generated_blog_posts",
+ )
+
+ # Final Output Items
+ title = models.CharField(max_length=250)
+ description = models.TextField(blank=True)
+ slug = models.SlugField(max_length=250)
+ tags = models.TextField()
+ content = models.TextField()
+ icon = models.ImageField(upload_to="generated_blog_post_icons/", blank=True)
+ image = models.ImageField(upload_to="generated_blog_post_images/", blank=True)
+
+ # Preparation
+ # GeneratedBlogPostSection model
+
+ # Other
+ posted = models.BooleanField(default=False)
+ date_posted = models.DateTimeField(null=True, blank=True)
+
+ def __str__(self):
+ return f"{self.project.name}: {self.title}"
+
+ @classmethod
+ def blog_post_structure_rules(cls):
+ return """
+ - Use markdown.
+ - Start with the title as h1 (#). Do no include any other metadata (description, slug, etc.)
+ - Then do and intro, starting with `## Introduction`, then a paragraph of text.
+ - Continue with h2 (##) topics as you see fit.
+ - Do not go deeper than h2 (##) for post structure.
+ - Never inlcude placeholder items (insert image here, link suggestions, etc.)
+ - Do not have `References` section, insert all the links into the post directly, organically.
+ - Do not include a call to action paragraph at the end of the post.
+ - Finish the post with a conclusion.
+ - Instead of using links as a reference, try to insert them into the post directly, organically.
+ """ # noqa: E501
+
+ @property
+ def generated_blog_post_schema(self):
+ return GeneratedBlogPostSchema(
+ description=self.description,
+ slug=self.slug,
+ tags=self.tags,
+ content=self.content,
+ )
+
+ def submit_blog_post_to_endpoint(self):
+ from core.utils import replace_placeholders
+
+ project = self.project
+ submission_settings = (
+ AutoSubmissionSetting.objects.filter(project=project).order_by("-id").first()
+ )
+
+ if not submission_settings or not submission_settings.endpoint_url:
+ logger.warning(
+ "No AutoSubmissionSetting or endpoint_url found for project", project_id=project.id
+ )
+ return False
+
+ url = submission_settings.endpoint_url
+ headers = replace_placeholders(submission_settings.header, self)
+ body = replace_placeholders(submission_settings.body, self)
+
+ logger.info(
+ "[Submit Blog Post] Submitting blog post to endpoint",
+ project_id=project.id,
+ profile_id=project.profile.id,
+ endpoint_url=url,
+ headers_configured=bool(headers),
+ body_configured=bool(body),
+ )
+
+ try:
+ session = requests.Session()
+ session.cookies.clear()
+
+ if headers is None:
+ headers = {}
+
+ if "content-type" not in headers and "Content-Type" not in headers:
+ headers["Content-Type"] = "application/json"
+
+ response = session.post(url, json=body, headers=headers, timeout=15)
+ response.raise_for_status()
+ return True
+
+ except requests.RequestException as e:
+ logger.error(
+ "[Submit Blog Post to Endpoint] Request error",
+ error=str(e),
+ url=url,
+ headers=headers,
+ exc_info=True,
+ )
+ return False
+
+ def generate_og_image(self) -> tuple[bool, str]:
+ """
+ Generate an Open Graph image for a blog post using Replicate flux-schnell model.
+
+ Args:
+ generated_post: The GeneratedBlogPost instance to generate an image for
+ replicate_api_token: Replicate API token for authentication
+
+ Returns:
+ A tuple of (success: bool, message: str)
+ """
+
+ if not settings.REPLICATE_API_TOKEN:
+ logger.error(
+ "[GenerateOGImage] Replicate API token not configured",
+ blog_post_id=self.id,
+ project_id=self.project_id,
+ )
+ return False, "Replicate API token not configured"
+
+ if self.image:
+ logger.info(
+ "[GenerateOGImage] Image already exists for blog post",
+ blog_post_id=self.id,
+ project_id=self.project_id,
+ )
+ return True, f"Image already exists for blog post {self.id}"
+
+ try:
+ blog_post_category = (
+ self.title_suggestion.category if self.title_suggestion.category else "technology"
+ )
+
+ project_og_style = self.project.og_image_style or OGImageStyle.MODERN_GRADIENT
+ prompt = get_og_image_prompt(project_og_style, blog_post_category)
+
+ logger.info(
+ "[GenerateOGImage] Starting image generation",
+ blog_post_id=self.id,
+ project_id=self.project_id,
+ category=blog_post_category,
+ og_style=project_og_style,
+ prompt=prompt,
+ )
+
+ replicate_client = replicate.Client(api_token=settings.REPLICATE_API_TOKEN)
+
+ output = replicate_client.run(
+ "black-forest-labs/flux-schnell",
+ input={
+ "prompt": prompt,
+ "aspect_ratio": "16:9",
+ "output_format": "png",
+ "output_quality": 90,
+ },
+ )
+
+ if not output:
+ logger.error(
+ "[GenerateOGImage] No output from Replicate",
+ blog_post_id=self.id,
+ project_id=self.project_id,
+ )
+ return False, f"Failed to generate image for blog post {self.id}"
+
+ file_output = output[0] if isinstance(output, list) else output
+ image_url = str(file_output)
+
+ logger.info(
+ "[GenerateOGImage] Image generated successfully",
+ blog_post_id=self.id,
+ project_id=self.project_id,
+ image_url=image_url,
+ )
+
+ image_response = urlopen(image_url)
+ image_content = ContentFile(image_response.read())
+
+ filename = f"og-image-{self.id}.png"
+ self.image.save(filename, image_content, save=True)
+
+ logger.info(
+ "[GenerateOGImage] Image saved to blog post",
+ blog_post_id=self.id,
+ project_id=self.project_id,
+ saved_url=self.image.url,
+ )
+
+ return True, f"Successfully generated and saved OG image for blog post {self.id}"
+
+ except replicate.exceptions.ReplicateError as replicate_error:
+ logger.error(
+ "[GenerateOGImage] Replicate API error",
+ error=str(replicate_error),
+ exc_info=True,
+ blog_post_id=self.id,
+ project_id=self.project_id,
+ )
+ return False, f"Replicate API error: {str(replicate_error)}"
+ except Exception as error:
+ logger.error(
+ "[GenerateOGImage] Unexpected error during image generation",
+ error=str(error),
+ exc_info=True,
+ blog_post_id=self.id,
+ project_id=self.project_id,
+ )
+ return False, f"Unexpected error: {str(error)}"
+
+ def insert_links_into_post(self, max_pages=4, max_external_pages=3):
+ """
+ Insert links from project pages into the blog post content organically.
+ Uses PydanticAI to intelligently place links without modifying the content.
+
+ Args:
+ max_pages: Maximum number of internal project pages to use for linking (default: 4)
+ max_external_pages: Maximum number of external project pages to use for linking (default: 3)
+
+ Returns:
+ str: The blog post content with links inserted
+ """ # noqa: E501
+ from core.utils import (
+ get_relevant_pages_for_blog_post,
+ run_agent_synchronously,
+ )
+
+ if not self.title_suggestion:
+ logger.warning(
+ "[InsertLinksIntoPost] No title suggestion found for blog post",
+ blog_post_id=self.id,
+ project_id=self.project_id,
+ )
+ return self.content
+
+ # Get internal project pages
+ manually_selected_project_pages = list(self.project.project_pages.filter(always_use=True))
+ relevant_project_pages = list(
+ get_relevant_pages_for_blog_post(
+ self.project,
+ self.title_suggestion.suggested_meta_description,
+ max_pages=max_pages,
+ )
+ )
+
+ all_project_pages = manually_selected_project_pages + relevant_project_pages
+
+ # Get external project pages if link exchange is enabled
+ external_project_pages = []
+ if self.project.particiate_in_link_exchange:
+ external_project_pages = list(
+ get_relevant_external_pages_for_blog_post(
+ meta_description=self.title_suggestion.suggested_meta_description,
+ exclude_project=self.project,
+ max_pages=max_external_pages,
+ )
+ )
+ # Filter to only include pages from projects that also participate in link exchange
+ external_project_pages = [
+ page for page in external_project_pages if page.project.particiate_in_link_exchange
+ ]
+
+ all_pages_to_link = all_project_pages + external_project_pages
+
+ if not all_pages_to_link:
+ logger.info(
+ "[InsertLinksIntoPost] No pages found for link insertion",
+ blog_post_id=self.id,
+ project_id=self.project_id,
+ )
+ return self.content
+
+ project_page_contexts = [
+ ProjectPageContext(
+ url=page.url,
+ title=page.title,
+ description=page.description,
+ summary=page.summary,
+ )
+ for page in all_pages_to_link
+ ]
+
+ # Extract URLs for logging
+ urls_to_insert = [page.url for page in all_pages_to_link]
+ internal_urls = [page.url for page in all_project_pages]
+ external_urls = [page.url for page in external_project_pages]
+
+ link_insertion_context = LinkInsertionContext(
+ blog_post_content=self.content,
+ project_pages=project_page_contexts,
+ )
+
+ insert_links_agent = create_insert_links_agent()
+
+ prompt = "Insert the provided project page links into the blog post content organically. Do not modify the existing content, only add links where appropriate." # noqa: E501
+
+ logger.info(
+ "[InsertLinksIntoPost] Running link insertion agent",
+ blog_post_id=self.id,
+ project_id=self.project_id,
+ num_total_pages=len(project_page_contexts),
+ num_internal_pages=len(all_project_pages),
+ num_external_pages=len(external_project_pages),
+ num_always_use_pages=len(manually_selected_project_pages),
+ participate_in_link_exchange=self.project.particiate_in_link_exchange,
+ urls_to_insert=urls_to_insert,
+ internal_urls=internal_urls,
+ external_urls=external_urls,
+ )
+
+ result = run_agent_synchronously(
+ insert_links_agent,
+ prompt,
+ deps=link_insertion_context,
+ function_name="insert_links_into_post",
+ model_name="GeneratedBlogPost",
+ )
+
+ content_with_links = result.output
+
+ self.content = content_with_links
+ self.save(update_fields=["content"])
+
+ logger.info(
+ "[InsertLinksIntoPost] Links inserted successfully",
+ blog_post_id=self.id,
+ project_id=self.project_id,
+ )
+
+ return content_with_links
diff --git a/content_generation/tests.py b/content_generation/tests.py
new file mode 100644
index 0000000..a39b155
--- /dev/null
+++ b/content_generation/tests.py
@@ -0,0 +1 @@
+# Create your tests here.
diff --git a/content_generation/views.py b/content_generation/views.py
new file mode 100644
index 0000000..60f00ef
--- /dev/null
+++ b/content_generation/views.py
@@ -0,0 +1 @@
+# Create your views here.
diff --git a/core/agents/generate_blog_post_intro_conclusion_agent.py b/core/agents/generate_blog_post_intro_conclusion_agent.py
new file mode 100644
index 0000000..281454f
--- /dev/null
+++ b/core/agents/generate_blog_post_intro_conclusion_agent.py
@@ -0,0 +1,100 @@
+from django.utils import timezone
+from pydantic_ai import Agent
+
+from core.agents.schemas import (
+ BlogPostIntroConclusionGenerationContext,
+ GeneratedBlogPostIntroConclusionSchema,
+)
+from core.choices import ContentType, get_default_ai_model
+from core.prompts import GENERATE_CONTENT_SYSTEM_PROMPTS
+
+INTRO_CONCLUSION_SYSTEM_PROMPT = """
+You are an expert blog post writer.
+
+Your task: write BOTH the Introduction and the Conclusion for a blog post in a single response.
+
+Rules:
+- Return two fields only: introduction and conclusion.
+- Do NOT include markdown headings for either section. No leading '#', '##', or '###'.
+- Use the existing section contents as the source of truth for what the post covers.
+- The introduction should set up the promise and smoothly lead into the first middle section.
+- The conclusion should summarize the key takeaways and close cleanly without adding new topics.
+- Do not add placeholders.
+"""
+
+
+def create_generate_blog_post_intro_conclusion_agent(
+ content_type: ContentType = ContentType.SHARING, model=None
+):
+ """
+ Create an agent to generate a blog post Introduction + Conclusion in one call.
+ """
+ agent = Agent(
+ model or get_default_ai_model(),
+ output_type=GeneratedBlogPostIntroConclusionSchema,
+ deps_type=BlogPostIntroConclusionGenerationContext,
+ system_prompt=(
+ INTRO_CONCLUSION_SYSTEM_PROMPT
+ + "\n\n"
+ + (GENERATE_CONTENT_SYSTEM_PROMPTS.get(content_type, "") or "")
+ ),
+ retries=2,
+ model_settings={"max_tokens": 6000, "temperature": 0.7},
+ )
+
+ @agent.system_prompt
+ def add_intro_conclusion_context(ctx) -> str:
+ intro_conclusion_context: BlogPostIntroConclusionGenerationContext = ctx.deps
+ generation_context = intro_conclusion_context.blog_post_generation_context
+ project_details = generation_context.project_details
+ title_suggestion = generation_context.title_suggestion
+ target_keywords = title_suggestion.target_keywords or []
+
+ section_titles_text = (
+ "\n".join(
+ [
+ f"- {title}"
+ for title in (intro_conclusion_context.section_titles_in_order or [])
+ if title
+ ]
+ )
+ or "- (none)"
+ )
+
+ sections_text = ""
+ for index, section in enumerate(intro_conclusion_context.sections_in_order or [], start=1):
+ sections_text += f"\nSection {index}: {section.title}\n{section.content}\n"
+
+ if not sections_text.strip():
+ sections_text = "\n(none)\n"
+
+ return f"""
+Today's date: {timezone.now().strftime("%Y-%m-%d")}
+
+Project details:
+- Project name: {project_details.name}
+- Project type: {project_details.type}
+- Project summary: {project_details.summary}
+- Blog theme: {project_details.blog_theme}
+- Key features: {project_details.key_features}
+- Target audience: {project_details.target_audience_summary}
+- Pain points: {project_details.pain_points}
+- Product usage: {project_details.product_usage}
+
+Blog post title suggestion:
+- Title: {title_suggestion.title}
+- Category: {title_suggestion.category}
+- Description: {title_suggestion.description}
+- Suggested meta description: {title_suggestion.suggested_meta_description}
+- Target keywords: {", ".join(target_keywords) if target_keywords else "None"}
+
+Outline:
+{section_titles_text}
+
+All existing section contents (use this as the truth of what the post covers):
+{sections_text}
+
+Language: Write in {project_details.language}.
+"""
+
+ return agent
diff --git a/core/agents/generate_blog_post_section_content_agent.py b/core/agents/generate_blog_post_section_content_agent.py
new file mode 100644
index 0000000..7c03082
--- /dev/null
+++ b/core/agents/generate_blog_post_section_content_agent.py
@@ -0,0 +1,123 @@
+from django.utils import timezone
+from pydantic_ai import Agent
+
+from core.agents.schemas import (
+ BlogPostSectionContentGenerationContext,
+ GeneratedBlogPostSectionContentSchema,
+)
+from core.choices import ContentType, get_default_ai_model
+from core.prompts import GENERATE_CONTENT_SYSTEM_PROMPTS
+
+SECTION_CONTENT_SYSTEM_PROMPT = """
+You are an expert blog post writer.
+
+Your task: write the content for ONE blog post section (the body of the section only).
+
+Rules:
+- Do NOT write the Introduction or Conclusion.
+- Do NOT include the section title as a markdown header. No leading '#', '##', or '###'.
+- Avoid markdown headings entirely. Use paragraphs, bullet lists, and numbered lists only when useful.
+- Use the provided "Previous sections" to maintain continuity and avoid repetition.
+- Use the provided research link outputs as factual grounding. Do not invent sources or cite URLs.
+- Keep the section coherent with the overall outline and the order position provided.
+- Do not add placeholders.
+"""
+
+
+def create_generate_blog_post_section_content_agent(
+ content_type: ContentType = ContentType.SHARING, model=None
+):
+ """
+ Create an agent to generate the content for a single middle section of a blog post.
+ """
+ agent = Agent(
+ model or get_default_ai_model(),
+ output_type=GeneratedBlogPostSectionContentSchema,
+ deps_type=BlogPostSectionContentGenerationContext,
+ system_prompt=(
+ SECTION_CONTENT_SYSTEM_PROMPT
+ + "\n\n"
+ + (GENERATE_CONTENT_SYSTEM_PROMPTS.get(content_type, "") or "")
+ ),
+ retries=2,
+ model_settings={"max_tokens": 16000, "temperature": 0.7},
+ )
+
+ @agent.system_prompt
+ def add_section_content_context(ctx) -> str:
+ section_context: BlogPostSectionContentGenerationContext = ctx.deps
+ generation_context = section_context.blog_post_generation_context
+ project_details = generation_context.project_details
+ title_suggestion = generation_context.title_suggestion
+ target_keywords = title_suggestion.target_keywords or []
+
+ other_titles = [title for title in (section_context.other_section_titles or []) if title]
+ other_titles_text = "\n".join([f"- {title}" for title in other_titles]) or "- (none)"
+
+ previous_sections = section_context.previous_sections or []
+ previous_sections_text = ""
+ for previous_section_index, previous_section in enumerate(previous_sections, start=1):
+ previous_sections_text += (
+ f"\nPrevious section {previous_section_index}: {previous_section.title}\n"
+ f"{previous_section.content}\n"
+ )
+ if not previous_sections_text.strip():
+ previous_sections_text = "\n(none)\n"
+
+ research_questions_text = ""
+ for question_index, question in enumerate(
+ section_context.research_questions or [], start=1
+ ):
+ research_questions_text += (
+ f"\nResearch question {question_index}: {question.question}\n"
+ )
+ for link_index, link in enumerate(question.research_links or [], start=1):
+ research_questions_text += (
+ f"\nAnswered research link {link_index}:\n"
+ f"- summary_for_question_research:\n{link.summary_for_question_research}\n"
+ f"- general_summary:\n{link.general_summary}\n"
+ f"- answer_to_question:\n{link.answer_to_question}\n"
+ )
+
+ if not research_questions_text.strip():
+ research_questions_text = "\n(none)\n"
+
+ return f"""
+Today's date: {timezone.now().strftime("%Y-%m-%d")}
+
+Project details:
+- Project name: {project_details.name}
+- Project type: {project_details.type}
+- Project summary: {project_details.summary}
+- Blog theme: {project_details.blog_theme}
+- Key features: {project_details.key_features}
+- Target audience: {project_details.target_audience_summary}
+- Pain points: {project_details.pain_points}
+- Product usage: {project_details.product_usage}
+
+Blog post title suggestion:
+- Title: {title_suggestion.title}
+- Category: {title_suggestion.category}
+- Description: {title_suggestion.description}
+- Suggested meta description: {title_suggestion.suggested_meta_description}
+- Target keywords: {", ".join(target_keywords) if target_keywords else "None"}
+
+Outline coherence:
+- Other section titles:
+{other_titles_text}
+
+Current section to write:
+- Section title: {section_context.section_title}
+- Section order in outline: {section_context.section_order} / {section_context.total_sections}
+- Section order among middle sections: {section_context.research_section_order} / {section_context.total_research_sections}
+
+Previous sections (for continuity; do not repeat content):
+{previous_sections_text}
+
+Research answers for this section (only include content that is supported by these answers):
+{research_questions_text}
+
+Language: Write in {project_details.language}.
+"""
+
+ return agent
diff --git a/core/agents/research_link_summary_agent.py b/core/agents/research_link_summary_agent.py
index e38bfbe..75aedea 100644
--- a/core/agents/research_link_summary_agent.py
+++ b/core/agents/research_link_summary_agent.py
@@ -2,6 +2,7 @@
from pydantic_ai import Agent, RunContext
from core.agents.schemas import (
+ ResearchLinkAnalysis,
ResearchLinkContextualSummaryContext,
TextSummary,
WebPageContent,
@@ -88,3 +89,35 @@ def create_contextual_research_link_summary_agent(model=None):
agent.system_prompt(_add_blog_post_research_context)
agent.system_prompt(_add_webpage_content_from_contextual_deps)
return agent
+
+
+def create_research_link_analysis_agent(model=None):
+ """
+ Analyze a research link in a single model call and return:
+ - a general page summary
+ - a contextual summary tailored to the blog post section's research question
+ - a direct answer to the research question (if possible from the page)
+ """
+ agent = Agent(
+ model or get_default_ai_model(),
+ output_type=ResearchLinkAnalysis,
+ deps_type=ResearchLinkContextualSummaryContext,
+ system_prompt=(
+ "You are a research assistant helping write a blog post.\n"
+ "Using only the web page content provided, produce three outputs:\n"
+ "1) general_summary: a context-free 2-3 sentence summary of what the page is about.\n"
+ "2) summary_for_question_research: a markdown summary tailored to the research question. "
+ "Include: a short paragraph summary, 'Key takeaways' (3-7 bullets), and "
+ "'How this helps our section' (1-3 bullets).\n"
+ "3) answer_to_question: directly answer the research question in 1-6 sentences. "
+ "If the page does not answer it (or is irrelevant), say so clearly.\n"
+ "\n"
+ "Be concrete and avoid speculation. Prefer facts, definitions, steps, examples, and stats "
+ "that are present in the page.\n"
+ ),
+ retries=2,
+ model_settings={"temperature": 0.3},
+ )
+ agent.system_prompt(_add_blog_post_research_context)
+ agent.system_prompt(_add_webpage_content_from_contextual_deps)
+ return agent
diff --git a/core/agents/schemas.py b/core/agents/schemas.py
index 163e66d..872597d 100644
--- a/core/agents/schemas.py
+++ b/core/agents/schemas.py
@@ -18,6 +18,27 @@ class TextSummary(BaseModel):
summary: str = Field(description="A concise summary of the provided content")
+class ResearchLinkAnalysis(BaseModel):
+ general_summary: str = Field(
+ description=(
+ "A general, context-free summary of the page content. Keep it to 2-3 sentences."
+ )
+ )
+ summary_for_question_research: str = Field(
+ description=(
+ "A markdown summary tailored to the blog post's research question. Include: "
+ "a short paragraph summary, 'Key takeaways' (3-7 bullets), and "
+ "'How this helps our section' (1-3 bullets)."
+ )
+ )
+ answer_to_question: str = Field(
+ description=(
+ "A direct answer to the research question, based strictly on the page content. "
+ "If the page does not answer the question, say so clearly."
+ )
+ )
+
+
class ProjectDetails(BaseModel):
name: str = Field(description="Official name of the project or organization")
type: str = Field(
@@ -202,6 +223,83 @@ class ResearchLinkContextualSummaryContext(BaseModel):
research_question: str = Field(description="Research question we are trying to answer")
+class ResearchLinkAnswerSnippet(BaseModel):
+ summary_for_question_research: str = Field(
+ description="A markdown summary tailored to the research question"
+ )
+ general_summary: str = Field(description="A general, context-free 2-3 sentence page summary")
+ answer_to_question: str = Field(description="A direct answer to the research question")
+
+
+class ResearchQuestionWithAnsweredLinks(BaseModel):
+ question: str = Field(description="The research question we were answering")
+ research_links: list[ResearchLinkAnswerSnippet] = Field(
+ default_factory=list,
+ description="Only research links that include a non-empty answer_to_question",
+ )
+
+
+class PriorSectionContext(BaseModel):
+ title: str = Field(description="Section title")
+ content: str = Field(description="Section content (markdown)")
+
+
+class BlogPostSectionContentGenerationContext(BaseModel):
+ blog_post_generation_context: BlogPostGenerationContext
+ blog_post_title: str = Field(description="Title of the blog post being written")
+ section_title: str = Field(description="Title of the section to write")
+ section_order: int = Field(description="Order of this section in the overall outline")
+ total_sections: int = Field(description="Total number of sections in the outline")
+ research_section_order: int = Field(
+ description="1-based order of this section among the middle (non-intro/non-conclusion) sections"
+ )
+ total_research_sections: int = Field(
+ description="Total number of middle (non-intro/non-conclusion) sections"
+ )
+ other_section_titles: list[str] = Field(
+ default_factory=list,
+ description="Titles of the other sections in the blog post (for coherence)",
+ )
+ previous_sections: list[PriorSectionContext] = Field(
+ default_factory=list,
+ description="Previously generated section content (in order) to keep the narrative coherent",
+ )
+ research_questions: list[ResearchQuestionWithAnsweredLinks] = Field(
+ default_factory=list,
+ description="Research questions for this section, with only answered research links included",
+ )
+
+
+class GeneratedBlogPostSectionContentSchema(BaseModel):
+ content: str = Field(
+ description=(
+ "Markdown content for the section body only (do not include the section title as a header)"
+ )
+ )
+
+
+class BlogPostIntroConclusionGenerationContext(BaseModel):
+ blog_post_generation_context: BlogPostGenerationContext
+ blog_post_title: str = Field(description="Title of the blog post being written")
+ section_titles_in_order: list[str] = Field(
+ default_factory=list,
+ description="All section titles in outline order (including Introduction and Conclusion)",
+ )
+ sections_in_order: list[PriorSectionContext] = Field(
+ default_factory=list,
+ description="All existing section contents in order (including middle sections) to base intro/conclusion on",
+ )
+
+
+class GeneratedBlogPostIntroConclusionSchema(BaseModel):
+ introduction: str = Field(
+ description="Markdown content for the Introduction section body only (no heading)"
+ )
+ conclusion: str = Field(
+ description="Markdown content for the Conclusion section body only (no heading)"
+ )
+
+
class GeneratedBlogPostSchema(BaseModel):
description: str = Field(
description="Meta description (150-160 characters) optimized for search engines"
diff --git a/core/content_generator/pipeline.py b/core/content_generator/pipeline.py
index dfc8a6b..88226e4 100644
--- a/core/content_generator/pipeline.py
+++ b/core/content_generator/pipeline.py
@@ -1,6 +1,7 @@
from __future__ import annotations
from django.conf import settings
+from django.core.cache import cache
from django.db import transaction
from django.utils import timezone
from django.utils.dateparse import parse_datetime
@@ -12,13 +13,25 @@
create_blog_post_outline_agent,
create_blog_post_section_research_questions_agent,
)
+from core.agents.generate_blog_post_intro_conclusion_agent import (
+ create_generate_blog_post_intro_conclusion_agent,
+)
+from core.agents.generate_blog_post_section_content_agent import (
+ create_generate_blog_post_section_content_agent,
+)
from core.agents.research_link_summary_agent import (
- create_contextual_research_link_summary_agent,
- create_general_research_link_summary_agent,
+ create_research_link_analysis_agent,
)
from core.agents.schemas import (
BlogPostGenerationContext,
+ BlogPostIntroConclusionGenerationContext,
+ BlogPostSectionContentGenerationContext,
+ GeneratedBlogPostIntroConclusionSchema,
+ GeneratedBlogPostSectionContentSchema,
+ PriorSectionContext,
+ ResearchLinkAnswerSnippet,
ResearchLinkContextualSummaryContext,
+ ResearchQuestionWithAnsweredLinks,
WebPageContent,
)
from core.choices import ContentType
@@ -39,6 +52,8 @@
CONCLUSION_SECTION_TITLE = "Conclusion"
NON_RESEARCH_SECTION_TITLES = {INTRODUCTION_SECTION_TITLE, CONCLUSION_SECTION_TITLE}
MAX_RESEARCH_LINK_MARKDOWN_CHARS_FOR_SUMMARY = 25_000
+LOCAL_MAX_RESEARCH_QUESTIONS_PER_SECTION = 1
+SECTION_SYNTHESIS_RETRY_CACHE_TTL_SECONDS = 6 * 60 * 60
def _create_blog_post_generation_context(
@@ -297,6 +312,11 @@ def populate_research_links_for_question_from_exa(
months_back=months_back,
)
+ # If Exa returned no links for this question, nothing will trigger scrape/analyze kicks.
+ # This "kick" is safe (it will only queue synthesis when the overall blog post is ready).
+ if num_links_upserted == 0:
+ maybe_queue_section_content_synthesis_for_blog_post(blog_post_id=blog_post.id)
+
return num_links_upserted
@@ -403,6 +423,7 @@ def analyze_research_link_content(*, research_link_id: int) -> int:
Step 4b: For a single research link (that already has content), generate:
- a general page summary
- a blog-post-contextual summary for the research question/section
+ - an answer to the research question (answer_to_question)
Returns: number of fields updated on the research link.
"""
@@ -434,16 +455,25 @@ def analyze_research_link_content(*, research_link_id: int) -> int:
blog_post_id=blog_post.id,
url=url,
)
+ research_link.date_analyzed = timezone.now()
+ research_link.save(update_fields=["date_analyzed"])
+ maybe_queue_section_content_synthesis_for_blog_post(blog_post_id=blog_post.id)
return 0
should_run_general_summary = not (research_link.general_summary or "").strip()
should_run_contextual_summary = not (research_link.summary_for_question_research or "").strip()
- if not should_run_general_summary and not should_run_contextual_summary:
+ should_run_answer_to_question = not (research_link.answer_to_question or "").strip()
+ if (
+ not should_run_general_summary
+ and not should_run_contextual_summary
+ and not should_run_answer_to_question
+ ):
logger.info(
"[ContentGenerator] Research link already analyzed; skipping",
research_link_id=research_link.id,
blog_post_id=blog_post.id,
)
+ maybe_queue_section_content_synthesis_for_blog_post(blog_post_id=blog_post.id)
return 0
webpage_content = WebPageContent(
@@ -454,53 +484,50 @@ def analyze_research_link_content(*, research_link_id: int) -> int:
update_fields: list[str] = []
- if should_run_general_summary:
- general_summary_agent = create_general_research_link_summary_agent()
- general_summary_result = run_agent_synchronously(
- general_summary_agent,
- "Summarize this page.",
- deps=webpage_content,
- function_name="analyze_research_link_content.general_summary",
- model_name="GeneratedBlogPostResearchLink",
- )
- research_link.general_summary = (general_summary_result.output.summary or "").strip()
- update_fields.append("general_summary")
+ title_suggestion = blog_post.title_suggestion
+ if not title_suggestion:
+ raise ValueError(f"GeneratedBlogPost missing title_suggestion: {blog_post.id}")
- if should_run_contextual_summary:
- title_suggestion = blog_post.title_suggestion
- if not title_suggestion:
- raise ValueError(f"GeneratedBlogPost missing title_suggestion: {blog_post.id}")
+ content_type_to_use = title_suggestion.content_type or ContentType.SHARING
+ blog_post_generation_context = _create_blog_post_generation_context(
+ title_suggestion=title_suggestion,
+ content_type_to_use=content_type_to_use,
+ )
- content_type_to_use = title_suggestion.content_type or ContentType.SHARING
- blog_post_generation_context = _create_blog_post_generation_context(
- title_suggestion=title_suggestion,
- content_type_to_use=content_type_to_use,
- )
+ section_title = (getattr(research_question.section, "title", "") or "").strip()
+ research_question_text = (research_question.question or "").strip()
- section_title = (getattr(research_question.section, "title", "") or "").strip()
- research_question_text = (research_question.question or "").strip()
+ analysis_agent = create_research_link_analysis_agent()
+ analysis_deps = ResearchLinkContextualSummaryContext(
+ url=url,
+ web_page_content=webpage_content,
+ blog_post_generation_context=blog_post_generation_context,
+ blog_post_title=(blog_post.title or title_suggestion.title or "").strip(),
+ section_title=section_title,
+ research_question=research_question_text,
+ )
+ analysis_result = run_agent_synchronously(
+ analysis_agent,
+ "Analyze this page for blog-post research.",
+ deps=analysis_deps,
+ function_name="analyze_research_link_content.research_link_analysis",
+ model_name="GeneratedBlogPostResearchLink",
+ )
- contextual_summary_agent = create_contextual_research_link_summary_agent()
- contextual_summary_deps = ResearchLinkContextualSummaryContext(
- url=url,
- web_page_content=webpage_content,
- blog_post_generation_context=blog_post_generation_context,
- blog_post_title=(blog_post.title or title_suggestion.title or "").strip(),
- section_title=section_title,
- research_question=research_question_text,
- )
- contextual_summary_result = run_agent_synchronously(
- contextual_summary_agent,
- "Summarize this page specifically to help answer the research question for the blog post section.", # noqa: E501
- deps=contextual_summary_deps,
- function_name="analyze_research_link_content.contextual_summary",
- model_name="GeneratedBlogPostResearchLink",
- )
+ if should_run_general_summary:
+ research_link.general_summary = (analysis_result.output.general_summary or "").strip()
+ update_fields.append("general_summary")
+
+ if should_run_contextual_summary:
research_link.summary_for_question_research = (
- contextual_summary_result.output.summary or ""
+ analysis_result.output.summary_for_question_research or ""
).strip()
update_fields.append("summary_for_question_research")
+ if should_run_answer_to_question:
+ research_link.answer_to_question = (analysis_result.output.answer_to_question or "").strip()
+ update_fields.append("answer_to_question")
+
research_link.date_analyzed = timezone.now()
update_fields.append("date_analyzed")
@@ -515,6 +542,8 @@ def analyze_research_link_content(*, research_link_id: int) -> int:
url=url,
)
+ maybe_queue_section_content_synthesis_for_blog_post(blog_post_id=blog_post.id)
+
return len(set(update_fields))
@@ -583,6 +612,9 @@ def generate_research_questions_for_section(*, section_id: int) -> list[int]:
)
)
+ if settings.DEBUG:
+ questions_to_create = questions_to_create[:LOCAL_MAX_RESEARCH_QUESTIONS_PER_SECTION]
+
created_questions = GeneratedBlogPostResearchQuestion.objects.bulk_create(questions_to_create)
created_question_ids = [
created_question.id for created_question in created_questions if created_question.id
@@ -595,4 +627,637 @@ def generate_research_questions_for_section(*, section_id: int) -> list[int]:
num_questions_created=len(created_question_ids),
)
+ # If no questions were created, nothing else will trigger Exa/scrape/analysis tasks.
+ # In that case, kick section synthesis so the pipeline can still proceed.
+ if not created_question_ids:
+ maybe_queue_section_content_synthesis_for_blog_post(blog_post_id=blog_post.id)
+
return created_question_ids
+
+
+def _build_research_questions_with_answered_links_for_section(
+ *, section: GeneratedBlogPostSection
+) -> list[ResearchQuestionWithAnsweredLinks]:
+ research_questions_with_answered_links: list[ResearchQuestionWithAnsweredLinks] = []
+
+ section_questions = list(section.research_questions.all())
+ for research_question in section_questions:
+ question_text = (research_question.question or "").strip()
+ if not question_text:
+ continue
+
+ research_links = list(research_question.research_links.all())
+ answered_links = [
+ research_link
+ for research_link in research_links
+ if (research_link.answer_to_question or "").strip()
+ ]
+
+ research_link_snippets = []
+ if answered_links:
+ research_link_snippets = [
+ ResearchLinkAnswerSnippet(
+ summary_for_question_research=(
+ (research_link.summary_for_question_research or "").strip()
+ ),
+ general_summary=(research_link.general_summary or "").strip(),
+ answer_to_question=(research_link.answer_to_question or "").strip(),
+ )
+ for research_link in answered_links
+ ]
+
+ research_questions_with_answered_links.append(
+ ResearchQuestionWithAnsweredLinks(
+ question=question_text,
+ research_links=research_link_snippets,
+ )
+ )
+
+ return research_questions_with_answered_links
+
+
+def _build_prior_section_contexts(
+ *, sections_in_order: list[GeneratedBlogPostSection], current_section_order: int
+) -> list[PriorSectionContext]:
+ prior_sections: list[PriorSectionContext] = []
+ for section in sections_in_order:
+ if section.order >= current_section_order:
+ continue
+ if (section.title or "").strip() in NON_RESEARCH_SECTION_TITLES:
+ continue
+ content = (section.content or "").strip()
+ if not content:
+ continue
+ prior_sections.append(
+ PriorSectionContext(title=(section.title or "").strip(), content=content)
+ )
+ return prior_sections
+
+
+def synthesize_section_contents_for_blog_post(*, blog_post_id: int) -> int:
+ """
+ Step 5: Synthesize content for each middle section sequentially (excluding Introduction/Conclusion).
+
+ Context passed to the model:
+ - Project details
+ - Title suggestion details
+ - Current section info
+ - Research link results (only for links with non-empty answer_to_question)
+ - Other section titles
+ - Section order + previous section content for coherence
+ """
+ blog_post = (
+ GeneratedBlogPost.objects.select_related(
+ "title_suggestion",
+ "project",
+ )
+ .prefetch_related(
+ "blog_post_sections__research_questions__research_links",
+ )
+ .filter(id=blog_post_id)
+ .first()
+ )
+ if not blog_post:
+ raise ValueError(f"GeneratedBlogPost not found: {blog_post_id}")
+
+ title_suggestion = blog_post.title_suggestion
+ if not title_suggestion:
+ raise ValueError(f"GeneratedBlogPost missing title_suggestion: {blog_post_id}")
+
+ content_type_to_use = title_suggestion.content_type or ContentType.SHARING
+ blog_post_generation_context = _create_blog_post_generation_context(
+ title_suggestion=title_suggestion,
+ content_type_to_use=content_type_to_use,
+ )
+
+ sections_in_order = sorted(
+ list(blog_post.blog_post_sections.all()),
+ key=lambda section: (section.order, section.id),
+ )
+
+ all_section_titles = [
+ (section.title or "").strip()
+ for section in sections_in_order
+ if (section.title or "").strip()
+ ]
+ total_sections = len(sections_in_order)
+
+ middle_sections_in_order = [
+ section
+ for section in sections_in_order
+ if (section.title or "").strip() not in NON_RESEARCH_SECTION_TITLES
+ ]
+ total_research_sections = len(middle_sections_in_order)
+
+ section_agent = create_generate_blog_post_section_content_agent(
+ content_type=content_type_to_use
+ )
+
+ num_sections_generated = 0
+ for research_section_index, section in enumerate(middle_sections_in_order, start=1):
+ section_title = (section.title or "").strip()
+ if not section_title:
+ continue
+
+ existing_content = (section.content or "").strip()
+ if existing_content:
+ continue
+
+ research_questions = _build_research_questions_with_answered_links_for_section(
+ section=section
+ )
+ prior_sections = _build_prior_section_contexts(
+ sections_in_order=sections_in_order,
+ current_section_order=section.order,
+ )
+
+ section_context = BlogPostSectionContentGenerationContext(
+ blog_post_generation_context=blog_post_generation_context,
+ blog_post_title=(blog_post.title or title_suggestion.title or "").strip(),
+ section_title=section_title,
+ section_order=section.order,
+ total_sections=total_sections,
+ research_section_order=research_section_index,
+ total_research_sections=total_research_sections,
+ other_section_titles=all_section_titles,
+ previous_sections=prior_sections,
+ research_questions=research_questions,
+ )
+
+ prompt = f"Write the section body content for: {section_title}"
+ generation_result = run_agent_synchronously(
+ section_agent,
+ prompt,
+ deps=section_context,
+ function_name="synthesize_section_contents_for_blog_post.section_content",
+ model_name="GeneratedBlogPostSection",
+ )
+
+ generated_schema: GeneratedBlogPostSectionContentSchema | None = (
+ generation_result.output if generation_result and generation_result.output else None
+ )
+ generated_content = (generated_schema.content if generated_schema else "").strip()
+ if not generated_content:
+ logger.warning(
+ "[ContentGenerator] Section content generation returned empty content",
+ blog_post_id=blog_post.id,
+ section_id=section.id,
+ section_title=section_title,
+ )
+ continue
+
+ section.content = generated_content
+ section.save(update_fields=["content"])
+ num_sections_generated += 1
+
+ logger.info(
+ "[ContentGenerator] Section content synthesized",
+ blog_post_id=blog_post.id,
+ section_id=section.id,
+ section_title=section_title,
+ section_order=section.order,
+ research_section_order=research_section_index,
+ total_research_sections=total_research_sections,
+ content_length=len(generated_content),
+ )
+
+ maybe_queue_intro_conclusion_generation_for_blog_post(blog_post_id=blog_post.id)
+ maybe_queue_section_content_synthesis_retry_for_blog_post(blog_post_id=blog_post.id)
+ return num_sections_generated
+
+
+def _get_section_synthesis_retry_cache_key(*, blog_post_id: int) -> str:
+ return f"content_generator:section_synthesis_retry_count:{blog_post_id}"
+
+
+def maybe_queue_section_content_synthesis_retry_for_blog_post(*, blog_post_id: int) -> bool:
+ """
+ Retry mechanism for Step 5:
+
+ If research is "done enough" (all links are in a terminal analyzed/attempted state),
+ but some middle sections still have empty content (e.g. a model returned empty output
+ or a task was missed), re-queue section synthesis a bounded number of times.
+ """
+ blog_post = (
+ GeneratedBlogPost.objects.prefetch_related("blog_post_sections")
+ .filter(id=blog_post_id)
+ .first()
+ )
+ if not blog_post:
+ return False
+
+ sections_in_order = _get_sections_in_order_for_blog_post(blog_post)
+ middle_sections = [
+ section
+ for section in sections_in_order
+ if (section.title or "").strip() not in NON_RESEARCH_SECTION_TITLES
+ ]
+ has_any_middle_section_missing_content = any(
+ not (section.content or "").strip() for section in middle_sections
+ )
+ if not has_any_middle_section_missing_content:
+ return False
+
+ # Only retry when link processing is "complete" (including failures).
+ # If there are links still being processed, let the normal kicks handle it.
+ links_queryset = GeneratedBlogPostResearchLink.objects.filter(blog_post_id=blog_post_id)
+ has_any_pending_link = links_queryset.filter(date_analyzed__isnull=True).exists()
+ if has_any_pending_link:
+ return False
+
+ max_retries = 5 if settings.DEBUG else 2
+ retry_cache_key = _get_section_synthesis_retry_cache_key(blog_post_id=blog_post_id)
+ retry_count = cache.get(retry_cache_key, 0) or 0
+ if retry_count >= max_retries:
+ logger.warning(
+ "[ContentGenerator] Not retrying section synthesis; max retries reached",
+ blog_post_id=blog_post_id,
+ retry_count=retry_count,
+ max_retries=max_retries,
+ num_middle_sections=len(middle_sections),
+ num_links_total=links_queryset.count(),
+ )
+ return False
+
+ cache.set(retry_cache_key, retry_count + 1, timeout=SECTION_SYNTHESIS_RETRY_CACHE_TTL_SECONDS)
+ async_task(
+ "core.content_generator.tasks.synthesize_section_contents_for_blog_post_task",
+ blog_post_id,
+ group="Synthesize Section Content (Retry)",
+ )
+ logger.info(
+ "[ContentGenerator] Queued section content synthesis retry task",
+ blog_post_id=blog_post_id,
+ retry_count=retry_count + 1,
+ max_retries=max_retries,
+ num_middle_sections=len(middle_sections),
+ num_links_total=links_queryset.count(),
+ )
+ return True
+
+
+def maybe_queue_section_content_synthesis_for_blog_post(*, blog_post_id: int) -> bool:
+ """
+ Queue Step 5 once research work is in a terminal state for all required inputs.
+
+ This is intentionally best-effort + idempotent:
+ - It may queue more than once, but the synthesis step skips sections that already have content.
+ """
+ blog_post = (
+ GeneratedBlogPost.objects.prefetch_related(
+ "blog_post_sections__research_questions__research_links"
+ )
+ .filter(id=blog_post_id)
+ .first()
+ )
+ if not blog_post:
+ return False
+
+ sections_in_order = _get_sections_in_order_for_blog_post(blog_post)
+ middle_sections_missing_content = [
+ section
+ for section in sections_in_order
+ if (section.title or "").strip() not in NON_RESEARCH_SECTION_TITLES
+ and not (section.content or "").strip()
+ ]
+
+ num_pending_links = 0
+ num_scrape_tasks_queued = 0
+ num_analyze_tasks_queued = 0
+
+ for section in middle_sections_missing_content:
+ section_questions = list(section.research_questions.all())
+ for research_question in section_questions:
+ research_links = list(research_question.research_links.all())
+ for research_link in research_links:
+ # Terminal state: we attempted analysis for this link (even if it failed).
+ if research_link.date_analyzed is not None:
+ continue
+
+ num_pending_links += 1
+
+ link_content = (research_link.content or "").strip()
+ if not link_content:
+ # If we haven't scraped content yet (or it failed previously but wasn't marked),
+ # re-queue a scrape attempt. The scrape task will always queue analysis next.
+ async_task(
+ "core.content_generator.tasks.scrape_research_link_content_task",
+ research_link.id,
+ group="Scrape Research Links (Retry/Kick)",
+ )
+ num_scrape_tasks_queued += 1
+ continue
+
+ # Content exists, but analysis hasn't run yet: queue AI augmentation.
+ async_task(
+ "core.content_generator.tasks.analyze_research_link_content_task",
+ research_link.id,
+ group="Analyze Research Links (Retry/Kick)",
+ )
+ num_analyze_tasks_queued += 1
+
+ if num_pending_links > 0:
+ logger.info(
+ "[ContentGenerator] Not queuing section synthesis; research links still pending",
+ blog_post_id=blog_post_id,
+ num_middle_sections_missing_content=len(middle_sections_missing_content),
+ num_pending_links=num_pending_links,
+ num_scrape_tasks_queued=num_scrape_tasks_queued,
+ num_analyze_tasks_queued=num_analyze_tasks_queued,
+ )
+ return False
+
+ async_task(
+ "core.content_generator.tasks.synthesize_section_contents_for_blog_post_task",
+ blog_post_id,
+ group="Synthesize Section Content",
+ )
+ logger.info(
+ "[ContentGenerator] Queued section content synthesis task",
+ blog_post_id=blog_post_id,
+ num_middle_sections_missing_content=len(middle_sections_missing_content),
+ )
+ return True
+
+
+def _get_sections_in_order_for_blog_post(
+ blog_post: GeneratedBlogPost,
+) -> list[GeneratedBlogPostSection]:
+ return sorted(
+ list(blog_post.blog_post_sections.all()),
+ key=lambda section: (section.order, section.id),
+ )
+
+
+def generate_intro_and_conclusion_for_blog_post(*, blog_post_id: int) -> int:
+ """
+ Step 6: Generate Introduction + Conclusion in a single model call.
+
+ Runs only when all middle sections have content.
+ """
+ blog_post = (
+ GeneratedBlogPost.objects.select_related(
+ "title_suggestion",
+ "project",
+ )
+ .prefetch_related("blog_post_sections")
+ .filter(id=blog_post_id)
+ .first()
+ )
+ if not blog_post:
+ raise ValueError(f"GeneratedBlogPost not found: {blog_post_id}")
+
+ title_suggestion = blog_post.title_suggestion
+ if not title_suggestion:
+ raise ValueError(f"GeneratedBlogPost missing title_suggestion: {blog_post_id}")
+
+ content_type_to_use = title_suggestion.content_type or ContentType.SHARING
+ blog_post_generation_context = _create_blog_post_generation_context(
+ title_suggestion=title_suggestion,
+ content_type_to_use=content_type_to_use,
+ )
+
+ sections_in_order = _get_sections_in_order_for_blog_post(blog_post)
+ section_titles_in_order = [
+ (section.title or "").strip()
+ for section in sections_in_order
+ if (section.title or "").strip()
+ ]
+
+ intro_section = next(
+ (
+ section
+ for section in sections_in_order
+ if (section.title or "").strip() == INTRODUCTION_SECTION_TITLE
+ ),
+ None,
+ )
+ conclusion_section = next(
+ (
+ section
+ for section in sections_in_order
+ if (section.title or "").strip() == CONCLUSION_SECTION_TITLE
+ ),
+ None,
+ )
+ if not intro_section or not conclusion_section:
+ raise ValueError(f"Blog post is missing Introduction/Conclusion sections: {blog_post_id}")
+
+ should_generate_intro = not (intro_section.content or "").strip()
+ should_generate_conclusion = not (conclusion_section.content or "").strip()
+ if not should_generate_intro and not should_generate_conclusion:
+ return 0
+
+ middle_sections = [
+ section
+ for section in sections_in_order
+ if (section.title or "").strip() not in NON_RESEARCH_SECTION_TITLES
+ ]
+ has_any_middle_section_missing_content = any(
+ not (section.content or "").strip() for section in middle_sections
+ )
+ if has_any_middle_section_missing_content:
+ logger.info(
+ "[ContentGenerator] Skipping intro/conclusion generation; middle sections not ready",
+ blog_post_id=blog_post.id,
+ num_middle_sections=len(middle_sections),
+ )
+ return 0
+
+ existing_sections_context = [
+ PriorSectionContext(
+ title=(section.title or "").strip(),
+ content=(section.content or "").strip(),
+ )
+ for section in sections_in_order
+ if (section.title or "").strip() and (section.content or "").strip()
+ ]
+
+ intro_conclusion_context = BlogPostIntroConclusionGenerationContext(
+ blog_post_generation_context=blog_post_generation_context,
+ blog_post_title=(blog_post.title or title_suggestion.title or "").strip(),
+ section_titles_in_order=section_titles_in_order,
+ sections_in_order=existing_sections_context,
+ )
+
+ agent = create_generate_blog_post_intro_conclusion_agent(content_type=content_type_to_use)
+ result = run_agent_synchronously(
+ agent,
+ "Write the Introduction and Conclusion for this blog post.",
+ deps=intro_conclusion_context,
+ function_name="generate_intro_and_conclusion_for_blog_post.intro_conclusion",
+ model_name="GeneratedBlogPostSection",
+ )
+
+ output: GeneratedBlogPostIntroConclusionSchema | None = (
+ result.output if result and result.output else None
+ )
+ if not output:
+ return 0
+
+ num_sections_updated = 0
+ if should_generate_intro:
+ introduction_content = (output.introduction or "").strip()
+ if introduction_content:
+ intro_section.content = introduction_content
+ intro_section.save(update_fields=["content"])
+ num_sections_updated += 1
+
+ if should_generate_conclusion:
+ conclusion_content = (output.conclusion or "").strip()
+ if conclusion_content:
+ conclusion_section.content = conclusion_content
+ conclusion_section.save(update_fields=["content"])
+ num_sections_updated += 1
+
+ logger.info(
+ "[ContentGenerator] Intro/conclusion generated",
+ blog_post_id=blog_post.id,
+ intro_generated=bool((intro_section.content or "").strip()),
+ conclusion_generated=bool((conclusion_section.content or "").strip()),
+ num_sections_updated=num_sections_updated,
+ )
+
+ maybe_populate_generated_blog_post_content(blog_post_id=blog_post.id)
+ return num_sections_updated
+
+
+def maybe_queue_intro_conclusion_generation_for_blog_post(*, blog_post_id: int) -> bool:
+ """
+ Queue Step 6 only when all middle sections have content.
+
+ Best-effort + idempotent: if it queues multiple times, the generation step skips when already present.
+ """
+ blog_post = (
+ GeneratedBlogPost.objects.prefetch_related("blog_post_sections")
+ .filter(id=blog_post_id)
+ .first()
+ )
+ if not blog_post:
+ return False
+
+ sections_in_order = _get_sections_in_order_for_blog_post(blog_post)
+ middle_sections = [
+ section
+ for section in sections_in_order
+ if (section.title or "").strip() not in NON_RESEARCH_SECTION_TITLES
+ ]
+
+ if any(not (section.content or "").strip() for section in middle_sections):
+ return False
+
+ async_task(
+ "core.content_generator.tasks.generate_intro_and_conclusion_for_blog_post_task",
+ blog_post_id,
+ group="Generate Intro and Conclusion",
+ )
+ logger.info(
+ "[ContentGenerator] Queued intro/conclusion generation task",
+ blog_post_id=blog_post_id,
+ num_middle_sections=len(middle_sections),
+ )
+ return True
+
+
+def _build_full_blog_post_markdown(*, blog_post: GeneratedBlogPost) -> str:
+ blog_post_title = (blog_post.title or "").strip()
+ if not blog_post_title:
+ return ""
+
+ sections_in_order = _get_sections_in_order_for_blog_post(blog_post)
+ markdown_chunks = [f"# {blog_post_title}", ""]
+
+ for section in sections_in_order:
+ section_title = (section.title or "").strip()
+ section_content = (section.content or "").strip()
+ if not section_title or not section_content:
+ continue
+
+ markdown_chunks.append(f"## {section_title}")
+ markdown_chunks.append("")
+ markdown_chunks.append(section_content)
+ markdown_chunks.append("")
+
+ full_markdown = "\n".join(markdown_chunks).strip() + "\n"
+ return full_markdown
+
+
+def populate_generated_blog_post_content(*, blog_post_id: int) -> bool:
+ """
+ Step 7: Populate GeneratedBlogPost.content from the generated section contents.
+
+ Runs only when:
+ - All sections (including Introduction + Conclusion) have non-empty content
+ - GeneratedBlogPost.content is currently empty
+ """
+ blog_post = (
+ GeneratedBlogPost.objects.prefetch_related("blog_post_sections")
+ .filter(id=blog_post_id)
+ .first()
+ )
+ if not blog_post:
+ raise ValueError(f"GeneratedBlogPost not found: {blog_post_id}")
+
+ if (blog_post.content or "").strip():
+ return False
+
+ sections_in_order = _get_sections_in_order_for_blog_post(blog_post)
+ if any(not (section.content or "").strip() for section in sections_in_order):
+ logger.info(
+ "[ContentGenerator] Skipping blog_post.content population; not all sections have content",
+ blog_post_id=blog_post.id,
+ num_sections=len(sections_in_order),
+ )
+ return False
+
+ full_markdown = _build_full_blog_post_markdown(blog_post=blog_post)
+ if not full_markdown.strip():
+ logger.warning(
+ "[ContentGenerator] Skipping blog_post.content population; built markdown is empty",
+ blog_post_id=blog_post.id,
+ )
+ return False
+
+ blog_post.content = full_markdown
+ blog_post.save(update_fields=["content"])
+
+ logger.info(
+ "[ContentGenerator] Populated GeneratedBlogPost.content from sections",
+ blog_post_id=blog_post.id,
+ content_length=len(full_markdown),
+ )
+ return True
+
+
+def maybe_populate_generated_blog_post_content(*, blog_post_id: int) -> bool:
+ """
+ Queue Step 7 when the whole pipeline is done.
+
+ Best-effort + idempotent: population skips if blog_post.content is already non-empty.
+ """
+ blog_post = (
+ GeneratedBlogPost.objects.prefetch_related("blog_post_sections")
+ .filter(id=blog_post_id)
+ .first()
+ )
+ if not blog_post:
+ return False
+
+ if (blog_post.content or "").strip():
+ return False
+
+ sections_in_order = _get_sections_in_order_for_blog_post(blog_post)
+ if any(not (section.content or "").strip() for section in sections_in_order):
+ return False
+
+ async_task(
+ "core.content_generator.tasks.populate_generated_blog_post_content_task",
+ blog_post_id,
+ group="Finalize Generated Blog Post Content",
+ )
+ logger.info(
+ "[ContentGenerator] Queued blog_post.content population task",
+ blog_post_id=blog_post_id,
+ num_sections=len(sections_in_order),
+ )
+ return True
diff --git a/core/content_generator/tasks.py b/core/content_generator/tasks.py
index ca432a6..5f2463c 100644
--- a/core/content_generator/tasks.py
+++ b/core/content_generator/tasks.py
@@ -1,17 +1,23 @@
from __future__ import annotations
+from django.conf import settings
from django_q.tasks import async_task
from core.content_generator.pipeline import (
analyze_research_link_content,
+ generate_intro_and_conclusion_for_blog_post,
generate_research_questions_for_section,
+ populate_generated_blog_post_content,
populate_research_links_for_question_from_exa,
scrape_research_link_content,
+ synthesize_section_contents_for_blog_post,
)
from tuxseo.utils import get_tuxseo_logger
logger = get_tuxseo_logger(__name__)
+LOCAL_NUM_EXA_RESULTS_PER_QUESTION = 2
+
def populate_research_links_for_question_from_exa_task(
research_question_id: int,
@@ -21,15 +27,20 @@ def populate_research_links_for_question_from_exa_task(
"""
Populate Exa research links for one research question.
"""
+ num_results_per_question_to_use = (
+ LOCAL_NUM_EXA_RESULTS_PER_QUESTION if settings.DEBUG else num_results_per_question
+ )
num_links = populate_research_links_for_question_from_exa(
research_question_id=research_question_id,
- num_results_per_question=num_results_per_question,
+ num_results_per_question=num_results_per_question_to_use,
months_back=months_back,
)
logger.info(
"[ContentGenerator Tasks] Populated Exa research links for question",
research_question_id=research_question_id,
num_links_upserted=num_links,
+ num_results_per_question=num_results_per_question_to_use,
+ months_back=months_back,
)
return f"Populated {num_links} research links for research question {research_question_id}"
@@ -37,7 +48,12 @@ def populate_research_links_for_question_from_exa_task(
def scrape_research_link_content_task(research_link_id: int):
"""
Fetch research link content using Jina Reader.
- If content is successfully fetched, queue the analysis task.
+ Always queue the analysis task after the scrape attempt.
+
+ Rationale:
+ - Jina can return empty content for some URLs (parsing failures).
+ - We still want the pipeline to progress and eventually synthesize sections using
+ whatever research succeeded, rather than stalling forever on a few bad links.
"""
did_fetch_content = scrape_research_link_content(research_link_id=research_link_id)
logger.info(
@@ -45,12 +61,11 @@ def scrape_research_link_content_task(research_link_id: int):
research_link_id=research_link_id,
did_fetch_content=did_fetch_content,
)
- if did_fetch_content:
- async_task(
- "core.content_generator.tasks.analyze_research_link_content_task",
- research_link_id,
- group="Analyze Research Links",
- )
+ async_task(
+ "core.content_generator.tasks.analyze_research_link_content_task",
+ research_link_id,
+ group="Analyze Research Links",
+ )
return f"Scraped research link {research_link_id} (did_fetch_content={did_fetch_content})"
@@ -59,6 +74,7 @@ def analyze_research_link_content_task(research_link_id: int):
Analyze a research link that has already been scraped:
- generate general summary
- generate blog-post contextual summary for the research question/section
+ - generate an answer to the research question
"""
num_fields_updated = analyze_research_link_content(research_link_id=research_link_id)
logger.info(
@@ -69,6 +85,48 @@ def analyze_research_link_content_task(research_link_id: int):
return f"Analyzed research link {research_link_id} (updated_fields={num_fields_updated})"
+def synthesize_section_contents_for_blog_post_task(blog_post_id: int):
+ """
+ Synthesize the content for each middle section sequentially (excluding Introduction/Conclusion).
+ """
+ num_sections_generated = synthesize_section_contents_for_blog_post(blog_post_id=blog_post_id)
+ logger.info(
+ "[ContentGenerator Tasks] Synthesized section contents for blog post",
+ blog_post_id=blog_post_id,
+ num_sections_generated=num_sections_generated,
+ )
+ return f"Synthesized {num_sections_generated} section(s) for blog post {blog_post_id}"
+
+
+def generate_intro_and_conclusion_for_blog_post_task(blog_post_id: int):
+ """
+ Generate Introduction + Conclusion in one AI call.
+ Only runs once all middle sections have content.
+ """
+ num_sections_updated = generate_intro_and_conclusion_for_blog_post(blog_post_id=blog_post_id)
+ logger.info(
+ "[ContentGenerator Tasks] Generated intro and conclusion for blog post",
+ blog_post_id=blog_post_id,
+ num_sections_updated=num_sections_updated,
+ )
+ return (
+ f"Generated intro/conclusion (updated={num_sections_updated}) for blog post {blog_post_id}"
+ )
+
+
+def populate_generated_blog_post_content_task(blog_post_id: int):
+ """
+ Populate GeneratedBlogPost.content from the generated sections.
+ """
+ did_populate = populate_generated_blog_post_content(blog_post_id=blog_post_id)
+ logger.info(
+ "[ContentGenerator Tasks] Populated GeneratedBlogPost.content",
+ blog_post_id=blog_post_id,
+ did_populate=did_populate,
+ )
+ return f"Populated GeneratedBlogPost.content for blog post {blog_post_id} (did_populate={did_populate})"
+
+
def generate_research_questions_for_section_task(section_id: int):
"""
Generate research questions for one section, then queue Exa research link tasks for each
diff --git a/core/migrations/0050_backlink_generatedblogpostresearchquestion_and_more.py b/core/migrations/0050_backlink_generatedblogpostresearchquestion_and_more.py
deleted file mode 100644
index 8e12c27..0000000
--- a/core/migrations/0050_backlink_generatedblogpostresearchquestion_and_more.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# Generated by Django 5.2.8 on 2025-12-26 21:43
-
-import django.db.models.deletion
-import pgvector.django.vector
-import uuid
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('core', '0049_alter_emailsent_email_type'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='Backlink',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('uuid', models.UUIDField(default=uuid.uuid4, editable=False)),
- ('created_at', models.DateTimeField(auto_now_add=True)),
- ('updated_at', models.DateTimeField(auto_now=True)),
- ('deleted_at', models.DateTimeField(blank=True, null=True)),
- ('linked_from_project_page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='backlinks_from', to='core.project')),
- ('linked_to_project_page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='backlinks_to', to='core.project')),
- ('linking_from_blog_post', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='backlinks', to='core.generatedblogpost')),
- ('linkning_to_project_page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='backlinks', to='core.projectpage')),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.CreateModel(
- name='GeneratedBlogPostResearchQuestion',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('uuid', models.UUIDField(default=uuid.uuid4, editable=False)),
- ('created_at', models.DateTimeField(auto_now_add=True)),
- ('updated_at', models.DateTimeField(auto_now=True)),
- ('deleted_at', models.DateTimeField(blank=True, null=True)),
- ('question', models.CharField(max_length=250)),
- ('blog_post', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='research_questions', to='core.generatedblogpost')),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.CreateModel(
- name='GeneratedBlogPostResearchLink',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('uuid', models.UUIDField(default=uuid.uuid4, editable=False)),
- ('created_at', models.DateTimeField(auto_now_add=True)),
- ('updated_at', models.DateTimeField(auto_now=True)),
- ('deleted_at', models.DateTimeField(blank=True, null=True)),
- ('url', models.URLField()),
- ('title', models.CharField(blank=True, default='', max_length=500)),
- ('author', models.CharField(blank=True, default='', max_length=250)),
- ('description', models.TextField(blank=True, default='')),
- ('markdown_content', models.TextField(blank=True, default='')),
- ('question', models.CharField(max_length=250)),
- ('date_scraped', models.DateTimeField(blank=True, null=True)),
- ('date_analyzed', models.DateTimeField(blank=True, null=True)),
- ('summary', models.TextField(blank=True)),
- ('embedding', pgvector.django.vector.VectorField(blank=True, default=None, dimensions=1024, null=True)),
- ('blog_post', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='research_links', to='core.generatedblogpost')),
- ('research_question', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='research_links', to='core.generatedblogpostresearchquestion')),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.CreateModel(
- name='GeneratedBlogPostSection',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('uuid', models.UUIDField(default=uuid.uuid4, editable=False)),
- ('created_at', models.DateTimeField(auto_now_add=True)),
- ('updated_at', models.DateTimeField(auto_now=True)),
- ('deleted_at', models.DateTimeField(blank=True, null=True)),
- ('title', models.CharField(max_length=250)),
- ('content', models.TextField(blank=True, default='')),
- ('order', models.IntegerField(default=0)),
- ('blog_post', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='blog_post_sections', to='core.generatedblogpost')),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.AddField(
- model_name='generatedblogpostresearchquestion',
- name='section',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='research_questions', to='core.generatedblogpostsection'),
- ),
- ]
diff --git a/core/migrations/0051_rename_markdown_content_generatedblogpostresearchlink_content_and_more.py b/core/migrations/0051_rename_markdown_content_generatedblogpostresearchlink_content_and_more.py
deleted file mode 100644
index dde01ab..0000000
--- a/core/migrations/0051_rename_markdown_content_generatedblogpostresearchlink_content_and_more.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Generated by Django 5.2.9 on 2025-12-27 12:14
-
-import django.utils.timezone
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('core', '0050_backlink_generatedblogpostresearchquestion_and_more'),
- ]
-
- operations = [
- migrations.RenameField(
- model_name='generatedblogpostresearchlink',
- old_name='markdown_content',
- new_name='content',
- ),
- migrations.RenameField(
- model_name='generatedblogpostresearchlink',
- old_name='summary',
- new_name='general_summary',
- ),
- migrations.RemoveField(
- model_name='generatedblogpostresearchlink',
- name='question',
- ),
- migrations.AddField(
- model_name='generatedblogpostresearchlink',
- name='published_date',
- field=models.DateTimeField(blank=True, null=True),
- ),
- migrations.AddField(
- model_name='generatedblogpostresearchlink',
- name='summary_for_question_research',
- field=models.TextField(blank=True, default=''),
- ),
- migrations.AlterField(
- model_name='generatedblogpostresearchlink',
- name='date_scraped',
- field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
- preserve_default=False,
- ),
- ]
diff --git a/core/models.py b/core/models.py
index 8ac9f57..0005af3 100644
--- a/core/models.py
+++ b/core/models.py
@@ -1249,7 +1249,8 @@ class GeneratedBlogPostResearchLink(BaseModel):
date_analyzed = models.DateTimeField(null=True, blank=True)
summary_for_question_research = models.TextField(blank=True, default="")
general_summary = models.TextField(blank=True)
- embedding = VectorField(dimensions=1024, default=None, null=True, blank=True)
+ general_summary_embedding = VectorField(dimensions=1024, default=None, null=True, blank=True)
+ answer_to_question = models.TextField(blank=True, default="")
class ProjectPage(BaseModel):
diff --git a/core/urls.py b/core/urls.py
index 8df36d2..b90da66 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -56,6 +56,11 @@
views.GeneratedBlogPostDetailView.as_view(),
name="generated_blog_post_detail",
),
+ path(
+ "project//title-suggestion//research/",
+ views.BlogPostResearchProcessView.as_view(),
+ name="blog_post_research_process",
+ ),
path(
"project//post//download-pdf/",
views.download_blog_post_pdf,
diff --git a/core/views.py b/core/views.py
index 2524cfa..21d30c2 100644
--- a/core/views.py
+++ b/core/views.py
@@ -27,6 +27,7 @@
from core.models import (
AutoSubmissionSetting,
BlogPost,
+ BlogPostTitleSuggestion,
Competitor,
GeneratedBlogPost,
KeywordTrend,
@@ -38,6 +39,7 @@
track_event,
try_create_posthog_alias,
)
+from core.utils import get_relevant_external_pages_for_blog_post
from tuxseo.utils import get_tuxseo_logger
stripe.api_key = settings.STRIPE_SECRET_KEY
@@ -842,6 +844,7 @@ def get_queryset(self):
def get_context_data(self, **kwargs):
from urllib.parse import urlparse
+
from django.core.paginator import Paginator
context = super().get_context_data(**kwargs)
@@ -977,6 +980,213 @@ def get_context_data(self, **kwargs):
return context
+class BlogPostResearchProcessView(LoginRequiredMixin, DetailView):
+ model = BlogPostTitleSuggestion
+ template_name = "blog/blog_post_research_process.html"
+ context_object_name = "title_suggestion"
+
+ def get_queryset(self):
+ return BlogPostTitleSuggestion.objects.filter(
+ project__profile=self.request.user.profile,
+ project__pk=self.kwargs["project_pk"],
+ )
+
+ def _get_generated_blog_posts(self, title_suggestion: BlogPostTitleSuggestion):
+ return (
+ title_suggestion.generated_blog_posts.select_related("project", "title_suggestion")
+ .prefetch_related(
+ "blog_post_sections__research_questions__research_links",
+ "research_questions__research_links",
+ )
+ .order_by("-id")
+ )
+
+ def _build_generated_blog_posts_data(self, generated_blog_posts):
+ generated_blog_posts_data = []
+
+ for generated_blog_post in generated_blog_posts:
+ sections = sorted(
+ list(generated_blog_post.blog_post_sections.all()),
+ key=lambda section: (section.order, section.id),
+ )
+ blog_level_questions = sorted(
+ [
+ question
+ for question in generated_blog_post.research_questions.all()
+ if not question.section_id
+ ],
+ key=lambda question: question.id,
+ )
+
+ sections_data = []
+ for section in sections:
+ section_questions = sorted(
+ list(section.research_questions.all()),
+ key=lambda question: question.id,
+ )
+ section_questions_data = []
+ for section_question in section_questions:
+ research_links = sorted(
+ list(section_question.research_links.all()),
+ key=lambda research_link: research_link.id,
+ )
+ section_questions_data.append(
+ {
+ "id": section_question.id,
+ "question": section_question.question,
+ "links": research_links,
+ }
+ )
+
+ sections_data.append(
+ {
+ "id": section.id,
+ "order": section.order,
+ "title": section.title,
+ "content": section.content or "",
+ "questions": section_questions_data,
+ }
+ )
+
+ blog_level_questions_data = []
+ for blog_level_question in blog_level_questions:
+ research_links = sorted(
+ list(blog_level_question.research_links.all()),
+ key=lambda research_link: research_link.id,
+ )
+ blog_level_questions_data.append(
+ {
+ "id": blog_level_question.id,
+ "question": blog_level_question.question,
+ "links": research_links,
+ }
+ )
+
+ generated_blog_posts_data.append(
+ {
+ "id": generated_blog_post.id,
+ "project_id": generated_blog_post.project_id,
+ "title_suggestion_id": generated_blog_post.title_suggestion_id,
+ "title": generated_blog_post.title,
+ "description": generated_blog_post.description,
+ "slug": generated_blog_post.slug,
+ "tags": generated_blog_post.tags,
+ "posted": generated_blog_post.posted,
+ "date_posted": generated_blog_post.date_posted,
+ "content_length": len(generated_blog_post.content or ""),
+ "sections": sections_data,
+ "blog_level_questions": blog_level_questions_data,
+ }
+ )
+
+ return generated_blog_posts_data
+
+ def _get_internal_links(
+ self, title_suggestion: BlogPostTitleSuggestion, should_compute_links: bool
+ ):
+ manually_selected_project_pages = list(
+ title_suggestion.project.project_pages.filter(always_use=True)
+ )
+ if not should_compute_links:
+ return manually_selected_project_pages
+
+ if not settings.JINA_READER_API_KEY:
+ return manually_selected_project_pages
+
+ return title_suggestion.get_internal_links(max_pages=2)
+
+ def _get_external_links(
+ self, title_suggestion: BlogPostTitleSuggestion, should_compute_links: bool
+ ):
+ if not should_compute_links:
+ return []
+
+ if not settings.JINA_READER_API_KEY:
+ return []
+
+ meta_description = title_suggestion.suggested_meta_description or ""
+ external_pages = get_relevant_external_pages_for_blog_post(
+ meta_description=meta_description,
+ exclude_project=title_suggestion.project,
+ max_pages=3,
+ )
+ return list(external_pages)
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ title_suggestion = self.object
+ project = title_suggestion.project
+ profile = self.request.user.profile
+
+ should_compute_links = self.request.GET.get("compute_links") == "true"
+
+ project_keywords = project.get_keywords()
+ title_suggestion.keywords_with_usage = []
+ if title_suggestion.target_keywords:
+ for keyword_text in title_suggestion.target_keywords:
+ keyword_info = project_keywords.get(
+ keyword_text.lower(),
+ {"keyword": None, "in_use": False, "project_keyword_id": None},
+ )
+ title_suggestion.keywords_with_usage.append(
+ {
+ "text": keyword_text,
+ "keyword": keyword_info["keyword"],
+ "in_use": keyword_info["in_use"],
+ "project_keyword_id": keyword_info["project_keyword_id"],
+ }
+ )
+
+ generated_blog_posts = self._get_generated_blog_posts(title_suggestion)
+ generated_blog_posts_data = self._build_generated_blog_posts_data(generated_blog_posts)
+
+ try:
+ keywords_to_use = title_suggestion.get_blog_post_keywords()
+ except (AttributeError, TypeError):
+ logger.warning(
+ "[BlogPostResearchProcessView] Failed to compute keywords_to_use",
+ title_suggestion_id=title_suggestion.id,
+ project_id=project.id,
+ exc_info=True,
+ )
+ keywords_to_use = []
+
+ try:
+ internal_links = self._get_internal_links(title_suggestion, should_compute_links)
+ except (AttributeError, TypeError, ValueError):
+ logger.warning(
+ "[BlogPostResearchProcessView] Failed to compute internal_links",
+ title_suggestion_id=title_suggestion.id,
+ project_id=project.id,
+ should_compute_links=should_compute_links,
+ exc_info=True,
+ )
+ internal_links = []
+
+ try:
+ external_links = self._get_external_links(title_suggestion, should_compute_links)
+ except (AttributeError, TypeError, ValueError):
+ logger.warning(
+ "[BlogPostResearchProcessView] Failed to compute external_links",
+ title_suggestion_id=title_suggestion.id,
+ project_id=project.id,
+ should_compute_links=should_compute_links,
+ exc_info=True,
+ )
+ external_links = []
+
+ context["project"] = project
+ context["has_pro_subscription"] = profile.is_on_pro_plan
+ context["jina_api_key_configured"] = bool(settings.JINA_READER_API_KEY)
+ context["should_compute_links"] = should_compute_links
+ context["keywords_to_use"] = keywords_to_use
+ context["internal_links"] = internal_links or []
+ context["external_links"] = external_links or []
+ context["generated_blog_posts"] = generated_blog_posts_data
+
+ return context
+
+
class CompetitorBlogPostDetailView(LoginRequiredMixin, DetailView):
model = Competitor
template_name = "project/competitor_blog_post_detail.html"
diff --git a/frontend/templates/blog/blog_post_research_process.html b/frontend/templates/blog/blog_post_research_process.html
new file mode 100644
index 0000000..d180a54
--- /dev/null
+++ b/frontend/templates/blog/blog_post_research_process.html
@@ -0,0 +1,336 @@
+{% extends "base_project.html" %}
+{% load static %}
+
+{% block meta %}
+Research Process - {{ title_suggestion.title }} - TuxSEO
+{% endblock meta %}
+
+{% block project_content %}
+
+
+
+
+
+
{{ title_suggestion.title }}
+ {% if title_suggestion.description %}
+
{{ title_suggestion.description }}
+ {% endif %}
+
+
+
+ {% if jina_api_key_configured %}
+ {% if should_compute_links %}
+
+ Hide computed links
+
+ {% else %}
+
+ Compute links
+
+ {% endif %}
+ {% else %}
+
+ Link computation requires Jina API key
+
+ {% endif %}
+
+
+
+
+
+
Title Suggestion
+
+
+
+
Suggestion ID
+
{{ title_suggestion.id }}
+
+
+
Content type
+
{{ title_suggestion.content_type }}
+
+
+
Category
+
{{ title_suggestion.category }}
+
+
+
Created
+
{{ title_suggestion.created_at|date:"F j, Y g:i A" }}
+
+
+
+ {% if title_suggestion.target_keywords %}
+
+
Target keywords
+
+ {% for keyword_data in title_suggestion.keywords_with_usage %}
+ {% include "components/keyword_chip.html" with keyword=keyword_data.text project_id=project.id keyword_in_use=keyword_data.in_use %}
+ {% endfor %}
+
+
+ {% endif %}
+
+ {% if title_suggestion.suggested_meta_description %}
+
+
Suggested meta description
+
+ {{ title_suggestion.suggested_meta_description }}
+
+
+ {% endif %}
+
+ {% if title_suggestion.prompt %}
+
+
Prompt
+
{{ title_suggestion.prompt }}
+
+ {% endif %}
+
+
+
+
+
Derived inputs
+
+ {% if should_compute_links %}
+ Computed links are enabled.
+ {% else %}
+ Computed links are disabled (fast mode).
+ {% endif %}
+
+
+
+
+
+
Keywords to use ({{ keywords_to_use|length }})
+ {% if keywords_to_use %}
+
+ {% for keyword_text in keywords_to_use %}
+
+ {{ keyword_text }}
+
+ {% endfor %}
+
+ {% else %}
+
No keywords computed.
+ {% endif %}
+
+
+
+
Internal links ({{ internal_links|length }})
+ {% if internal_links %}
+
+
+
+
+ | Title |
+ URL |
+ Always use |
+
+
+
+ {% for project_page in internal_links %}
+
+ | {{ project_page.title }} |
+
+
+ {{ project_page.url }}
+
+ |
+ {{ project_page.always_use|yesno:"Yes,No" }} |
+
+ {% endfor %}
+
+
+
+ {% else %}
+
No internal links available.
+ {% endif %}
+
+
+
+
External links ({{ external_links|length }})
+ {% if external_links %}
+
+
+
+
+ | Project |
+ Title |
+ URL |
+
+
+
+ {% for project_page in external_links %}
+
+ | {{ project_page.project.name }} |
+ {{ project_page.title }} |
+
+
+ {{ project_page.url }}
+
+ |
+
+ {% endfor %}
+
+
+
+ {% else %}
+
No external links available.
+ {% endif %}
+
+
+
+
+
+
Generated blog posts ({{ generated_blog_posts|length }})
+
+ {% if generated_blog_posts %}
+
+ {% for blog_post in generated_blog_posts %}
+
+
+
+
#{{ blog_post.id }} — {{ blog_post.title }}
+
+ content_length={{ blog_post.content_length }} · posted={{ blog_post.posted|yesno:"true,false" }}
+ {% if blog_post.slug %} · slug={{ blog_post.slug }}{% endif %}
+
+
+
+ {% if blog_post.content_length > 0 %}
+
+ View post
+
+ {% endif %}
+
+
+
+
+ {% if blog_post.description %}
+
+
Description
+
{{ blog_post.description }}
+
+ {% endif %}
+
+ {% if blog_post.sections %}
+
+
Sections ({{ blog_post.sections|length }})
+
+ {% for section in blog_post.sections %}
+
+
+ [{{ section.order }}] {{ section.title }}
+
+
+ {% if section.content %}
+
{{ section.content }}
+ {% endif %}
+
+
+
Research questions ({{ section.questions|length }})
+ {% if section.questions %}
+
+ {% for research_question in section.questions %}
+
+
Q{{ research_question.id }}: {{ research_question.question }}
+
+
Links ({{ research_question.links|length }})
+ {% if research_question.links %}
+
+ {% for research_link in research_question.links %}
+ -
+
{{ research_link.title|default:"(no title)" }}
+
+ {{ research_link.url }}
+
+
+ author={{ research_link.author|default:"" }}
+ {% if research_link.published_date %} · published={{ research_link.published_date|date:"Y-m-d" }}{% endif %}
+ {% if research_link.date_scraped %} · scraped={{ research_link.date_scraped|date:"Y-m-d H:i" }}{% endif %}
+ {% if research_link.date_analyzed %} · analyzed={{ research_link.date_analyzed|date:"Y-m-d H:i" }}{% endif %}
+
+
+ {% endfor %}
+
+ {% else %}
+
No links.
+ {% endif %}
+
+
+ {% endfor %}
+
+ {% else %}
+
No research questions.
+ {% endif %}
+
+
+
+ {% endfor %}
+
+
+ {% endif %}
+
+ {% if blog_post.blog_level_questions %}
+
+
Blog-level research questions ({{ blog_post.blog_level_questions|length }})
+
+ {% for research_question in blog_post.blog_level_questions %}
+
+
Q{{ research_question.id }}: {{ research_question.question }}
+
+
Links ({{ research_question.links|length }})
+ {% if research_question.links %}
+
+ {% for research_link in research_question.links %}
+ -
+
{{ research_link.title|default:"(no title)" }}
+
+ {{ research_link.url }}
+
+
+ author={{ research_link.author|default:"" }}
+ {% if research_link.published_date %} · published={{ research_link.published_date|date:"Y-m-d" }}{% endif %}
+ {% if research_link.date_scraped %} · scraped={{ research_link.date_scraped|date:"Y-m-d H:i" }}{% endif %}
+ {% if research_link.date_analyzed %} · analyzed={{ research_link.date_analyzed|date:"Y-m-d H:i" }}{% endif %}
+
+
+ {% endfor %}
+
+ {% else %}
+
No links.
+ {% endif %}
+
+
+ {% endfor %}
+
+
+ {% endif %}
+
+
+ {% endfor %}
+
+ {% else %}
+
No generated blog posts found for this title suggestion yet.
+ {% endif %}
+
+
+
+{% endblock project_content %}
diff --git a/frontend/templates/blog/generated_blog_post_detail.html b/frontend/templates/blog/generated_blog_post_detail.html
index c5daf0e..9dc8002 100644
--- a/frontend/templates/blog/generated_blog_post_detail.html
+++ b/frontend/templates/blog/generated_blog_post_detail.html
@@ -30,6 +30,20 @@
{{ generated_post.description }}
{% endif %}
+
+ {% if generated_post.title_suggestion_id %}
+
+ {% endif %}
diff --git a/frontend/templates/components/blog_post_suggestion_card.html b/frontend/templates/components/blog_post_suggestion_card.html
index 408b6ce..00ab42e 100644
--- a/frontend/templates/components/blog_post_suggestion_card.html
+++ b/frontend/templates/components/blog_post_suggestion_card.html
@@ -164,8 +164,18 @@
-
-
+
+
+
+ Research
+
+
+