Merge branch 'feat/docker-release-electron-sync' of github.com:presenton/presenton into feat/docker-release-electron-sync

This commit is contained in:
shiva raj badu 2026-04-20 19:55:28 +05:45
commit 7c96044688
No known key found for this signature in database
20 changed files with 1168 additions and 24 deletions

View file

@ -214,6 +214,15 @@ These settings apply to both Docker and the Electron app's backend. You may want
- TOOL_CALLS=[Enable/Disable Tool Calls on Custom LLM]: If **true**, **LLM** will use Tool Call instead of Json Schema for Structured Output.
- DISABLE_THINKING=[Enable/Disable Thinking on Custom LLM]: If **true**, Thinking will be disabled.
- WEB_GROUNDING=[Enable/Disable Web Search for OpenAI, Google And Anthropic]: If **true**, LLM will be able to search web for better results.
- MEM0_ENABLED=[true/false]: Enables mem0 OSS presentation memory. Default is **true**.
- MEM0_DIR=[Path]: Directory for mem0 OSS local storage (Qdrant path + history DB). Default is **/app_data/mem0**.
- MEM0_EMBEDDER_PROVIDER=[Provider]: Embedder provider for mem0 OSS. Default is **fastembed**.
- MEM0_EMBEDDER_MODEL=[Model]: Mid-range local embedding model for memory search. Default is **BAAI/bge-small-en-v1.5**.
- MEM0_EMBEDDING_DIMS=[Number]: Embedding dimensions used by mem0 embedder and qdrant collection. Default is **384**.
- LITEPARSE_DPI=[Number]: LiteParse OCR render DPI (higher can increase memory use). Default is **120**.
- LITEPARSE_NUM_WORKERS=[Number]: LiteParse OCR worker count. Default is **1** for stable Docker parsing.
Mem0 in Docker uses OSS/self-hosted mode (not Mem0 Platform API). Memory is isolated per presentation ID. Prompt context, extracted document text, generated outline context, and subsequent edit interactions are stored and retrieved only for that same presentation (including when revisiting later).
You can also set the following environment variables to customize the image generation provider and API keys:

View file

@ -36,6 +36,13 @@ services:
- DISABLE_ANONYMOUS_TRACKING=${DISABLE_ANONYMOUS_TRACKING}
- COMFYUI_URL=${COMFYUI_URL}
- COMFYUI_WORKFLOW=${COMFYUI_WORKFLOW}
- MEM0_ENABLED=${MEM0_ENABLED:-true}
- MEM0_DIR=${MEM0_DIR:-/app_data/mem0}
- MEM0_EMBEDDER_PROVIDER=${MEM0_EMBEDDER_PROVIDER:-fastembed}
- MEM0_EMBEDDER_MODEL=${MEM0_EMBEDDER_MODEL:-BAAI/bge-small-en-v1.5}
- MEM0_EMBEDDING_DIMS=${MEM0_EMBEDDING_DIMS:-384}
- LITEPARSE_DPI=${LITEPARSE_DPI:-120}
- LITEPARSE_NUM_WORKERS=${LITEPARSE_NUM_WORKERS:-1}
production-gpu:
# image: ghcr.io/presenton/presenton:latest
@ -81,6 +88,13 @@ services:
- DISABLE_ANONYMOUS_TRACKING=${DISABLE_ANONYMOUS_TRACKING}
- COMFYUI_URL=${COMFYUI_URL}
- COMFYUI_WORKFLOW=${COMFYUI_WORKFLOW}
- MEM0_ENABLED=${MEM0_ENABLED:-true}
- MEM0_DIR=${MEM0_DIR:-/app_data/mem0}
- MEM0_EMBEDDER_PROVIDER=${MEM0_EMBEDDER_PROVIDER:-fastembed}
- MEM0_EMBEDDER_MODEL=${MEM0_EMBEDDER_MODEL:-BAAI/bge-small-en-v1.5}
- MEM0_EMBEDDING_DIMS=${MEM0_EMBEDDING_DIMS:-384}
- LITEPARSE_DPI=${LITEPARSE_DPI:-120}
- LITEPARSE_NUM_WORKERS=${LITEPARSE_NUM_WORKERS:-1}
development:
build:
@ -122,6 +136,13 @@ services:
- DISABLE_ANONYMOUS_TRACKING=${DISABLE_ANONYMOUS_TRACKING}
- COMFYUI_URL=${COMFYUI_URL}
- COMFYUI_WORKFLOW=${COMFYUI_WORKFLOW}
- MEM0_ENABLED=${MEM0_ENABLED:-true}
- MEM0_DIR=${MEM0_DIR:-/app_data/mem0}
- MEM0_EMBEDDER_PROVIDER=${MEM0_EMBEDDER_PROVIDER:-fastembed}
- MEM0_EMBEDDER_MODEL=${MEM0_EMBEDDER_MODEL:-BAAI/bge-small-en-v1.5}
- MEM0_EMBEDDING_DIMS=${MEM0_EMBEDDING_DIMS:-384}
- LITEPARSE_DPI=${LITEPARSE_DPI:-120}
- LITEPARSE_NUM_WORKERS=${LITEPARSE_NUM_WORKERS:-1}
development-gpu:
build:
@ -169,6 +190,13 @@ services:
- DISABLE_ANONYMOUS_TRACKING=${DISABLE_ANONYMOUS_TRACKING}
- COMFYUI_URL=${COMFYUI_URL}
- COMFYUI_WORKFLOW=${COMFYUI_WORKFLOW}
- MEM0_ENABLED=${MEM0_ENABLED:-true}
- MEM0_DIR=${MEM0_DIR:-/app_data/mem0}
- MEM0_EMBEDDER_PROVIDER=${MEM0_EMBEDDER_PROVIDER:-fastembed}
- MEM0_EMBEDDER_MODEL=${MEM0_EMBEDDER_MODEL:-BAAI/bge-small-en-v1.5}
- MEM0_EMBEDDING_DIMS=${MEM0_EMBEDDING_DIMS:-384}
- LITEPARSE_DPI=${LITEPARSE_DPI:-120}
- LITEPARSE_NUM_WORKERS=${LITEPARSE_NUM_WORKERS:-1}
volumes:
presenton_root_node_modules:

View file

@ -4,4 +4,4 @@ OPENAI_URL = "https://api.openai.com/v1"
DEFAULT_OPENAI_MODEL = "gpt-4.1"
DEFAULT_GOOGLE_MODEL = "models/gemini-2.5-flash"
DEFAULT_ANTHROPIC_MODEL = "claude-sonnet-4-20250514"
DEFAULT_CODEX_MODEL = "gpt-5.1-codex-mini"
DEFAULT_CODEX_MODEL = "gpt-5.2"

View file

@ -52,10 +52,9 @@ const CHATGPT_MODELS: CodexModel[] = [
{ id: "gpt-5.4-mini", name: "GPT-5.4-Mini" },
{ id: "gpt-5.3-codex", name: "GPT-5.3-Codex" },
{ id: "gpt-5.2", name: "GPT-5.2" },
{ id: "gpt-5.1-codex-mini", name: "GPT-5.1-Codex-Mini" },
];
const DEFAULT_CODEX_MODEL = "gpt-5.1-codex-mini";
const DEFAULT_CODEX_MODEL = "gpt-5.2";
export default function CodexConfig({
codexModel,

View file

@ -39,10 +39,9 @@ export const CHATGPT_MODELS: CodexModel[] = [
{ id: "gpt-5.4-mini", name: "GPT-5.4-Mini" },
{ id: "gpt-5.3-codex", name: "GPT-5.3-Codex" },
{ id: "gpt-5.2", name: "GPT-5.2" },
{ id: "gpt-5.1-codex-mini", name: "GPT-5.1-Codex-Mini" },
];
export const DEFAULT_CODEX_MODEL = "gpt-5.1-codex-mini";
export const DEFAULT_CODEX_MODEL = "gpt-5.2";
export default function CodexConfig({
codexModel,

View file

@ -18,11 +18,17 @@ from models.sse_response import (
from services.temp_file_service import TEMP_FILE_SERVICE
from services.database import get_async_session
from services.documents_loader import DocumentsLoader
from services.mem0_presentation_memory_service import (
MEM0_PRESENTATION_MEMORY_SERVICE,
)
from utils.outline_utils import (
get_no_of_outlines_to_generate_for_n_slides,
get_presentation_title_from_presentation_outline,
)
from utils.llm_calls.generate_presentation_outlines import generate_ppt_outline
from utils.llm_calls.generate_presentation_outlines import (
generate_ppt_outline,
get_messages as get_outline_messages,
)
OUTLINES_ROUTER = APIRouter(prefix="/outlines", tags=["Outlines"])
@ -65,6 +71,34 @@ async def stream_outlines(
else:
n_slides_to_generate = None
outline_messages = get_outline_messages(
presentation.content,
n_slides_to_generate,
presentation.language,
additional_context,
presentation.tone,
presentation.verbosity,
presentation.instructions,
presentation.include_title_slide,
presentation.include_table_of_contents,
)
await MEM0_PRESENTATION_MEMORY_SERVICE.store_generation_context(
presentation_id=presentation.id,
system_prompt=(
outline_messages[0].content
if len(outline_messages) > 0
else None
),
user_prompt=(
outline_messages[1].content
if len(outline_messages) > 1
else None
),
extracted_document_text=additional_context,
source_content=presentation.content,
instructions=presentation.instructions,
)
async for chunk in generate_ppt_outline(
presentation.content,
n_slides_to_generate,
@ -132,6 +166,11 @@ async def stream_outlines(
sql_session.add(presentation)
await sql_session.commit()
await MEM0_PRESENTATION_MEMORY_SERVICE.store_generated_outlines(
presentation.id,
presentation.outlines,
)
yield SSECompleteResponse(
key="presentation", value=presentation.model_dump(mode="json")
).to_string()

View file

@ -32,9 +32,15 @@ from models.sql.template import TemplateModel
from services.documents_loader import DocumentsLoader
from services.webhook_service import WebhookService
from services.image_generation_service import ImageGenerationService
from services.mem0_presentation_memory_service import (
MEM0_PRESENTATION_MEMORY_SERVICE,
)
from utils.dict_utils import deep_update
from utils.export_utils import export_presentation
from utils.llm_calls.generate_presentation_outlines import generate_ppt_outline
from utils.llm_calls.generate_presentation_outlines import (
generate_ppt_outline,
get_messages as get_outline_messages,
)
from models.sql.slide import SlideModel
from models.sql.presentation_layout_code import PresentationLayoutCodeModel
from models.sse_response import SSECompleteResponse, SSEErrorResponse, SSEResponse
@ -320,6 +326,11 @@ async def prepare_presentation(
presentation.set_structure(presentation_structure)
await sql_session.commit()
await MEM0_PRESENTATION_MEMORY_SERVICE.store_generated_outlines(
presentation.id,
presentation.outlines,
)
return presentation
@ -606,14 +617,13 @@ async def generate_presentation_handler(
try:
using_slides_markdown = False
language_to_use = (request.language or "").strip() or None
additional_context = ""
if request.slides_markdown:
using_slides_markdown = True
request.n_slides = len(request.slides_markdown)
if not using_slides_markdown:
additional_context = ""
# Updating async status
if async_status:
async_status.message = "Generating presentation outlines"
@ -642,6 +652,34 @@ async def generate_presentation_handler(
)
)
outline_messages = get_outline_messages(
request.content,
n_slides_to_generate,
language_to_use,
additional_context,
request.tone.value,
request.verbosity.value,
request.instructions,
request.include_title_slide,
request.include_table_of_contents,
)
await MEM0_PRESENTATION_MEMORY_SERVICE.store_generation_context(
presentation_id=presentation_id,
system_prompt=(
outline_messages[0].content
if len(outline_messages) > 0
else None
),
user_prompt=(
outline_messages[1].content
if len(outline_messages) > 1
else None
),
extracted_document_text=additional_context,
source_content=request.content,
instructions=request.instructions,
)
presentation_outlines_text = ""
async for chunk in generate_ppt_outline(
request.content,
@ -699,6 +737,20 @@ async def generate_presentation_handler(
)
total_outlines = len(request.slides_markdown)
await MEM0_PRESENTATION_MEMORY_SERVICE.store_generation_context(
presentation_id=presentation_id,
system_prompt=None,
user_prompt=None,
extracted_document_text=None,
source_content=request.content,
instructions=request.instructions,
)
await MEM0_PRESENTATION_MEMORY_SERVICE.store_generated_outlines(
presentation_id,
presentation_outlines.model_dump(mode="json"),
)
# Updating async status
if async_status:
async_status.message = "Selecting layout for each slide"

View file

@ -7,12 +7,14 @@ from models.sql.presentation import PresentationModel
from models.sql.slide import SlideModel
from services.database import get_async_session
from services.image_generation_service import ImageGenerationService
from services.mem0_presentation_memory_service import (
MEM0_PRESENTATION_MEMORY_SERVICE,
)
from utils.asset_directory_utils import get_images_directory
from utils.llm_calls.edit_slide import get_edited_slide_content
from utils.llm_calls.edit_slide_html import get_edited_slide_html
from utils.llm_calls.select_slide_type_on_edit import get_slide_layout_from_prompt
from utils.process_slides import process_old_and_new_slides_and_fetch_assets
import uuid
SLIDE_ROUTER = APIRouter(prefix="/slide", tags=["Slide"])
@ -31,13 +33,28 @@ async def edit_slide(
if not presentation:
raise HTTPException(status_code=404, detail="Presentation not found")
memory_context = await MEM0_PRESENTATION_MEMORY_SERVICE.retrieve_context(
presentation.id,
prompt,
)
presentation_layout = presentation.get_layout()
slide_layout = await get_slide_layout_from_prompt(
prompt, presentation_layout, slide
prompt,
presentation_layout,
slide,
memory_context,
)
edited_slide_content = await get_edited_slide_content(
prompt, slide, presentation.language, slide_layout
prompt,
slide,
presentation.language,
slide_layout,
presentation.tone,
presentation.verbosity,
presentation.instructions,
memory_context,
)
image_generation_service = ImageGenerationService(get_images_directory())
@ -59,6 +76,13 @@ async def edit_slide(
sql_session.add_all(new_assets)
await sql_session.commit()
await MEM0_PRESENTATION_MEMORY_SERVICE.store_slide_edit(
presentation_id=presentation.id,
slide_index=slide.index,
edit_prompt=prompt,
edited_slide_content=edited_slide_content,
)
return slide
@ -73,11 +97,24 @@ async def edit_slide_html(
if not slide:
raise HTTPException(status_code=404, detail="Slide not found")
presentation = await sql_session.get(PresentationModel, slide.presentation)
if not presentation:
raise HTTPException(status_code=404, detail="Presentation not found")
html_to_edit = html or slide.html_content
if not html_to_edit:
raise HTTPException(status_code=400, detail="No HTML to edit")
edited_slide_html = await get_edited_slide_html(prompt, html_to_edit)
memory_context = await MEM0_PRESENTATION_MEMORY_SERVICE.retrieve_context(
presentation.id,
prompt,
)
edited_slide_html = await get_edited_slide_html(
prompt,
html_to_edit,
memory_context,
)
# Always assign a new unique id to the slide
# This is to ensure that the nextjs can track slide updates
@ -87,4 +124,11 @@ async def edit_slide_html(
slide.html_content = edited_slide_html
await sql_session.commit()
await MEM0_PRESENTATION_MEMORY_SERVICE.store_slide_edit(
presentation_id=presentation.id,
slide_index=slide.index,
edit_prompt=prompt,
edited_slide_content=edited_slide_html,
)
return slide

View file

@ -4,4 +4,4 @@ OPENAI_URL = "https://api.openai.com/v1"
DEFAULT_OPENAI_MODEL = "gpt-4.1"
DEFAULT_GOOGLE_MODEL = "models/gemini-2.5-flash"
DEFAULT_ANTHROPIC_MODEL = "claude-sonnet-4-20250514"
DEFAULT_CODEX_MODEL = "gpt-5.1-codex-mini"
DEFAULT_CODEX_MODEL = "gpt-5.2"

View file

@ -20,6 +20,7 @@ dependencies = [
"fastembed-vectorstore>=0.5.2",
"fastmcp>=2.11.0",
"google-genai>=1.28.0",
"mem0ai[nlp]>=0.1.115",
"nltk>=3.9.1",
"openai>=1.98.0",
"pathvalidate>=3.3.1",

View file

@ -11,6 +11,8 @@ class LiteParseError(Exception):
LOGGER = logging.getLogger(__name__)
_LOG_SNIPPET_LIMIT = 600
_DEFAULT_DPI = 120
_DEFAULT_NUM_WORKERS = 1
def _snippet(value: str, limit: int = _LOG_SNIPPET_LIMIT) -> str:
@ -36,10 +38,36 @@ def _subprocess_text_kwargs() -> Mapping[str, object]:
return {"text": True, "encoding": "utf-8", "errors": "replace"}
def _env_int(name: str, default: int, minimum: int, maximum: int) -> int:
raw = (os.getenv(name) or "").strip()
if not raw:
return default
try:
parsed = int(raw)
except Exception:
LOGGER.warning(
"[LiteParse] Invalid %s=%r, using default=%s",
name,
raw,
default,
)
return default
return min(max(parsed, minimum), maximum)
class LiteParseService:
def __init__(self, timeout_seconds: int = 180):
self.timeout_seconds = timeout_seconds
self.node_binary = os.getenv("LITEPARSE_NODE_BINARY", "node")
self.dpi = _env_int("LITEPARSE_DPI", _DEFAULT_DPI, minimum=72, maximum=600)
self.num_workers = _env_int(
"LITEPARSE_NUM_WORKERS",
_DEFAULT_NUM_WORKERS,
minimum=1,
maximum=64,
)
self.runner_path = os.getenv("LITEPARSE_RUNNER_PATH", self._resolve_runner_path())
self.runner_dir = os.path.dirname(self.runner_path)
self._npm_project_root = self._resolve_npm_project_root()
@ -231,6 +259,10 @@ class LiteParseService:
"true" if ocr_enabled else "false",
"--ocr-language",
ocr_language,
"--dpi",
str(self.dpi),
"--num-workers",
str(self.num_workers),
]
ocr_server = (os.getenv("LITEPARSE_OCR_SERVER_URL") or "").strip()
if ocr_server:
@ -240,10 +272,12 @@ class LiteParseService:
command.extend(["--tessdata-path", tessdata])
LOGGER.info(
"[LiteParse] Parsing file=%s ocr_enabled=%s ocr_language=%s",
"[LiteParse] Parsing file=%s ocr_enabled=%s ocr_language=%s dpi=%s num_workers=%s",
file_path,
ocr_enabled,
ocr_language,
self.dpi,
self.num_workers,
)
process = subprocess.run(

View file

@ -0,0 +1,291 @@
import asyncio
import json
import logging
import os
from importlib import import_module
from typing import Any, Optional
from uuid import UUID
LOGGER = logging.getLogger(__name__)
class Mem0PresentationMemoryService:
def __init__(self):
self._enabled = self._to_bool(os.getenv("MEM0_ENABLED"), default=True)
self._top_k = self._to_int(os.getenv("MEM0_TOP_K"), default=8)
self._max_context_chars = self._to_int(
os.getenv("MEM0_MAX_CONTEXT_CHARS"), default=6000
)
self._namespace_prefix = (
os.getenv("MEM0_PRESENTATION_NAMESPACE_PREFIX") or "presentation"
).strip() or "presentation"
self._embedder_provider = (
os.getenv("MEM0_EMBEDDER_PROVIDER") or "fastembed"
).strip() or "fastembed"
self._embedder_model = (
os.getenv("MEM0_EMBEDDER_MODEL") or "BAAI/bge-small-en-v1.5"
).strip() or "BAAI/bge-small-en-v1.5"
self._embedding_dims = self._to_int(
os.getenv("MEM0_EMBEDDING_DIMS"),
default=384,
)
app_data_dir = (os.getenv("APP_DATA_DIRECTORY") or "/tmp/presenton").strip()
self._mem0_dir = (os.getenv("MEM0_DIR") or os.path.join(app_data_dir, "mem0")).strip()
self._qdrant_path = (os.getenv("MEM0_QDRANT_PATH") or os.path.join(self._mem0_dir, "qdrant")).strip()
self._history_db_path = (
os.getenv("MEM0_HISTORY_DB_PATH")
or os.path.join(self._mem0_dir, "history.db")
).strip()
self._collection_name = (
os.getenv("MEM0_COLLECTION_NAME") or "presenton_memories"
).strip() or "presenton_memories"
self._client: Any = None
self._attempted_client_init = False
@staticmethod
def _to_bool(value: Optional[str], default: bool = False) -> bool:
if value is None:
return default
return str(value).strip().lower() in {"1", "true", "yes", "on"}
@staticmethod
def _to_int(value: Optional[str], default: int) -> int:
try:
parsed = int(value) if value is not None else default
return max(1, parsed)
except Exception:
return default
def _scope_user_id(self, presentation_id: UUID) -> str:
return f"{self._namespace_prefix}:{presentation_id}"
def _truncate(self, text: str, limit: int = 20000) -> str:
if len(text) <= limit:
return text
return f"{text[:limit]}\n\n[TRUNCATED]"
def _get_oss_config(self) -> dict:
return {
"vector_store": {
"provider": "qdrant",
"config": {
"collection_name": self._collection_name,
"path": self._qdrant_path,
"on_disk": True,
"embedding_model_dims": self._embedding_dims,
},
},
"embedder": {
"provider": self._embedder_provider,
"config": {
"model": self._embedder_model,
"embedding_dims": self._embedding_dims,
},
},
"history_db_path": self._history_db_path,
}
async def _get_client(self):
if not self._enabled:
return None
if self._client is not None:
return self._client
if self._attempted_client_init:
return None
self._attempted_client_init = True
try:
module = import_module("mem0")
memory_cls = getattr(module, "Memory")
os.makedirs(self._mem0_dir, exist_ok=True)
os.makedirs(self._qdrant_path, exist_ok=True)
config = self._get_oss_config()
try:
self._client = memory_cls.from_config(config)
except Exception:
# Backward compatibility across mem0 OSS versions.
self._client = memory_cls(config)
LOGGER.info(
"Mem0 OSS presentation memory service initialized (qdrant_path=%s, history_db_path=%s)",
self._qdrant_path,
self._history_db_path,
)
except Exception:
LOGGER.exception("Failed to initialize Mem0 OSS Memory")
self._client = None
return self._client
async def _add_message(self, presentation_id: UUID, message: str):
client = await self._get_client()
if client is None or not message.strip():
return
scoped_user_id = self._scope_user_id(presentation_id)
payload = [{"role": "user", "content": self._truncate(message)}]
def _add():
try:
return client.add(payload, user_id=scoped_user_id, infer=False)
except TypeError:
return client.add(
messages=payload,
user_id=scoped_user_id,
infer=False,
)
try:
await asyncio.to_thread(_add)
except Exception:
LOGGER.exception(
"Failed to add mem0 memory for presentation_id=%s", presentation_id
)
async def store_generation_context(
self,
presentation_id: UUID,
system_prompt: Optional[str],
user_prompt: Optional[str],
extracted_document_text: Optional[str],
source_content: Optional[str],
instructions: Optional[str],
):
if source_content:
await self._add_message(
presentation_id,
"[presentation_source_prompt]\n" + source_content,
)
if instructions:
await self._add_message(
presentation_id,
"[presentation_generation_instructions]\n" + instructions,
)
if system_prompt:
await self._add_message(
presentation_id,
"[outline_system_prompt]\n" + system_prompt,
)
if user_prompt:
await self._add_message(
presentation_id,
"[outline_user_prompt]\n" + user_prompt,
)
if extracted_document_text:
await self._add_message(
presentation_id,
"[document_extracted_text]\n" + extracted_document_text,
)
async def store_generated_outlines(self, presentation_id: UUID, outlines: Any):
if outlines is None:
return
try:
outlines_text = (
outlines
if isinstance(outlines, str)
else json.dumps(outlines, ensure_ascii=False)
)
except Exception:
outlines_text = str(outlines)
await self._add_message(
presentation_id,
"[generated_outlines]\n" + outlines_text,
)
async def store_slide_edit(
self,
presentation_id: UUID,
slide_index: Optional[int],
edit_prompt: str,
edited_slide_content: Any,
):
try:
edited_text = (
edited_slide_content
if isinstance(edited_slide_content, str)
else json.dumps(edited_slide_content, ensure_ascii=False)
)
except Exception:
edited_text = str(edited_slide_content)
index_text = f"{slide_index}" if slide_index is not None else "unknown"
message = (
f"[slide_edit]\n"
f"slide_index={index_text}\n"
f"user_edit_prompt={edit_prompt}\n"
f"edited_slide_content={edited_text}"
)
await self._add_message(presentation_id, message)
async def retrieve_context(self, presentation_id: UUID, query: str) -> str:
client = await self._get_client()
if client is None:
return ""
scoped_user_id = self._scope_user_id(presentation_id)
def _search():
try:
return client.search(
query,
filters={"user_id": scoped_user_id},
top_k=self._top_k,
)
except TypeError:
return client.search(
query,
user_id=scoped_user_id,
top_k=self._top_k,
)
try:
response = await asyncio.to_thread(_search)
except Exception:
LOGGER.exception(
"Failed to search mem0 context for presentation_id=%s", presentation_id
)
return ""
results = []
if isinstance(response, dict):
results = response.get("results") or []
elif isinstance(response, list):
results = response
memories: list[str] = []
for item in results:
if not isinstance(item, dict):
continue
memory_text = item.get("memory") or item.get("text") or item.get("data")
if not memory_text:
continue
normalized = str(memory_text).strip()
if normalized:
memories.append(normalized)
if not memories:
return ""
deduped_memories = list(dict.fromkeys(memories))
context = "\n\n".join(deduped_memories)
return self._truncate(context, self._max_context_chars)
MEM0_PRESENTATION_MEMORY_SERVICE = Mem0PresentationMemoryService()

View file

@ -0,0 +1,81 @@
from types import SimpleNamespace
from unittest.mock import patch
from services.liteparse_service import LiteParseService
def _ok_process(stdout: str = '{"ok": true, "text": "ok"}'):
return SimpleNamespace(returncode=0, stdout=stdout, stderr="")
class TestLiteParseService:
def test_parse_uses_safe_defaults(self):
with patch.dict(
"os.environ",
{
"LITEPARSE_DPI": "",
"LITEPARSE_NUM_WORKERS": "",
},
clear=False,
), patch.object(
LiteParseService,
"check_runtime_ready",
return_value=(True, "ok"),
), patch(
"services.liteparse_service.subprocess.run",
return_value=_ok_process(),
) as mock_run:
service = LiteParseService(timeout_seconds=30)
service.parse("/tmp/sample.pdf", ocr_enabled=True, ocr_language="eng")
command = mock_run.call_args.args[0]
assert "--dpi" in command
assert command[command.index("--dpi") + 1] == "120"
assert "--num-workers" in command
assert command[command.index("--num-workers") + 1] == "1"
def test_parse_uses_env_overrides(self):
with patch.dict(
"os.environ",
{
"LITEPARSE_DPI": "96",
"LITEPARSE_NUM_WORKERS": "2",
},
clear=False,
), patch.object(
LiteParseService,
"check_runtime_ready",
return_value=(True, "ok"),
), patch(
"services.liteparse_service.subprocess.run",
return_value=_ok_process(),
) as mock_run:
service = LiteParseService(timeout_seconds=30)
service.parse("/tmp/sample.pdf", ocr_enabled=True, ocr_language="eng")
command = mock_run.call_args.args[0]
assert command[command.index("--dpi") + 1] == "96"
assert command[command.index("--num-workers") + 1] == "2"
def test_parse_clamps_invalid_env_values(self):
with patch.dict(
"os.environ",
{
"LITEPARSE_DPI": "-1",
"LITEPARSE_NUM_WORKERS": "0",
},
clear=False,
), patch.object(
LiteParseService,
"check_runtime_ready",
return_value=(True, "ok"),
), patch(
"services.liteparse_service.subprocess.run",
return_value=_ok_process(),
) as mock_run:
service = LiteParseService(timeout_seconds=30)
service.parse("/tmp/sample.pdf", ocr_enabled=True, ocr_language="eng")
command = mock_run.call_args.args[0]
assert command[command.index("--dpi") + 1] == "72"
assert command[command.index("--num-workers") + 1] == "1"

View file

@ -0,0 +1,156 @@
import asyncio
import uuid
from unittest.mock import patch
from services.mem0_presentation_memory_service import Mem0PresentationMemoryService
class FakeMemoryClient:
instances = []
def __init__(self, config=None):
self.config = config
self.add_calls = []
self.search_calls = []
self.next_search_response = {"results": []}
FakeMemoryClient.instances.append(self)
@classmethod
def from_config(cls, config):
return cls(config=config)
def add(self, *args, **kwargs):
messages = kwargs.get("messages") if "messages" in kwargs else None
if messages is None and args:
messages = args[0]
self.add_calls.append(
{
"messages": messages,
"user_id": kwargs.get("user_id"),
"infer": kwargs.get("infer"),
}
)
return {"ok": True}
def search(self, query, *args, **kwargs):
self.search_calls.append(
{
"query": query,
"filters": kwargs.get("filters"),
"user_id": kwargs.get("user_id"),
"top_k": kwargs.get("top_k"),
}
)
return self.next_search_response
class FakeMem0Module:
Memory = FakeMemoryClient
class TestMem0PresentationMemoryService:
def setup_method(self):
FakeMemoryClient.instances = []
def test_store_generation_context_uses_presentation_scope(self):
with patch.dict(
"os.environ",
{
"MEM0_ENABLED": "true",
"APP_DATA_DIRECTORY": "/tmp/presenton-test",
},
clear=False,
), patch(
"services.mem0_presentation_memory_service.import_module",
return_value=FakeMem0Module,
):
service = Mem0PresentationMemoryService()
presentation_id = uuid.uuid4()
asyncio.run(
service.store_generation_context(
presentation_id=presentation_id,
system_prompt="system prompt",
user_prompt="user prompt",
extracted_document_text="doc text",
source_content="seed prompt",
instructions="be concise",
)
)
assert len(FakeMemoryClient.instances) == 1
client = FakeMemoryClient.instances[0]
assert client.config is not None
assert client.config["vector_store"]["provider"] == "qdrant"
assert client.config["embedder"]["provider"] == "fastembed"
assert (
client.config["embedder"]["config"]["model"]
== "BAAI/bge-small-en-v1.5"
)
assert client.config["embedder"]["config"]["embedding_dims"] == 384
assert client.config["vector_store"]["config"]["on_disk"] is True
assert client.config["vector_store"]["config"]["embedding_model_dims"] == 384
assert len(client.add_calls) == 5
scoped_user_id = f"presentation:{presentation_id}"
for call in client.add_calls:
assert call["user_id"] == scoped_user_id
assert call["infer"] is False
serialized_messages = "\n".join(
str(call["messages"][0]["content"]) for call in client.add_calls
)
assert "[outline_system_prompt]" in serialized_messages
assert "[outline_user_prompt]" in serialized_messages
assert "[document_extracted_text]" in serialized_messages
assert "[presentation_source_prompt]" in serialized_messages
def test_retrieve_context_uses_same_scope_and_deduplicates(self):
with patch.dict(
"os.environ",
{
"MEM0_ENABLED": "true",
"MEM0_TOP_K": "5",
"APP_DATA_DIRECTORY": "/tmp/presenton-test",
},
clear=False,
), patch(
"services.mem0_presentation_memory_service.import_module",
return_value=FakeMem0Module,
):
service = Mem0PresentationMemoryService()
presentation_id = uuid.uuid4()
asyncio.run(
service.store_generated_outlines(
presentation_id,
{"slides": [{"content": "One"}]},
)
)
client = FakeMemoryClient.instances[0]
client.next_search_response = {
"results": [
{"memory": "Memory A"},
{"memory": "Memory A"},
{"memory": "Memory B"},
]
}
context = asyncio.run(
service.retrieve_context(
presentation_id=presentation_id,
query="change the conclusion",
)
)
assert "Memory A" in context
assert "Memory B" in context
assert context.count("Memory A") == 1
assert len(client.search_calls) == 1
assert client.search_calls[0]["query"] == "change the conclusion"
assert client.search_calls[0]["filters"] == {
"user_id": f"presentation:{presentation_id}"
}
assert client.search_calls[0]["top_k"] == 5

View file

@ -24,7 +24,17 @@ def get_system_prompt(
tone: Optional[str] = None,
verbosity: Optional[str] = None,
instructions: Optional[str] = None,
memory_context: Optional[str] = None,
):
memory_block = (
"\n # Retrieved Presentation Memory Context\n"
f" {memory_context}\n"
" - Use this context only if it is relevant to the user prompt.\n"
" - Prefer this context over assumptions when resolving ambiguity.\n"
if memory_context
else ""
)
return f"""
Edit Slide data and speaker note based on provided prompt, follow mentioned steps and notes and provide structured output.
@ -45,6 +55,7 @@ def get_system_prompt(
- Make sure to follow language guidelines.
- Speaker note should be normal text, not markdown.
- Speaker note should be simple, clear, concise and to the point.
{memory_block}
**Go through all notes and steps and make sure they are followed, including mentioned constraints**
"""
@ -77,10 +88,11 @@ def get_messages(
tone: Optional[str] = None,
verbosity: Optional[str] = None,
instructions: Optional[str] = None,
memory_context: Optional[str] = None,
):
return [
LLMSystemMessage(
content=get_system_prompt(tone, verbosity, instructions),
content=get_system_prompt(tone, verbosity, instructions, memory_context),
),
LLMUserMessage(
content=get_user_prompt(prompt, slide_data, language),
@ -96,6 +108,7 @@ async def get_edited_slide_content(
tone: Optional[str] = None,
verbosity: Optional[str] = None,
instructions: Optional[str] = None,
memory_context: Optional[str] = None,
):
model = get_model()
@ -120,7 +133,13 @@ async def get_edited_slide_content(
response = await client.generate_structured(
model=model,
messages=get_messages(
prompt, slide.content, language, tone, verbosity, instructions
prompt,
slide.content,
language,
tone,
verbosity,
instructions,
memory_context,
),
response_format=response_schema,
strict=False,

View file

@ -32,11 +32,18 @@ system_prompt = """
"""
def get_user_prompt(prompt: str, html: str):
def get_user_prompt(prompt: str, html: str, memory_context: Optional[str] = None):
memory_block = (
f"\n **Retrieved Presentation Memory Context:**\n {memory_context}\n"
if memory_context
else ""
)
return f"""
Please edit the following slide HTML based on this prompt:
**Edit Request:** {prompt}
{memory_block}
**Current HTML:**
```html
@ -47,7 +54,9 @@ def get_user_prompt(prompt: str, html: str):
"""
async def get_edited_slide_html(prompt: str, html: str):
async def get_edited_slide_html(
prompt: str, html: str, memory_context: Optional[str] = None
):
model = get_model()
client = LLMClient()
@ -56,7 +65,9 @@ async def get_edited_slide_html(prompt: str, html: str):
model=model,
messages=[
LLMSystemMessage(content=system_prompt),
LLMUserMessage(content=get_user_prompt(prompt, html)),
LLMUserMessage(
content=get_user_prompt(prompt, html, memory_context)
),
],
)
return extract_html_from_response(response) or html

View file

@ -12,12 +12,20 @@ def get_messages(
slide_data: dict,
layout: PresentationLayoutModel,
current_slide_layout: int,
memory_context: str = "",
):
memory_block = (
f"\n # Retrieved Presentation Memory Context\n {memory_context}\n"
if memory_context
else ""
)
return [
LLMSystemMessage(
content=f"""
Select a Slide Layout index based on provided user prompt and current slide data.
{layout.to_string()}
{memory_block}
# Notes
- Do not select different slide layout than current unless absolutely necessary as per user prompt.
@ -40,6 +48,7 @@ async def get_slide_layout_from_prompt(
prompt: str,
layout: PresentationLayoutModel,
slide: SlideModel,
memory_context: str = "",
) -> SlideLayoutModel:
client = LLMClient()
@ -55,6 +64,7 @@ async def get_slide_layout_from_prompt(
slide.content,
layout,
slide_layout_index,
memory_context,
),
response_format=SlideLayoutIndex.model_json_schema(),
strict=True,

373
servers/fastapi/uv.lock generated
View file

@ -237,6 +237,24 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/63/13/47bba97924ebe86a62ef83dc75b7c8a881d53c535f83e2c54c4bd701e05c/bcrypt-4.3.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:57967b7a28d855313a963aaea51bf6df89f833db4320da458e5b3c5ab6d4c938", size = 280110, upload-time = "2025-02-28T01:24:05.896Z" },
]
[[package]]
name = "blis"
version = "1.3.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "numpy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d0/d0/d8cc8c9a4488a787e7fa430f6055e5bd1ddb22c340a751d9e901b82e2efe/blis-1.3.3.tar.gz", hash = "sha256:034d4560ff3cc43e8aa37e188451b0440e3261d989bb8a42ceee865607715ecd", size = 2644873, upload-time = "2025-11-17T12:28:30.511Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a1/0a/a4c8736bc497d386b0ffc76d321f478c03f1a4725e52092f93b38beb3786/blis-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e10c8d3e892b1dbdff365b9d00e08291876fc336915bf1a5e9f188ed087e1a91", size = 6925522, upload-time = "2025-11-17T12:27:29.199Z" },
{ url = "https://files.pythonhosted.org/packages/83/5a/3437009282f23684ecd3963a8b034f9307cdd2bf4484972e5a6b096bf9ac/blis-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66e6249564f1db22e8af1e0513ff64134041fa7e03c8dd73df74db3f4d8415a7", size = 1232787, upload-time = "2025-11-17T12:27:30.996Z" },
{ url = "https://files.pythonhosted.org/packages/d1/0e/82221910d16259ce3017c1442c468a3f206a4143a96fbba9f5b5b81d62e8/blis-1.3.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7260da065958b4e5475f62f44895ef9d673b0f47dcf61b672b22b7dae1a18505", size = 2844596, upload-time = "2025-11-17T12:27:32.601Z" },
{ url = "https://files.pythonhosted.org/packages/6c/93/ab547f1a5c23e20bca16fbcf04021c32aac3f969be737ea4980509a7ca90/blis-1.3.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9327a6ca67de8ae76fe071e8584cc7f3b2e8bfadece4961d40f2826e1cda2df", size = 11377746, upload-time = "2025-11-17T12:27:35.342Z" },
{ url = "https://files.pythonhosted.org/packages/6e/a6/7733820aa62da32526287a63cd85c103b2b323b186c8ee43b7772ff7017c/blis-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c4ae70629cf302035d268858a10ca4eb6242a01b2dc8d64422f8e6dcb8a8ee74", size = 3041954, upload-time = "2025-11-17T12:27:37.479Z" },
{ url = "https://files.pythonhosted.org/packages/87/53/e39d67fd3296b649772780ca6aab081412838ecb54e0b0c6432d01626a50/blis-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45866a9027d43b93e8b59980a23c5d7358b6536fc04606286e39fdcfce1101c2", size = 14251222, upload-time = "2025-11-17T12:27:39.705Z" },
{ url = "https://files.pythonhosted.org/packages/ea/44/b749f8777b020b420bceaaf60f66432fc30cc904ca5b69640ec9cbef11ed/blis-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:27f82b8633030f8d095d2b412dffa7eb6dbc8ee43813139909a20012e54422ea", size = 6171233, upload-time = "2025-11-17T12:27:41.921Z" },
]
[[package]]
name = "build"
version = "1.3.0"
@ -260,6 +278,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" },
]
[[package]]
name = "catalogue"
version = "2.0.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/38/b4/244d58127e1cdf04cf2dc7d9566f0d24ef01d5ce21811bab088ecc62b5ea/catalogue-2.0.10.tar.gz", hash = "sha256:4f56daa940913d3f09d589c191c74e5a6d51762b3a9e37dd53b7437afd6cda15", size = 19561, upload-time = "2023-09-25T06:29:24.962Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9e/96/d32b941a501ab566a16358d68b6eb4e4acc373fab3c3c4d7d9e649f7b4bb/catalogue-2.0.10-py3-none-any.whl", hash = "sha256:58c2de0020aa90f4a2da7dfad161bf7b3b054c86a5f09fcedc0b2b740c109a9f", size = 17325, upload-time = "2023-09-25T06:29:23.337Z" },
]
[[package]]
name = "certifi"
version = "2025.8.3"
@ -368,6 +395,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" },
]
[[package]]
name = "cloudpathlib"
version = "0.23.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f4/18/2ac35d6b3015a0c74e923d94fc69baf8307f7c3233de015d69f99e17afa8/cloudpathlib-0.23.0.tar.gz", hash = "sha256:eb38a34c6b8a048ecfd2b2f60917f7cbad4a105b7c979196450c2f541f4d6b4b", size = 53126, upload-time = "2025-10-07T22:47:56.278Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ae/8a/c4bb04426d608be4a3171efa2e233d2c59a5c8937850c10d098e126df18e/cloudpathlib-0.23.0-py3-none-any.whl", hash = "sha256:8520b3b01468fee77de37ab5d50b1b524ea6b4a8731c35d1b7407ac0cd716002", size = 62755, upload-time = "2025-10-07T22:47:54.905Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
@ -389,6 +425,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" },
]
[[package]]
name = "confection"
version = "1.3.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ca/65/efd0fe8a936fc8ca2978cb7b82581fb20d901c6039e746a808f746b7647b/confection-1.3.3.tar.gz", hash = "sha256:f0f6810d567ff73993fe74d218ca5e1ffb6a44fb03f391257fc5d033546cbfaa", size = 54895, upload-time = "2026-03-24T18:45:24.331Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8d/e4/d66708bdf0d92fb4d49b22cdff4b10cec38aca5dcd7e81d909bb55c65cd7/confection-1.3.3-py3-none-any.whl", hash = "sha256:b9fef9ee84b237ef4611ec3eb5797b70e13063e6310ad9f15536373f5e313c82", size = 35902, upload-time = "2026-03-24T18:45:22.664Z" },
]
[[package]]
name = "cryptography"
version = "45.0.5"
@ -445,6 +490,22 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/df/e5/a7b6db64f08cfe065e531ec6b508fa7dac704fab70d05adb5bc0c2c1d1b6/cyclopts-3.22.5-py3-none-any.whl", hash = "sha256:92efb4a094d9812718d7efe0bffa319a19cb661f230dbf24406c18cd8809fb82", size = 84994, upload-time = "2025-07-31T18:18:35.939Z" },
]
[[package]]
name = "cymem"
version = "2.0.13"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/2f0fbb32535c3731b7c2974c569fb9325e0a38ed5565a08e1139a3b71e82/cymem-2.0.13.tar.gz", hash = "sha256:1c91a92ae8c7104275ac26bd4d29b08ccd3e7faff5893d3858cb6fadf1bc1588", size = 12320, upload-time = "2025-11-14T14:58:36.902Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/10/64/1db41f7576a6b69f70367e3c15e968fd775ba7419e12059c9966ceb826f8/cymem-2.0.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:673183466b0ff2e060d97ec5116711d44200b8f7be524323e080d215ee2d44a5", size = 43587, upload-time = "2025-11-14T14:57:22.39Z" },
{ url = "https://files.pythonhosted.org/packages/81/13/57f936fc08551323aab3f92ff6b7f4d4b89d5b4e495c870a67cb8d279757/cymem-2.0.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bee2791b3f6fc034ce41268851462bf662ff87e8947e35fb6dd0115b4644a61f", size = 43139, upload-time = "2025-11-14T14:57:23.363Z" },
{ url = "https://files.pythonhosted.org/packages/32/a6/9345754be51e0479aa387b7b6cffc289d0fd3201aaeb8dade4623abd1e02/cymem-2.0.13-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f3aee3adf16272bca81c5826eed55ba3c938add6d8c9e273f01c6b829ecfde22", size = 245063, upload-time = "2025-11-14T14:57:24.839Z" },
{ url = "https://files.pythonhosted.org/packages/d6/01/6bc654101526fa86e82bf6b05d99b2cd47c30a333cfe8622c26c0592beb2/cymem-2.0.13-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:30c4e75a3a1d809e89106b0b21803eb78e839881aa1f5b9bd27b454bc73afde3", size = 244496, upload-time = "2025-11-14T14:57:26.42Z" },
{ url = "https://files.pythonhosted.org/packages/c4/fb/853b7b021e701a1f41687f3704d5f469aeb2a4f898c3fbb8076806885955/cymem-2.0.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec99efa03cf8ec11c8906aa4d4cc0c47df393bc9095c9dd64b89b9b43e220b04", size = 243287, upload-time = "2025-11-14T14:57:27.542Z" },
{ url = "https://files.pythonhosted.org/packages/d4/2b/0e4664cafc581de2896d75000651fd2ce7094d33263f466185c28ffc96e4/cymem-2.0.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c90a6ecba994a15b17a3f45d7ec74d34081df2f73bd1b090e2adc0317e4e01b6", size = 248287, upload-time = "2025-11-14T14:57:29.055Z" },
{ url = "https://files.pythonhosted.org/packages/21/0f/f94c6950edbfc2aafb81194fc40b6cacc8e994e9359d3cb4328c5705b9b5/cymem-2.0.13-cp311-cp311-win_amd64.whl", hash = "sha256:ce821e6ba59148ed17c4567113b8683a6a0be9c9ac86f14e969919121efb61a5", size = 40116, upload-time = "2025-11-14T14:57:30.592Z" },
{ url = "https://files.pythonhosted.org/packages/00/df/2455eff6ac0381ff165db6883b311f7016e222e3dd62185517f8e8187ed0/cymem-2.0.13-cp311-cp311-win_arm64.whl", hash = "sha256:0dca715e708e545fd1d97693542378a00394b20a37779c1ae2c8bdbb43acef79", size = 36349, upload-time = "2025-11-14T14:57:31.573Z" },
]
[[package]]
name = "dirtyjson"
version = "1.0.8"
@ -783,6 +844,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
]
[[package]]
name = "h2"
version = "4.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "hpack" },
{ name = "hyperframe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026, upload-time = "2025-08-23T18:12:19.778Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779, upload-time = "2025-08-23T18:12:17.779Z" },
]
[[package]]
name = "hf-xet"
version = "1.1.5"
@ -798,6 +872,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f0/55/ef77a85ee443ae05a9e9cba1c9f0dd9241eb42da2aeba1dc50f51154c81a/hf_xet-1.1.5-cp37-abi3-win_amd64.whl", hash = "sha256:73e167d9807d166596b4b2f0b585c6d5bd84a26dea32843665a8b58f6edba245", size = 2738931, upload-time = "2025-06-20T21:48:39.482Z" },
]
[[package]]
name = "hpack"
version = "4.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276, upload-time = "2025-01-22T21:44:58.347Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357, upload-time = "2025-01-22T21:44:56.92Z" },
]
[[package]]
name = "httpcore"
version = "1.0.9"
@ -841,6 +924,11 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
]
[package.optional-dependencies]
http2 = [
{ name = "h2" },
]
[[package]]
name = "httpx-sse"
version = "0.4.1"
@ -881,6 +969,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" },
]
[[package]]
name = "hyperframe"
version = "6.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566, upload-time = "2025-01-22T21:41:49.302Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" },
]
[[package]]
name = "idna"
version = "3.10"
@ -1156,6 +1253,29 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
]
[[package]]
name = "mem0ai"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "openai" },
{ name = "posthog" },
{ name = "protobuf" },
{ name = "pydantic" },
{ name = "pytz" },
{ name = "qdrant-client" },
{ name = "sqlalchemy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a8/41/4b786308d595ff5dc958b9916c0a5ae19af6337c7c09c4f22075400440d5/mem0ai-2.0.0.tar.gz", hash = "sha256:69a029d6e36d6f9cd30a3f2baab68b8e94e984ee91737ed5b6d78bc61df41cd8", size = 210776, upload-time = "2026-04-16T11:51:02.135Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ff/83/433c9e433aa8eede63bc1538bf97a43af3b6eb9cd4cd78ae53dcd484b5e4/mem0ai-2.0.0-py3-none-any.whl", hash = "sha256:77b185d9a490358e806497c42164ab2464109ff44fde10e5fc834996b0011649", size = 298513, upload-time = "2026-04-16T11:51:00.233Z" },
]
[package.optional-dependencies]
nlp = [
{ name = "spacy" },
]
[[package]]
name = "mmh3"
version = "5.2.0"
@ -1225,6 +1345,22 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d8/30/9aec301e9772b098c1f5c0ca0279237c9766d94b97802e9888010c64b0ed/multidict-6.6.3-py3-none-any.whl", hash = "sha256:8db10f29c7541fc5da4defd8cd697e1ca429db743fa716325f236079b96f775a", size = 12313, upload-time = "2025-06-30T15:53:45.437Z" },
]
[[package]]
name = "murmurhash"
version = "1.0.15"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/23/2e/88c147931ea9725d634840d538622e94122bceaf346233349b7b5c62964b/murmurhash-1.0.15.tar.gz", hash = "sha256:58e2b27b7847f9e2a6edf10b47a8c8dd70a4705f45dccb7bf76aeadacf56ba01", size = 13291, upload-time = "2025-11-14T09:51:15.272Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6b/ca/77d3e69924a8eb4508bb4f0ad34e46adbeedeb93616a71080e61e53dad71/murmurhash-1.0.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f32307fb9347680bb4fe1cbef6362fb39bd994f1b59abd8c09ca174e44199081", size = 27397, upload-time = "2025-11-14T09:50:03.077Z" },
{ url = "https://files.pythonhosted.org/packages/e6/53/a936f577d35b245d47b310f29e5e9f09fcac776c8c992f1ab51a9fb0cee2/murmurhash-1.0.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:539d8405885d1d19c005f3a2313b47e8e54b0ee89915eb8dfbb430b194328e6c", size = 27692, upload-time = "2025-11-14T09:50:04.144Z" },
{ url = "https://files.pythonhosted.org/packages/4d/64/5f8cfd1fd9cbeb43fcff96672f5bd9e7e1598d1c970f808ecd915490dc20/murmurhash-1.0.15-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c4cd739a00f5a4602201b74568ddabae46ec304719d9be752fd8f534a9464b5e", size = 128396, upload-time = "2025-11-14T09:50:05.268Z" },
{ url = "https://files.pythonhosted.org/packages/ac/10/d9ce29d559a75db0d8a3f13ea12c7f541ec9de2afca38dc70418b890eedb/murmurhash-1.0.15-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:44d211bcc3ec203c47dac06f48ee871093fcbdffa6652a6cc5ea7180306680a8", size = 128687, upload-time = "2025-11-14T09:50:06.527Z" },
{ url = "https://files.pythonhosted.org/packages/48/cd/dc97ab7e68cdfa1537a56e36dbc846c5a66701cc39ecee2d4399fe61996c/murmurhash-1.0.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f9bf47101354fb1dc4b2e313192566f04ba295c28a37e2f71c692759acc1ba3c", size = 128198, upload-time = "2025-11-14T09:50:08.062Z" },
{ url = "https://files.pythonhosted.org/packages/53/73/32f2aaa22c1e4afae337106baf0c938abf36a6cc879cfee83a00461bbbf7/murmurhash-1.0.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c69b4d3bcd6233782a78907fe10b9b7a796bdc5d28060cf097d067bec280a5d", size = 127214, upload-time = "2025-11-14T09:50:09.265Z" },
{ url = "https://files.pythonhosted.org/packages/82/ed/812103a7f353eba2d83655b08205e13a38c93b4db0692f94756e1eb44516/murmurhash-1.0.15-cp311-cp311-win_amd64.whl", hash = "sha256:e43a69496342ce530bdd670264cb7c8f45490b296e4764c837ce577e3c7ebd53", size = 25241, upload-time = "2025-11-14T09:50:10.373Z" },
{ url = "https://files.pythonhosted.org/packages/eb/5f/2c511bdd28f7c24da37a00116ffd0432b65669d098f0d0260c66ac0ffdc2/murmurhash-1.0.15-cp311-cp311-win_arm64.whl", hash = "sha256:f3e99a6ee36ef5372df5f138e3d9c801420776d3641a34a49e5c2555f44edba7", size = 23216, upload-time = "2025-11-14T09:50:11.651Z" },
]
[[package]]
name = "nltk"
version = "3.9.1"
@ -1586,6 +1722,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
]
[[package]]
name = "portalocker"
version = "3.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pywin32", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5e/77/65b857a69ed876e1951e88aaba60f5ce6120c33703f7cb61a3c894b8c1b6/portalocker-3.2.0.tar.gz", hash = "sha256:1f3002956a54a8c3730586c5c77bf18fae4149e07eaf1c29fc3faf4d5a3f89ac", size = 95644, upload-time = "2025-06-14T13:20:40.03Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4b/a6/38c8e2f318bf67d338f4d629e93b0b4b9af331f455f0390ea8ce4a099b26/portalocker-3.2.0-py3-none-any.whl", hash = "sha256:3cdc5f565312224bc570c49337bd21428bba0ef363bbcf58b9ef4a9f11779968", size = 22424, upload-time = "2025-06-14T13:20:38.083Z" },
]
[[package]]
name = "posthog"
version = "5.4.0"
@ -1619,6 +1767,7 @@ dependencies = [
{ name = "fastembed-vectorstore" },
{ name = "fastmcp" },
{ name = "google-genai" },
{ name = "mem0ai", extra = ["nlp"] },
{ name = "nltk" },
{ name = "openai" },
{ name = "pathvalidate" },
@ -1643,6 +1792,7 @@ requires-dist = [
{ name = "fastembed-vectorstore", specifier = ">=0.5.2" },
{ name = "fastmcp", specifier = ">=2.11.0" },
{ name = "google-genai", specifier = ">=1.28.0" },
{ name = "mem0ai", extras = ["nlp"], specifier = ">=0.1.115" },
{ name = "nltk", specifier = ">=3.9.1" },
{ name = "openai", specifier = ">=1.98.0" },
{ name = "pathvalidate", specifier = ">=3.3.1" },
@ -1653,6 +1803,26 @@ requires-dist = [
{ name = "sqlmodel", specifier = ">=0.0.24" },
]
[[package]]
name = "preshed"
version = "3.0.13"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cymem" },
{ name = "murmurhash" },
]
sdist = { url = "https://files.pythonhosted.org/packages/43/75/fe6b7bbd0dea530a001b0e24c331b21a0be2786e402abf3c57f5dce43d4b/preshed-3.0.13.tar.gz", hash = "sha256:d75f718bbfd97e992f7827e0fa7faf6a91bdd9c922d5baa4b50d62731396cb89", size = 18338, upload-time = "2026-03-23T08:57:31.378Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7c/d1/7bc39738388b38ff48cecbb326a9b2bb3f422bb32097be92e010f3162395/preshed-3.0.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5268c0e6fa96f50cdf87f516c2d4b32563c12706ee768e75c00e8d0098acd545", size = 136718, upload-time = "2026-03-23T08:56:23.889Z" },
{ url = "https://files.pythonhosted.org/packages/f6/65/de465b6801740140c2b5d2db6c312ca7937dcfd0442f1ae7d50dee529544/preshed-3.0.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:df642547a1a94079978a0ea8f4593ab4b8d3bd43f767bef0ef64d9a214f8c4c9", size = 137261, upload-time = "2026-03-23T08:56:25.303Z" },
{ url = "https://files.pythonhosted.org/packages/89/83/478ee078746a4a413c841542caebd2ea74b659475b8bf5f2e3724b6fe655/preshed-3.0.13-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09397592d333a77f88454e72b7f1f941b2afaf040b392b9e74898dbc4648cdf5", size = 821010, upload-time = "2026-03-23T08:56:26.455Z" },
{ url = "https://files.pythonhosted.org/packages/ee/2e/1ac761e973966893cd3a0ad3256360365276e2d1e779e351448981a1156a/preshed-3.0.13-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f8e6fe0620ed0f96a246d46447055c447e071cd8222731a045c235e8a758c918", size = 823096, upload-time = "2026-03-23T08:56:28.126Z" },
{ url = "https://files.pythonhosted.org/packages/3c/51/7824cfd85dd7fe547888de20228ebd87d9acd3708206d30b82211e382d23/preshed-3.0.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:502f93f49a22788203f02d3067d4ea077a0cca3864de6a792eae12e7ce589e14", size = 1812148, upload-time = "2026-03-23T08:56:29.755Z" },
{ url = "https://files.pythonhosted.org/packages/34/48/32160a24705d56179de6af838c10a0c735c955dae5f9e4bb344750b79bc2/preshed-3.0.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:acd4d89abeca3678c5d8c89b3cd351314465bc67c7fa053d2644f8513e543386", size = 1881154, upload-time = "2026-03-23T08:56:31.49Z" },
{ url = "https://files.pythonhosted.org/packages/ed/22/0344b50f8b1ad9e3aac08099c47e1aba91c81602fd117d2673f6606ecae6/preshed-3.0.13-cp311-cp311-win_amd64.whl", hash = "sha256:de87fbabb0f37c3c92d4dd9b94fc82ab73cdab4247cdfbd57ab3926caa983919", size = 122219, upload-time = "2026-03-23T08:56:32.74Z" },
{ url = "https://files.pythonhosted.org/packages/33/c4/812eeaa568510f396e27edab01100ca71418f032fd7098b107f12e572361/preshed-3.0.13-cp311-cp311-win_arm64.whl", hash = "sha256:5e2753779832e411e93eb727f3d409c0a6b7408e5ce4dd868076d8ece48c7693", size = 109308, upload-time = "2026-03-23T08:56:33.839Z" },
]
[[package]]
name = "propcache"
version = "0.3.2"
@ -1975,6 +2145,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d9/4f/00be2196329ebbff56ce564aa94efb0fbc828d00de250b1980de1a34ab49/python_pptx-1.0.2-py3-none-any.whl", hash = "sha256:160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba", size = 472788, upload-time = "2024-08-07T17:33:28.192Z" },
]
[[package]]
name = "pytz"
version = "2026.1.post1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/56/db/b8721d71d945e6a8ac63c0fc900b2067181dbb50805958d4d4661cf7d277/pytz-2026.1.post1.tar.gz", hash = "sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1", size = 321088, upload-time = "2026-03-03T07:47:50.683Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/10/99/781fe0c827be2742bcc775efefccb3b048a3a9c6ce9aec0cbf4a101677e5/pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a", size = 510489, upload-time = "2026-03-03T07:47:49.167Z" },
]
[[package]]
name = "pywin32"
version = "311"
@ -2002,6 +2181,24 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" },
]
[[package]]
name = "qdrant-client"
version = "1.17.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "grpcio" },
{ name = "httpx", extra = ["http2"] },
{ name = "numpy" },
{ name = "portalocker" },
{ name = "protobuf" },
{ name = "pydantic" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/30/dd/f8a8261b83946af3cd65943c93c4f83e044f01184e8525404989d22a81a5/qdrant_client-1.17.1.tar.gz", hash = "sha256:22f990bbd63485ed97ba551a4c498181fcb723f71dcab5d6e4e43fe1050a2bc0", size = 344979, upload-time = "2026-03-13T17:13:44.678Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/68/69/77d1a971c4b933e8c79403e99bcbb790463da5e48333cc4fd5d412c63c98/qdrant_client-1.17.1-py3-none-any.whl", hash = "sha256:6cda4064adfeaf211c751f3fbc00edbbdb499850918c7aff4855a9a759d56cbd", size = 389947, upload-time = "2026-03-13T17:13:43.156Z" },
]
[[package]]
name = "redis"
version = "6.2.0"
@ -2220,6 +2417,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2d/3e/bb34de65a5787f76848a533afbb6610e01fbcdd59e76d8679c254e02255c/sentry_sdk-2.34.1-py2.py3-none-any.whl", hash = "sha256:b7a072e1cdc5abc48101d5146e1ae680fa81fe886d8d95aaa25a0b450c818d32", size = 357743, upload-time = "2025-07-30T11:13:36.145Z" },
]
[[package]]
name = "setuptools"
version = "82.0.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" },
]
[[package]]
name = "shellingham"
version = "1.5.4"
@ -2238,6 +2444,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
]
[[package]]
name = "smart-open"
version = "7.6.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "wrapt" },
]
sdist = { url = "https://files.pythonhosted.org/packages/14/33/7a00ac9b4a63afb4279b99a766f6cbe56c443526dcbf5db97b219e21fde9/smart_open-7.6.0.tar.gz", hash = "sha256:44717f46b5ff276fac03b88e5d13d1c416f064f3b7b081381b0fa8889004bd7e", size = 54548, upload-time = "2026-04-13T09:48:04.347Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/de/bc/2761410d0541e975f384bc89f062d716bf119499dd097eb1af33dcd3b1c0/smart_open-7.6.0-py3-none-any.whl", hash = "sha256:2a78f454610a826aa688065b54b4a0a9b12a5599fa61d5190e9bac2df5e5f53f", size = 64591, upload-time = "2026-04-13T09:48:02.687Z" },
]
[[package]]
name = "sniffio"
version = "1.3.1"
@ -2247,6 +2465,60 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
]
[[package]]
name = "spacy"
version = "3.8.14"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "catalogue" },
{ name = "confection" },
{ name = "cymem" },
{ name = "jinja2" },
{ name = "murmurhash" },
{ name = "numpy" },
{ name = "packaging" },
{ name = "preshed" },
{ name = "pydantic" },
{ name = "requests" },
{ name = "setuptools" },
{ name = "spacy-legacy" },
{ name = "spacy-loggers" },
{ name = "srsly" },
{ name = "thinc" },
{ name = "tqdm" },
{ name = "typer" },
{ name = "wasabi" },
{ name = "weasel" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/a9/8c/0ccf32d9a6b4fd8737bba33d599ddb98934399c1d523f825a4beb4bd1495/spacy-3.8.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c55cb123c3edfba8c252ce6ae27ffb3d7f60a53ba5e108c3534421586c5fdda", size = 6617470, upload-time = "2026-03-29T10:40:25.572Z" },
{ url = "https://files.pythonhosted.org/packages/e1/61/7f7d38e71daac7f91ffd362fb15645b6f9a68ad231e0ed6ff5c1dc6f6930/spacy-3.8.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab6c1ace316338dac334fc93c849994bbd717f9ebf59d2bc4158e978b2f542ee", size = 6441524, upload-time = "2026-03-29T10:40:27.648Z" },
{ url = "https://files.pythonhosted.org/packages/5c/ef/18385aa5aeb9bcb299e8074da162b24e5c8bea5aa4d1dfa3dbafb35e9d1f/spacy-3.8.14-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7bece0450cd8ab841cfa8527fcc0ce18c4454f28e3b9fca42a450803a067355b", size = 32050591, upload-time = "2026-03-29T10:40:29.704Z" },
{ url = "https://files.pythonhosted.org/packages/fa/67/5c4a65ed2cedc598ad000a2b9f45afc76bb8d17a592cc01082dffa8bbc50/spacy-3.8.14-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc5e5f2ed121d57d819d247bb59253dc320a58acbd237b85f86c2aa38cab6bd1", size = 32296467, upload-time = "2026-03-29T10:40:32.557Z" },
{ url = "https://files.pythonhosted.org/packages/03/a7/28c118879791b3a7ffa81796d22203daac428e6f75572f1b8da1539e1ac6/spacy-3.8.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c4e5bc5cdefe39ea139985776a2e8eae05e7ff2bf51ca1bd65247dc45feeb8e", size = 32288404, upload-time = "2026-03-29T10:40:35.583Z" },
{ url = "https://files.pythonhosted.org/packages/72/1c/32aefcea2468782fcdb994f2f96cac93dc74f6589ce01047db42d9a299a2/spacy-3.8.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c228f4c9ae618173334c17adb748d66b574b6594bc3575233e15cd5ad1cb26b", size = 33113476, upload-time = "2026-03-29T10:40:38.577Z" },
{ url = "https://files.pythonhosted.org/packages/86/32/fc00532eabeace451175dd9b152ddd636e8f6a42248b5d90141f98be2af5/spacy-3.8.14-cp311-cp311-win_amd64.whl", hash = "sha256:6f51d1ce8b1ba30123f6bef6e795c4bc5466608e6e8a015dc828bd21d399aa9c", size = 15359704, upload-time = "2026-03-29T10:40:41.25Z" },
{ url = "https://files.pythonhosted.org/packages/de/31/89ff6722ec91f328dc717932849c6f57249c8a9d429d8670a6c8f70e576b/spacy-3.8.14-cp311-cp311-win_arm64.whl", hash = "sha256:c0c6c9d8771cc3708e309b07310d330fc8443a6bca34f4ff20b0f22751d8faf9", size = 14717168, upload-time = "2026-03-29T10:40:43.916Z" },
]
[[package]]
name = "spacy-legacy"
version = "3.0.12"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d9/79/91f9d7cc8db5642acad830dcc4b49ba65a7790152832c4eceb305e46d681/spacy-legacy-3.0.12.tar.gz", hash = "sha256:b37d6e0c9b6e1d7ca1cf5bc7152ab64a4c4671f59c85adaf7a3fcb870357a774", size = 23806, upload-time = "2023-01-23T09:04:15.104Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c3/55/12e842c70ff8828e34e543a2c7176dac4da006ca6901c9e8b43efab8bc6b/spacy_legacy-3.0.12-py2.py3-none-any.whl", hash = "sha256:476e3bd0d05f8c339ed60f40986c07387c0a71479245d6d0f4298dbd52cda55f", size = 29971, upload-time = "2023-01-23T09:04:13.45Z" },
]
[[package]]
name = "spacy-loggers"
version = "1.0.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/67/3d/926db774c9c98acf66cb4ed7faf6c377746f3e00b84b700d0868b95d0712/spacy-loggers-1.0.5.tar.gz", hash = "sha256:d60b0bdbf915a60e516cc2e653baeff946f0cfc461b452d11a4d5458c6fe5f24", size = 20811, upload-time = "2023-09-11T12:26:52.323Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/33/78/d1a1a026ef3af911159398c939b1509d5c36fe524c7b644f34a5146c4e16/spacy_loggers-1.0.5-py3-none-any.whl", hash = "sha256:196284c9c446cc0cdb944005384270d775fdeaf4f494d8e269466cfa497ef645", size = 22343, upload-time = "2023-09-11T12:26:50.586Z" },
]
[[package]]
name = "sqlalchemy"
version = "2.0.42"
@ -2281,6 +2553,25 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/16/91/484cd2d05569892b7fef7f5ceab3bc89fb0f8a8c0cde1030d383dbc5449c/sqlmodel-0.0.24-py3-none-any.whl", hash = "sha256:6778852f09370908985b667d6a3ab92910d0d5ec88adcaf23dbc242715ff7193", size = 28622, upload-time = "2025-03-07T05:43:30.37Z" },
]
[[package]]
name = "srsly"
version = "2.5.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "catalogue" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2b/db/f794f219a6c788b881252d2536a8c4a97d2bdaadc690391e1cb53d123d71/srsly-2.5.3.tar.gz", hash = "sha256:08f98dbecbff3a31466c4ae7c833131f59d3655a0ad8ac749e6e2c149e2b0680", size = 490881, upload-time = "2026-03-23T11:56:59.865Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9a/36/5d7bb412d52e9cca787f9bfe838b596367189b254e50bf90f234a97184bf/srsly-2.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:785a09216ac31570fb301ddb9f61ee73d1f18f8b9561f712dce0b8ac8628bc88", size = 656760, upload-time = "2026-03-23T11:55:47.155Z" },
{ url = "https://files.pythonhosted.org/packages/d6/dc/124f008cd2be3e887e972cbdeb17c5aee0f42093eca02c7cfd63bb5daf19/srsly-2.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0017c7d2a0cd9a4f1bdc00d946b45edcf90bb0e271e8f084c1ce542bf6708c32", size = 657503, upload-time = "2026-03-23T11:55:48.681Z" },
{ url = "https://files.pythonhosted.org/packages/35/8a/2c97244ebab125d55f1bfb7bb94e9572b3e819410dffd6a040eca1112350/srsly-2.5.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:66ebae2c70305987341519ec1a720072a3cb3e4b1d52ac0e9e841f4d02658d3d", size = 1139161, upload-time = "2026-03-23T11:55:50.179Z" },
{ url = "https://files.pythonhosted.org/packages/fc/ea/ecd396188f7591d80b89665f7af9e3ae02e42683daef57033ad7993ad3f9/srsly-2.5.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4ca4a068f6e14d84113a02fcb875c6b50a6285a12938c0e7a157eb3a63c50a86", size = 1142438, upload-time = "2026-03-23T11:55:52.607Z" },
{ url = "https://files.pythonhosted.org/packages/9c/65/143e2e143c53d498ad0956f69d0e09189aa7a6e0ee6017758c285ba1ab2d/srsly-2.5.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e283fa2a8f7350fb9fb70ecdee28d59d39c92f4c7f1cc90a44d6b86db3b3a8b3", size = 1101783, upload-time = "2026-03-23T11:55:53.906Z" },
{ url = "https://files.pythonhosted.org/packages/6b/86/1392a5593de0cd3d08c2d6c071b877c84358a37f63172c4e9cb71706842d/srsly-2.5.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9ffc97e22730ea97b00f7c303ccc60b1305e786afadb2a4a46578dafa4d29da0", size = 1115876, upload-time = "2026-03-23T11:55:55.624Z" },
{ url = "https://files.pythonhosted.org/packages/d4/a5/6193aa4c08e488821538fcbce2282449e228fd2183ed67d118bb5ccd8b54/srsly-2.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:f09b551f6c3e334652831ac68c770ee4284741ce0a3895bf1ccf2a1178d66cdd", size = 651733, upload-time = "2026-03-23T11:55:56.964Z" },
{ url = "https://files.pythonhosted.org/packages/66/a8/a73181743b6d237026615ca75c3fb3e4780736f1390550a7350d0c7f1149/srsly-2.5.3-cp311-cp311-win_arm64.whl", hash = "sha256:21cf09e417d3e4f3fbf7dd337fd6d948c97abd01896b9b4cb80e81cd9778a73a", size = 639124, upload-time = "2026-03-23T11:55:58.532Z" },
]
[[package]]
name = "sse-starlette"
version = "3.0.2"
@ -2327,6 +2618,36 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/3f/8ba87d9e287b9d385a02a7114ddcef61b26f86411e121c9003eb509a1773/tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687", size = 28165, upload-time = "2024-07-05T07:25:29.591Z" },
]
[[package]]
name = "thinc"
version = "8.3.13"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "blis" },
{ name = "catalogue" },
{ name = "confection" },
{ name = "cymem" },
{ name = "murmurhash" },
{ name = "numpy" },
{ name = "packaging" },
{ name = "preshed" },
{ name = "pydantic" },
{ name = "setuptools" },
{ name = "srsly" },
{ name = "wasabi" },
]
sdist = { url = "https://files.pythonhosted.org/packages/13/46/76df95f2c327f9a9cef30c1523bf285627897097163584dcf5f77b2ebce2/thinc-8.3.13.tar.gz", hash = "sha256:68e658549fc1eb3ff92aed5147fcbb9c15d6e9cc0e623b4d0998d16522ffb4f9", size = 194640, upload-time = "2026-03-23T07:22:36.41Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b4/72/ca06842a007e8c794e8c59462f242cdfd6167d7cc9d0155ad004b194b015/thinc-8.3.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4565102638038a01a2193c7f5d41ccbd6233fbdcb1f1b184322a06add4f51f18", size = 844359, upload-time = "2026-03-23T07:21:43.017Z" },
{ url = "https://files.pythonhosted.org/packages/48/44/e6aef092f478d263f72eb3933b55a6f37ba97c6a0ea0a61d13fbf9bf0c19/thinc-8.3.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:859fbd9d9b16af5278da23589b4afbe2ab6b0dd615df4d3229b7c4e67cd3107e", size = 812089, upload-time = "2026-03-23T07:21:44.618Z" },
{ url = "https://files.pythonhosted.org/packages/ff/8a/9ce0424d456cd3580cc3a855b23a7ff86b81d5299fceb496a2f56f06c1c0/thinc-8.3.13-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a518d5c761a0f2341e530e867de133dc3ed814558365b2a68ec53b89c482a43f", size = 4101388, upload-time = "2026-03-23T07:21:46.135Z" },
{ url = "https://files.pythonhosted.org/packages/ad/51/ec91c0434bd9a1096ab874bbd6dc110c5089d7fc513137e6af59bd051eec/thinc-8.3.13-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:81337dfbee37f58f36c0c70f9a819dce1b32cdc13d959181e10de079621f6ac6", size = 4131972, upload-time = "2026-03-23T07:21:48.403Z" },
{ url = "https://files.pythonhosted.org/packages/ff/67/e30dea753c90cff5cb9e5feb34948fdb89a6774b84d849585b49e16a730e/thinc-8.3.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fbc0ee16edd260c6a4a9e365ff36d0a682c9e7ca6d7b985682659ef2e3e73826", size = 5101283, upload-time = "2026-03-23T07:21:49.991Z" },
{ url = "https://files.pythonhosted.org/packages/00/e9/b7544eddababa16e548b26a96fff29eeb307ce938df5fa4af9371fe8ed5d/thinc-8.3.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0355c37e40d1a9fc2a1b8e9c2e294d8586f6baa97bcac6b9002f2dddb4b82ae9", size = 5264488, upload-time = "2026-03-23T07:21:51.747Z" },
{ url = "https://files.pythonhosted.org/packages/4c/a9/49391a40d703efc0f7a451310373261835f71fd3e6e2e8cfc08ee02f78ad/thinc-8.3.13-cp311-cp311-win_amd64.whl", hash = "sha256:0a0fa13dcfe4b319c3a396432c1dbff30d3de37dbbdee559e76600ee2b9486df", size = 1795058, upload-time = "2026-03-23T07:21:53.424Z" },
{ url = "https://files.pythonhosted.org/packages/c1/31/fd5348d44beda12a3ee415cbba9ed4fd0b17ce65db1d473c38a29a8d6153/thinc-8.3.13-cp311-cp311-win_arm64.whl", hash = "sha256:cd8a2b714c061969eee65802965167a6ada1fe708d82fe176d98dcb95ebe182a", size = 1721215, upload-time = "2026-03-23T07:21:55.027Z" },
]
[[package]]
name = "tokenizers"
version = "0.21.4"
@ -2447,6 +2768,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/da/e2/5cf6ef37e3daf2f06e651aae5ea108ad30df3cb269102678b61ebf1fdf42/uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d", size = 3937323, upload-time = "2024-10-14T23:37:45.337Z" },
]
[[package]]
name = "wasabi"
version = "1.1.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ac/f9/054e6e2f1071e963b5e746b48d1e3727470b2a490834d18ad92364929db3/wasabi-1.1.3.tar.gz", hash = "sha256:4bb3008f003809db0c3e28b4daf20906ea871a2bb43f9914197d540f4f2e0878", size = 30391, upload-time = "2024-05-31T16:56:18.99Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/06/7c/34330a89da55610daa5f245ddce5aab81244321101614751e7537f125133/wasabi-1.1.3-py3-none-any.whl", hash = "sha256:f76e16e8f7e79f8c4c8be49b4024ac725713ab10cd7f19350ad18a8e3f71728c", size = 27880, upload-time = "2024-05-31T16:56:16.699Z" },
]
[[package]]
name = "watchfiles"
version = "1.1.0"
@ -2475,6 +2808,26 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/bd/d3/254cea30f918f489db09d6a8435a7de7047f8cb68584477a515f160541d6/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792", size = 454009, upload-time = "2025-06-15T19:06:52.896Z" },
]
[[package]]
name = "weasel"
version = "1.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cloudpathlib" },
{ name = "confection" },
{ name = "httpx" },
{ name = "packaging" },
{ name = "pydantic" },
{ name = "smart-open" },
{ name = "srsly" },
{ name = "typer" },
{ name = "wasabi" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ce/e5/e272bb9a045105a1fdf4b798d8086f5932a178f4d738f17a74f5c9e0ae9a/weasel-1.0.0.tar.gz", hash = "sha256:7b129b44c90cc543b760532974ca1e4eb30dad2aa2026f57bdce66354ae610fc", size = 38682, upload-time = "2026-03-20T08:10:25.266Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0a/07/57ebf7a6798b016c064bd0ca81b4c6a99daa4dc377b898bc7b41eb6b5af0/weasel-1.0.0-py3-none-any.whl", hash = "sha256:89518acee027f49d743126c3502d35e6dd14f5768be5c37c9af47c171b6005cc", size = 50713, upload-time = "2026-03-20T08:10:23.637Z" },
]
[[package]]
name = "websocket-client"
version = "1.8.0"
@ -2525,6 +2878,26 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" },
]
[[package]]
name = "wrapt"
version = "2.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/2e/64/925f213fdcbb9baeb1530449ac71a4d57fc361c053d06bf78d0c5c7cd80c/wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e", size = 81678, upload-time = "2026-03-06T02:53:25.134Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/81/60c4471fce95afa5922ca09b88a25f03c93343f759aae0f31fb4412a85c7/wrapt-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb", size = 60666, upload-time = "2026-03-06T02:52:58.934Z" },
{ url = "https://files.pythonhosted.org/packages/6b/be/80e80e39e7cb90b006a0eaf11c73ac3a62bbfb3068469aec15cc0bc795de/wrapt-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d", size = 61601, upload-time = "2026-03-06T02:53:00.487Z" },
{ url = "https://files.pythonhosted.org/packages/b0/be/d7c88cd9293c859fc74b232abdc65a229bb953997995d6912fc85af18323/wrapt-2.1.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894", size = 114057, upload-time = "2026-03-06T02:52:44.08Z" },
{ url = "https://files.pythonhosted.org/packages/ea/25/36c04602831a4d685d45a93b3abea61eca7fe35dab6c842d6f5d570ef94a/wrapt-2.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842", size = 116099, upload-time = "2026-03-06T02:54:56.74Z" },
{ url = "https://files.pythonhosted.org/packages/5c/4e/98a6eb417ef551dc277bec1253d5246b25003cf36fdf3913b65cb7657a56/wrapt-2.1.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8", size = 112457, upload-time = "2026-03-06T02:53:52.842Z" },
{ url = "https://files.pythonhosted.org/packages/cb/a6/a6f7186a5297cad8ec53fd7578533b28f795fdf5372368c74bd7e6e9841c/wrapt-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6", size = 115351, upload-time = "2026-03-06T02:53:32.684Z" },
{ url = "https://files.pythonhosted.org/packages/97/6f/06e66189e721dbebd5cf20e138acc4d1150288ce118462f2fcbff92d38db/wrapt-2.1.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9", size = 111748, upload-time = "2026-03-06T02:53:08.455Z" },
{ url = "https://files.pythonhosted.org/packages/ef/43/4808b86f499a51370fbdbdfa6cb91e9b9169e762716456471b619fca7a70/wrapt-2.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15", size = 113783, upload-time = "2026-03-06T02:53:02.02Z" },
{ url = "https://files.pythonhosted.org/packages/91/2c/a3f28b8fa7ac2cefa01cfcaca3471f9b0460608d012b693998cd61ef43df/wrapt-2.1.2-cp311-cp311-win32.whl", hash = "sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b", size = 57977, upload-time = "2026-03-06T02:53:27.844Z" },
{ url = "https://files.pythonhosted.org/packages/3f/c3/2b1c7bd07a27b1db885a2fab469b707bdd35bddf30a113b4917a7e2139d2/wrapt-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1", size = 60336, upload-time = "2026-03-06T02:54:28.104Z" },
{ url = "https://files.pythonhosted.org/packages/ec/5c/76ece7b401b088daa6503d6264dd80f9a727df3e6042802de9a223084ea2/wrapt-2.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a", size = 58756, upload-time = "2026-03-06T02:53:16.319Z" },
{ url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" },
]
[[package]]
name = "xlsxwriter"
version = "3.2.5"

View file

@ -52,10 +52,9 @@ const CHATGPT_MODELS: CodexModel[] = [
{ id: "gpt-5.4-mini", name: "GPT-5.4-Mini" },
{ id: "gpt-5.3-codex", name: "GPT-5.3-Codex" },
{ id: "gpt-5.2", name: "GPT-5.2" },
{ id: "gpt-5.1-codex-mini", name: "GPT-5.1-Codex-Mini" },
];
const DEFAULT_CODEX_MODEL = "gpt-5.1-codex-mini";
const DEFAULT_CODEX_MODEL = "gpt-5.2";
export default function CodexConfig({
codexModel,

View file

@ -39,10 +39,9 @@ export const CHATGPT_MODELS: CodexModel[] = [
{ id: "gpt-5.4-mini", name: "GPT-5.4-Mini" },
{ id: "gpt-5.3-codex", name: "GPT-5.3-Codex" },
{ id: "gpt-5.2", name: "GPT-5.2" },
{ id: "gpt-5.1-codex-mini", name: "GPT-5.1-Codex-Mini" },
];
export const DEFAULT_CODEX_MODEL = "gpt-5.1-codex-mini";
export const DEFAULT_CODEX_MODEL = "gpt-5.2";
export default function CodexConfig({
codexModel,