From 9605d1f79c93d1cd803e904499cc2aa51c56ee1a Mon Sep 17 00:00:00 2001 From: Matthew Zhou Date: Thu, 3 Jul 2025 22:37:55 -0700 Subject: [PATCH 1/6] feat: Add pinecone for cloud embedding (#3160) --- ...dd_total_chunks_and_chunks_embedded_to_.py | 33 + letta/constants.py | 7 + letta/functions/function_sets/files.py | 3 +- letta/functions/functions.py | 1 - letta/helpers/pinecone_utils.py | 143 +++++ letta/orm/file.py | 4 + letta/schemas/file.py | 6 + letta/server/rest_api/app.py | 11 + letta/server/rest_api/routers/v1/sources.py | 43 +- letta/services/file_manager.py | 10 +- .../file_processor/embedder/base_embedder.py | 16 + .../embedder/openai_embedder.py | 5 +- .../embedder/pinecone_embedder.py | 74 +++ .../services/file_processor/file_processor.py | 44 +- letta/services/job_manager.py | 4 - letta/services/source_manager.py | 1 - .../tool_executor/files_tool_executor.py | 112 +++- letta/settings.py | 7 + poetry.lock | 594 ++++-------------- pyproject.toml | 2 + tests/conftest.py | 18 + tests/test_managers.py | 39 +- tests/test_sources.py | 159 ++++- 23 files changed, 810 insertions(+), 526 deletions(-) create mode 100644 alembic/versions/47d2277e530d_add_total_chunks_and_chunks_embedded_to_.py create mode 100644 letta/helpers/pinecone_utils.py create mode 100644 letta/services/file_processor/embedder/base_embedder.py create mode 100644 letta/services/file_processor/embedder/pinecone_embedder.py diff --git a/alembic/versions/47d2277e530d_add_total_chunks_and_chunks_embedded_to_.py b/alembic/versions/47d2277e530d_add_total_chunks_and_chunks_embedded_to_.py new file mode 100644 index 00000000..194d2acf --- /dev/null +++ b/alembic/versions/47d2277e530d_add_total_chunks_and_chunks_embedded_to_.py @@ -0,0 +1,33 @@ +"""Add total_chunks and chunks_embedded to files + +Revision ID: 47d2277e530d +Revises: 56254216524f +Create Date: 2025-07-03 14:32:08.539280 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "47d2277e530d" +down_revision: Union[str, None] = "56254216524f" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column("files", sa.Column("total_chunks", sa.Integer(), nullable=True)) + op.add_column("files", sa.Column("chunks_embedded", sa.Integer(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("files", "chunks_embedded") + op.drop_column("files", "total_chunks") + # ### end Alembic commands ### diff --git a/letta/constants.py b/letta/constants.py index c83eefe4..bd8d2c49 100644 --- a/letta/constants.py +++ b/letta/constants.py @@ -364,3 +364,10 @@ REDIS_RUN_ID_PREFIX = "agent:send_message:run_id" MAX_FILES_OPEN = 5 GET_PROVIDERS_TIMEOUT_SECONDS = 10 + +# Pinecone related fields +PINECONE_EMBEDDING_MODEL: str = "llama-text-embed-v2" +PINECONE_TEXT_FIELD_NAME = "chunk_text" +PINECONE_METRIC = "cosine" +PINECONE_CLOUD = "aws" +PINECONE_REGION = "us-east-1" diff --git a/letta/functions/function_sets/files.py b/letta/functions/function_sets/files.py index d2e523aa..43b34bd9 100644 --- a/letta/functions/function_sets/files.py +++ b/letta/functions/function_sets/files.py @@ -65,7 +65,7 @@ async def grep_files( raise NotImplementedError("Tool not implemented. Please contact the Letta team.") -async def semantic_search_files(agent_state: "AgentState", query: str) -> List["FileMetadata"]: +async def semantic_search_files(agent_state: "AgentState", query: str, limit: int = 5) -> List["FileMetadata"]: """ Get list of most relevant chunks from any file using vector/embedding search. @@ -76,6 +76,7 @@ async def semantic_search_files(agent_state: "AgentState", query: str) -> List[" Args: query (str): The search query. + limit: Maximum number of results to return (default: 5) Returns: List[FileMetadata]: List of matching files. diff --git a/letta/functions/functions.py b/letta/functions/functions.py index 39a25381..8681ac38 100644 --- a/letta/functions/functions.py +++ b/letta/functions/functions.py @@ -29,7 +29,6 @@ def derive_openai_json_schema(source_code: str, name: Optional[str] = None) -> d # "Field": Field, } env.update(globals()) - # print("About to execute source code...") exec(source_code, env) # print("Source code executed successfully") diff --git a/letta/helpers/pinecone_utils.py b/letta/helpers/pinecone_utils.py new file mode 100644 index 00000000..d7cd1d4a --- /dev/null +++ b/letta/helpers/pinecone_utils.py @@ -0,0 +1,143 @@ +from typing import Any, Dict, List + +from pinecone import PineconeAsyncio + +from letta.constants import PINECONE_CLOUD, PINECONE_EMBEDDING_MODEL, PINECONE_METRIC, PINECONE_REGION, PINECONE_TEXT_FIELD_NAME +from letta.log import get_logger +from letta.schemas.user import User +from letta.settings import settings + +logger = get_logger(__name__) + + +def should_use_pinecone(verbose: bool = False): + if verbose: + logger.info( + "Pinecone check: enable_pinecone=%s, api_key=%s, agent_index=%s, source_index=%s", + settings.enable_pinecone, + bool(settings.pinecone_api_key), + bool(settings.pinecone_agent_index), + bool(settings.pinecone_source_index), + ) + + return settings.enable_pinecone and settings.pinecone_api_key and settings.pinecone_agent_index and settings.pinecone_source_index + + +async def upsert_pinecone_indices(): + from pinecone import IndexEmbed, PineconeAsyncio + + for index_name in get_pinecone_indices(): + async with PineconeAsyncio(api_key=settings.pinecone_api_key) as pc: + if not await pc.has_index(index_name): + await pc.create_index_for_model( + name=index_name, + cloud=PINECONE_CLOUD, + region=PINECONE_REGION, + embed=IndexEmbed(model=PINECONE_EMBEDDING_MODEL, field_map={"text": PINECONE_TEXT_FIELD_NAME}, metric=PINECONE_METRIC), + ) + + +def get_pinecone_indices() -> List[str]: + return [settings.pinecone_agent_index, settings.pinecone_source_index] + + +async def upsert_file_records_to_pinecone_index(file_id: str, source_id: str, chunks: List[str], actor: User): + records = [] + for i, chunk in enumerate(chunks): + record = { + "_id": f"{file_id}_{i}", + PINECONE_TEXT_FIELD_NAME: chunk, + "file_id": file_id, + "source_id": source_id, + } + records.append(record) + + return await upsert_records_to_pinecone_index(records, actor) + + +async def delete_file_records_from_pinecone_index(file_id: str, actor: User): + from pinecone.exceptions.exceptions import NotFoundException + + namespace = actor.organization_id + try: + async with PineconeAsyncio(api_key=settings.pinecone_api_key) as pc: + description = await pc.describe_index(name=settings.pinecone_source_index) + async with pc.IndexAsyncio(host=description.index.host) as dense_index: + await dense_index.delete( + filter={ + "file_id": {"$eq": file_id}, + }, + namespace=namespace, + ) + except NotFoundException: + logger.warning(f"Pinecone namespace {namespace} not found for {file_id} and {actor.organization_id}") + + +async def delete_source_records_from_pinecone_index(source_id: str, actor: User): + from pinecone.exceptions.exceptions import NotFoundException + + namespace = actor.organization_id + try: + async with PineconeAsyncio(api_key=settings.pinecone_api_key) as pc: + description = await pc.describe_index(name=settings.pinecone_source_index) + async with pc.IndexAsyncio(host=description.index.host) as dense_index: + await dense_index.delete(filter={"source_id": {"$eq": source_id}}, namespace=namespace) + except NotFoundException: + logger.warning(f"Pinecone namespace {namespace} not found for {source_id} and {actor.organization_id}") + + +async def upsert_records_to_pinecone_index(records: List[dict], actor: User): + async with PineconeAsyncio(api_key=settings.pinecone_api_key) as pc: + description = await pc.describe_index(name=settings.pinecone_source_index) + async with pc.IndexAsyncio(host=description.index.host) as dense_index: + await dense_index.upsert_records(actor.organization_id, records) + + +async def search_pinecone_index(query: str, limit: int, filter: Dict[str, Any], actor: User) -> Dict[str, Any]: + async with PineconeAsyncio(api_key=settings.pinecone_api_key) as pc: + description = await pc.describe_index(name=settings.pinecone_source_index) + async with pc.IndexAsyncio(host=description.index.host) as dense_index: + namespace = actor.organization_id + + try: + # Search the dense index with reranking + search_results = await dense_index.search( + namespace=namespace, + query={ + "top_k": limit, + "inputs": {"text": query}, + "filter": filter, + }, + rerank={"model": "bge-reranker-v2-m3", "top_n": limit, "rank_fields": [PINECONE_TEXT_FIELD_NAME]}, + ) + return search_results + except Exception as e: + logger.warning(f"Failed to search Pinecone namespace {actor.organization_id}: {str(e)}") + raise e + + +async def list_pinecone_index_for_files(file_id: str, actor: User, limit: int = None, pagination_token: str = None) -> List[str]: + from pinecone.exceptions.exceptions import NotFoundException + + namespace = actor.organization_id + try: + async with PineconeAsyncio(api_key=settings.pinecone_api_key) as pc: + description = await pc.describe_index(name=settings.pinecone_source_index) + async with pc.IndexAsyncio(host=description.index.host) as dense_index: + + kwargs = {"namespace": namespace, "prefix": file_id} + if limit is not None: + kwargs["limit"] = limit + if pagination_token is not None: + kwargs["pagination_token"] = pagination_token + + try: + result = [] + async for ids in dense_index.list(**kwargs): + result.extend(ids) + return result + except Exception as e: + logger.warning(f"Failed to list Pinecone namespace {actor.organization_id}: {str(e)}") + raise e + except NotFoundException: + logger.warning(f"Pinecone namespace {namespace} not found for {file_id} and {actor.organization_id}") diff --git a/letta/orm/file.py b/letta/orm/file.py index 35184f67..885731e5 100644 --- a/letta/orm/file.py +++ b/letta/orm/file.py @@ -60,6 +60,8 @@ class FileMetadata(SqlalchemyBase, OrganizationMixin, SourceMixin, AsyncAttrs): ) error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True, doc="Any error message encountered during processing.") + total_chunks: Mapped[Optional[int]] = mapped_column(Integer, nullable=True, doc="Total number of chunks for the file.") + chunks_embedded: Mapped[Optional[int]] = mapped_column(Integer, nullable=True, doc="Number of chunks that have been embedded.") # relationships organization: Mapped["Organization"] = relationship("Organization", back_populates="files", lazy="selectin") @@ -112,6 +114,8 @@ class FileMetadata(SqlalchemyBase, OrganizationMixin, SourceMixin, AsyncAttrs): file_last_modified_date=self.file_last_modified_date, processing_status=self.processing_status, error_message=self.error_message, + total_chunks=self.total_chunks, + chunks_embedded=self.chunks_embedded, created_at=self.created_at, updated_at=self.updated_at, is_deleted=self.is_deleted, diff --git a/letta/schemas/file.py b/letta/schemas/file.py index 64b6eed5..14e2a122 100644 --- a/letta/schemas/file.py +++ b/letta/schemas/file.py @@ -41,6 +41,8 @@ class FileMetadata(FileMetadataBase): description="The current processing status of the file (e.g. pending, parsing, embedding, completed, error).", ) error_message: Optional[str] = Field(default=None, description="Optional error message if the file failed processing.") + total_chunks: Optional[int] = Field(default=None, description="Total number of chunks for the file.") + chunks_embedded: Optional[int] = Field(default=None, description="Number of chunks that have been embedded.") # orm metadata, optional fields created_at: Optional[datetime] = Field(default_factory=datetime.utcnow, description="The creation date of the file.") @@ -52,6 +54,10 @@ class FileMetadata(FileMetadataBase): default=None, description="Optional full-text content of the file; only populated on demand due to its size." ) + def is_processing_terminal(self) -> bool: + """Check if the file processing status is in a terminal state (completed or error).""" + return self.processing_status in (FileProcessingStatus.COMPLETED, FileProcessingStatus.ERROR) + class FileAgentBase(LettaBase): """Base class for the FileMetadata-⇄-Agent association schemas""" diff --git a/letta/server/rest_api/app.py b/letta/server/rest_api/app.py index a3075a8e..8ddb175f 100644 --- a/letta/server/rest_api/app.py +++ b/letta/server/rest_api/app.py @@ -17,6 +17,7 @@ from letta.__init__ import __version__ as letta_version from letta.agents.exceptions import IncompatibleAgentType from letta.constants import ADMIN_PREFIX, API_PREFIX, OPENAI_API_PREFIX from letta.errors import BedrockPermissionError, LettaAgentNotFoundError, LettaUserNotFoundError +from letta.helpers.pinecone_utils import get_pinecone_indices, should_use_pinecone, upsert_pinecone_indices from letta.jobs.scheduler import start_scheduler_with_leader_election from letta.log import get_logger from letta.orm.errors import DatabaseTimeoutError, ForeignKeyConstraintViolationError, NoResultFound, UniqueConstraintViolationError @@ -127,6 +128,16 @@ async def lifespan(app_: FastAPI): db_registry.initialize_async() logger.info(f"[Worker {worker_id}] Database connections initialized") + if should_use_pinecone(): + if settings.upsert_pinecone_indices: + logger.info(f"[Worker {worker_id}] Upserting pinecone indices: {get_pinecone_indices()}") + await upsert_pinecone_indices() + logger.info(f"[Worker {worker_id}] Upserted pinecone indices") + else: + logger.info(f"[Worker {worker_id}] Enabled pinecone") + else: + logger.info(f"[Worker {worker_id}] Disabled pinecone") + logger.info(f"[Worker {worker_id}] Starting scheduler with leader election") global server try: diff --git a/letta/server/rest_api/routers/v1/sources.py b/letta/server/rest_api/routers/v1/sources.py index a3e32df2..bb6694eb 100644 --- a/letta/server/rest_api/routers/v1/sources.py +++ b/letta/server/rest_api/routers/v1/sources.py @@ -9,6 +9,12 @@ from fastapi import APIRouter, Depends, Header, HTTPException, Query, UploadFile from starlette import status import letta.constants as constants +from letta.helpers.pinecone_utils import ( + delete_file_records_from_pinecone_index, + delete_source_records_from_pinecone_index, + list_pinecone_index_for_files, + should_use_pinecone, +) from letta.log import get_logger from letta.otel.tracing import trace_method from letta.schemas.agent import AgentState @@ -22,6 +28,7 @@ from letta.server.rest_api.utils import get_letta_server from letta.server.server import SyncServer from letta.services.file_processor.chunker.llama_index_chunker import LlamaIndexChunker from letta.services.file_processor.embedder.openai_embedder import OpenAIEmbedder +from letta.services.file_processor.embedder.pinecone_embedder import PineconeEmbedder from letta.services.file_processor.file_processor import FileProcessor from letta.services.file_processor.file_types import ( get_allowed_media_types, @@ -163,6 +170,10 @@ async def delete_source( files = await server.file_manager.list_files(source_id, actor) file_ids = [f.id for f in files] + if should_use_pinecone(): + logger.info(f"Deleting source {source_id} from pinecone index") + await delete_source_records_from_pinecone_index(source_id=source_id, actor=actor) + for agent_state in agent_states: await server.remove_files_from_context_window(agent_state=agent_state, file_ids=file_ids, actor=actor) @@ -326,16 +337,24 @@ async def get_file_metadata( """ actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id) - # Verify the source exists and user has access - source = await server.source_manager.get_source_by_id(source_id=source_id, actor=actor) - if not source: - raise HTTPException(status_code=404, detail=f"Source with id={source_id} not found.") - # Get file metadata using the file manager file_metadata = await server.file_manager.get_file_by_id( file_id=file_id, actor=actor, include_content=include_content, strip_directory_prefix=True ) + if should_use_pinecone() and not file_metadata.is_processing_terminal(): + ids = await list_pinecone_index_for_files(file_id=file_id, actor=actor, limit=file_metadata.total_chunks) + logger.info(f"Embedded chunks {len(ids)}/{file_metadata.total_chunks} for {file_id} in organization {actor.organization_id}") + + if len(ids) != file_metadata.chunks_embedded or len(ids) == file_metadata.total_chunks: + if len(ids) != file_metadata.total_chunks: + file_status = file_metadata.processing_status + else: + file_status = FileProcessingStatus.COMPLETED + await server.file_manager.update_file_status( + file_id=file_metadata.id, actor=actor, chunks_embedded=len(ids), processing_status=file_status + ) + if not file_metadata: raise HTTPException(status_code=404, detail=f"File with id={file_id} not found.") @@ -364,6 +383,10 @@ async def delete_file_from_source( await server.remove_file_from_context_windows(source_id=source_id, file_id=deleted_file.id, actor=actor) + if should_use_pinecone(): + logger.info(f"Deleting file {file_id} from pinecone index") + await delete_file_records_from_pinecone_index(file_id=file_id, actor=actor) + asyncio.create_task(sleeptime_document_ingest_async(server, source_id, actor, clear_history=True)) if deleted_file is None: raise HTTPException(status_code=404, detail=f"File with id={file_id} not found.") @@ -402,8 +425,14 @@ async def load_file_to_source_cloud( ): file_processor = MistralFileParser() text_chunker = LlamaIndexChunker(chunk_size=embedding_config.embedding_chunk_size) - embedder = OpenAIEmbedder(embedding_config=embedding_config) - file_processor = FileProcessor(file_parser=file_processor, text_chunker=text_chunker, embedder=embedder, actor=actor) + using_pinecone = should_use_pinecone() + if using_pinecone: + embedder = PineconeEmbedder() + else: + embedder = OpenAIEmbedder(embedding_config=embedding_config) + file_processor = FileProcessor( + file_parser=file_processor, text_chunker=text_chunker, embedder=embedder, actor=actor, using_pinecone=using_pinecone + ) await file_processor.process( server=server, agent_states=agent_states, source_id=source_id, content=content, file_metadata=file_metadata ) diff --git a/letta/services/file_manager.py b/letta/services/file_manager.py index fbfa44e8..ed1ee7bd 100644 --- a/letta/services/file_manager.py +++ b/letta/services/file_manager.py @@ -109,15 +109,17 @@ class FileManager: actor: PydanticUser, processing_status: Optional[FileProcessingStatus] = None, error_message: Optional[str] = None, + total_chunks: Optional[int] = None, + chunks_embedded: Optional[int] = None, ) -> PydanticFileMetadata: """ - Update processing_status and/or error_message on a FileMetadata row. + Update processing_status, error_message, total_chunks, and/or chunks_embedded on a FileMetadata row. * 1st round-trip → UPDATE * 2nd round-trip → SELECT fresh row (same as read_async) """ - if processing_status is None and error_message is None: + if processing_status is None and error_message is None and total_chunks is None and chunks_embedded is None: raise ValueError("Nothing to update") values: dict[str, object] = {"updated_at": datetime.utcnow()} @@ -125,6 +127,10 @@ class FileManager: values["processing_status"] = processing_status if error_message is not None: values["error_message"] = error_message + if total_chunks is not None: + values["total_chunks"] = total_chunks + if chunks_embedded is not None: + values["chunks_embedded"] = chunks_embedded async with db_registry.async_session() as session: # Fast in-place update – no ORM hydration diff --git a/letta/services/file_processor/embedder/base_embedder.py b/letta/services/file_processor/embedder/base_embedder.py new file mode 100644 index 00000000..b9310c3e --- /dev/null +++ b/letta/services/file_processor/embedder/base_embedder.py @@ -0,0 +1,16 @@ +from abc import ABC, abstractmethod +from typing import List + +from letta.log import get_logger +from letta.schemas.passage import Passage +from letta.schemas.user import User + +logger = get_logger(__name__) + + +class BaseEmbedder(ABC): + """Abstract base class for embedding generation""" + + @abstractmethod + async def generate_embedded_passages(self, file_id: str, source_id: str, chunks: List[str], actor: User) -> List[Passage]: + """Generate embeddings for chunks with batching and concurrent processing""" diff --git a/letta/services/file_processor/embedder/openai_embedder.py b/letta/services/file_processor/embedder/openai_embedder.py index b831c819..ce43a72d 100644 --- a/letta/services/file_processor/embedder/openai_embedder.py +++ b/letta/services/file_processor/embedder/openai_embedder.py @@ -9,12 +9,13 @@ from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import ProviderType from letta.schemas.passage import Passage from letta.schemas.user import User +from letta.services.file_processor.embedder.base_embedder import BaseEmbedder from letta.settings import model_settings logger = get_logger(__name__) -class OpenAIEmbedder: +class OpenAIEmbedder(BaseEmbedder): """OpenAI-based embedding generation""" def __init__(self, embedding_config: Optional[EmbeddingConfig] = None): @@ -24,6 +25,7 @@ class OpenAIEmbedder: else EmbeddingConfig.default_config(model_name="letta") ) self.embedding_config = embedding_config or self.default_embedding_config + self.max_concurrent_requests = 20 # TODO: Unify to global OpenAI client self.client: OpenAIClient = cast( @@ -34,7 +36,6 @@ class OpenAIEmbedder: actor=None, # Not necessary ), ) - self.max_concurrent_requests = 20 @trace_method async def _embed_batch(self, batch: List[str], batch_indices: List[int]) -> List[Tuple[int, List[float]]]: diff --git a/letta/services/file_processor/embedder/pinecone_embedder.py b/letta/services/file_processor/embedder/pinecone_embedder.py new file mode 100644 index 00000000..212ba95d --- /dev/null +++ b/letta/services/file_processor/embedder/pinecone_embedder.py @@ -0,0 +1,74 @@ +from typing import List + +from letta.helpers.pinecone_utils import upsert_file_records_to_pinecone_index +from letta.log import get_logger +from letta.otel.tracing import log_event, trace_method +from letta.schemas.passage import Passage +from letta.schemas.user import User +from letta.services.file_processor.embedder.base_embedder import BaseEmbedder + +try: + PINECONE_AVAILABLE = True +except ImportError: + PINECONE_AVAILABLE = False + +logger = get_logger(__name__) + + +class PineconeEmbedder(BaseEmbedder): + """Pinecone-based embedding generation""" + + def __init__(self): + if not PINECONE_AVAILABLE: + raise ImportError("Pinecone package is not installed. Install it with: pip install pinecone") + + super().__init__() + + @trace_method + async def generate_embedded_passages(self, file_id: str, source_id: str, chunks: List[str], actor: User) -> List[Passage]: + """Generate embeddings and upsert to Pinecone, then return Passage objects""" + if not chunks: + return [] + + logger.info(f"Upserting {len(chunks)} chunks to Pinecone using namespace {source_id}") + log_event( + "embedder.generation_started", + { + "total_chunks": len(chunks), + "file_id": file_id, + "source_id": source_id, + }, + ) + + # Upsert records to Pinecone using source_id as namespace + try: + await upsert_file_records_to_pinecone_index(file_id=file_id, source_id=source_id, chunks=chunks, actor=actor) + logger.info(f"Successfully kicked off upserting {len(chunks)} records to Pinecone") + log_event( + "embedder.upsert_started", + {"records_upserted": len(chunks), "namespace": source_id, "file_id": file_id}, + ) + except Exception as e: + logger.error(f"Failed to upsert records to Pinecone: {str(e)}") + log_event("embedder.upsert_failed", {"error": str(e), "error_type": type(e).__name__}) + raise + + # Create Passage objects (without embeddings since Pinecone handles them) + passages = [] + for i, text in enumerate(chunks): + passage = Passage( + text=text, + file_id=file_id, + source_id=source_id, + embedding=None, # Pinecone handles embeddings internally + embedding_config=None, # None + organization_id=actor.organization_id, + ) + passages.append(passage) + + logger.info(f"Successfully created {len(passages)} passages") + log_event( + "embedder.generation_completed", + {"passages_created": len(passages), "total_chunks_processed": len(chunks), "file_id": file_id, "source_id": source_id}, + ) + return passages diff --git a/letta/services/file_processor/file_processor.py b/letta/services/file_processor/file_processor.py index 67c086ed..cbf70b1c 100644 --- a/letta/services/file_processor/file_processor.py +++ b/letta/services/file_processor/file_processor.py @@ -11,7 +11,7 @@ from letta.server.server import SyncServer from letta.services.file_manager import FileManager from letta.services.file_processor.chunker.line_chunker import LineChunker from letta.services.file_processor.chunker.llama_index_chunker import LlamaIndexChunker -from letta.services.file_processor.embedder.openai_embedder import OpenAIEmbedder +from letta.services.file_processor.embedder.base_embedder import BaseEmbedder from letta.services.file_processor.parser.mistral_parser import MistralFileParser from letta.services.job_manager import JobManager from letta.services.passage_manager import PassageManager @@ -27,8 +27,9 @@ class FileProcessor: self, file_parser: MistralFileParser, text_chunker: LlamaIndexChunker, - embedder: OpenAIEmbedder, + embedder: BaseEmbedder, actor: User, + using_pinecone: bool, max_file_size: int = 50 * 1024 * 1024, # 50MB default ): self.file_parser = file_parser @@ -41,6 +42,7 @@ class FileProcessor: self.passage_manager = PassageManager() self.job_manager = JobManager() self.actor = actor + self.using_pinecone = using_pinecone # TODO: Factor this function out of SyncServer @trace_method @@ -109,7 +111,7 @@ class FileProcessor: logger.info("Chunking extracted text") log_event("file_processor.chunking_started", {"filename": filename, "pages_to_process": len(ocr_response.pages)}) - all_passages = [] + all_chunks = [] for page in ocr_response.pages: chunks = self.text_chunker.chunk_text(page) @@ -118,24 +120,17 @@ class FileProcessor: log_event("file_processor.chunking_failed", {"filename": filename, "page_index": ocr_response.pages.index(page)}) raise ValueError("No chunks created from text") - passages = await self.embedder.generate_embedded_passages( - file_id=file_metadata.id, source_id=source_id, chunks=chunks, actor=self.actor - ) - log_event( - "file_processor.page_processed", - { - "filename": filename, - "page_index": ocr_response.pages.index(page), - "chunks_created": len(chunks), - "passages_generated": len(passages), - }, - ) - all_passages.extend(passages) + all_chunks.extend(self.text_chunker.chunk_text(page)) - all_passages = await self.passage_manager.create_many_source_passages_async( - passages=all_passages, file_metadata=file_metadata, actor=self.actor + all_passages = await self.embedder.generate_embedded_passages( + file_id=file_metadata.id, source_id=source_id, chunks=all_chunks, actor=self.actor ) - log_event("file_processor.passages_created", {"filename": filename, "total_passages": len(all_passages)}) + + if not self.using_pinecone: + all_passages = await self.passage_manager.create_many_source_passages_async( + passages=all_passages, file_metadata=file_metadata, actor=self.actor + ) + log_event("file_processor.passages_created", {"filename": filename, "total_passages": len(all_passages)}) logger.info(f"Successfully processed {filename}: {len(all_passages)} passages") log_event( @@ -149,9 +144,14 @@ class FileProcessor: ) # update job status - await self.file_manager.update_file_status( - file_id=file_metadata.id, actor=self.actor, processing_status=FileProcessingStatus.COMPLETED - ) + if not self.using_pinecone: + await self.file_manager.update_file_status( + file_id=file_metadata.id, actor=self.actor, processing_status=FileProcessingStatus.COMPLETED + ) + else: + await self.file_manager.update_file_status( + file_id=file_metadata.id, actor=self.actor, total_chunks=len(all_passages), chunks_embedded=0 + ) return all_passages diff --git a/letta/services/job_manager.py b/letta/services/job_manager.py index ca5fa9b9..5b8e2693 100644 --- a/letta/services/job_manager.py +++ b/letta/services/job_manager.py @@ -115,10 +115,6 @@ class JobManager: job.completed_at = get_utc_time().replace(tzinfo=None) if job.callback_url: await self._dispatch_callback_async(job) - else: - logger.info(f"Job does not contain callback url: {job}") - else: - logger.info(f"Job update is not terminal {job_update}") # Save the updated job to the database await job.update_async(db_session=session, actor=actor) diff --git a/letta/services/source_manager.py b/letta/services/source_manager.py index 88aa4c6d..9e3ee4d2 100644 --- a/letta/services/source_manager.py +++ b/letta/services/source_manager.py @@ -19,7 +19,6 @@ class SourceManager: @trace_method async def create_source(self, source: PydanticSource, actor: PydanticUser) -> PydanticSource: """Create a new source based on the PydanticSource schema.""" - # Try getting the source first by id db_source = await self.get_source_by_id(source.id, actor=actor) if db_source: return db_source diff --git a/letta/services/tool_executor/files_tool_executor.py b/letta/services/tool_executor/files_tool_executor.py index de8c0a2d..1de53724 100644 --- a/letta/services/tool_executor/files_tool_executor.py +++ b/letta/services/tool_executor/files_tool_executor.py @@ -2,8 +2,9 @@ import asyncio import re from typing import Any, Dict, List, Optional -from letta.constants import MAX_FILES_OPEN +from letta.constants import MAX_FILES_OPEN, PINECONE_TEXT_FIELD_NAME from letta.functions.types import FileOpenRequest +from letta.helpers.pinecone_utils import search_pinecone_index, should_use_pinecone from letta.log import get_logger from letta.otel.tracing import trace_method from letta.schemas.agent import AgentState @@ -463,14 +464,15 @@ class LettaFileToolExecutor(ToolExecutor): return "\n".join(formatted_results) @trace_method - async def semantic_search_files(self, agent_state: AgentState, query: str, limit: int = 10) -> str: + async def semantic_search_files(self, agent_state: AgentState, query: str, limit: int = 5) -> str: """ Search for text within attached files using semantic search and return passages with their source filenames. + Uses Pinecone if configured, otherwise falls back to traditional search. Args: agent_state: Current agent state query: Search query for semantic matching - limit: Maximum number of results to return (default: 10) + limit: Maximum number of results to return (default: 5) Returns: Formatted string with search results in IDE/terminal style @@ -485,6 +487,110 @@ class LettaFileToolExecutor(ToolExecutor): self.logger.info(f"Semantic search started for agent {agent_state.id} with query '{query}' (limit: {limit})") + # Check if Pinecone is enabled and use it if available + if should_use_pinecone(): + return await self._search_files_pinecone(agent_state, query, limit) + else: + return await self._search_files_traditional(agent_state, query, limit) + + async def _search_files_pinecone(self, agent_state: AgentState, query: str, limit: int) -> str: + """Search files using Pinecone vector database.""" + + # Extract unique source_ids + # TODO: Inefficient + attached_sources = await self.agent_manager.list_attached_sources_async(agent_id=agent_state.id, actor=self.actor) + source_ids = [source.id for source in attached_sources] + if not source_ids: + return f"No valid source IDs found for attached files" + + # Get all attached files for this agent + file_agents = await self.files_agents_manager.list_files_for_agent(agent_id=agent_state.id, actor=self.actor) + if not file_agents: + return "No files are currently attached to search" + + results = [] + total_hits = 0 + files_with_matches = {} + + try: + filter = {"source_id": {"$in": source_ids}} + search_results = await search_pinecone_index(query, limit, filter, self.actor) + + # Process search results + if "result" in search_results and "hits" in search_results["result"]: + for hit in search_results["result"]["hits"]: + if total_hits >= limit: + break + + total_hits += 1 + + # Extract hit information + hit_id = hit.get("_id", "unknown") + score = hit.get("_score", 0.0) + fields = hit.get("fields", {}) + text = fields.get(PINECONE_TEXT_FIELD_NAME, "") + file_id = fields.get("file_id", "") + + # Find corresponding file name + file_name = "Unknown File" + for fa in file_agents: + if fa.file_id == file_id: + file_name = fa.file_name + break + + # Group by file name + if file_name not in files_with_matches: + files_with_matches[file_name] = [] + files_with_matches[file_name].append({"text": text, "score": score, "hit_id": hit_id}) + + except Exception as e: + self.logger.error(f"Pinecone search failed: {str(e)}") + raise e + + if not files_with_matches: + return f"No semantic matches found in Pinecone for query: '{query}'" + + # Format results + passage_num = 0 + for file_name, matches in files_with_matches.items(): + for match in matches: + passage_num += 1 + + # Format each passage with terminal-style header + score_display = f"(score: {match['score']:.3f})" + passage_header = f"\n=== {file_name} (passage #{passage_num}) {score_display} ===" + + # Format the passage text + passage_text = match["text"].strip() + lines = passage_text.splitlines() + formatted_lines = [] + for line in lines[:20]: # Limit to first 20 lines per passage + formatted_lines.append(f" {line}") + + if len(lines) > 20: + formatted_lines.append(f" ... [truncated {len(lines) - 20} more lines]") + + passage_content = "\n".join(formatted_lines) + results.append(f"{passage_header}\n{passage_content}") + + # Mark access for files that had matches + if files_with_matches: + matched_file_names = [name for name in files_with_matches.keys() if name != "Unknown File"] + if matched_file_names: + await self.files_agents_manager.mark_access_bulk(agent_id=agent_state.id, file_names=matched_file_names, actor=self.actor) + + # Create summary header + file_count = len(files_with_matches) + summary = f"Found {total_hits} Pinecone matches in {file_count} file{'s' if file_count != 1 else ''} for query: '{query}'" + + # Combine all results + formatted_results = [summary, "=" * len(summary)] + results + + self.logger.info(f"Pinecone search completed: {total_hits} matches across {file_count} files") + return "\n".join(formatted_results) + + async def _search_files_traditional(self, agent_state: AgentState, query: str, limit: int) -> str: + """Traditional search using existing passage manager.""" # Get semantic search results passages = await self.agent_manager.list_source_passages_async( actor=self.actor, diff --git a/letta/settings.py b/letta/settings.py index 4a0983b1..1e8b9134 100644 --- a/letta/settings.py +++ b/letta/settings.py @@ -253,6 +253,13 @@ class Settings(BaseSettings): llm_request_timeout_seconds: float = Field(default=60.0, ge=10.0, le=1800.0, description="Timeout for LLM requests in seconds") llm_stream_timeout_seconds: float = Field(default=60.0, ge=10.0, le=1800.0, description="Timeout for LLM streaming requests in seconds") + # For embeddings + enable_pinecone: bool = False + pinecone_api_key: Optional[str] = None + pinecone_source_index: Optional[str] = "sources" + pinecone_agent_index: Optional[str] = "recall" + upsert_pinecone_indices: bool = False + @property def letta_pg_uri(self) -> str: if self.pg_uri: diff --git a/poetry.lock b/poetry.lock index 2bf2659a..2336811d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,13 +1,12 @@ # This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. + [[package]] name = "aioboto3" version = "14.3.0" description = "Async boto3 wrapper" optional = true python-versions = "<4.0,>=3.8" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "aioboto3-14.3.0-py3-none-any.whl", hash = "sha256:aec5de94e9edc1ffbdd58eead38a37f00ddac59a519db749a910c20b7b81bca7"}, {file = "aioboto3-14.3.0.tar.gz", hash = "sha256:1d18f88bb56835c607b62bb6cb907754d717bedde3ddfff6935727cb48a80135"}, @@ -27,8 +26,6 @@ version = "2.22.0" description = "Async client for aws services using botocore and aiohttp" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "aiobotocore-2.22.0-py3-none-any.whl", hash = "sha256:b4e6306f79df9d81daff1f9d63189a2dbee4b77ce3ab937304834e35eaaeeccf"}, {file = "aiobotocore-2.22.0.tar.gz", hash = "sha256:11091477266b75c2b5d28421c1f2bc9a87d175d0b8619cb830805e7a113a170b"}, @@ -54,8 +51,6 @@ version = "24.1.0" description = "File support for asyncio." optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, @@ -67,7 +62,6 @@ version = "2.6.1" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, @@ -79,7 +73,6 @@ version = "3.11.16" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "aiohttp-3.11.16-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb46bb0f24813e6cede6cc07b1961d4b04f331f7112a23b5e21f567da4ee50aa"}, {file = "aiohttp-3.11.16-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:54eb3aead72a5c19fad07219acd882c1643a1027fbcdefac9b502c267242f955"}, @@ -175,7 +168,21 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] + +[[package]] +name = "aiohttp-retry" +version = "2.9.1" +description = "Simple retry client for aiohttp" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiohttp_retry-2.9.1-py3-none-any.whl", hash = "sha256:66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54"}, + {file = "aiohttp_retry-2.9.1.tar.gz", hash = "sha256:8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1"}, +] + +[package.dependencies] +aiohttp = "*" [[package]] name = "aioitertools" @@ -183,8 +190,6 @@ version = "0.12.0" description = "itertools and builtins for AsyncIO and mixed iterables" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "aioitertools-0.12.0-py3-none-any.whl", hash = "sha256:fc1f5fac3d737354de8831cbba3eb04f79dd649d8f3afb4c5b114925e662a796"}, {file = "aioitertools-0.12.0.tar.gz", hash = "sha256:c2a9055b4fbb7705f561b9d86053e8af5d10cc845d22c32008c43490b2d8dd6b"}, @@ -200,14 +205,13 @@ version = "0.9.1" description = "AsyncIO version of the standard multiprocessing module" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "aiomultiprocess-0.9.1-py3-none-any.whl", hash = "sha256:3a7b3bb3c38dbfb4d9d1194ece5934b6d32cf0280e8edbe64a7d215bba1322c6"}, {file = "aiomultiprocess-0.9.1.tar.gz", hash = "sha256:f0231dbe0291e15325d7896ebeae0002d95a4f2675426ca05eb35f24c60e495b"}, ] [package.extras] -dev = ["attribution (==1.7.1)", "black (==24.4.0)", "coverage (==7.4.4)", "flake8 (==7.0.0)", "flake8-bugbear (==24.4.21)", "flit (==3.9.0)", "mypy (==1.9.0)", "usort (==1.0.8.post1)", "uvloop (==0.19.0) ; sys_platform != \"win32\""] +dev = ["attribution (==1.7.1)", "black (==24.4.0)", "coverage (==7.4.4)", "flake8 (==7.0.0)", "flake8-bugbear (==24.4.21)", "flit (==3.9.0)", "mypy (==1.9.0)", "usort (==1.0.8.post1)", "uvloop (==0.19.0)"] docs = ["sphinx (==7.3.7)", "sphinx-mdinclude (==0.6.0)"] [[package]] @@ -216,7 +220,6 @@ version = "1.3.2" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, @@ -231,7 +234,6 @@ version = "1.15.2" description = "A database migration tool for SQLAlchemy." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "alembic-1.15.2-py3-none-any.whl", hash = "sha256:2e76bd916d547f6900ec4bb5a90aeac1485d2c92536923d0b138c02b126edc53"}, {file = "alembic-1.15.2.tar.gz", hash = "sha256:1c72391bbdeffccfe317eefba686cb9a3c078005478885413b95c3b26c57a8a7"}, @@ -251,7 +253,6 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -263,7 +264,6 @@ version = "0.49.0" description = "The official Python library for the anthropic API" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "anthropic-0.49.0-py3-none-any.whl", hash = "sha256:bbc17ad4e7094988d2fa86b87753ded8dce12498f4b85fe5810f208f454a8375"}, {file = "anthropic-0.49.0.tar.gz", hash = "sha256:c09e885b0f674b9119b4f296d8508907f6cff0009bc20d5cf6b35936c40b4398"}, @@ -288,7 +288,6 @@ version = "4.9.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"}, {file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"}, @@ -302,7 +301,7 @@ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""] +test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] [[package]] @@ -311,8 +310,6 @@ version = "0.1.4" description = "Disable App Nap on macOS >= 10.9" optional = false python-versions = ">=3.6" -groups = ["dev"] -markers = "platform_system == \"Darwin\"" files = [ {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, @@ -324,7 +321,6 @@ version = "3.11.0" description = "In-process task scheduler with Cron-like capabilities" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "APScheduler-3.11.0-py3-none-any.whl", hash = "sha256:fc134ca32e50f5eadcc4938e3a4545ab19131435e851abb40b34d63d5141c6da"}, {file = "apscheduler-3.11.0.tar.gz", hash = "sha256:4c622d250b0955a65d5d0eb91c33e6d43fd879834bf541e0a18661ae60460133"}, @@ -341,7 +337,7 @@ mongodb = ["pymongo (>=3.0)"] redis = ["redis (>=3.0)"] rethinkdb = ["rethinkdb (>=2.4.0)"] sqlalchemy = ["sqlalchemy (>=1.4)"] -test = ["APScheduler[etcd,mongodb,redis,rethinkdb,sqlalchemy,tornado,zookeeper]", "PySide6 ; platform_python_implementation == \"CPython\" and python_version < \"3.14\"", "anyio (>=4.5.2)", "gevent ; python_version < \"3.14\"", "pytest", "pytz", "twisted ; python_version < \"3.14\""] +test = ["APScheduler[etcd,mongodb,redis,rethinkdb,sqlalchemy,tornado,zookeeper]", "PySide6", "anyio (>=4.5.2)", "gevent", "pytest", "pytz", "twisted"] tornado = ["tornado (>=4.3)"] twisted = ["twisted"] zookeeper = ["kazoo"] @@ -352,7 +348,6 @@ version = "3.6.2" description = "Bash tab completion for argparse" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "argcomplete-3.6.2-py3-none-any.whl", hash = "sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591"}, {file = "argcomplete-3.6.2.tar.gz", hash = "sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf"}, @@ -367,8 +362,6 @@ version = "1.5.1" description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" optional = true python-versions = "*" -groups = ["main"] -markers = "extra == \"postgres\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, @@ -380,12 +373,10 @@ version = "3.0.0" description = "Annotate AST trees with source code positions" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] astroid = ["astroid (>=2,<4)"] @@ -395,10 +386,8 @@ test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"] name = "async-timeout" version = "4.0.3" description = "Timeout context manager for asyncio programs" -optional = true +optional = false python-versions = ">=3.7" -groups = ["main"] -markers = "(extra == \"redis\" or extra == \"all\") and python_full_version < \"3.11.3\" or python_version == \"3.10\"" files = [ {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, @@ -410,8 +399,6 @@ version = "0.30.0" description = "An asyncio PostgreSQL driver" optional = true python-versions = ">=3.8.0" -groups = ["main"] -markers = "extra == \"postgres\"" files = [ {file = "asyncpg-0.30.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfb4dd5ae0699bad2b233672c8fc5ccbd9ad24b89afded02341786887e37927e"}, {file = "asyncpg-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc1f62c792752a49f88b7e6f774c26077091b44caceb1983509edc18a2222ec0"}, @@ -469,8 +456,8 @@ async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.11.0\""} [package.extras] docs = ["Sphinx (>=8.1.3,<8.2.0)", "sphinx-rtd-theme (>=1.2.2)"] -gssauth = ["gssapi ; platform_system != \"Windows\"", "sspilib ; platform_system == \"Windows\""] -test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0,<24.2.0)", "gssapi ; platform_system == \"Linux\"", "k5test ; platform_system == \"Linux\"", "mypy (>=1.8.0,<1.9.0)", "sspilib ; platform_system == \"Windows\"", "uvloop (>=0.15.3) ; platform_system != \"Windows\" and python_version < \"3.14.0\""] +gssauth = ["gssapi", "sspilib"] +test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0,<24.2.0)", "gssapi", "k5test", "mypy (>=1.8.0,<1.9.0)", "sspilib", "uvloop (>=0.15.3)"] [[package]] name = "attrs" @@ -478,19 +465,18 @@ version = "25.3.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, ] [package.extras] -benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "autoflake" @@ -498,8 +484,6 @@ version = "2.3.1" description = "Removes unused imports and unused variables" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "autoflake-2.3.1-py3-none-any.whl", hash = "sha256:3ae7495db9084b7b32818b4140e6dc4fc280b712fb414f5b8fe57b0a8e85a840"}, {file = "autoflake-2.3.1.tar.gz", hash = "sha256:c98b75dc5b0a86459c4f01a1d32ac7eb4338ec4317a4469515ff1e687ecd909e"}, @@ -515,7 +499,6 @@ version = "2.1.1" description = "A prompt programming language" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "banks-2.1.1-py3-none-any.whl", hash = "sha256:06e4ee46a0ff2fcdf5f64a5f028a7b7ceb719d5c7b9339f5aa90b24936fbb7f5"}, {file = "banks-2.1.1.tar.gz", hash = "sha256:95ec9c8f3c173c9f1c21eb2451ba0e21dda87f1ceb738854fabadb54bc387b86"}, @@ -537,7 +520,6 @@ version = "4.3.0" description = "Modern password hashing for your software and your servers" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "bcrypt-4.3.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f01e060f14b6b57bbb72fc5b4a83ac21c443c9a2ee708e04a10e9192f90a6281"}, {file = "bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5eeac541cefd0bb887a371ef73c62c3cd78535e4887b310626036a7c0a817bb"}, @@ -602,7 +584,6 @@ version = "4.13.3" description = "Screen-scraping library" optional = false python-versions = ">=3.7.0" -groups = ["main"] files = [ {file = "beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16"}, {file = "beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b"}, @@ -638,7 +619,6 @@ version = "24.10.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" -groups = ["main", "dev"] files = [ {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, @@ -687,8 +667,6 @@ version = "1.9.0" description = "Fast, simple object-to-object and broadcast signaling" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, @@ -700,8 +678,6 @@ version = "1.37.3" description = "The AWS SDK for Python" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "boto3-1.37.3-py3-none-any.whl", hash = "sha256:2063b40af99fd02f6228ff52397b552ff3353831edaf8d25cc04801827ab9794"}, {file = "boto3-1.37.3.tar.gz", hash = "sha256:21f3ce0ef111297e63a6eb998a25197b8c10982970c320d4c6e8db08be2157be"}, @@ -721,8 +697,6 @@ version = "1.37.3" description = "Low-level, data-driven core of boto 3." optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "botocore-1.37.3-py3-none-any.whl", hash = "sha256:d01bd3bf4c80e61fa88d636ad9f5c9f60a551d71549b481386c6b4efe0bb2b2e"}, {file = "botocore-1.37.3.tar.gz", hash = "sha256:fe8403eb55a88faf9b0f9da6615e5bee7be056d75e17af66c3c8f0a3b0648da4"}, @@ -742,7 +716,6 @@ version = "1.1.0" description = "Python bindings for the Brotli compression library" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752"}, {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9"}, @@ -754,10 +727,6 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -770,14 +739,8 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -788,24 +751,8 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, - {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, - {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -815,10 +762,6 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -830,10 +773,6 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -846,10 +785,6 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -862,10 +797,6 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -877,8 +808,6 @@ version = "5.5.2" description = "Extensible memoizing collections and decorators" optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"google\"" files = [ {file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"}, {file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"}, @@ -890,7 +819,6 @@ version = "2025.6.15" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057"}, {file = "certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b"}, @@ -902,7 +830,6 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -972,7 +899,6 @@ files = [ {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, ] -markers = {dev = "implementation_name == \"pypy\""} [package.dependencies] pycparser = "*" @@ -983,8 +909,6 @@ version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -996,7 +920,6 @@ version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -1098,7 +1021,6 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" -groups = ["main", "dev"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -1113,7 +1035,6 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "dev", "dev,tests"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -1125,7 +1046,6 @@ version = "0.2.2" description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, @@ -1143,7 +1063,6 @@ version = "0.7.15" description = "Core package to act as a bridge between composio platform and other services." optional = false python-versions = "<4,>=3.9" -groups = ["main"] files = [ {file = "composio_core-0.7.15-py3-none-any.whl", hash = "sha256:3b4c19fcf8c084ed8b873b8e24ea55084495a046eb971af994e6aae6e1bfcea7"}, {file = "composio_core-0.7.15.tar.gz", hash = "sha256:6797c46f404b9c265cd05c07bd28b4104f425024ef683ac24092087e9590033d"}, @@ -1198,7 +1117,6 @@ version = "1.3.2" description = "Python library for calculating contours of 2D quadrilateral grids" optional = false python-versions = ">=3.10" -groups = ["main"] files = [ {file = "contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934"}, {file = "contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989"}, @@ -1275,7 +1193,6 @@ version = "44.0.2" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.7" -groups = ["main"] files = [ {file = "cryptography-44.0.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7"}, {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1"}, @@ -1318,10 +1235,10 @@ files = [ cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} [package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0) ; python_version >= \"3.8\""] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"] docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] -nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_version >= \"3.8\""] -pep8test = ["check-sdist ; python_version >= \"3.8\"", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] +nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] +pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] test = ["certifi (>=2024)", "cryptography-vectors (==44.0.2)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] @@ -1333,7 +1250,6 @@ version = "0.12.1" description = "Composable style cycles" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, @@ -1349,7 +1265,6 @@ version = "0.6.7" description = "Easily serialize dataclasses to and from JSON." optional = false python-versions = "<4.0,>=3.7" -groups = ["main"] files = [ {file = "dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a"}, {file = "dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0"}, @@ -1365,7 +1280,6 @@ version = "0.25.9" description = "Datamodel Code Generator" optional = false python-versions = "<4.0,>=3.7" -groups = ["main"] files = [ {file = "datamodel_code_generator-0.25.9-py3-none-any.whl", hash = "sha256:9e0324233123d6e39a35bc0004771956935889a974aacfd7a0651de11d2219a9"}, {file = "datamodel_code_generator-0.25.9.tar.gz", hash = "sha256:65ca9807d8edbd88a7f7931c10f4bc1c08bd9bbc5bb0508418a2b6a16590eb65"}, @@ -1381,9 +1295,9 @@ isort = ">=4.3.21,<6.0" jinja2 = ">=2.10.1,<4.0" packaging = "*" pydantic = [ + {version = ">=1.10.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.9.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, {version = ">=1.10.0,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.12\" and python_version < \"4.0\""}, - {version = ">=1.10.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.11\" and python_version < \"4.0\""}, - {version = ">=1.9.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version == \"3.10\""}, ] pyyaml = ">=6.0.1" toml = {version = ">=0.10.0,<1.0.0", markers = "python_version < \"3.11\""} @@ -1400,7 +1314,6 @@ version = "1.8.13" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "debugpy-1.8.13-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:06859f68e817966723ffe046b896b1bd75c665996a77313370336ee9e1de3e90"}, {file = "debugpy-1.8.13-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c2db69fb8df3168bc857d7b7d2494fed295dfdbde9a45f27b4b152f37520"}, @@ -1436,12 +1349,10 @@ version = "5.2.1" description = "Decorators for Humans" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"}, {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [[package]] name = "demjson3" @@ -1449,7 +1360,6 @@ version = "3.0.6" description = "encoder, decoder, and lint/validator for JSON (JavaScript Object Notation) compliant with RFC 7159" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "demjson3-3.0.6.tar.gz", hash = "sha256:37c83b0c6eb08d25defc88df0a2a4875d58a7809a9650bd6eee7afd8053cdbac"}, ] @@ -1460,7 +1370,6 @@ version = "1.2.18" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -groups = ["main"] files = [ {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"}, {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"}, @@ -1470,7 +1379,7 @@ files = [ wrapt = ">=1.10,<2" [package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools", "tox"] [[package]] name = "dirtyjson" @@ -1478,7 +1387,6 @@ version = "1.0.8" description = "JSON decoder for Python that can extract data from the muck" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "dirtyjson-1.0.8-py3-none-any.whl", hash = "sha256:125e27248435a58acace26d5c2c4c11a1c0de0a9c5124c5a94ba78e517d74f53"}, {file = "dirtyjson-1.0.8.tar.gz", hash = "sha256:90ca4a18f3ff30ce849d100dcf4a003953c79d3a2348ef056f1d9c22231a25fd"}, @@ -1490,8 +1398,6 @@ version = "0.3.9" description = "Distribution utilities" optional = true python-versions = "*" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, @@ -1503,7 +1409,6 @@ version = "1.9.0" description = "Distro - an OS platform information API" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, @@ -1515,7 +1420,6 @@ version = "2.7.0" description = "DNS toolkit" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, @@ -1536,8 +1440,6 @@ version = "7.1.0" description = "A Python library for the Docker Engine API." optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0"}, {file = "docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c"}, @@ -1560,7 +1462,6 @@ version = "0.16" description = "Parse Python docstrings in reST, Google and Numpydoc format" optional = false python-versions = ">=3.6,<4.0" -groups = ["main"] files = [ {file = "docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637"}, {file = "docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e"}, @@ -1572,8 +1473,6 @@ version = "1.3.3" description = "E2B SDK that give agents cloud environments" optional = true python-versions = "<4.0,>=3.9" -groups = ["main"] -markers = "extra == \"cloud-tool-sandbox\"" files = [ {file = "e2b-1.3.3-py3-none-any.whl", hash = "sha256:1f851345fd22abc1f16fb82197e88786909323edfd140a7a17b8a6ed4bd4ef03"}, {file = "e2b-1.3.3.tar.gz", hash = "sha256:b0afb2e3b8edaade44a50d40dbf8934cbd9e2bc3c1c214ce11dbfd9e1d6815b0"}, @@ -1594,8 +1493,6 @@ version = "1.2.0" description = "E2B Code Interpreter - Stateful code execution" optional = true python-versions = "<4.0,>=3.9" -groups = ["main"] -markers = "extra == \"cloud-tool-sandbox\"" files = [ {file = "e2b_code_interpreter-1.2.0-py3-none-any.whl", hash = "sha256:4f94ba29eceada30ec7d379f76b243d69b76da6b67324b986778743346446505"}, {file = "e2b_code_interpreter-1.2.0.tar.gz", hash = "sha256:9e02d043ab5986232a684018d718014bd5038b421b04a8726952094ef0387e78"}, @@ -1612,7 +1509,6 @@ version = "2.2.0" description = "A robust email address syntax and deliverability validation library." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"}, {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"}, @@ -1628,7 +1524,6 @@ version = "0.2.2" description = "Like `typing._eval_type`, but lets older Python versions use newer typing features." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "eval_type_backport-0.2.2-py3-none-any.whl", hash = "sha256:cb6ad7c393517f476f96d456d0412ea80f0a8cf96f6892834cd9340149111b0a"}, {file = "eval_type_backport-0.2.2.tar.gz", hash = "sha256:f0576b4cf01ebb5bd358d02314d31846af5e07678387486e2c798af0e7d849c1"}, @@ -1643,8 +1538,6 @@ version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" -groups = ["main", "dev", "dev,tests"] -markers = "python_version == \"3.10\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -1659,15 +1552,13 @@ version = "2.2.0" description = "Get the currently executing AST node of a frame, and other information" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa"}, {file = "executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] -tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] [[package]] name = "faker" @@ -1675,7 +1566,6 @@ version = "36.2.3" description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "Faker-36.2.3-py3-none-any.whl", hash = "sha256:7ca995d65ec08d013f3c1230da7f628cb2c169a77e89cd265d7a59f443f0febd"}, {file = "faker-36.2.3.tar.gz", hash = "sha256:1ed2d7a9c3a5657fc11a4298e8cf19f71d83740560d4ed0895b30399d482d538"}, @@ -1690,7 +1580,6 @@ version = "0.115.12" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d"}, {file = "fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681"}, @@ -1711,8 +1600,6 @@ version = "3.18.0" description = "A platform independent file lock." optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"}, {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"}, @@ -1721,7 +1608,7 @@ files = [ [package.extras] docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] -typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] +typing = ["typing-extensions (>=4.12.2)"] [[package]] name = "filetype" @@ -1729,7 +1616,6 @@ version = "1.2.0" description = "Infer file type and MIME type of any file/buffer. No external dependencies." optional = false python-versions = "*" -groups = ["main"] files = [ {file = "filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25"}, {file = "filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb"}, @@ -1741,8 +1627,6 @@ version = "2.8.0" description = "Python SDK for Firecrawl API" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"external-tools\"" files = [ {file = "firecrawl_py-2.8.0-py3-none-any.whl", hash = "sha256:f2e148086aa1ca42f603a56009577b4f66a2c23893eaa71f7c9c0082b4fdcf60"}, {file = "firecrawl_py-2.8.0.tar.gz", hash = "sha256:657795b6ddd63f0bd38b38bf0571187e0a66becda23d97c032801895257403c9"}, @@ -1762,8 +1646,6 @@ version = "3.1.0" description = "A simple framework for building complex web applications." optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136"}, {file = "flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac"}, @@ -1786,8 +1668,6 @@ version = "5.0.1" description = "A Flask extension simplifying CORS support" optional = true python-versions = "<4.0,>=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "flask_cors-5.0.1-py3-none-any.whl", hash = "sha256:fa5cb364ead54bbf401a26dbf03030c6b18fb2fcaf70408096a572b409586b0c"}, {file = "flask_cors-5.0.1.tar.gz", hash = "sha256:6ccb38d16d6b72bbc156c1c3f192bc435bfcc3c2bc864b2df1eb9b2d97b2403c"}, @@ -1803,8 +1683,6 @@ version = "0.6.3" description = "User authentication and session management for Flask." optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "Flask-Login-0.6.3.tar.gz", hash = "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333"}, {file = "Flask_Login-0.6.3-py3-none-any.whl", hash = "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d"}, @@ -1820,7 +1698,6 @@ version = "4.57.0" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "fonttools-4.57.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:babe8d1eb059a53e560e7bf29f8e8f4accc8b6cfb9b5fd10e485bde77e71ef41"}, {file = "fonttools-4.57.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:81aa97669cd726349eb7bd43ca540cf418b279ee3caba5e2e295fb4e8f841c02"}, @@ -1875,18 +1752,18 @@ files = [ ] [package.extras] -all = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "fs (>=2.2.0,<3)", "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)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] graphite = ["lz4 (>=1.7.4.2)"] -interpolatable = ["munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\""] +interpolatable = ["munkres", "pycairo", "scipy"] lxml = ["lxml (>=4.0)"] pathops = ["skia-pathops (>=0.5.0)"] plot = ["matplotlib"] repacker = ["uharfbuzz (>=0.23.0)"] symfont = ["sympy"] -type1 = ["xattr ; sys_platform == \"darwin\""] +type1 = ["xattr"] ufo = ["fs (>=2.2.0,<3)"] -unicode = ["unicodedata2 (>=15.1.0) ; python_version <= \"3.12\""] -woff = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "zopfli (>=0.1.4)"] +unicode = ["unicodedata2 (>=15.1.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] [[package]] name = "frozenlist" @@ -1894,7 +1771,6 @@ version = "1.5.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, @@ -1996,7 +1872,6 @@ version = "2025.3.2" description = "File-system specification" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "fsspec-2025.3.2-py3-none-any.whl", hash = "sha256:2daf8dc3d1dfa65b6aa37748d112773a7a08416f6c70d96b264c96476ecaf711"}, {file = "fsspec-2025.3.2.tar.gz", hash = "sha256:e52c77ef398680bbd6a98c0e628fbc469491282981209907bbc8aea76a04fdc6"}, @@ -2036,7 +1911,6 @@ version = "1.3.0" description = "GenSON is a powerful, user-friendly JSON Schema generator." optional = false python-versions = "*" -groups = ["main"] files = [ {file = "genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7"}, {file = "genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37"}, @@ -2048,8 +1922,6 @@ version = "24.11.1" description = "Coroutine-based network library" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "gevent-24.11.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:92fe5dfee4e671c74ffaa431fd7ffd0ebb4b339363d24d0d944de532409b935e"}, {file = "gevent-24.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7bfcfe08d038e1fa6de458891bca65c1ada6d145474274285822896a858c870"}, @@ -2098,11 +1970,11 @@ greenlet = {version = ">=3.1.1", markers = "platform_python_implementation == \" "zope.interface" = "*" [package.extras] -dnspython = ["dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\""] +dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"] -monitor = ["psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\""] -recommended = ["cffi (>=1.17.1) ; platform_python_implementation == \"CPython\"", "dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\"", "psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\""] -test = ["cffi (>=1.17.1) ; platform_python_implementation == \"CPython\"", "coverage (>=5.0) ; sys_platform != \"win32\"", "dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\"", "objgraph", "psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\"", "requests"] +monitor = ["psutil (>=5.7.0)"] +recommended = ["cffi (>=1.17.1)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] +test = ["cffi (>=1.17.1)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests"] [[package]] name = "geventhttpclient" @@ -2110,8 +1982,6 @@ version = "2.3.3" description = "HTTP client library for gevent" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "geventhttpclient-2.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d61cad95f80d5bd599e28933c187b3c4eeb0b2f6306e06fa0edcac5c9c4bac0a"}, {file = "geventhttpclient-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7a00e130577c0cf9749d1143e71543c50c7103321b7f37afc42782ad1d3c0ef7"}, @@ -2216,8 +2086,6 @@ version = "2.38.0" description = "Google Authentication Library" optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"google\"" files = [ {file = "google_auth-2.38.0-py2.py3-none-any.whl", hash = "sha256:e7dae6694313f434a2727bf2906f27ad259bae090d7aa896590d86feec3d9d4a"}, {file = "google_auth-2.38.0.tar.gz", hash = "sha256:8285113607d3b80a3f1543b75962447ba8a09fe85783432a784fdeef6ac094c4"}, @@ -2242,8 +2110,6 @@ version = "1.15.0" description = "GenAI Python SDK" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"google\"" files = [ {file = "google_genai-1.15.0-py3-none-any.whl", hash = "sha256:6d7f149cc735038b680722bed495004720514c234e2a445ab2f27967955071dd"}, {file = "google_genai-1.15.0.tar.gz", hash = "sha256:118bb26960d6343cd64f1aeb5c2b02144a36ad06716d0d1eb1fa3e0904db51f1"}, @@ -2264,7 +2130,6 @@ version = "1.69.2" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "googleapis_common_protos-1.69.2-py3-none-any.whl", hash = "sha256:0b30452ff9c7a27d80bfc5718954063e8ab53dd3697093d3bc99581f5fd24212"}, {file = "googleapis_common_protos-1.69.2.tar.gz", hash = "sha256:3e1b904a27a33c821b4b749fd31d334c0c9c30e6113023d495e48979a3dc9c5f"}, @@ -2282,8 +2147,6 @@ version = "2.3.2" description = "A Rust HTTP server for Python applications" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"experimental\" or extra == \"all\"" files = [ {file = "granian-2.3.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d5d1554aae36fc324c1aac6e4675f328f30b1218054d74aac28cb584ddcda1de"}, {file = "granian-2.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df2287786224a35edc5e7cb0ab6e075544938b473e3997d276b74275bb72a1c"}, @@ -2376,8 +2239,8 @@ watchfiles = {version = ">=1.0,<2.0", optional = true, markers = "extra == \"rel all = ["granian[pname,reload]"] pname = ["setproctitle (>=1.3.3,<1.4.0)"] reload = ["watchfiles (>=1.0,<2.0)"] -rloop = ["rloop (>=0.1,<1.0) ; sys_platform != \"win32\""] -uvloop = ["uvloop (>=0.18.0) ; platform_python_implementation == \"CPython\" and sys_platform != \"win32\""] +rloop = ["rloop (>=0.1,<1.0)"] +uvloop = ["uvloop (>=0.18.0)"] [[package]] name = "greenlet" @@ -2385,8 +2248,6 @@ version = "3.1.1" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" -groups = ["main"] -markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\" or (extra == \"dev\" or extra == \"desktop\" or extra == \"all\") and platform_python_implementation == \"CPython\"" files = [ {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, @@ -2473,7 +2334,6 @@ version = "1.7.2" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "griffe-1.7.2-py3-none-any.whl", hash = "sha256:1ed9c2e338a75741fc82083fe5a1bc89cb6142efe126194cc313e34ee6af5423"}, {file = "griffe-1.7.2.tar.gz", hash = "sha256:98d396d803fab3b680c2608f300872fd57019ed82f0672f5b5323a9ad18c540c"}, @@ -2488,7 +2348,6 @@ version = "1.71.0" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "grpcio-1.71.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:c200cb6f2393468142eb50ab19613229dcc7829b5ccee8b658a36005f6669fdd"}, {file = "grpcio-1.71.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:b2266862c5ad664a380fbbcdbdb8289d71464c42a8c29053820ee78ba0119e5d"}, @@ -2552,7 +2411,6 @@ version = "1.71.0" description = "Protobuf code generator for gRPC" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "grpcio_tools-1.71.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:f4ad7f0d756546902597053d70b3af2606fbd70d7972876cd75c1e241d22ae00"}, {file = "grpcio_tools-1.71.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:64bdb291df61cf570b5256777ad5fe2b1db6d67bc46e55dc56a0a862722ae329"}, @@ -2618,7 +2476,6 @@ version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -2630,7 +2487,6 @@ version = "2020.1.16" description = "Turn HTML into equivalent Markdown-structured text." optional = false python-versions = ">=3.5" -groups = ["main"] files = [ {file = "html2text-2020.1.16-py3-none-any.whl", hash = "sha256:c7c629882da0cf377d66f073329ccf34a12ed2adf0169b9285ae4e63ef54c82b"}, {file = "html2text-2020.1.16.tar.gz", hash = "sha256:e296318e16b059ddb97f7a8a1d6a5c1d7af4544049a01e261731d2d5cc277bbb"}, @@ -2642,7 +2498,6 @@ version = "1.0.7" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, @@ -2664,7 +2519,6 @@ version = "0.28.1" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, @@ -2677,7 +2531,7 @@ httpcore = "==1.*" idna = "*" [package.extras] -brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +brotli = ["brotli", "brotlicffi"] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] @@ -2689,7 +2543,6 @@ version = "0.4.0" description = "Consume Server-Sent Event (SSE) messages with HTTPX." optional = false python-versions = ">=3.8" -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"}, @@ -2701,8 +2554,6 @@ version = "2.6.9" description = "File identification library for Python" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "identify-2.6.9-py2.py3-none-any.whl", hash = "sha256:c98b4322da415a8e5a70ff6e51fbc2d2932c015532d77e9f8537b4ba7813b150"}, {file = "identify-2.6.9.tar.gz", hash = "sha256:d40dfe3142a1421d8518e3d3985ef5ac42890683e32306ad614a29490abeb6bf"}, @@ -2717,7 +2568,6 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -2732,7 +2582,6 @@ version = "8.5.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, @@ -2742,12 +2591,12 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] 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)"] 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)"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -2756,7 +2605,6 @@ version = "5.6.2" description = "Correctly generate plurals, singular nouns, ordinals, indefinite articles; convert numbers to words" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "inflect-5.6.2-py3-none-any.whl", hash = "sha256:b45d91a4a28a4e617ff1821117439b06eaa86e2a4573154af0149e9be6687238"}, {file = "inflect-5.6.2.tar.gz", hash = "sha256:aadc7ed73928f5e014129794bbac03058cca35d0a973a5fc4eb45c7fa26005f9"}, @@ -2764,7 +2612,7 @@ files = [ [package.extras] docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] -testing = ["pygments", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\""] +testing = ["pygments", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [[package]] name = "inflection" @@ -2772,7 +2620,6 @@ version = "0.5.1" description = "A port of Ruby on Rails inflector to Python" optional = false python-versions = ">=3.5" -groups = ["main"] files = [ {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, @@ -2784,12 +2631,10 @@ version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.8" -groups = ["main", "dev", "dev,tests"] files = [ {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [[package]] name = "ipdb" @@ -2797,15 +2642,14 @@ version = "0.13.13" description = "IPython-enabled pdb" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["dev"] files = [ {file = "ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4"}, {file = "ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726"}, ] [package.dependencies] -decorator = {version = "*", markers = "python_version >= \"3.11\""} -ipython = {version = ">=7.31.1", markers = "python_version >= \"3.11\""} +decorator = {version = "*", markers = "python_version > \"3.6\""} +ipython = {version = ">=7.31.1", markers = "python_version > \"3.6\""} tomli = {version = "*", markers = "python_version > \"3.6\" and python_version < \"3.11\""} [[package]] @@ -2814,7 +2658,6 @@ version = "6.29.5" description = "IPython Kernel for Jupyter" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "ipykernel-6.29.5-py3-none-any.whl", hash = "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5"}, {file = "ipykernel-6.29.5.tar.gz", hash = "sha256:f093a22c4a40f8828f8e330a9c297cb93dcab13bd9678ded6de8e5cf81c56215"}, @@ -2848,12 +2691,10 @@ version = "8.35.0" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.10" -groups = ["main", "dev"] files = [ {file = "ipython-8.35.0-py3-none-any.whl", hash = "sha256:e6b7470468ba6f1f0a7b116bb688a3ece2f13e2f94138e508201fad677a788ba"}, {file = "ipython-8.35.0.tar.gz", hash = "sha256:d200b7d93c3f5883fc36ab9ce28a18249c7706e51347681f80a0aef9895f2520"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} @@ -2871,7 +2712,7 @@ typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} [package.extras] all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] black = ["black"] -doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli ; python_version < \"3.11\"", "typing_extensions"] +doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing_extensions"] kernel = ["ipykernel"] matplotlib = ["matplotlib"] nbconvert = ["nbconvert"] @@ -2888,7 +2729,6 @@ version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" -groups = ["main"] files = [ {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, @@ -2903,8 +2743,6 @@ version = "2.2.0" description = "Safely pass data to untrusted environments and back." optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, @@ -2916,12 +2754,10 @@ version = "0.19.2" description = "An autocompletion tool for Python that can be used for text editors." optional = false python-versions = ">=3.6" -groups = ["main", "dev"] files = [ {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.dependencies] parso = ">=0.8.4,<0.9.0" @@ -2937,7 +2773,6 @@ version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, @@ -2955,7 +2790,6 @@ version = "0.9.0" description = "Fast iterable JSON parser." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "jiter-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:816ec9b60fdfd1fec87da1d7ed46c66c44ffec37ab2ef7de5b147b2fce3fd5ad"}, {file = "jiter-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b1d3086f8a3ee0194ecf2008cf81286a5c3e540d977fa038ff23576c023c0ea"}, @@ -3041,8 +2875,6 @@ version = "1.0.1" description = "JSON Matching Expressions" optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, @@ -3054,7 +2886,6 @@ version = "1.4.2" description = "Lightweight pipelining with Python functions" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"}, {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, @@ -3066,8 +2897,6 @@ version = "1.33" description = "Apply JSON-Patches (RFC 6902)" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"}, {file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"}, @@ -3082,8 +2911,6 @@ version = "3.0.0" description = "Identify specific nodes in a JSON document (RFC 6901)" optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, @@ -3095,7 +2922,6 @@ version = "1.1.0" description = "jsonref is a library for automatic dereferencing of JSON Reference objects for Python." optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9"}, {file = "jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552"}, @@ -3107,7 +2933,6 @@ version = "4.23.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, @@ -3129,7 +2954,6 @@ version = "2024.10.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, @@ -3144,7 +2968,6 @@ version = "8.6.3" description = "Jupyter protocol implementation and client libraries" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f"}, {file = "jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419"}, @@ -3159,7 +2982,7 @@ traitlets = ">=5.3" [package.extras] docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko ; sys_platform == \"win32\"", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] [[package]] name = "jupyter-core" @@ -3167,7 +2990,6 @@ version = "5.7.2" description = "Jupyter core package. A base package on which Jupyter projects rely." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, @@ -3188,7 +3010,6 @@ version = "1.4.8" description = "A fast implementation of the Cassowary constraint solver" optional = false python-versions = ">=3.10" -groups = ["main"] files = [ {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db"}, {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b"}, @@ -3278,8 +3099,6 @@ version = "0.3.23" description = "Building applications with LLMs through composability" optional = true python-versions = "<4.0,>=3.9" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "langchain-0.3.23-py3-none-any.whl", hash = "sha256:084f05ee7e80b7c3f378ebadd7309f2a37868ce2906fa0ae64365a67843ade3d"}, {file = "langchain-0.3.23.tar.gz", hash = "sha256:d95004afe8abebb52d51d6026270248da3f4b53d93e9bf699f76005e0c83ad34"}, @@ -3320,8 +3139,6 @@ version = "0.3.21" description = "Community contributed LangChain integrations." optional = true python-versions = "<4.0,>=3.9" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "langchain_community-0.3.21-py3-none-any.whl", hash = "sha256:8cb9bbb7ef15e5eea776193528dd0e0e1299047146d0c78b6c696ae2dc62e81f"}, {file = "langchain_community-0.3.21.tar.gz", hash = "sha256:b87b9992cbeea7553ed93e3d39faf9893a8690318485f7dc861751c7878729f7"}, @@ -3347,8 +3164,6 @@ version = "0.3.51" description = "Building applications with LLMs through composability" optional = true python-versions = "<4.0,>=3.9" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "langchain_core-0.3.51-py3-none-any.whl", hash = "sha256:4bd71e8acd45362aa428953f2a91d8162318014544a2216e4b769463caf68e13"}, {file = "langchain_core-0.3.51.tar.gz", hash = "sha256:db76b9cc331411602cb40ba0469a161febe7a0663fbcaddbc9056046ac2d22f4"}, @@ -3372,8 +3187,6 @@ version = "0.3.8" description = "LangChain text splitting utilities" optional = true python-versions = "<4.0,>=3.9" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "langchain_text_splitters-0.3.8-py3-none-any.whl", hash = "sha256:e75cc0f4ae58dcf07d9f18776400cf8ade27fadd4ff6d264df6278bb302f6f02"}, {file = "langchain_text_splitters-0.3.8.tar.gz", hash = "sha256:116d4b9f2a22dda357d0b79e30acf005c5518177971c66a9f1ab0edfdb0f912e"}, @@ -3388,8 +3201,6 @@ version = "0.3.28" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = true python-versions = "<4.0,>=3.9" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "langsmith-0.3.28-py3-none-any.whl", hash = "sha256:54ac8815514af52d9c801ad7970086693667e266bf1db90fc453c1759e8407cd"}, {file = "langsmith-0.3.28.tar.gz", hash = "sha256:4666595207131d7f8d83418e54dc86c05e28562e5c997633e7c33fc18f9aeb89"}, @@ -3419,7 +3230,6 @@ version = "0.1.183" description = "" optional = false python-versions = "<4.0,>=3.8" -groups = ["main"] files = [ {file = "letta_client-0.1.183-py3-none-any.whl", hash = "sha256:72d7d53ae3093a6d79fcefd24d079a135be806b9ff1f77b1501d7966a14222dd"}, {file = "letta_client-0.1.183.tar.gz", hash = "sha256:f89026e96ffd0c24fec9cedb5f76895fdb8a5414d440f4f66c7e427f9a37e110"}, @@ -3438,7 +3248,6 @@ version = "0.1.18" description = "" optional = false python-versions = "<4,>=3.8" -groups = ["main"] files = [ {file = "llama_cloud-0.1.18-py3-none-any.whl", hash = "sha256:5842722a0c3033afa930b4a50d43e6f1e77ff1dab12383a769dc51a15fb87c9b"}, {file = "llama_cloud-0.1.18.tar.gz", hash = "sha256:65cb88b1cb1a3a0e63e4438e8c8a2e6013dfdafbb4201d274c0459e5d04fb328"}, @@ -3455,7 +3264,6 @@ version = "0.6.10" description = "Tailored SDK clients for LlamaCloud services." optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_cloud_services-0.6.10-py3-none-any.whl", hash = "sha256:61d13ace89c6090274c118dfdb2a8d5d098ef39a4c0a58907cc255bc4c41a1ac"}, {file = "llama_cloud_services-0.6.10.tar.gz", hash = "sha256:86efc70546963ca0893da46effdcdd16d6c787b5412cbf26de17ce5750e8050f"}, @@ -3475,7 +3283,6 @@ version = "0.12.29" description = "Interface between LLMs and your data" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index-0.12.29-py3-none-any.whl", hash = "sha256:fc581374417a1ce787fc8580247dc3d36ef3c670a18ee77282662c17b72e380e"}, {file = "llama_index-0.12.29.tar.gz", hash = "sha256:2a07e1509550638964daab7ddd8f7c6a282a72c17bab60930f5a1059dd3caeac"}, @@ -3501,7 +3308,6 @@ version = "0.4.6" description = "llama-index agent openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_agent_openai-0.4.6-py3-none-any.whl", hash = "sha256:4103e479c874cb3426aa59a13f91b6e2dc6b350c51457966631f8bdaf9a6a8e8"}, {file = "llama_index_agent_openai-0.4.6.tar.gz", hash = "sha256:4f66c1731836ab66c4b441255a95f33a51743e4993b8aa9daf430cb31aa7d48e"}, @@ -3518,7 +3324,6 @@ version = "0.4.1" description = "llama-index cli" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_cli-0.4.1-py3-none-any.whl", hash = "sha256:6dfc931aea5b90c256e476b48dfac76f48fb2308fdf656bb02ee1e4f2cab8b06"}, {file = "llama_index_cli-0.4.1.tar.gz", hash = "sha256:3f97f1f8f5f401dfb5b6bc7170717c176dcd981538017430073ef12ffdcbddfa"}, @@ -3535,7 +3340,6 @@ version = "0.12.30" description = "Interface between LLMs and your data" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_core-0.12.30-py3-none-any.whl", hash = "sha256:6e0b33158de52108debe66528adb84adb626aed5c6c7762874fb7a799d8c48a9"}, {file = "llama_index_core-0.12.30.tar.gz", hash = "sha256:dfbed9cdba18358750ad3895cd97fa1ea0dd0b856bbd623a4904ac61641e2765"}, @@ -3572,7 +3376,6 @@ version = "0.3.1" description = "llama-index embeddings openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_embeddings_openai-0.3.1-py3-none-any.whl", hash = "sha256:f15a3d13da9b6b21b8bd51d337197879a453d1605e625a1c6d45e741756c0290"}, {file = "llama_index_embeddings_openai-0.3.1.tar.gz", hash = "sha256:1368aad3ce24cbaed23d5ad251343cef1eb7b4a06d6563d6606d59cb347fef20"}, @@ -3588,7 +3391,6 @@ version = "0.6.11" description = "llama-index indices llama-cloud integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_indices_managed_llama_cloud-0.6.11-py3-none-any.whl", hash = "sha256:64e82e2ac178cd3721b76c0817edd57e05a3bd877c412b4148d3abbdeea62d59"}, {file = "llama_index_indices_managed_llama_cloud-0.6.11.tar.gz", hash = "sha256:925532f760cd2ebb2594828da311adac3d54cd2cae3dff2908491eebb2b8bd0f"}, @@ -3604,7 +3406,6 @@ version = "0.3.33" description = "llama-index llms openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_llms_openai-0.3.33-py3-none-any.whl", hash = "sha256:d76eab8658d905514b2fa3de7702e47aecd9dbd70d34a0faac39e1ed50c116a5"}, {file = "llama_index_llms_openai-0.3.33.tar.gz", hash = "sha256:112942ca221c2233ed2cf995fdaf0865f2b7f9295d9b01c5a7fcaef373c1ac87"}, @@ -3620,7 +3421,6 @@ version = "0.4.3" description = "llama-index multi-modal-llms openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_multi_modal_llms_openai-0.4.3-py3-none-any.whl", hash = "sha256:1ceb42716472ac8bd5130afa29b793869d367946aedd02e48a3b03184e443ad1"}, {file = "llama_index_multi_modal_llms_openai-0.4.3.tar.gz", hash = "sha256:5e6ca54069d3d18c2f5f7ca34f3720fba1d1b9126482ad38feb0c858f4feb63b"}, @@ -3636,7 +3436,6 @@ version = "0.3.1" description = "llama-index program openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_program_openai-0.3.1-py3-none-any.whl", hash = "sha256:93646937395dc5318fd095153d2f91bd632b25215d013d14a87c088887d205f9"}, {file = "llama_index_program_openai-0.3.1.tar.gz", hash = "sha256:6039a6cdbff62c6388c07e82a157fe2edd3bbef0c5adf292ad8546bf4ec75b82"}, @@ -3653,7 +3452,6 @@ version = "0.3.0" description = "llama-index question_gen openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_question_gen_openai-0.3.0-py3-none-any.whl", hash = "sha256:9b60ec114273a63b50349948666e5744a8f58acb645824e07c979041e8fec598"}, {file = "llama_index_question_gen_openai-0.3.0.tar.gz", hash = "sha256:efd3b468232808e9d3474670aaeab00e41b90f75f52d0c9bfbf11207e0963d62"}, @@ -3670,7 +3468,6 @@ version = "0.4.7" description = "llama-index readers file integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_readers_file-0.4.7-py3-none-any.whl", hash = "sha256:dff86f9b6079bddad37896f26756b508be5a052096ced34c9917b76646cf0c02"}, {file = "llama_index_readers_file-0.4.7.tar.gz", hash = "sha256:89a765238a106af0f1e31ab8d4cb3ee33ac897080285bcce59101b420265ebd1"}, @@ -3692,7 +3489,6 @@ version = "0.4.0" description = "llama-index readers llama-parse integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_readers_llama_parse-0.4.0-py3-none-any.whl", hash = "sha256:574e48386f28d2c86c3f961ca4a4906910312f3400dd0c53014465bfbc6b32bf"}, {file = "llama_index_readers_llama_parse-0.4.0.tar.gz", hash = "sha256:e99ec56f4f8546d7fda1a7c1ae26162fb9acb7ebcac343b5abdb4234b4644e0f"}, @@ -3708,7 +3504,6 @@ version = "0.6.9" description = "Parse files into RAG-Optimized formats." optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_parse-0.6.9-py3-none-any.whl", hash = "sha256:fb40d3ab8d6c03bdcde66cc897d72412242f6f7a3a0b991907038fb47e554d8e"}, {file = "llama_parse-0.6.9.tar.gz", hash = "sha256:37016bee67be8c9377ddf00afae0054903e934e54db7406ba3a422b7e9ad8259"}, @@ -3723,8 +3518,6 @@ version = "2.37.11" description = "Developer-friendly load testing framework" optional = true python-versions = ">=3.10" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "locust-2.37.11-py3-none-any.whl", hash = "sha256:b826f95fbfd5d9a32df6ab1b74672b88e65bbc33ec99fdc10af98079952ad517"}, {file = "locust-2.37.11.tar.gz", hash = "sha256:89c79bc599aa57160bd41dd3876e35d8b9dee5abded78e35008d01fd8f1640ed"}, @@ -3743,8 +3536,8 @@ psutil = ">=5.9.1" pywin32 = {version = "*", markers = "sys_platform == \"win32\""} pyzmq = ">=25.0.0" requests = [ - {version = ">=2.32.2", markers = "python_version > \"3.11\""}, {version = ">=2.26.0", markers = "python_version <= \"3.11\""}, + {version = ">=2.32.2", markers = "python_version > \"3.11\""}, ] setuptools = ">=70.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -3778,7 +3571,6 @@ version = "1.3.10" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59"}, {file = "mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28"}, @@ -3798,7 +3590,6 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -3823,7 +3614,6 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -3894,7 +3684,6 @@ version = "3.26.1" 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"}, @@ -3914,7 +3703,6 @@ version = "1.4.2" description = "SQLAlchemy integration with the marshmallow (de)serialization library" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "marshmallow_sqlalchemy-1.4.2-py3-none-any.whl", hash = "sha256:65aee301c4601e76a2fdb02764a65c18913afba2a3506a326c625d13ab405b40"}, {file = "marshmallow_sqlalchemy-1.4.2.tar.gz", hash = "sha256:6410304bf98ec26ea35f3f9d3cee82e51fd093c434612add32a0bdcdb5668f7c"}, @@ -3926,7 +3714,7 @@ SQLAlchemy = ">=1.4.40,<3.0" [package.extras] dev = ["marshmallow-sqlalchemy[tests]", "pre-commit (>=3.5,<5.0)", "tox"] -docs = ["furo (==2024.8.6)", "sphinx (==8.2.3) ; python_version >= \"3.11\"", "sphinx-copybutton (==0.5.2)", "sphinx-design (==0.6.1)", "sphinx-issues (==5.0.0)", "sphinxext-opengraph (==0.10.0)"] +docs = ["furo (==2024.8.6)", "sphinx (==8.2.3)", "sphinx-copybutton (==0.5.2)", "sphinx-design (==0.6.1)", "sphinx-issues (==5.0.0)", "sphinxext-opengraph (==0.10.0)"] tests = ["pytest (<9)", "pytest-lazy-fixtures"] [[package]] @@ -3935,7 +3723,6 @@ version = "3.10.1" description = "Python plotting package" optional = false python-versions = ">=3.10" -groups = ["main"] files = [ {file = "matplotlib-3.10.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ff2ae14910be903f4a24afdbb6d7d3a6c44da210fc7d42790b87aeac92238a16"}, {file = "matplotlib-3.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0721a3fd3d5756ed593220a8b86808a36c5031fce489adb5b31ee6dbb47dd5b2"}, @@ -3993,12 +3780,10 @@ version = "0.1.7" description = "Inline Matplotlib backend for Jupyter" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.dependencies] traitlets = "*" @@ -4009,7 +3794,6 @@ version = "1.9.4" description = "Model Context Protocol SDK" optional = false python-versions = ">=3.10" -groups = ["main"] files = [ {file = "mcp-1.9.4-py3-none-any.whl", hash = "sha256:7fcf36b62936adb8e63f89346bccca1268eeca9bf6dfb562ee10b1dfbda9dac0"}, {file = "mcp-1.9.4.tar.gz", hash = "sha256:cfb0bcd1a9535b42edaef89947b9e18a8feb49362e1cc059d6e7fc636f2cb09f"}, @@ -4039,7 +3823,6 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -4051,7 +3834,6 @@ version = "1.8.1" description = "Python Client SDK for the Mistral AI API." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "mistralai-1.8.1-py3-none-any.whl", hash = "sha256:badfc7e6832d894b3e9071d92ad621212b7cccd7df622c6cacdb525162ae338f"}, {file = "mistralai-1.8.1.tar.gz", hash = "sha256:b967ca443726b71ec45632cb33825ee2e55239a652e73c2bda11f7cc683bf6e5"}, @@ -4065,7 +3847,7 @@ python-dateutil = ">=2.8.2" typing-inspection = ">=0.4.0" [package.extras] -agents = ["authlib (>=1.5.2,<2.0)", "griffe (>=1.7.3,<2.0)", "mcp (>=1.0,<2.0) ; python_version >= \"3.10\""] +agents = ["authlib (>=1.5.2,<2.0)", "griffe (>=1.7.3,<2.0)", "mcp (>=1.0,<2.0)"] gcp = ["google-auth (>=2.27.0)", "requests (>=2.32.3)"] [[package]] @@ -4074,8 +3856,6 @@ version = "1.1.0" description = "MessagePack serializer" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"}, {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"}, @@ -4149,7 +3929,6 @@ version = "6.4.2" description = "multidict implementation" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "multidict-6.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:48f775443154d99e1b1c727ea20137ddc1e4b29448a9b24875b2a348cc143b85"}, {file = "multidict-6.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3d17d8b2d4643d4f59629758b0dd229cda61e2319f81b470aa4a99717081ba0c"}, @@ -4266,7 +4045,6 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" -groups = ["main", "dev"] files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -4278,7 +4056,6 @@ version = "1.6.0" description = "Patch asyncio to allow nested event loops" optional = false python-versions = ">=3.5" -groups = ["main", "dev"] files = [ {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, @@ -4290,7 +4067,6 @@ version = "3.4.2" description = "Python package for creating and manipulating graphs and networks" optional = false python-versions = ">=3.10" -groups = ["main"] files = [ {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, @@ -4310,7 +4086,6 @@ version = "3.9.1" description = "Natural Language Toolkit" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "nltk-3.9.1-py3-none-any.whl", hash = "sha256:4fa26829c5b00715afe3061398a8989dc643b92ce7dd93fb4585a70930d168a1"}, {file = "nltk-3.9.1.tar.gz", hash = "sha256:87d127bd3de4bd89a4f81265e5fa59cb1b199b27440175370f7417d2bc7ae868"}, @@ -4336,8 +4111,6 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = true python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\" or extra == \"desktop\"" files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -4349,7 +4122,6 @@ version = "1.26.4" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, @@ -4395,7 +4167,6 @@ version = "1.72.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "openai-1.72.0-py3-none-any.whl", hash = "sha256:34f5496ba5c8cb06c592831d69e847e2d164526a2fb92afdc3b5cf2891c328c3"}, {file = "openai-1.72.0.tar.gz", hash = "sha256:f51de971448905cc90ed5175a5b19e92fd94e31f68cde4025762f9f5257150db"}, @@ -4422,7 +4193,6 @@ version = "1.30.0" description = "OpenTelemetry Python API" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_api-1.30.0-py3-none-any.whl", hash = "sha256:d5f5284890d73fdf47f843dda3210edf37a38d66f44f2b5aedc1e89ed455dc09"}, {file = "opentelemetry_api-1.30.0.tar.gz", hash = "sha256:375893400c1435bf623f7dfb3bcd44825fe6b56c34d0667c542ea8257b1a1240"}, @@ -4438,7 +4208,6 @@ version = "1.30.0" description = "OpenTelemetry Collector Exporters" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_exporter_otlp-1.30.0-py3-none-any.whl", hash = "sha256:44e11054ec571ccfed73a83c6429dee5d334d061d0e0572e3160d6de97156dbc"}, {file = "opentelemetry_exporter_otlp-1.30.0.tar.gz", hash = "sha256:da7769f95cd5be5b09dd4188ac153a33709eda652217f2d10aed6518c8e60f0d"}, @@ -4454,7 +4223,6 @@ version = "1.30.0" description = "OpenTelemetry Protobuf encoding" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_exporter_otlp_proto_common-1.30.0-py3-none-any.whl", hash = "sha256:5468007c81aa9c44dc961ab2cf368a29d3475977df83b4e30aeed42aa7bc3b38"}, {file = "opentelemetry_exporter_otlp_proto_common-1.30.0.tar.gz", hash = "sha256:ddbfbf797e518411857d0ca062c957080279320d6235a279f7b64ced73c13897"}, @@ -4469,7 +4237,6 @@ version = "1.30.0" description = "OpenTelemetry Collector Protobuf over gRPC Exporter" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_exporter_otlp_proto_grpc-1.30.0-py3-none-any.whl", hash = "sha256:2906bcae3d80acc54fd1ffcb9e44d324e8631058b502ebe4643ca71d1ff30830"}, {file = "opentelemetry_exporter_otlp_proto_grpc-1.30.0.tar.gz", hash = "sha256:d0f10f0b9b9a383b7d04a144d01cb280e70362cccc613987e234183fd1f01177"}, @@ -4490,7 +4257,6 @@ version = "1.30.0" description = "OpenTelemetry Collector Protobuf over HTTP Exporter" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_exporter_otlp_proto_http-1.30.0-py3-none-any.whl", hash = "sha256:9578e790e579931c5ffd50f1e6975cbdefb6a0a0a5dea127a6ae87df10e0a589"}, {file = "opentelemetry_exporter_otlp_proto_http-1.30.0.tar.gz", hash = "sha256:c3ae75d4181b1e34a60662a6814d0b94dd33b628bee5588a878bed92cee6abdc"}, @@ -4511,7 +4277,6 @@ version = "0.51b0" description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_instrumentation-0.51b0-py3-none-any.whl", hash = "sha256:c6de8bd26b75ec8b0e54dff59e198946e29de6a10ec65488c357d4b34aa5bdcf"}, {file = "opentelemetry_instrumentation-0.51b0.tar.gz", hash = "sha256:4ca266875e02f3988536982467f7ef8c32a38b8895490ddce9ad9604649424fa"}, @@ -4529,7 +4294,6 @@ version = "0.51b0" description = "OpenTelemetry requests instrumentation" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_instrumentation_requests-0.51b0-py3-none-any.whl", hash = "sha256:0723aaafaeb2a825723f31c0bf644f9642377046063d1a52fc86571ced87feac"}, {file = "opentelemetry_instrumentation_requests-0.51b0.tar.gz", hash = "sha256:e7f4bd3ffcab6ebcce8a1c652af218e050354c8e7cac2c34814292d4de75167a"}, @@ -4550,7 +4314,6 @@ version = "1.30.0" description = "OpenTelemetry Python Proto" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_proto-1.30.0-py3-none-any.whl", hash = "sha256:c6290958ff3ddacc826ca5abbeb377a31c2334387352a259ba0df37c243adc11"}, {file = "opentelemetry_proto-1.30.0.tar.gz", hash = "sha256:afe5c9c15e8b68d7c469596e5b32e8fc085eb9febdd6fb4e20924a93a0389179"}, @@ -4565,7 +4328,6 @@ version = "1.30.0" description = "OpenTelemetry Python SDK" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_sdk-1.30.0-py3-none-any.whl", hash = "sha256:14fe7afc090caad881addb6926cec967129bd9260c4d33ae6a217359f6b61091"}, {file = "opentelemetry_sdk-1.30.0.tar.gz", hash = "sha256:c9287a9e4a7614b9946e933a67168450b9ab35f08797eb9bc77d998fa480fa18"}, @@ -4582,7 +4344,6 @@ version = "0.51b0" description = "OpenTelemetry Semantic Conventions" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_semantic_conventions-0.51b0-py3-none-any.whl", hash = "sha256:fdc777359418e8d06c86012c3dc92c88a6453ba662e941593adb062e48c2eeae"}, {file = "opentelemetry_semantic_conventions-0.51b0.tar.gz", hash = "sha256:3fabf47f35d1fd9aebcdca7e6802d86bd5ebc3bc3408b7e3248dde6e87a18c47"}, @@ -4598,7 +4359,6 @@ version = "0.51b0" description = "Web util for OpenTelemetry" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_util_http-0.51b0-py3-none-any.whl", hash = "sha256:0561d7a6e9c422b9ef9ae6e77eafcfcd32a2ab689f5e801475cbb67f189efa20"}, {file = "opentelemetry_util_http-0.51b0.tar.gz", hash = "sha256:05edd19ca1cc3be3968b1e502fd94816901a365adbeaab6b6ddb974384d3a0b9"}, @@ -4610,8 +4370,6 @@ version = "3.10.16" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "(extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\") and platform_python_implementation != \"PyPy\"" files = [ {file = "orjson-3.10.16-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4cb473b8e79154fa778fb56d2d73763d977be3dcc140587e07dbc545bbfc38f8"}, {file = "orjson-3.10.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:622a8e85eeec1948690409a19ca1c7d9fd8ff116f4861d261e6ae2094fe59a00"}, @@ -4689,7 +4447,6 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["main", "dev", "dev,tests"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -4701,7 +4458,6 @@ version = "2.2.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, @@ -4749,9 +4505,9 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.23.2", markers = "python_version == \"3.11\""}, {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -4788,7 +4544,6 @@ version = "3.5.1" description = "SSH2 protocol library" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "paramiko-3.5.1-py3-none-any.whl", hash = "sha256:43b9a0501fc2b5e70680388d9346cf252cfb7d00b0667c39e80eb43a408b8f61"}, {file = "paramiko-3.5.1.tar.gz", hash = "sha256:b2c665bc45b2b215bd7d7f039901b14b067da00f3a11e6640995fd58f2664822"}, @@ -4800,8 +4555,8 @@ cryptography = ">=3.3" pynacl = ">=1.5" [package.extras] -all = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system == \"Windows\""] -gssapi = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system == \"Windows\""] +all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] +gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] invoke = ["invoke (>=2.0)"] [[package]] @@ -4810,12 +4565,10 @@ version = "0.8.4" description = "A Python Parser" optional = false python-versions = ">=3.6" -groups = ["main", "dev"] files = [ {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] @@ -4827,7 +4580,6 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -4839,7 +4591,6 @@ version = "3.2.3" description = "pathvalidate is a Python library to sanitize/validate a string such as filenames/file-paths/etc." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pathvalidate-3.2.3-py3-none-any.whl", hash = "sha256:5eaf0562e345d4b6d0c0239d0f690c3bd84d2a9a3c4c73b99ea667401b27bee1"}, {file = "pathvalidate-3.2.3.tar.gz", hash = "sha256:59b5b9278e30382d6d213497623043ebe63f10e29055be4419a9c04c721739cb"}, @@ -4856,12 +4607,10 @@ version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." optional = false python-versions = "*" -groups = ["main", "dev"] files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\"", dev = "sys_platform != \"win32\" and sys_platform != \"emscripten\" or extra == \"dev\" or extra == \"all\""} [package.dependencies] ptyprocess = ">=0.5" @@ -4872,8 +4621,6 @@ version = "1.31.2" description = "PostgreSQL interface library" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"postgres\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6"}, {file = "pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876"}, @@ -4889,8 +4636,6 @@ version = "0.2.5" description = "pgvector support for Python" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"postgres\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "pgvector-0.2.5-py2.py3-none-any.whl", hash = "sha256:5e5e93ec4d3c45ab1fa388729d56c602f6966296e19deee8878928c6d567e41b"}, ] @@ -4904,7 +4649,6 @@ version = "11.1.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8"}, {file = "pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192"}, @@ -4984,16 +4728,69 @@ docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline fpx = ["olefile"] mic = ["olefile"] tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "trove-classifiers (>=2024.10.12)"] -typing = ["typing-extensions ; python_version < \"3.10\""] +typing = ["typing-extensions"] xmp = ["defusedxml"] +[[package]] +name = "pinecone" +version = "7.3.0" +description = "Pinecone client and SDK" +optional = false +python-versions = "<4.0,>=3.9" +files = [ + {file = "pinecone-7.3.0-py3-none-any.whl", hash = "sha256:315b8fef20320bef723ecbb695dec0aafa75d8434d86e01e5a0e85933e1009a8"}, + {file = "pinecone-7.3.0.tar.gz", hash = "sha256:307edc155621d487c20dc71b76c3ad5d6f799569ba42064190d03917954f9a7b"}, +] + +[package.dependencies] +aiohttp = {version = ">=3.9.0", optional = true, markers = "extra == \"asyncio\""} +aiohttp-retry = {version = ">=2.9.1,<3.0.0", optional = true, markers = "extra == \"asyncio\""} +certifi = ">=2019.11.17" +pinecone-plugin-assistant = ">=1.6.0,<2.0.0" +pinecone-plugin-interface = ">=0.0.7,<0.0.8" +python-dateutil = ">=2.5.3" +typing-extensions = ">=3.7.4" +urllib3 = [ + {version = ">=1.26.0", markers = "python_version >= \"3.8\" and python_version < \"3.12\""}, + {version = ">=1.26.5", markers = "python_version >= \"3.12\" and python_version < \"4.0\""}, +] + +[package.extras] +asyncio = ["aiohttp (>=3.9.0)", "aiohttp-retry (>=2.9.1,<3.0.0)"] +grpc = ["googleapis-common-protos (>=1.66.0)", "grpcio (>=1.44.0)", "grpcio (>=1.59.0)", "grpcio (>=1.68.0)", "lz4 (>=3.1.3)", "protobuf (>=5.29,<6.0)", "protoc-gen-openapiv2 (>=0.0.1,<0.0.2)"] + +[[package]] +name = "pinecone-plugin-assistant" +version = "1.7.0" +description = "Assistant plugin for Pinecone SDK" +optional = false +python-versions = "<4.0,>=3.9" +files = [ + {file = "pinecone_plugin_assistant-1.7.0-py3-none-any.whl", hash = "sha256:864cb8e7930588e6c2da97c6d44f0240969195f43fa303c5db76cbc12bf903a5"}, + {file = "pinecone_plugin_assistant-1.7.0.tar.gz", hash = "sha256:e26e3ba10a8b71c3da0d777cff407668022e82963c4913d0ffeb6c552721e482"}, +] + +[package.dependencies] +packaging = ">=24.2,<25.0" +requests = ">=2.32.3,<3.0.0" + +[[package]] +name = "pinecone-plugin-interface" +version = "0.0.7" +description = "Plugin interface for the Pinecone python client" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "pinecone_plugin_interface-0.0.7-py3-none-any.whl", hash = "sha256:875857ad9c9fc8bbc074dbe780d187a2afd21f5bfe0f3b08601924a61ef1bba8"}, + {file = "pinecone_plugin_interface-0.0.7.tar.gz", hash = "sha256:b8e6675e41847333aa13923cc44daa3f85676d7157324682dc1640588a982846"}, +] + [[package]] name = "platformdirs" version = "4.3.7" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.9" -groups = ["main", "dev"] files = [ {file = "platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94"}, {file = "platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351"}, @@ -5010,12 +4807,10 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" -groups = ["main", "dev", "dev,tests"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] dev = ["pre-commit", "tox"] @@ -5027,8 +4822,6 @@ version = "3.8.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, @@ -5047,7 +4840,6 @@ version = "3.16.0" description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "prettytable-3.16.0-py3-none-any.whl", hash = "sha256:b5eccfabb82222f5aa46b798ff02a8452cf530a352c31bddfa29be41242863aa"}, {file = "prettytable-3.16.0.tar.gz", hash = "sha256:3c64b31719d961bf69c9a7e03d0c1e477320906a98da63952bc6698d6164ff57"}, @@ -5065,7 +4857,6 @@ version = "3.0.50" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.8.0" -groups = ["main", "dev"] files = [ {file = "prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198"}, {file = "prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab"}, @@ -5080,7 +4871,6 @@ version = "0.3.1" description = "Accelerated property cache" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "propcache-0.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f27785888d2fdd918bc36de8b8739f2d6c791399552333721b58193f68ea3e98"}, {file = "propcache-0.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4e89cde74154c7b5957f87a355bb9c8ec929c167b59c83d90654ea36aeb6180"}, @@ -5188,7 +4978,6 @@ version = "5.29.4" description = "" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "protobuf-5.29.4-cp310-abi3-win32.whl", hash = "sha256:13eb236f8eb9ec34e63fc8b1d6efd2777d062fa6aaa68268fb67cf77f6839ad7"}, {file = "protobuf-5.29.4-cp310-abi3-win_amd64.whl", hash = "sha256:bcefcdf3976233f8a502d265eb65ea740c989bacc6c30a58290ed0e519eb4b8d"}, @@ -5209,7 +4998,6 @@ version = "7.0.0" description = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7." optional = false python-versions = ">=3.6" -groups = ["main", "dev"] files = [ {file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"}, {file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"}, @@ -5222,7 +5010,6 @@ files = [ {file = "psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553"}, {file = "psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456"}, ] -markers = {main = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\""} [package.extras] dev = ["abi3audit", "black (==24.10.0)", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest", "pytest-cov", "pytest-xdist", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"] @@ -5234,8 +5021,6 @@ version = "2.9.10" description = "psycopg2 - Python-PostgreSQL Database Adapter" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"postgres\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "psycopg2-2.9.10-cp310-cp310-win32.whl", hash = "sha256:5df2b672140f95adb453af93a7d669d7a7bf0a56bcd26f1502329166f4a61716"}, {file = "psycopg2-2.9.10-cp310-cp310-win_amd64.whl", hash = "sha256:c6f7b8561225f9e711a9c47087388a97fdc948211c10a4bccbf0ba68ab7b3b5a"}, @@ -5243,7 +5028,6 @@ files = [ {file = "psycopg2-2.9.10-cp311-cp311-win_amd64.whl", hash = "sha256:0435034157049f6846e95103bd8f5a668788dd913a7c30162ca9503fdf542cb4"}, {file = "psycopg2-2.9.10-cp312-cp312-win32.whl", hash = "sha256:65a63d7ab0e067e2cdb3cf266de39663203d38d6a8ed97f5ca0cb315c73fe067"}, {file = "psycopg2-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:4a579d6243da40a7b3182e0430493dbd55950c493d8c68f4eec0b302f6bbf20e"}, - {file = "psycopg2-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:91fd603a2155da8d0cfcdbf8ab24a2d54bca72795b90d2a3ed2b6da8d979dee2"}, {file = "psycopg2-2.9.10-cp39-cp39-win32.whl", hash = "sha256:9d5b3b94b79a844a986d029eee38998232451119ad653aea42bb9220a8c5066b"}, {file = "psycopg2-2.9.10-cp39-cp39-win_amd64.whl", hash = "sha256:88138c8dedcbfa96408023ea2b0c369eda40fe5d75002c0964c78f46f11fa442"}, {file = "psycopg2-2.9.10.tar.gz", hash = "sha256:12ec0b40b0273f95296233e8750441339298e6a572f7039da5b260e3c8b60e11"}, @@ -5255,8 +5039,6 @@ version = "2.9.10" description = "psycopg2 - Python-PostgreSQL Database Adapter" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"postgres\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f"}, @@ -5305,7 +5087,6 @@ files = [ {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567"}, - {file = "psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864"}, @@ -5334,12 +5115,10 @@ version = "0.7.0" description = "Run a subprocess in a pseudo terminal" optional = false python-versions = "*" -groups = ["main", "dev"] files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\"", dev = "sys_platform != \"win32\" and sys_platform != \"emscripten\" or extra == \"dev\" or extra == \"all\""} [[package]] name = "pure-eval" @@ -5347,12 +5126,10 @@ version = "0.2.3" description = "Safely evaluate AST nodes without side effects" optional = false python-versions = "*" -groups = ["main", "dev"] files = [ {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] tests = ["pytest"] @@ -5363,8 +5140,6 @@ version = "0.6.1" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"google\"" files = [ {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, @@ -5376,8 +5151,6 @@ version = "0.4.2" description = "A collection of ASN.1-based protocols modules" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"google\"" files = [ {file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"}, {file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"}, @@ -5392,12 +5165,10 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] -markers = {dev = "implementation_name == \"pypy\""} [[package]] name = "pydantic" @@ -5405,7 +5176,6 @@ version = "2.11.3" description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pydantic-2.11.3-py3-none-any.whl", hash = "sha256:a082753436a07f9ba1289c6ffa01cd93db3548776088aa917cc43b63f68fa60f"}, {file = "pydantic-2.11.3.tar.gz", hash = "sha256:7471657138c16adad9322fe3070c0116dd6c3ad8d649300e3cbdfe91f4db4ec3"}, @@ -5420,7 +5190,7 @@ typing-inspection = ">=0.4.0" [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] +timezone = ["tzdata"] [[package]] name = "pydantic-core" @@ -5428,7 +5198,6 @@ version = "2.33.1" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pydantic_core-2.33.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3077cfdb6125cc8dab61b155fdd714663e401f0e6883f9632118ec12cf42df26"}, {file = "pydantic_core-2.33.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8ffab8b2908d152e74862d276cf5017c81a2f3719f14e8e3e8d6b83fda863927"}, @@ -5540,7 +5309,6 @@ version = "2.8.1" description = "Settings management using Pydantic" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c"}, {file = "pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585"}, @@ -5561,8 +5329,6 @@ version = "3.3.2" description = "passive checker of Python programs" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a"}, {file = "pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b"}, @@ -5574,7 +5340,6 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -5589,7 +5354,6 @@ version = "3.8.0" description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, @@ -5601,7 +5365,6 @@ version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, @@ -5628,7 +5391,6 @@ version = "3.2.3" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf"}, {file = "pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be"}, @@ -5643,7 +5405,6 @@ version = "5.4.0" description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "pypdf-5.4.0-py3-none-any.whl", hash = "sha256:db994ab47cadc81057ea1591b90e5b543e2b7ef2d0e31ef41a9bfe763c119dab"}, {file = "pypdf-5.4.0.tar.gz", hash = "sha256:9af476a9dc30fcb137659b0dec747ea94aa954933c52cf02ee33e39a16fe9175"}, @@ -5666,7 +5427,6 @@ version = "1.9.0" description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310"}, ] @@ -5677,8 +5437,6 @@ version = "1.1.399" description = "Command line wrapper for pyright" optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "pyright-1.1.399-py3-none-any.whl", hash = "sha256:55f9a875ddf23c9698f24208c764465ffdfd38be6265f7faf9a176e1dc549f3b"}, {file = "pyright-1.1.399.tar.gz", hash = "sha256:439035d707a36c3d1b443aec980bc37053fbda88158eded24b8eedcf1c7b7a1b"}, @@ -5699,7 +5457,6 @@ version = "1.0.8" description = "Pusher websocket client for python, based on Erik Kulyk's PythonPusherClient" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "Pysher-1.0.8.tar.gz", hash = "sha256:7849c56032b208e49df67d7bd8d49029a69042ab0bb45b2ed59fa08f11ac5988"}, ] @@ -5714,12 +5471,10 @@ version = "8.3.5" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" -groups = ["main", "dev", "dev,tests"] files = [ {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} @@ -5738,8 +5493,6 @@ version = "0.24.0" description = "Pytest support for asyncio" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b"}, {file = "pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276"}, @@ -5758,7 +5511,6 @@ version = "1.5.0" description = "A pytest plugin to report test results as JSON files" optional = false python-versions = "*" -groups = ["dev,tests"] files = [ {file = "pytest-json-report-1.5.0.tar.gz", hash = "sha256:2dde3c647851a19b5f3700729e8310a6e66efb2077d674f27ddea3d34dc615de"}, {file = "pytest_json_report-1.5.0-py3-none-any.whl", hash = "sha256:9897b68c910b12a2e48dd849f9a284b2c79a732a8a9cb398452ddd23d3c8c325"}, @@ -5774,7 +5526,6 @@ version = "3.1.1" description = "pytest plugin for test session metadata" optional = false python-versions = ">=3.8" -groups = ["dev,tests"] files = [ {file = "pytest_metadata-3.1.1-py3-none-any.whl", hash = "sha256:c8e0844db684ee1c798cfa38908d20d67d0463ecb6137c72e91f418558dd5f4b"}, {file = "pytest_metadata-3.1.1.tar.gz", hash = "sha256:d2a29b0355fbc03f168aa96d41ff88b1a3b44a3b02acbe491801c98a048017c8"}, @@ -5792,7 +5543,6 @@ version = "3.14.0" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, @@ -5810,8 +5560,6 @@ version = "1.3.0" description = "pytest plugin to run your tests in a specific order" optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "pytest_order-1.3.0-py3-none-any.whl", hash = "sha256:2cd562a21380345dd8d5774aa5fd38b7849b6ee7397ca5f6999bbe6e89f07f6e"}, {file = "pytest_order-1.3.0.tar.gz", hash = "sha256:51608fec3d3ee9c0adaea94daa124a5c4c1d2bb99b00269f098f414307f23dde"}, @@ -5826,7 +5574,6 @@ version = "7.3.2" description = "Advanced Python dictionaries with dot notation access" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "python_box-7.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d136163294fd61a1554db7dd203f2e3035064798d30c17d67d948f0de5c572de"}, {file = "python_box-7.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d72e96547d8e2c2c333909826e9fae338d9a7e4cde07d5c6058cdd468432c0"}, @@ -5853,7 +5600,7 @@ msgpack = ["msgpack"] pyyaml = ["PyYAML"] ruamel-yaml = ["ruamel.yaml (>=0.17)"] toml = ["toml"] -tomli = ["tomli ; python_version < \"3.11\"", "tomli-w"] +tomli = ["tomli", "tomli-w"] yaml = ["ruamel.yaml (>=0.17)"] [[package]] @@ -5862,7 +5609,6 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main", "dev"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -5877,7 +5623,6 @@ version = "1.1.0" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d"}, {file = "python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5"}, @@ -5913,7 +5658,6 @@ version = "0.0.19" description = "A streaming multipart parser for Python" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "python_multipart-0.0.19-py3-none-any.whl", hash = "sha256:f8d5b0b9c618575bf9df01c684ded1d94a338839bdd8223838afacfb4bb2082d"}, {file = "python_multipart-0.0.19.tar.gz", hash = "sha256:905502ef39050557b7a6af411f454bc19526529ca46ae6831508438890ce12cc"}, @@ -5949,7 +5693,6 @@ version = "2023.4" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "pytz-2023.4-py2.py3-none-any.whl", hash = "sha256:f90ef520d95e7c46951105338d918664ebfd6f1d995bd7d153127ce90efafa6a"}, {file = "pytz-2023.4.tar.gz", hash = "sha256:31d4583c4ed539cd037956140d695e42c033a19e984bfce9964a3f7d59bc2b40"}, @@ -5961,7 +5704,6 @@ version = "310" description = "Python for Window Extensions" optional = false python-versions = "*" -groups = ["main", "dev"] files = [ {file = "pywin32-310-cp310-cp310-win32.whl", hash = "sha256:6dd97011efc8bf51d6793a82292419eba2c71cf8e7250cfac03bba284454abc1"}, {file = "pywin32-310-cp310-cp310-win_amd64.whl", hash = "sha256:c3e78706e4229b915a0821941a84e7ef420bf2b77e08c9dae3c76fd03fd2ae3d"}, @@ -5980,7 +5722,6 @@ files = [ {file = "pywin32-310-cp39-cp39-win32.whl", hash = "sha256:851c8d927af0d879221e616ae1f66145253537bbdd321a77e8ef701b443a9a1a"}, {file = "pywin32-310-cp39-cp39-win_amd64.whl", hash = "sha256:96867217335559ac619f00ad70e513c0fcf84b8a3af9fc2bba3b59b97da70475"}, ] -markers = {main = "(extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\" or extra == \"dev\") and sys_platform == \"win32\"", dev = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} [[package]] name = "pyyaml" @@ -5988,7 +5729,6 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -6051,7 +5791,6 @@ version = "26.4.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "pyzmq-26.4.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:0329bdf83e170ac133f44a233fc651f6ed66ef8e66693b5af7d54f45d1ef5918"}, {file = "pyzmq-26.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:398a825d2dea96227cf6460ce0a174cf7657d6f6827807d4d1ae9d0f9ae64315"}, @@ -6147,7 +5886,6 @@ files = [ {file = "pyzmq-26.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:49b6ca2e625b46f499fb081aaf7819a177f41eeb555acb05758aa97f4f95d147"}, {file = "pyzmq-26.4.0.tar.gz", hash = "sha256:4bd13f85f80962f91a651a7356fe0472791a5f7a92f227822b5acf44795c626d"}, ] -markers = {main = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\""} [package.dependencies] cffi = {version = "*", markers = "implementation_name == \"pypy\""} @@ -6158,7 +5896,6 @@ version = "2.1.0" description = "Python library to build pretty command line user prompts ⭐️" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "questionary-2.1.0-py3-none-any.whl", hash = "sha256:44174d237b68bc828e4878c763a9ad6790ee61990e0ae72927694ead57bab8ec"}, {file = "questionary-2.1.0.tar.gz", hash = "sha256:6302cdd645b19667d8f6e6634774e9538bfcd1aad9be287e743d96cacaf95587"}, @@ -6173,8 +5910,6 @@ version = "6.2.0" description = "Python client for Redis database and key-value store" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"redis\" or extra == \"all\"" files = [ {file = "redis-6.2.0-py3-none-any.whl", hash = "sha256:c8ddf316ee0aab65f04a11229e94a64b2618451dab7a67cb2f77eb799d872d5e"}, {file = "redis-6.2.0.tar.gz", hash = "sha256:e821f129b75dde6cb99dd35e5c76e8c49512a5a0d8dfdc560b2fbd44b85ca977"}, @@ -6194,7 +5929,6 @@ version = "0.36.2" description = "JSON Referencing + Python" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, @@ -6211,7 +5945,6 @@ version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, @@ -6315,7 +6048,6 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -6337,8 +6069,6 @@ version = "1.0.0" description = "A utility belt for advanced users of python-requests" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, @@ -6353,7 +6083,6 @@ version = "13.9.4" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" -groups = ["main"] files = [ {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, @@ -6373,7 +6102,6 @@ version = "0.24.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "rpds_py-0.24.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:006f4342fe729a368c6df36578d7a348c7c716be1da0a1a0f86e3021f8e98724"}, {file = "rpds_py-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2d53747da70a4e4b17f559569d5f9506420966083a31c5fbd84e764461c4444b"}, @@ -6497,8 +6225,6 @@ version = "4.9" description = "Pure-Python RSA implementation" optional = true python-versions = ">=3.6,<4" -groups = ["main"] -markers = "extra == \"google\"" files = [ {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, @@ -6513,8 +6239,6 @@ version = "0.11.3" description = "An Amazon S3 Transfer Manager" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "s3transfer-0.11.3-py3-none-any.whl", hash = "sha256:ca855bdeb885174b5ffa95b9913622459d4ad8e331fc98eb01e6d5eb6a30655d"}, {file = "s3transfer-0.11.3.tar.gz", hash = "sha256:edae4977e3a122445660c7c114bba949f9d191bae3b34a096f18a1c8c354527a"}, @@ -6532,8 +6256,6 @@ version = "1.4.5" description = "An implementation of the SCRAM protocol." optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"postgres\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "scramp-1.4.5-py3-none-any.whl", hash = "sha256:50e37c464fc67f37994e35bee4151e3d8f9320e9c204fca83a5d313c121bbbe7"}, {file = "scramp-1.4.5.tar.gz", hash = "sha256:be3fbe774ca577a7a658117dca014e5d254d158cecae3dd60332dfe33ce6d78e"}, @@ -6548,7 +6270,6 @@ version = "3.0.4" description = "Python helper for Semantic Versioning (https://semver.org)" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746"}, {file = "semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602"}, @@ -6560,7 +6281,6 @@ version = "2.19.1" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "sentry_sdk-2.19.1-py2.py3-none-any.whl", hash = "sha256:b056e04b766f805fdf0aa620482cafe2ff000c8fcb51cb266cdb90873e93837b"}, {file = "sentry_sdk-2.19.1.tar.gz", hash = "sha256:6ad8507457a379b72f832aca55787b21e7391751892faef1fd8bace350aa5e17"}, @@ -6616,7 +6336,6 @@ version = "70.3.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "setuptools-70.3.0-py3-none-any.whl", hash = "sha256:fe384da74336c398e0d956d1cae0669bc02eed936cdb1d49b57de1990dc11ffc"}, {file = "setuptools-70.3.0.tar.gz", hash = "sha256:f171bab1dfbc86b132997f26a119f6056a57950d058587841a0082e8830f9dc5"}, @@ -6624,7 +6343,7 @@ files = [ [package.extras] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-ruff (>=0.3.2) ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "shellingham" @@ -6632,7 +6351,6 @@ version = "1.5.4" description = "Tool to Detect Surrounding Shell" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, @@ -6664,7 +6382,6 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main", "dev"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -6676,7 +6393,6 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -6688,7 +6404,6 @@ version = "2.6" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, @@ -6700,7 +6415,6 @@ version = "2.0.41" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "SQLAlchemy-2.0.41-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6854175807af57bdb6425e47adbce7d20a4d79bbfd6f6d6519cd10bb7109a7f8"}, {file = "SQLAlchemy-2.0.41-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05132c906066142103b83d9c250b60508af556982a385d96c4eaa9fb9720ac2b"}, @@ -6796,7 +6510,6 @@ version = "0.7.0" description = "JSON type with nested change tracking for SQLAlchemy" optional = false python-versions = ">= 3.6" -groups = ["main"] files = [ {file = "sqlalchemy-json-0.7.0.tar.gz", hash = "sha256:620d0b26f648f21a8fa9127df66f55f83a5ab4ae010e5397a5c6989a08238561"}, {file = "sqlalchemy_json-0.7.0-py3-none-any.whl", hash = "sha256:27881d662ca18363a4ac28175cc47ea2a6f2bef997ae1159c151026b741818e6"}, @@ -6814,7 +6527,6 @@ version = "0.41.2" description = "Various utility functions for SQLAlchemy." optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "SQLAlchemy-Utils-0.41.2.tar.gz", hash = "sha256:bc599c8c3b3319e53ce6c5c3c471120bd325d0071fb6f38a10e924e3d07b9990"}, {file = "SQLAlchemy_Utils-0.41.2-py3-none-any.whl", hash = "sha256:85cf3842da2bf060760f955f8467b87983fb2e30f1764fd0e24a48307dc8ec6e"}, @@ -6832,8 +6544,8 @@ intervals = ["intervals (>=0.7.1)"] password = ["passlib (>=1.6,<2.0)"] pendulum = ["pendulum (>=2.0.5)"] phone = ["phonenumbers (>=5.9.2)"] -test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo ; python_version < \"3.9\"", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] -test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo ; python_version < \"3.9\"", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] timezone = ["python-dateutil"] url = ["furl (>=0.4.1)"] @@ -6843,7 +6555,6 @@ version = "0.0.16" description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness." optional = false python-versions = ">=3.7,<4.0" -groups = ["main"] files = [ {file = "sqlmodel-0.0.16-py3-none-any.whl", hash = "sha256:b972f5d319580d6c37ecc417881f6ec4d1ad3ed3583d0ac0ed43234a28bf605a"}, {file = "sqlmodel-0.0.16.tar.gz", hash = "sha256:966656f18a8e9a2d159eb215b07fb0cf5222acfae3362707ca611848a8a06bd1"}, @@ -6859,7 +6570,6 @@ version = "2.2.1" description = "SSE plugin for Starlette" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99"}, {file = "sse_starlette-2.2.1.tar.gz", hash = "sha256:54470d5f19274aeed6b2d473430b08b4b379ea851d953b11d7f1c4a2c118b419"}, @@ -6879,12 +6589,10 @@ version = "0.6.3" description = "Extract data from python stack frames and tracebacks for informative displays" optional = false python-versions = "*" -groups = ["main", "dev"] files = [ {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.dependencies] asttokens = ">=2.1.0" @@ -6900,7 +6608,6 @@ version = "0.46.1" description = "The little ASGI library that shines." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "starlette-0.46.1-py3-none-any.whl", hash = "sha256:77c74ed9d2720138b25875133f3a2dae6d854af2ec37dceb56aef370c1d8a227"}, {file = "starlette-0.46.1.tar.gz", hash = "sha256:3c88d58ee4bd1bb807c0d1acb381838afc7752f9ddaec81bbe4383611d833230"}, @@ -6918,7 +6625,6 @@ version = "0.0.26" description = "A simple library to convert rtf to text" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "striprtf-0.0.26-py3-none-any.whl", hash = "sha256:8c8f9d32083cdc2e8bfb149455aa1cc5a4e0a035893bedc75db8b73becb3a1bb"}, {file = "striprtf-0.0.26.tar.gz", hash = "sha256:fdb2bba7ac440072d1c41eab50d8d74ae88f60a8b6575c6e2c7805dc462093aa"}, @@ -6930,7 +6636,6 @@ version = "25.4.0" description = "Structured Logging for Python" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c"}, {file = "structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4"}, @@ -6945,7 +6650,6 @@ version = "0.7.2" description = "Python wrapper for the Tavily API" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "tavily_python-0.7.2-py3-none-any.whl", hash = "sha256:0d7cc8b1a2f95ac10cf722094c3b5807aade67cc7750f7ca605edef7455d4c62"}, {file = "tavily_python-0.7.2.tar.gz", hash = "sha256:34f713002887df2b5e6b8d7db7bc64ae107395bdb5f53611e80a89dac9cbdf19"}, @@ -6962,7 +6666,6 @@ version = "9.1.2" description = "Retry code until it succeeds" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138"}, {file = "tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb"}, @@ -6978,7 +6681,6 @@ version = "0.9.0" description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "tiktoken-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:586c16358138b96ea804c034b8acf3f5d3f0258bd2bc3b0227af4af5d622e382"}, {file = "tiktoken-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9c59ccc528c6c5dd51820b3474402f69d9a9e1d656226848ad68a8d5b2e5108"}, @@ -7026,8 +6728,6 @@ version = "6.1.0" description = "A wrapper around the stdlib `tokenize` which roundtrips." optional = false python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "tokenize_rt-6.1.0-py2.py3-none-any.whl", hash = "sha256:d706141cdec4aa5f358945abe36b911b8cbdc844545da99e811250c0cee9b6fc"}, {file = "tokenize_rt-6.1.0.tar.gz", hash = "sha256:e8ee836616c0877ab7c7b54776d2fefcc3bde714449a206762425ae114b53c86"}, @@ -7039,8 +6739,6 @@ version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -groups = ["main"] -markers = "python_version == \"3.10\"" files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -7052,8 +6750,6 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" -groups = ["main", "dev", "dev,tests"] -markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -7095,7 +6791,6 @@ version = "6.4.2" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1"}, {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803"}, @@ -7116,7 +6811,6 @@ version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -7138,12 +6832,10 @@ version = "5.14.3" description = "Traitlets Python configuration system" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] @@ -7155,7 +6847,6 @@ version = "0.15.4" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "typer-0.15.4-py3-none-any.whl", hash = "sha256:eb0651654dcdea706780c466cf06d8f174405a659ffff8f163cfbfee98c0e173"}, {file = "typer-0.15.4.tar.gz", hash = "sha256:89507b104f9b6a0730354f27c39fae5b63ccd0c95b1ce1f1a6ba0cfd329997c3"}, @@ -7173,12 +6864,10 @@ version = "4.13.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, ] -markers = {dev = "python_version < \"3.12\""} [[package]] name = "typing-inspect" @@ -7186,7 +6875,6 @@ version = "0.9.0" description = "Runtime inspection utilities for typing module." optional = false python-versions = "*" -groups = ["main"] files = [ {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"}, {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"}, @@ -7202,7 +6890,6 @@ version = "0.4.0" description = "Runtime typing introspection tools" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f"}, {file = "typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122"}, @@ -7217,7 +6904,6 @@ version = "2025.2" 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"}, @@ -7229,7 +6915,6 @@ version = "5.3.1" description = "tzinfo object for the local timezone" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d"}, {file = "tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd"}, @@ -7247,14 +6932,13 @@ version = "2.4.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, ] [package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -7265,7 +6949,6 @@ version = "0.24.0.post1" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "uvicorn-0.24.0.post1-py3-none-any.whl", hash = "sha256:7c84fea70c619d4a710153482c0d230929af7bcf76c7bfa6de151f0a3a80121e"}, {file = "uvicorn-0.24.0.post1.tar.gz", hash = "sha256:09c8e5a79dc466bdf28dead50093957db184de356fcdc48697bad3bde4c2588e"}, @@ -7277,7 +6960,7 @@ h11 = ">=0.8" typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} [package.extras] -standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.4)"] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] [[package]] name = "uvloop" @@ -7285,8 +6968,6 @@ version = "0.21.0" description = "Fast implementation of asyncio event loop on top of libuv" optional = true python-versions = ">=3.8.0" -groups = ["main"] -markers = "extra == \"experimental\" or extra == \"all\"" files = [ {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f"}, {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d"}, @@ -7338,8 +7019,6 @@ version = "20.30.0" description = "Virtual Python Environment builder" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "virtualenv-20.30.0-py3-none-any.whl", hash = "sha256:e34302959180fca3af42d1800df014b35019490b119eba981af27f2fa486e5d6"}, {file = "virtualenv-20.30.0.tar.gz", hash = "sha256:800863162bcaa5450a6e4d721049730e7f2dae07720e0902b0e4040bd6f9ada8"}, @@ -7352,7 +7031,7 @@ platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "watchfiles" @@ -7360,8 +7039,6 @@ version = "1.0.5" description = "Simple, modern and high performance file watching and code reload in python." optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"experimental\" or extra == \"all\"" files = [ {file = "watchfiles-1.0.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5c40fe7dd9e5f81e0847b1ea64e1f5dd79dd61afbedb57759df06767ac719b40"}, {file = "watchfiles-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c0db396e6003d99bb2d7232c957b5f0b5634bbd1b24e381a5afcc880f7373fb"}, @@ -7445,7 +7122,6 @@ version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" -groups = ["main", "dev"] files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, @@ -7457,7 +7133,6 @@ version = "1.8.0" description = "WebSocket client for Python with low level API options" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, @@ -7474,8 +7149,6 @@ version = "15.0.1" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"google\" or extra == \"external-tools\"" files = [ {file = "websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b"}, {file = "websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205"}, @@ -7554,8 +7227,6 @@ version = "3.1.3" description = "The comprehensive WSGI web application library." optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, @@ -7573,8 +7244,6 @@ version = "1.4.0" description = "Wikipedia API for Python" optional = true python-versions = "*" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"tests\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "wikipedia-1.4.0.tar.gz", hash = "sha256:db0fad1829fdd441b1852306e9856398204dc0786d2996dd2e0c8bb8e26133b2"}, ] @@ -7589,7 +7258,6 @@ version = "1.17.2" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"}, {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"}, @@ -7694,7 +7362,6 @@ version = "1.19.0" description = "Yet another URL library" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "yarl-1.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0bae32f8ebd35c04d6528cedb4a26b8bf25339d3616b04613b97347f919b76d3"}, {file = "yarl-1.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8015a076daf77823e7ebdcba474156587391dab4e70c732822960368c01251e6"}, @@ -7796,18 +7463,17 @@ version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] 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)"] -test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [[package]] @@ -7816,8 +7482,6 @@ version = "5.0" description = "Very basic event publishing system" optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26"}, {file = "zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd"}, @@ -7836,8 +7500,6 @@ version = "7.2" description = "Interfaces for Python" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "zope.interface-7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2"}, {file = "zope.interface-7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a"}, @@ -7892,8 +7554,6 @@ version = "0.23.0" description = "Zstandard bindings for Python" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "zstandard-0.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf0a05b6059c0528477fba9054d09179beb63744355cab9f38059548fedd46a9"}, {file = "zstandard-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fc9ca1c9718cb3b06634c7c8dec57d24e9438b2aa9a0f02b8bb36bf478538880"}, @@ -8015,6 +7675,6 @@ server = ["fastapi", "uvicorn"] tests = ["wikipedia"] [metadata] -lock-version = "2.1" +lock-version = "2.0" python-versions = "<3.14,>=3.10" -content-hash = "185b0f643ff7eb3de0ddba12cdbbe1fd1553df2d6a7b35edc5ac4046c85f5b29" +content-hash = "da458cbf536613c920adb0364c1cf443fdfed396a8866bd72cfbb869813ccf1c" diff --git a/pyproject.toml b/pyproject.toml index 8d23d091..e212d618 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,6 +98,7 @@ redis = {version = "^6.2.0", optional = true} structlog = "^25.4.0" certifi = "^2025.6.15" aioboto3 = {version = "^14.3.0", optional = true} +pinecone = {extras = ["asyncio"], version = "^7.3.0"} [tool.poetry.extras] @@ -119,6 +120,7 @@ black = "^24.4.2" ipykernel = "^6.29.5" ipdb = "^0.13.13" pytest-mock = "^3.14.0" +pinecone = "^7.3.0" [tool.poetry.group."dev,tests".dependencies] diff --git a/tests/conftest.py b/tests/conftest.py index cfca0ebf..0abe389d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -28,6 +28,24 @@ def disable_e2b_api_key() -> Generator[None, None, None]: tool_settings.e2b_api_key = original_api_key +@pytest.fixture +def disable_pinecone() -> Generator[None, None, None]: + """ + Temporarily disables Pinecone by setting `settings.enable_pinecone` to False + and `settings.pinecone_api_key` to None for the duration of the test. + Restores the original values afterward. + """ + from letta.settings import settings + + original_enable_pinecone = settings.enable_pinecone + original_pinecone_api_key = settings.pinecone_api_key + settings.enable_pinecone = False + settings.pinecone_api_key = None + yield + settings.enable_pinecone = original_enable_pinecone + settings.pinecone_api_key = original_pinecone_api_key + + @pytest.fixture def check_e2b_key_is_set(): from letta.settings import tool_settings diff --git a/tests/test_managers.py b/tests/test_managers.py index 630f8c8b..9edc8af3 100644 --- a/tests/test_managers.py +++ b/tests/test_managers.py @@ -3320,7 +3320,7 @@ async def test_update_tool_pip_requirements(server: SyncServer, print_tool, defa # Add pip requirements to existing tool pip_reqs = [ PipRequirement(name="pandas", version="1.5.0"), - PipRequirement(name="matplotlib"), + PipRequirement(name="sumy"), ] tool_update = ToolUpdate(pip_requirements=pip_reqs) @@ -3334,7 +3334,7 @@ async def test_update_tool_pip_requirements(server: SyncServer, print_tool, defa assert len(updated_tool.pip_requirements) == 2 assert updated_tool.pip_requirements[0].name == "pandas" assert updated_tool.pip_requirements[0].version == "1.5.0" - assert updated_tool.pip_requirements[1].name == "matplotlib" + assert updated_tool.pip_requirements[1].name == "sumy" assert updated_tool.pip_requirements[1].version is None @@ -5218,6 +5218,41 @@ async def test_update_file_status_error_only(server, default_user, default_sourc assert updated.processing_status == FileProcessingStatus.PENDING # default from creation +@pytest.mark.asyncio +async def test_update_file_status_with_chunks(server, default_user, default_source): + """Update chunk progress fields along with status.""" + meta = PydanticFileMetadata( + file_name="chunks_test.txt", + file_path="/tmp/chunks_test.txt", + file_type="text/plain", + file_size=500, + source_id=default_source.id, + ) + created = await server.file_manager.create_file(file_metadata=meta, actor=default_user) + + # Update with chunk progress + updated = await server.file_manager.update_file_status( + file_id=created.id, + actor=default_user, + processing_status=FileProcessingStatus.EMBEDDING, + total_chunks=100, + chunks_embedded=50, + ) + assert updated.processing_status == FileProcessingStatus.EMBEDDING + assert updated.total_chunks == 100 + assert updated.chunks_embedded == 50 + + # Update only chunk progress + updated = await server.file_manager.update_file_status( + file_id=created.id, + actor=default_user, + chunks_embedded=100, + ) + assert updated.chunks_embedded == 100 + assert updated.total_chunks == 100 # unchanged + assert updated.processing_status == FileProcessingStatus.EMBEDDING # unchanged + + @pytest.mark.asyncio async def test_upsert_file_content_basic(server: SyncServer, default_user, default_source, async_session): """Test creating and updating file content with upsert_file_content().""" diff --git a/tests/test_sources.py b/tests/test_sources.py index 2e239580..6a1a4af2 100644 --- a/tests/test_sources.py +++ b/tests/test_sources.py @@ -9,9 +9,10 @@ from letta_client import CreateBlock from letta_client import Letta as LettaSDKClient from letta_client.types import AgentState -from letta.constants import FILES_TOOLS +from letta.constants import DEFAULT_ORG_ID, FILES_TOOLS from letta.orm.enums import ToolType from letta.schemas.message import MessageCreate +from letta.schemas.user import User from tests.utils import wait_for_server # Constants @@ -49,7 +50,7 @@ def client() -> LettaSDKClient: yield client -def upload_file_and_wait(client: LettaSDKClient, source_id: str, file_path: str, max_wait: int = 30): +def upload_file_and_wait(client: LettaSDKClient, source_id: str, file_path: str, max_wait: int = 60): """Helper function to upload a file and wait for processing to complete""" with open(file_path, "rb") as f: file_metadata = client.sources.files.upload(source_id=source_id, file=f) @@ -70,7 +71,7 @@ def upload_file_and_wait(client: LettaSDKClient, source_id: str, file_path: str, @pytest.fixture -def agent_state(client: LettaSDKClient): +def agent_state(disable_pinecone, client: LettaSDKClient): open_file_tool = client.tools.list(name="open_files")[0] search_files_tool = client.tools.list(name="semantic_search_files")[0] grep_tool = client.tools.list(name="grep_files")[0] @@ -93,7 +94,7 @@ def agent_state(client: LettaSDKClient): # Tests -def test_auto_attach_detach_files_tools(client: LettaSDKClient): +def test_auto_attach_detach_files_tools(disable_pinecone, client: LettaSDKClient): """Test automatic attachment and detachment of file tools when managing agent sources.""" # Create agent with basic configuration agent = client.agents.create( @@ -164,6 +165,7 @@ def test_auto_attach_detach_files_tools(client: LettaSDKClient): ], ) def test_file_upload_creates_source_blocks_correctly( + disable_pinecone, client: LettaSDKClient, agent_state: AgentState, file_path: str, @@ -204,7 +206,7 @@ def test_file_upload_creates_source_blocks_correctly( assert not any(re.fullmatch(expected_label_regex, b.label) for b in blocks) -def test_attach_existing_files_creates_source_blocks_correctly(client: LettaSDKClient, agent_state: AgentState): +def test_attach_existing_files_creates_source_blocks_correctly(disable_pinecone, client: LettaSDKClient, agent_state: AgentState): # Create a new source source = client.sources.create(name="test_source", embedding="openai/text-embedding-3-small") assert len(client.sources.list()) == 1 @@ -240,7 +242,7 @@ def test_attach_existing_files_creates_source_blocks_correctly(client: LettaSDKC assert not any("test" in b.value for b in blocks) -def test_delete_source_removes_source_blocks_correctly(client: LettaSDKClient, agent_state: AgentState): +def test_delete_source_removes_source_blocks_correctly(disable_pinecone, client: LettaSDKClient, agent_state: AgentState): # Create a new source source = client.sources.create(name="test_source", embedding="openai/text-embedding-3-small") assert len(client.sources.list()) == 1 @@ -270,7 +272,7 @@ def test_delete_source_removes_source_blocks_correctly(client: LettaSDKClient, a assert not any("test" in b.value for b in blocks) -def test_agent_uses_open_close_file_correctly(client: LettaSDKClient, agent_state: AgentState): +def test_agent_uses_open_close_file_correctly(disable_pinecone, client: LettaSDKClient, agent_state: AgentState): # Create a new source source = client.sources.create(name="test_source", embedding="openai/text-embedding-3-small") @@ -377,7 +379,7 @@ def test_agent_uses_open_close_file_correctly(client: LettaSDKClient, agent_stat print("✓ File successfully opened with different range - content differs as expected") -def test_agent_uses_search_files_correctly(client: LettaSDKClient, agent_state: AgentState): +def test_agent_uses_search_files_correctly(disable_pinecone, client: LettaSDKClient, agent_state: AgentState): # Create a new source source = client.sources.create(name="test_source", embedding="openai/text-embedding-3-small") @@ -423,7 +425,7 @@ def test_agent_uses_search_files_correctly(client: LettaSDKClient, agent_state: assert all(tr.status == "success" for tr in tool_returns), "Tool call failed" -def test_agent_uses_grep_correctly_basic(client: LettaSDKClient, agent_state: AgentState): +def test_agent_uses_grep_correctly_basic(disable_pinecone, client: LettaSDKClient, agent_state: AgentState): # Create a new source source = client.sources.create(name="test_source", embedding="openai/text-embedding-3-small") @@ -465,7 +467,7 @@ def test_agent_uses_grep_correctly_basic(client: LettaSDKClient, agent_state: Ag assert all(tr.status == "success" for tr in tool_returns), "Tool call failed" -def test_agent_uses_grep_correctly_advanced(client: LettaSDKClient, agent_state: AgentState): +def test_agent_uses_grep_correctly_advanced(disable_pinecone, client: LettaSDKClient, agent_state: AgentState): # Create a new source source = client.sources.create(name="test_source", embedding="openai/text-embedding-3-small") @@ -517,7 +519,7 @@ def test_agent_uses_grep_correctly_advanced(client: LettaSDKClient, agent_state: assert "513:" in tool_return_message.tool_return -def test_create_agent_with_source_ids_creates_source_blocks_correctly(client: LettaSDKClient): +def test_create_agent_with_source_ids_creates_source_blocks_correctly(disable_pinecone, client: LettaSDKClient): """Test that creating an agent with source_ids parameter correctly creates source blocks.""" # Create a new source source = client.sources.create(name="test_source", embedding="openai/text-embedding-3-small") @@ -560,7 +562,7 @@ def test_create_agent_with_source_ids_creates_source_blocks_correctly(client: Le assert file_tools == set(FILES_TOOLS) -def test_view_ranges_have_metadata(client: LettaSDKClient, agent_state: AgentState): +def test_view_ranges_have_metadata(disable_pinecone, client: LettaSDKClient, agent_state: AgentState): # Create a new source source = client.sources.create(name="test_source", embedding="openai/text-embedding-3-small") @@ -623,7 +625,7 @@ def test_view_ranges_have_metadata(client: LettaSDKClient, agent_state: AgentSta ) -def test_duplicate_file_renaming(client: LettaSDKClient): +def test_duplicate_file_renaming(disable_pinecone, client: LettaSDKClient): """Test that duplicate files are renamed with count-based suffixes (e.g., file.txt, file (1).txt, file (2).txt)""" # Create a new source source = client.sources.create(name="test_duplicate_source", embedding="openai/text-embedding-3-small") @@ -662,7 +664,7 @@ def test_duplicate_file_renaming(client: LettaSDKClient): print(f" File {i+1}: original='{file.original_file_name}' → renamed='{file.file_name}'") -def test_open_files_schema_descriptions(client: LettaSDKClient): +def test_open_files_schema_descriptions(disable_pinecone, client: LettaSDKClient): """Test that open_files tool schema contains correct descriptions from docstring""" # Get the open_files tool @@ -743,3 +745,132 @@ def test_open_files_schema_descriptions(client: LettaSDKClient): expected_length_desc = "Optional number of lines to view from offset (inclusive). If not specified, views to end of file." assert length_prop["description"] == expected_length_desc assert length_prop["type"] == "integer" + + +# --- Pinecone Tests --- + + +def test_pinecone_search_files_tool(client: LettaSDKClient): + """Test that search_files tool uses Pinecone when enabled""" + from letta.helpers.pinecone_utils import should_use_pinecone + + if not should_use_pinecone(verbose=True): + pytest.skip("Pinecone not configured (missing API key or disabled), skipping Pinecone-specific tests") + + print("Testing Pinecone search_files tool functionality") + + # Create agent with file tools + agent = client.agents.create( + name="test_pinecone_agent", + memory_blocks=[ + CreateBlock(label="human", value="username: testuser"), + ], + model="openai/gpt-4o-mini", + embedding="openai/text-embedding-3-small", + ) + + # Create source and attach to agent + source = client.sources.create(name="test_pinecone_source", embedding="openai/text-embedding-3-small") + client.agents.sources.attach(source_id=source.id, agent_id=agent.id) + + # Upload a file with searchable content + file_path = "tests/data/long_test.txt" + upload_file_and_wait(client, source.id, file_path) + + # Test semantic search using Pinecone + search_response = client.agents.messages.create( + agent_id=agent.id, + messages=[MessageCreate(role="user", content="Use the semantic_search_files tool to search for 'electoral history' in the files.")], + ) + + # Verify tool was called successfully + tool_calls = [msg for msg in search_response.messages if msg.message_type == "tool_call_message"] + assert len(tool_calls) > 0, "No tool calls found" + assert any(tc.tool_call.name == "semantic_search_files" for tc in tool_calls), "semantic_search_files not called" + + # Verify tool returned results + tool_returns = [msg for msg in search_response.messages if msg.message_type == "tool_return_message"] + assert len(tool_returns) > 0, "No tool returns found" + assert all(tr.status == "success" for tr in tool_returns), "Tool call failed" + + # Check that results contain expected content + search_results = tool_returns[0].tool_return + print(search_results) + assert ( + "electoral" in search_results.lower() or "history" in search_results.lower() + ), f"Search results should contain relevant content: {search_results}" + + +def test_pinecone_lifecycle_file_and_source_deletion(client: LettaSDKClient): + """Test that file and source deletion removes records from Pinecone""" + import asyncio + + from letta.helpers.pinecone_utils import list_pinecone_index_for_files, should_use_pinecone + + if not should_use_pinecone(): + pytest.skip("Pinecone not configured (missing API key or disabled), skipping Pinecone-specific tests") + + print("Testing Pinecone file and source deletion lifecycle") + + # Create source + source = client.sources.create(name="test_lifecycle_source", embedding="openai/text-embedding-3-small") + + # Upload multiple files and wait for processing + file_paths = ["tests/data/test.txt", "tests/data/test.md"] + uploaded_files = [] + for file_path in file_paths: + file_metadata = upload_file_and_wait(client, source.id, file_path) + uploaded_files.append(file_metadata) + + # Get temp user for Pinecone operations + user = User(name="temp", organization_id=DEFAULT_ORG_ID) + + # Test file-level deletion first + if len(uploaded_files) > 1: + file_to_delete = uploaded_files[0] + + # Check records for the specific file using list function + records_before = asyncio.run(list_pinecone_index_for_files(file_to_delete.id, user)) + print(f"Found {len(records_before)} records for file before deletion") + + # Delete the file + client.sources.files.delete(source_id=source.id, file_id=file_to_delete.id) + + # Allow time for deletion to propagate + time.sleep(2) + + # Verify file records are removed + records_after = asyncio.run(list_pinecone_index_for_files(file_to_delete.id, user)) + print(f"Found {len(records_after)} records for file after deletion") + + assert len(records_after) == 0, f"File records should be removed from Pinecone after deletion, but found {len(records_after)}" + + # Test source-level deletion - check remaining files + # Check records for remaining files + remaining_records = [] + for file_metadata in uploaded_files[1:]: # Skip the already deleted file + file_records = asyncio.run(list_pinecone_index_for_files(file_metadata.id, user)) + remaining_records.extend(file_records) + + records_before = len(remaining_records) + print(f"Found {records_before} records for remaining files before source deletion") + + # Delete the entire source + client.sources.delete(source_id=source.id) + + # Allow time for deletion to propagate + time.sleep(3) + + # Verify all remaining file records are removed + records_after = [] + for file_metadata in uploaded_files[1:]: + file_records = asyncio.run(list_pinecone_index_for_files(file_metadata.id, user)) + records_after.extend(file_records) + + print(f"Found {len(records_after)} records for files after source deletion") + + assert ( + len(records_after) == 0 + ), f"All source records should be removed from Pinecone after source deletion, but found {len(records_after)}" + + print("✓ Pinecone lifecycle verified - namespace is clean after source deletion") From 72b472896d97737a99e81fc45236e09f810a2fdf Mon Sep 17 00:00:00 2001 From: Charles Packer Date: Sun, 6 Jul 2025 11:05:31 -0700 Subject: [PATCH 2/6] fix: add frequency penalty for gpt-4o-mini (#3166) --- letta/llm_api/openai_client.py | 4 ++++ letta/schemas/llm_config.py | 4 ++++ letta/schemas/providers.py | 27 +++++++++++++++++---------- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/letta/llm_api/openai_client.py b/letta/llm_api/openai_client.py index ea17d0da..3b12095f 100644 --- a/letta/llm_api/openai_client.py +++ b/letta/llm_api/openai_client.py @@ -216,6 +216,10 @@ class OpenAIClient(LLMClientBase): # NOTE: the reasoners that don't support temperature require 1.0, not None temperature=llm_config.temperature if supports_temperature_param(model) else 1.0, ) + + if llm_config.frequency_penalty is not None: + data.frequency_penalty = llm_config.frequency_penalty + if tools and supports_parallel_tool_calling(model): data.parallel_tool_calls = False diff --git a/letta/schemas/llm_config.py b/letta/schemas/llm_config.py index ab024708..cccf93b0 100644 --- a/letta/schemas/llm_config.py +++ b/letta/schemas/llm_config.py @@ -77,6 +77,10 @@ class LLMConfig(BaseModel): max_reasoning_tokens: int = Field( 0, description="Configurable thinking budget for extended thinking, only used if enable_reasoner is True. Minimum value is 1024." ) + frequency_penalty: Optional[float] = Field( + None, # Can also deafult to 0.0? + description="Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. From OpenAI: Number between -2.0 and 2.0.", + ) # FIXME hack to silence pydantic protected namespace warning model_config = ConfigDict(protected_namespaces=()) diff --git a/letta/schemas/providers.py b/letta/schemas/providers.py index 9e741dd8..51d988bc 100644 --- a/letta/schemas/providers.py +++ b/letta/schemas/providers.py @@ -324,18 +324,25 @@ class OpenAIProvider(Provider): else: handle = self.get_handle(model_name) - configs.append( - LLMConfig( - model=model_name, - model_endpoint_type="openai", - model_endpoint=self.base_url, - context_window=context_window_size, - handle=handle, - provider_name=self.name, - provider_category=self.provider_category, - ) + llm_config = LLMConfig( + model=model_name, + model_endpoint_type="openai", + model_endpoint=self.base_url, + context_window=context_window_size, + handle=handle, + provider_name=self.name, + provider_category=self.provider_category, ) + # gpt-4o-mini has started to regress with pretty bad emoji spam loops + # this is to counteract that + if "gpt-4o-mini" in model_name: + llm_config.frequency_penalty = 1.0 + if "gpt-4.1-mini" in model_name: + llm_config.frequency_penalty = 1.0 + + configs.append(llm_config) + # for OpenAI, sort in reverse order if self.base_url == "https://api.openai.com/v1": # alphnumeric sort From bbcc9482134a8f28396f70c474af9a780b1a8831 Mon Sep 17 00:00:00 2001 From: Charles Packer Date: Sun, 6 Jul 2025 12:58:21 -0700 Subject: [PATCH 3/6] fix: fix pywright lint errors caused by bad use of pydantic (#3170) --- letta/schemas/letta_base.py | 8 ++-- letta/schemas/letta_message.py | 22 ++++++--- letta/schemas/letta_message_content.py | 30 ++++++------ letta/schemas/message.py | 64 ++++++++++++++------------ 4 files changed, 68 insertions(+), 56 deletions(-) diff --git a/letta/schemas/letta_base.py b/letta/schemas/letta_base.py index 5d2a3da3..abd87d5d 100644 --- a/letta/schemas/letta_base.py +++ b/letta/schemas/letta_base.py @@ -97,7 +97,7 @@ class LettaBase(BaseModel): class OrmMetadataBase(LettaBase): # metadata fields - created_by_id: Optional[str] = Field(None, description="The id of the user that made this object.") - last_updated_by_id: Optional[str] = Field(None, description="The id of the user that made this object.") - created_at: Optional[datetime] = Field(None, description="The timestamp when the object was created.") - updated_at: Optional[datetime] = Field(None, description="The timestamp when the object was last updated.") + created_by_id: Optional[str] = Field(default=None, description="The id of the user that made this object.") + last_updated_by_id: Optional[str] = Field(default=None, description="The id of the user that made this object.") + created_at: Optional[datetime] = Field(default=None, description="The timestamp when the object was created.") + updated_at: Optional[datetime] = Field(default=None, description="The timestamp when the object was last updated.") diff --git a/letta/schemas/letta_message.py b/letta/schemas/letta_message.py index fb773fa6..cfae6b38 100644 --- a/letta/schemas/letta_message.py +++ b/letta/schemas/letta_message.py @@ -72,7 +72,7 @@ class SystemMessage(LettaMessage): content (str): The message content sent by the system """ - message_type: Literal[MessageType.system_message] = Field(MessageType.system_message, description="The type of the message.") + message_type: Literal[MessageType.system_message] = Field(default=MessageType.system_message, description="The type of the message.") content: str = Field(..., description="The message content sent by the system") @@ -87,7 +87,7 @@ class UserMessage(LettaMessage): content (Union[str, List[LettaUserMessageContentUnion]]): The message content sent by the user (can be a string or an array of multi-modal content parts) """ - message_type: Literal[MessageType.user_message] = Field(MessageType.user_message, description="The type of the message.") + message_type: Literal[MessageType.user_message] = Field(default=MessageType.user_message, description="The type of the message.") content: Union[str, List[LettaUserMessageContentUnion]] = Field( ..., description="The message content sent by the user (can be a string or an array of multi-modal content parts)", @@ -109,7 +109,9 @@ class ReasoningMessage(LettaMessage): signature (Optional[str]): The model-generated signature of the reasoning step """ - message_type: Literal[MessageType.reasoning_message] = Field(MessageType.reasoning_message, description="The type of the message.") + message_type: Literal[MessageType.reasoning_message] = Field( + default=MessageType.reasoning_message, description="The type of the message." + ) source: Literal["reasoner_model", "non_reasoner_model"] = "non_reasoner_model" reasoning: str signature: Optional[str] = None @@ -130,7 +132,7 @@ class HiddenReasoningMessage(LettaMessage): """ message_type: Literal[MessageType.hidden_reasoning_message] = Field( - MessageType.hidden_reasoning_message, description="The type of the message." + default=MessageType.hidden_reasoning_message, description="The type of the message." ) state: Literal["redacted", "omitted"] hidden_reasoning: Optional[str] = None @@ -170,7 +172,9 @@ class ToolCallMessage(LettaMessage): tool_call (Union[ToolCall, ToolCallDelta]): The tool call """ - message_type: Literal[MessageType.tool_call_message] = Field(MessageType.tool_call_message, description="The type of the message.") + message_type: Literal[MessageType.tool_call_message] = Field( + default=MessageType.tool_call_message, description="The type of the message." + ) tool_call: Union[ToolCall, ToolCallDelta] def model_dump(self, *args, **kwargs): @@ -222,7 +226,9 @@ class ToolReturnMessage(LettaMessage): stderr (Optional[List(str)]): Captured stderr from the tool invocation """ - message_type: Literal[MessageType.tool_return_message] = Field(MessageType.tool_return_message, description="The type of the message.") + message_type: Literal[MessageType.tool_return_message] = Field( + default=MessageType.tool_return_message, description="The type of the message." + ) tool_return: str status: Literal["success", "error"] tool_call_id: str @@ -241,7 +247,9 @@ class AssistantMessage(LettaMessage): content (Union[str, List[LettaAssistantMessageContentUnion]]): The message content sent by the agent (can be a string or an array of content parts) """ - message_type: Literal[MessageType.assistant_message] = Field(MessageType.assistant_message, description="The type of the message.") + message_type: Literal[MessageType.assistant_message] = Field( + default=MessageType.assistant_message, description="The type of the message." + ) content: Union[str, List[LettaAssistantMessageContentUnion]] = Field( ..., description="The message content sent by the agent (can be a string or an array of content parts)", diff --git a/letta/schemas/letta_message_content.py b/letta/schemas/letta_message_content.py index a9ca2144..dd03c728 100644 --- a/letta/schemas/letta_message_content.py +++ b/letta/schemas/letta_message_content.py @@ -24,7 +24,7 @@ class MessageContent(BaseModel): class TextContent(MessageContent): - type: Literal[MessageContentType.text] = Field(MessageContentType.text, description="The type of the message.") + type: Literal[MessageContentType.text] = Field(default=MessageContentType.text, description="The type of the message.") text: str = Field(..., description="The text content of the message.") @@ -44,27 +44,27 @@ class ImageSource(BaseModel): class UrlImage(ImageSource): - type: Literal[ImageSourceType.url] = Field(ImageSourceType.url, description="The source type for the image.") + type: Literal[ImageSourceType.url] = Field(default=ImageSourceType.url, description="The source type for the image.") url: str = Field(..., description="The URL of the image.") class Base64Image(ImageSource): - type: Literal[ImageSourceType.base64] = Field(ImageSourceType.base64, description="The source type for the image.") + type: Literal[ImageSourceType.base64] = Field(default=ImageSourceType.base64, description="The source type for the image.") media_type: str = Field(..., description="The media type for the image.") data: str = Field(..., description="The base64 encoded image data.") detail: Optional[str] = Field( - None, + default=None, description="What level of detail to use when processing and understanding the image (low, high, or auto to let the model decide)", ) class LettaImage(ImageSource): - type: Literal[ImageSourceType.letta] = Field(ImageSourceType.letta, description="The source type for the image.") + type: Literal[ImageSourceType.letta] = Field(default=ImageSourceType.letta, description="The source type for the image.") file_id: str = Field(..., description="The unique identifier of the image file persisted in storage.") - media_type: Optional[str] = Field(None, description="The media type for the image.") - data: Optional[str] = Field(None, description="The base64 encoded image data.") + media_type: Optional[str] = Field(default=None, description="The media type for the image.") + data: Optional[str] = Field(default=None, description="The base64 encoded image data.") detail: Optional[str] = Field( - None, + default=None, description="What level of detail to use when processing and understanding the image (low, high, or auto to let the model decide)", ) @@ -73,7 +73,7 @@ ImageSourceUnion = Annotated[Union[UrlImage, Base64Image, LettaImage], Field(dis class ImageContent(MessageContent): - type: Literal[MessageContentType.image] = Field(MessageContentType.image, description="The type of the message.") + type: Literal[MessageContentType.image] = Field(default=MessageContentType.image, description="The type of the message.") source: ImageSourceUnion = Field(..., description="The source of the image.") @@ -164,7 +164,7 @@ def get_letta_assistant_message_content_union_str_json_schema(): class ToolCallContent(MessageContent): type: Literal[MessageContentType.tool_call] = Field( - MessageContentType.tool_call, description="Indicates this content represents a tool call event." + default=MessageContentType.tool_call, description="Indicates this content represents a tool call event." ) id: str = Field(..., description="A unique identifier for this specific tool call instance.") name: str = Field(..., description="The name of the tool being called.") @@ -175,7 +175,7 @@ class ToolCallContent(MessageContent): class ToolReturnContent(MessageContent): type: Literal[MessageContentType.tool_return] = Field( - MessageContentType.tool_return, description="Indicates this content represents a tool return event." + default=MessageContentType.tool_return, description="Indicates this content represents a tool return event." ) tool_call_id: str = Field(..., description="References the ID of the ToolCallContent that initiated this tool call.") content: str = Field(..., description="The content returned by the tool execution.") @@ -184,23 +184,23 @@ class ToolReturnContent(MessageContent): class ReasoningContent(MessageContent): type: Literal[MessageContentType.reasoning] = Field( - MessageContentType.reasoning, description="Indicates this is a reasoning/intermediate step." + default=MessageContentType.reasoning, description="Indicates this is a reasoning/intermediate step." ) is_native: bool = Field(..., description="Whether the reasoning content was generated by a reasoner model that processed this step.") reasoning: str = Field(..., description="The intermediate reasoning or thought process content.") - signature: Optional[str] = Field(None, description="A unique identifier for this reasoning step.") + signature: Optional[str] = Field(default=None, description="A unique identifier for this reasoning step.") class RedactedReasoningContent(MessageContent): type: Literal[MessageContentType.redacted_reasoning] = Field( - MessageContentType.redacted_reasoning, description="Indicates this is a redacted thinking step." + default=MessageContentType.redacted_reasoning, description="Indicates this is a redacted thinking step." ) data: str = Field(..., description="The redacted or filtered intermediate reasoning content.") class OmittedReasoningContent(MessageContent): type: Literal[MessageContentType.omitted_reasoning] = Field( - MessageContentType.omitted_reasoning, description="Indicates this is an omitted reasoning step." + default=MessageContentType.omitted_reasoning, description="Indicates this is an omitted reasoning step." ) # NOTE: dropping because we don't track this kind of information for the other reasoning types # tokens: int = Field(..., description="The reasoning token count for intermediate reasoning content.") diff --git a/letta/schemas/message.py b/letta/schemas/message.py index 34a0dfd9..5b7cedb7 100644 --- a/letta/schemas/message.py +++ b/letta/schemas/message.py @@ -84,11 +84,11 @@ class MessageCreate(BaseModel): description="The content of the message.", json_schema_extra=get_letta_message_content_union_str_json_schema(), ) - name: Optional[str] = Field(None, description="The name of the participant.") - otid: Optional[str] = Field(None, description="The offline threading id associated with this message") - sender_id: Optional[str] = Field(None, description="The id of the sender of the message, can be an identity id or agent id") - batch_item_id: Optional[str] = Field(None, description="The id of the LLMBatchItem that this message is associated with") - group_id: Optional[str] = Field(None, description="The multi-agent group that the message was sent in") + name: Optional[str] = Field(default=None, description="The name of the participant.") + otid: Optional[str] = Field(default=None, description="The offline threading id associated with this message") + sender_id: Optional[str] = Field(default=None, description="The id of the sender of the message, can be an identity id or agent id") + batch_item_id: Optional[str] = Field(default=None, description="The id of the LLMBatchItem that this message is associated with") + group_id: Optional[str] = Field(default=None, description="The multi-agent group that the message was sent in") def model_dump(self, to_orm: bool = False, **kwargs) -> Dict[str, Any]: data = super().model_dump(**kwargs) @@ -101,9 +101,9 @@ class MessageCreate(BaseModel): class MessageUpdate(BaseModel): """Request to update a message""" - role: Optional[MessageRole] = Field(None, description="The role of the participant.") + role: Optional[MessageRole] = Field(default=None, description="The role of the participant.") content: Optional[Union[str, List[LettaMessageContentUnion]]] = Field( - None, + default=None, description="The content of the message.", json_schema_extra=get_letta_message_content_union_str_json_schema(), ) @@ -112,11 +112,11 @@ class MessageUpdate(BaseModel): # agent_id: Optional[str] = Field(None, description="The unique identifier of the agent.") # NOTE: we probably shouldn't allow updating the model field, otherwise this loses meaning # model: Optional[str] = Field(None, description="The model used to make the function call.") - name: Optional[str] = Field(None, description="The name of the participant.") + name: Optional[str] = Field(default=None, description="The name of the participant.") # NOTE: we probably shouldn't allow updating the created_at field, right? # created_at: Optional[datetime] = Field(None, description="The time the message was created.") - tool_calls: Optional[List[OpenAIToolCall,]] = Field(None, description="The list of tool calls requested.") - tool_call_id: Optional[str] = Field(None, description="The id of the tool call.") + tool_calls: Optional[List[OpenAIToolCall,]] = Field(default=None, description="The list of tool calls requested.") + tool_call_id: Optional[str] = Field(default=None, description="The id of the tool call.") def model_dump(self, to_orm: bool = False, **kwargs) -> Dict[str, Any]: data = super().model_dump(**kwargs) @@ -150,28 +150,28 @@ class Message(BaseMessage): """ id: str = BaseMessage.generate_id_field() - organization_id: Optional[str] = Field(None, description="The unique identifier of the organization.") - agent_id: Optional[str] = Field(None, description="The unique identifier of the agent.") - model: Optional[str] = Field(None, description="The model used to make the function call.") + organization_id: Optional[str] = Field(default=None, description="The unique identifier of the organization.") + agent_id: Optional[str] = Field(default=None, description="The unique identifier of the agent.") + model: Optional[str] = Field(default=None, description="The model used to make the function call.") # Basic OpenAI-style fields role: MessageRole = Field(..., description="The role of the participant.") - content: Optional[List[LettaMessageContentUnion]] = Field(None, description="The content of the message.") + content: Optional[List[LettaMessageContentUnion]] = Field(default=None, description="The content of the message.") # NOTE: in OpenAI, this field is only used for roles 'user', 'assistant', and 'function' (now deprecated). 'tool' does not use it. name: Optional[str] = Field( - None, + default=None, description="For role user/assistant: the (optional) name of the participant. For role tool/function: the name of the function called.", ) tool_calls: Optional[List[OpenAIToolCall]] = Field( - None, description="The list of tool calls requested. Only applicable for role assistant." + default=None, description="The list of tool calls requested. Only applicable for role assistant." ) - tool_call_id: Optional[str] = Field(None, description="The ID of the tool call. Only applicable for role tool.") + tool_call_id: Optional[str] = Field(default=None, description="The ID of the tool call. Only applicable for role tool.") # Extras - step_id: Optional[str] = Field(None, description="The id of the step that this message was created in.") - otid: Optional[str] = Field(None, description="The offline threading id associated with this message") - tool_returns: Optional[List[ToolReturn]] = Field(None, description="Tool execution return information for prior tool calls") - group_id: Optional[str] = Field(None, description="The multi-agent group that the message was sent in") - sender_id: Optional[str] = Field(None, description="The id of the sender of the message, can be an identity id or agent id") - batch_item_id: Optional[str] = Field(None, description="The id of the LLMBatchItem that this message is associated with") + step_id: Optional[str] = Field(default=None, description="The id of the step that this message was created in.") + otid: Optional[str] = Field(default=None, description="The offline threading id associated with this message") + tool_returns: Optional[List[ToolReturn]] = Field(default=None, description="Tool execution return information for prior tool calls") + group_id: Optional[str] = Field(default=None, description="The multi-agent group that the message was sent in") + sender_id: Optional[str] = Field(default=None, description="The id of the sender of the message, can be an identity id or agent id") + batch_item_id: Optional[str] = Field(default=None, description="The id of the LLMBatchItem that this message is associated with") # This overrides the optional base orm schema, created_at MUST exist on all messages objects created_at: datetime = Field(default_factory=get_utc_time, description="The timestamp when the object was created.") @@ -482,7 +482,9 @@ class Message(BaseMessage): # TODO(caren) implicit support for only non-parts/list content types if openai_message_dict["content"] is not None and type(openai_message_dict["content"]) is not str: raise ValueError(f"Invalid content type: {type(openai_message_dict['content'])}") - content = [TextContent(text=openai_message_dict["content"])] if openai_message_dict["content"] else [] + content: List[LettaMessageContentUnion] = ( + [TextContent(text=openai_message_dict["content"])] if openai_message_dict["content"] else [] + ) # TODO(caren) bad assumption here that "reasoning_content" always comes before "redacted_reasoning_content" if "reasoning_content" in openai_message_dict and openai_message_dict["reasoning_content"]: @@ -491,14 +493,16 @@ class Message(BaseMessage): reasoning=openai_message_dict["reasoning_content"], is_native=True, signature=( - openai_message_dict["reasoning_content_signature"] if openai_message_dict["reasoning_content_signature"] else None + str(openai_message_dict["reasoning_content_signature"]) + if "reasoning_content_signature" in openai_message_dict + else None ), ), ) if "redacted_reasoning_content" in openai_message_dict and openai_message_dict["redacted_reasoning_content"]: content.append( RedactedReasoningContent( - data=openai_message_dict["redacted_reasoning_content"] if "redacted_reasoning_content" in openai_message_dict else None, + data=str(openai_message_dict["redacted_reasoning_content"]), ), ) if "omitted_reasoning_content" in openai_message_dict and openai_message_dict["omitted_reasoning_content"]: @@ -733,7 +737,7 @@ class Message(BaseMessage): else: warnings.warn(f"Using OpenAI with invalid 'name' field (name={self.name} role={self.role}).") - if parse_content_parts: + if parse_content_parts and self.content is not None: for content in self.content: if isinstance(content, ReasoningContent): openai_message["reasoning_content"] = content.reasoning @@ -819,7 +823,7 @@ class Message(BaseMessage): } content = [] # COT / reasoning / thinking - if len(self.content) > 1: + if self.content is not None and len(self.content) > 1: for content_part in self.content: if isinstance(content_part, ReasoningContent): content.append( @@ -1154,6 +1158,6 @@ class Message(BaseMessage): class ToolReturn(BaseModel): status: Literal["success", "error"] = Field(..., description="The status of the tool call") - stdout: Optional[List[str]] = Field(None, description="Captured stdout (e.g. prints, logs) from the tool invocation") - stderr: Optional[List[str]] = Field(None, description="Captured stderr from the tool invocation") + stdout: Optional[List[str]] = Field(default=None, description="Captured stdout (e.g. prints, logs) from the tool invocation") + stderr: Optional[List[str]] = Field(default=None, description="Captured stderr from the tool invocation") # func_return: Optional[Any] = Field(None, description="The function return object") From d2f5bb676f66c748504cd6bcf9d8d430802cd215 Mon Sep 17 00:00:00 2001 From: Charles Packer Date: Sun, 6 Jul 2025 15:24:45 -0700 Subject: [PATCH 4/6] fix: patch bug will `None` content when assistant messages have no tool calls (#3172) --- letta/schemas/message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letta/schemas/message.py b/letta/schemas/message.py index 5b7cedb7..2243cc96 100644 --- a/letta/schemas/message.py +++ b/letta/schemas/message.py @@ -698,7 +698,7 @@ class Message(BaseMessage): elif self.role == "assistant": assert self.tool_calls is not None or text_content is not None openai_message = { - "content": None if put_inner_thoughts_in_kwargs else text_content, + "content": None if (put_inner_thoughts_in_kwargs and self.tool_calls is not None) else text_content, "role": self.role, } From d81382b9fe3f980140433add4b1a3261a4f09be1 Mon Sep 17 00:00:00 2001 From: Charles Packer Date: Sun, 6 Jul 2025 20:15:22 -0700 Subject: [PATCH 5/6] refactor: revert the default summarizer to the old style (#3168) --- letta/agents/letta_agent.py | 31 ++- letta/agents/voice_agent.py | 2 +- letta/agents/voice_sleeptime_agent.py | 2 +- letta/prompts/gpt_summarize.py | 10 +- letta/server/rest_api/routers/v1/agents.py | 19 ++ letta/services/summarizer/enums.py | 1 + letta/services/summarizer/summarizer.py | 245 +++++++++++++++++- letta/settings.py | 7 +- letta/system.py | 16 ++ .../llm_model_configs/openai-gpt-4o-mini.json | 2 +- 10 files changed, 311 insertions(+), 24 deletions(-) diff --git a/letta/agents/letta_agent.py b/letta/agents/letta_agent.py index 65478b31..9a6aaadb 100644 --- a/letta/agents/letta_agent.py +++ b/letta/agents/letta_agent.py @@ -82,11 +82,16 @@ class LettaAgent(BaseAgent): step_manager: StepManager = NoopStepManager(), telemetry_manager: TelemetryManager = NoopTelemetryManager(), current_run_id: str | None = None, + ## summarizer settings + summarizer_mode: SummarizationMode = summarizer_settings.mode, + # for static_buffer mode summary_block_label: str = DEFAULT_SUMMARY_BLOCK_LABEL, message_buffer_limit: int = summarizer_settings.message_buffer_limit, message_buffer_min: int = summarizer_settings.message_buffer_min, enable_summarization: bool = summarizer_settings.enable_summarization, max_summarization_retries: int = summarizer_settings.max_summarization_retries, + # for partial_evict mode + partial_evict_summarizer_percentage: float = summarizer_settings.partial_evict_summarizer_percentage, ): super().__init__(agent_id=agent_id, openai_client=None, message_manager=message_manager, agent_manager=agent_manager, actor=actor) @@ -124,11 +129,13 @@ class LettaAgent(BaseAgent): ) self.summarizer = Summarizer( - mode=SummarizationMode(summarizer_settings.mode), + mode=summarizer_mode, + # TODO consolidate to not use this, or push it into the Summarizer() class summarizer_agent=self.summarization_agent, # TODO: Make this configurable message_buffer_limit=message_buffer_limit, message_buffer_min=message_buffer_min, + partial_evict_summarizer_percentage=partial_evict_summarizer_percentage, ) async def _check_run_cancellation(self) -> bool: @@ -872,25 +879,35 @@ class LettaAgent(BaseAgent): self.logger.warning( f"Total tokens {total_tokens} exceeds configured max tokens {llm_config.context_window}, forcefully clearing message history." ) - new_in_context_messages, updated = self.summarizer.summarize( - in_context_messages=in_context_messages, new_letta_messages=new_letta_messages, force=True, clear=True + new_in_context_messages, updated = await self.summarizer.summarize( + in_context_messages=in_context_messages, + new_letta_messages=new_letta_messages, + force=True, + clear=True, ) else: - new_in_context_messages, updated = self.summarizer.summarize( - in_context_messages=in_context_messages, new_letta_messages=new_letta_messages + self.logger.info( + f"Total tokens {total_tokens} does not exceed configured max tokens {llm_config.context_window}, passing summarizing w/o force." + ) + new_in_context_messages, updated = await self.summarizer.summarize( + in_context_messages=in_context_messages, + new_letta_messages=new_letta_messages, ) await self.agent_manager.set_in_context_messages_async( - agent_id=self.agent_id, message_ids=[m.id for m in new_in_context_messages], actor=self.actor + agent_id=self.agent_id, + message_ids=[m.id for m in new_in_context_messages], + actor=self.actor, ) return new_in_context_messages @trace_method async def summarize_conversation_history(self) -> AgentState: + """Called when the developer explicitly triggers compaction via the API""" agent_state = await self.agent_manager.get_agent_by_id_async(agent_id=self.agent_id, actor=self.actor) message_ids = agent_state.message_ids in_context_messages = await self.message_manager.get_messages_by_ids_async(message_ids=message_ids, actor=self.actor) - new_in_context_messages, updated = self.summarizer.summarize( + new_in_context_messages, updated = await self.summarizer.summarize( in_context_messages=in_context_messages, new_letta_messages=[], force=True ) return await self.agent_manager.set_in_context_messages_async( diff --git a/letta/agents/voice_agent.py b/letta/agents/voice_agent.py index 63ee8da2..0c77626e 100644 --- a/letta/agents/voice_agent.py +++ b/letta/agents/voice_agent.py @@ -295,7 +295,7 @@ class VoiceAgent(BaseAgent): new_letta_messages = await self.message_manager.create_many_messages_async(letta_message_db_queue, actor=self.actor) # TODO: Make this more general and configurable, less brittle - new_in_context_messages, updated = summarizer.summarize( + new_in_context_messages, updated = await summarizer.summarize( in_context_messages=in_context_messages, new_letta_messages=new_letta_messages ) diff --git a/letta/agents/voice_sleeptime_agent.py b/letta/agents/voice_sleeptime_agent.py index 1d997be2..e9d013f9 100644 --- a/letta/agents/voice_sleeptime_agent.py +++ b/letta/agents/voice_sleeptime_agent.py @@ -90,7 +90,7 @@ class VoiceSleeptimeAgent(LettaAgent): current_in_context_messages, new_in_context_messages, stop_reason, usage = await super()._step( agent_state=agent_state, input_messages=input_messages, max_steps=max_steps ) - new_in_context_messages, updated = self.summarizer.summarize( + new_in_context_messages, updated = await self.summarizer.summarize( in_context_messages=current_in_context_messages, new_letta_messages=new_in_context_messages ) self.agent_manager.set_in_context_messages( diff --git a/letta/prompts/gpt_summarize.py b/letta/prompts/gpt_summarize.py index 945268de..c9e9ccdd 100644 --- a/letta/prompts/gpt_summarize.py +++ b/letta/prompts/gpt_summarize.py @@ -1,14 +1,12 @@ WORD_LIMIT = 100 -SYSTEM = f""" -Your job is to summarize a history of previous messages in a conversation between an AI persona and a human. +SYSTEM = f"""Your job is to summarize a history of previous messages in a conversation between an AI persona and a human. The conversation you are given is a from a fixed context window and may not be complete. Messages sent by the AI are marked with the 'assistant' role. -The AI 'assistant' can also make calls to functions, whose outputs can be seen in messages with the 'function' role. +The AI 'assistant' can also make calls to tools, whose outputs can be seen in messages with the 'tool' role. Things the AI says in the message content are considered inner monologue and are not seen by the user. The only AI messages seen by the user are from when the AI uses 'send_message'. Messages the user sends are in the 'user' role. The 'user' role is also used for important system events, such as login events and heartbeat events (heartbeats run the AI's program without user action, allowing the AI to act without prompting from the user sending them a message). -Summarize what happened in the conversation from the perspective of the AI (use the first person). +Summarize what happened in the conversation from the perspective of the AI (use the first person from the perspective of the AI). Keep your summary less than {WORD_LIMIT} words, do NOT exceed this word limit. -Only output the summary, do NOT include anything else in your output. -""" +Only output the summary, do NOT include anything else in your output.""" diff --git a/letta/server/rest_api/routers/v1/agents.py b/letta/server/rest_api/routers/v1/agents.py index 7f8ce572..6f0df27d 100644 --- a/letta/server/rest_api/routers/v1/agents.py +++ b/letta/server/rest_api/routers/v1/agents.py @@ -38,6 +38,7 @@ from letta.schemas.user import User from letta.serialize_schemas.pydantic_agent_schema import AgentSchema from letta.server.rest_api.utils import get_letta_server from letta.server.server import SyncServer +from letta.services.summarizer.enums import SummarizationMode from letta.services.telemetry_manager import NoopTelemetryManager from letta.settings import settings from letta.utils import safe_create_task @@ -750,6 +751,12 @@ async def send_message( step_manager=server.step_manager, telemetry_manager=server.telemetry_manager if settings.llm_api_logging else NoopTelemetryManager(), current_run_id=run.id, + # summarizer settings to be added here + summarizer_mode=( + SummarizationMode.STATIC_MESSAGE_BUFFER + if agent.agent_type == AgentType.voice_convo_agent + else SummarizationMode.PARTIAL_EVICT_MESSAGE_BUFFER + ), ) result = await agent_loop.step( @@ -878,6 +885,12 @@ async def send_message_streaming( step_manager=server.step_manager, telemetry_manager=server.telemetry_manager if settings.llm_api_logging else NoopTelemetryManager(), current_run_id=run.id, + # summarizer settings to be added here + summarizer_mode=( + SummarizationMode.STATIC_MESSAGE_BUFFER + if agent.agent_type == AgentType.voice_convo_agent + else SummarizationMode.PARTIAL_EVICT_MESSAGE_BUFFER + ), ) from letta.server.rest_api.streaming_response import StreamingResponseWithStatusCode @@ -1014,6 +1027,12 @@ async def _process_message_background( actor=actor, step_manager=server.step_manager, telemetry_manager=server.telemetry_manager if settings.llm_api_logging else NoopTelemetryManager(), + # summarizer settings to be added here + summarizer_mode=( + SummarizationMode.STATIC_MESSAGE_BUFFER + if agent.agent_type == AgentType.voice_convo_agent + else SummarizationMode.PARTIAL_EVICT_MESSAGE_BUFFER + ), ) result = await agent_loop.step( diff --git a/letta/services/summarizer/enums.py b/letta/services/summarizer/enums.py index 33c42d65..620ec332 100644 --- a/letta/services/summarizer/enums.py +++ b/letta/services/summarizer/enums.py @@ -7,3 +7,4 @@ class SummarizationMode(str, Enum): """ STATIC_MESSAGE_BUFFER = "static_message_buffer_mode" + PARTIAL_EVICT_MESSAGE_BUFFER = "partial_evict_message_buffer_mode" diff --git a/letta/services/summarizer/summarizer.py b/letta/services/summarizer/summarizer.py index 7795117e..699998e6 100644 --- a/letta/services/summarizer/summarizer.py +++ b/letta/services/summarizer/summarizer.py @@ -4,13 +4,19 @@ import traceback from typing import List, Optional, Tuple, Union from letta.agents.ephemeral_summary_agent import EphemeralSummaryAgent -from letta.constants import DEFAULT_MESSAGE_TOOL, DEFAULT_MESSAGE_TOOL_KWARG +from letta.constants import DEFAULT_MESSAGE_TOOL, DEFAULT_MESSAGE_TOOL_KWARG, MESSAGE_SUMMARY_REQUEST_ACK +from letta.helpers.message_helper import convert_message_creates_to_messages +from letta.llm_api.llm_client import LLMClient from letta.log import get_logger from letta.otel.tracing import trace_method +from letta.prompts import gpt_summarize from letta.schemas.enums import MessageRole from letta.schemas.letta_message_content import TextContent +from letta.schemas.llm_config import LLMConfig from letta.schemas.message import Message, MessageCreate +from letta.schemas.user import User from letta.services.summarizer.enums import SummarizationMode +from letta.system import package_summarize_message_no_counts from letta.templates.template_helper import render_template logger = get_logger(__name__) @@ -29,18 +35,24 @@ class Summarizer: summarizer_agent: Optional[Union[EphemeralSummaryAgent, "VoiceSleeptimeAgent"]] = None, message_buffer_limit: int = 10, message_buffer_min: int = 3, + partial_evict_summarizer_percentage: float = 0.30, ): self.mode = mode # Need to do validation on this + # TODO: Move this to config self.message_buffer_limit = message_buffer_limit self.message_buffer_min = message_buffer_min self.summarizer_agent = summarizer_agent - # TODO: Move this to config + self.partial_evict_summarizer_percentage = partial_evict_summarizer_percentage @trace_method - def summarize( - self, in_context_messages: List[Message], new_letta_messages: List[Message], force: bool = False, clear: bool = False + async def summarize( + self, + in_context_messages: List[Message], + new_letta_messages: List[Message], + force: bool = False, + clear: bool = False, ) -> Tuple[List[Message], bool]: """ Summarizes or trims in_context_messages according to the chosen mode, @@ -58,7 +70,19 @@ class Summarizer: (could be appended to the conversation if desired) """ if self.mode == SummarizationMode.STATIC_MESSAGE_BUFFER: - return self._static_buffer_summarization(in_context_messages, new_letta_messages, force=force, clear=clear) + return self._static_buffer_summarization( + in_context_messages, + new_letta_messages, + force=force, + clear=clear, + ) + elif self.mode == SummarizationMode.PARTIAL_EVICT_MESSAGE_BUFFER: + return await self._partial_evict_buffer_summarization( + in_context_messages, + new_letta_messages, + force=force, + clear=clear, + ) else: # Fallback or future logic return in_context_messages, False @@ -75,9 +99,131 @@ class Summarizer: task.add_done_callback(callback) return task - def _static_buffer_summarization( - self, in_context_messages: List[Message], new_letta_messages: List[Message], force: bool = False, clear: bool = False + async def _partial_evict_buffer_summarization( + self, + in_context_messages: List[Message], + new_letta_messages: List[Message], + force: bool = False, + clear: bool = False, ) -> Tuple[List[Message], bool]: + """Summarization as implemented in the original MemGPT loop, but using message count instead of token count. + Evict a partial amount of messages, and replace message[1] with a recursive summary. + + Note that this can't be made sync, because we're waiting on the summary to inject it into the context window, + unlike the version that writes it to a block. + + Unless force is True, don't summarize. + Ignore clear, we don't use it. + """ + all_in_context_messages = in_context_messages + new_letta_messages + + if not force: + logger.debug("Not forcing summarization, returning in-context messages as is.") + return all_in_context_messages, False + + # Very ugly code to pull LLMConfig etc from the SummarizerAgent if we're not using it for anything else + assert self.summarizer_agent is not None + + # First step: determine how many messages to retain + total_message_count = len(all_in_context_messages) + assert self.partial_evict_summarizer_percentage >= 0.0 and self.partial_evict_summarizer_percentage <= 1.0 + target_message_start = round((1.0 - self.partial_evict_summarizer_percentage) * total_message_count) + logger.info(f"Target message count: {total_message_count}->{(total_message_count-target_message_start)}") + + # The summary message we'll insert is role 'user' (vs 'assistant', 'tool', or 'system') + # We are going to put it at index 1 (index 0 is the system message) + # That means that index 2 needs to be role 'assistant', so walk up the list starting at + # the target_message_count and find the first assistant message + for i in range(target_message_start, total_message_count): + if all_in_context_messages[i].role == MessageRole.assistant: + assistant_message_index = i + break + else: + raise ValueError(f"No assistant message found from indices {target_message_start} to {total_message_count}") + + # The sequence to summarize is index 1 -> assistant_message_index + messages_to_summarize = all_in_context_messages[1:assistant_message_index] + logger.info(f"Eviction indices: {1}->{assistant_message_index}(/{total_message_count})") + + # Dynamically get the LLMConfig from the summarizer agent + # Pretty cringe code here that we need the agent for this but we don't use it + agent_state = await self.summarizer_agent.agent_manager.get_agent_by_id_async( + agent_id=self.summarizer_agent.agent_id, actor=self.summarizer_agent.actor + ) + + # TODO if we do this via the "agent", then we can more easily allow toggling on the memory block version + summary_message_str = await simple_summary( + messages=messages_to_summarize, + llm_config=agent_state.llm_config, + actor=self.summarizer_agent.actor, + include_ack=True, + ) + + # TODO add counts back + # Recall message count + # num_recall_messages_current = await self.message_manager.size_async(actor=self.actor, agent_id=agent_state.id) + # num_messages_evicted = len(messages_to_summarize) + # num_recall_messages_hidden = num_recall_messages_total - len() + + # Create the summary message + summary_message_str_packed = package_summarize_message_no_counts( + summary=summary_message_str, + timezone=agent_state.timezone, + ) + summary_message_obj = convert_message_creates_to_messages( + message_creates=[ + MessageCreate( + role=MessageRole.user, + content=[TextContent(text=summary_message_str_packed)], + ) + ], + agent_id=agent_state.id, + timezone=agent_state.timezone, + # We already packed, don't pack again + wrap_user_message=False, + wrap_system_message=False, + )[0] + + # Create the message in the DB + await self.summarizer_agent.message_manager.create_many_messages_async( + pydantic_msgs=[summary_message_obj], + actor=self.summarizer_agent.actor, + ) + + updated_in_context_messages = all_in_context_messages[assistant_message_index:] + return [all_in_context_messages[0], summary_message_obj] + updated_in_context_messages, True + + def _static_buffer_summarization( + self, + in_context_messages: List[Message], + new_letta_messages: List[Message], + force: bool = False, + clear: bool = False, + ) -> Tuple[List[Message], bool]: + """ + Implements static buffer summarization by maintaining a fixed-size message buffer (< N messages). + + Logic: + 1. Combine existing context messages with new messages + 2. If total messages <= buffer limit and not forced, return unchanged + 3. Calculate how many messages to retain (0 if clear=True, otherwise message_buffer_min) + 4. Find the trim index to keep the most recent messages while preserving user message boundaries + 5. Evict older messages (everything between system message and trim index) + 6. If summarizer agent is available, trigger background summarization of evicted messages + 7. Return updated context with system message + retained recent messages + + Args: + in_context_messages: Existing conversation context messages + new_letta_messages: Newly added messages to append + force: Force summarization even if buffer limit not exceeded + clear: Clear all messages except system message (retain_count = 0) + + Returns: + Tuple of (updated_messages, was_summarized) + - updated_messages: New context after trimming/summarization + - was_summarized: True if messages were evicted and summarization triggered + """ + all_in_context_messages = in_context_messages + new_letta_messages if len(all_in_context_messages) <= self.message_buffer_limit and not force: @@ -139,6 +285,91 @@ class Summarizer: return [all_in_context_messages[0]] + updated_in_context_messages, True +def simple_formatter(messages: List[Message], include_system: bool = False) -> str: + """Go from an OpenAI-style list of messages to a concatenated string""" + + parsed_messages = [message.to_openai_dict() for message in messages if message.role != MessageRole.system or include_system] + return "\n".join(json.dumps(msg) for msg in parsed_messages) + + +def simple_message_wrapper(openai_msg: dict) -> Message: + """Extremely simple way to map from role/content to Message object w/ throwaway dummy fields""" + + if "role" not in openai_msg: + raise ValueError(f"Missing role in openai_msg: {openai_msg}") + if "content" not in openai_msg: + raise ValueError(f"Missing content in openai_msg: {openai_msg}") + + if openai_msg["role"] == "user": + return Message( + role=MessageRole.user, + content=[TextContent(text=openai_msg["content"])], + ) + elif openai_msg["role"] == "assistant": + return Message( + role=MessageRole.assistant, + content=[TextContent(text=openai_msg["content"])], + ) + elif openai_msg["role"] == "system": + return Message( + role=MessageRole.system, + content=[TextContent(text=openai_msg["content"])], + ) + else: + raise ValueError(f"Unknown role: {openai_msg['role']}") + + +async def simple_summary(messages: List[Message], llm_config: LLMConfig, actor: User, include_ack: bool = True) -> str: + """Generate a simple summary from a list of messages. + + Intentionally kept functional due to the simplicity of the prompt. + """ + + # Create an LLMClient from the config + llm_client = LLMClient.create( + provider_type=llm_config.model_endpoint_type, + put_inner_thoughts_first=True, + actor=actor, + ) + assert llm_client is not None + + # Prepare the messages payload to send to the LLM + system_prompt = gpt_summarize.SYSTEM + summary_transcript = simple_formatter(messages) + + if include_ack: + input_messages = [ + {"role": "system", "content": system_prompt}, + {"role": "assistant", "content": MESSAGE_SUMMARY_REQUEST_ACK}, + {"role": "user", "content": summary_transcript}, + ] + else: + input_messages = [ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": summary_transcript}, + ] + print("messages going to summarizer:", input_messages) + input_messages_obj = [simple_message_wrapper(msg) for msg in input_messages] + print("messages going to summarizer (objs):", input_messages_obj) + + request_data = llm_client.build_request_data(input_messages_obj, llm_config, tools=[]) + print("request data:", request_data) + # NOTE: we should disable the inner_thoughts_in_kwargs here, because we don't use it + # I'm leaving it commented it out for now for safety but is fine assuming the var here is a copy not a reference + # llm_config.put_inner_thoughts_in_kwargs = False + response_data = await llm_client.request_async(request_data, llm_config) + response = llm_client.convert_response_to_chat_completion(response_data, input_messages_obj, llm_config) + if response.choices[0].message.content is None: + logger.warning("No content returned from summarizer") + # TODO raise an error error instead? + # return "[Summary failed to generate]" + raise Exception("Summary failed to generate") + else: + summary = response.choices[0].message.content.strip() + + return summary + + def format_transcript(messages: List[Message], include_system: bool = False) -> List[str]: """ Turn a list of Message objects into a human-readable transcript. diff --git a/letta/settings.py b/letta/settings.py index 1e8b9134..fb2751c3 100644 --- a/letta/settings.py +++ b/letta/settings.py @@ -39,12 +39,17 @@ class ToolSettings(BaseSettings): class SummarizerSettings(BaseSettings): model_config = SettingsConfigDict(env_prefix="letta_summarizer_", extra="ignore") - mode: SummarizationMode = SummarizationMode.STATIC_MESSAGE_BUFFER + # mode: SummarizationMode = SummarizationMode.STATIC_MESSAGE_BUFFER + mode: SummarizationMode = SummarizationMode.PARTIAL_EVICT_MESSAGE_BUFFER message_buffer_limit: int = 60 message_buffer_min: int = 15 enable_summarization: bool = True max_summarization_retries: int = 3 + # partial evict summarizer percentage + # eviction based on percentage of message count, not token count + partial_evict_summarizer_percentage: float = 0.30 + # TODO(cliandy): the below settings are tied to old summarization and should be deprecated or moved # Controls if we should evict all messages # TODO: Can refactor this into an enum if we have a bunch of different kinds of summarizers diff --git a/letta/system.py b/letta/system.py index 33337569..e4031bcc 100644 --- a/letta/system.py +++ b/letta/system.py @@ -188,6 +188,22 @@ def package_summarize_message(summary, summary_message_count, hidden_message_cou return json_dumps(packaged_message) +def package_summarize_message_no_counts(summary, timezone): + context_message = ( + f"Note: prior messages have been hidden from view due to conversation memory constraints.\n" + + f"The following is a summary of the previous messages:\n {summary}" + ) + + formatted_time = get_local_time(timezone=timezone) + packaged_message = { + "type": "system_alert", + "message": context_message, + "time": formatted_time, + } + + return json_dumps(packaged_message) + + def package_summarize_message_no_summary(hidden_message_count, message=None, timezone=None): """Add useful metadata to the summary message""" diff --git a/tests/configs/llm_model_configs/openai-gpt-4o-mini.json b/tests/configs/llm_model_configs/openai-gpt-4o-mini.json index 0e6c32b2..661b8aa1 100644 --- a/tests/configs/llm_model_configs/openai-gpt-4o-mini.json +++ b/tests/configs/llm_model_configs/openai-gpt-4o-mini.json @@ -1,5 +1,5 @@ { - "context_window": 8192, + "context_window": 128000, "model": "gpt-4o-mini", "model_endpoint_type": "openai", "model_endpoint": "https://api.openai.com/v1", From 5775d416a1bd85c12d48b9f36d4d98aa5af8ae80 Mon Sep 17 00:00:00 2001 From: Sarah Wooders Date: Sun, 6 Jul 2025 20:21:27 -0700 Subject: [PATCH 6/6] chore: bump version 0.8.10 (#3176) --- letta/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/letta/__init__.py b/letta/__init__.py index 46e8a36e..f4ba592d 100644 --- a/letta/__init__.py +++ b/letta/__init__.py @@ -5,7 +5,7 @@ try: __version__ = version("letta") except PackageNotFoundError: # Fallback for development installations - __version__ = "0.8.9" + __version__ = "0.8.10" if os.environ.get("LETTA_VERSION"): __version__ = os.environ["LETTA_VERSION"] diff --git a/pyproject.toml b/pyproject.toml index e212d618..e9e72b52 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "letta" -version = "0.8.9" +version = "0.8.10" packages = [ {include = "letta"}, ]