From 92b175a52e9b4495f84b14f750749149b6dbae3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 15 Nov 2024 13:43:37 +0100 Subject: [PATCH 01/24] feat: started working on RAG (again) --- docs/tools.rst | 112 ++++++++++++++++++++++++++++++++ gptme/cache.py | 121 +++++++++++++++++++++++++++++++++++ gptme/config.py | 1 + gptme/context.py | 137 ++++++++++++++++++++++++++++++++++++++++ gptme/logmanager.py | 12 +++- gptme/tools/__init__.py | 2 + gptme/tools/base.py | 15 ++++- gptme/tools/rag.py | 103 ++++++++++++++++++++++++++++++ pyproject.toml | 4 ++ tests/test_cache.py | 112 ++++++++++++++++++++++++++++++++ tests/test_context.py | 101 +++++++++++++++++++++++++++++ tests/test_tools_rag.py | 105 ++++++++++++++++++++++++++++++ 12 files changed, 820 insertions(+), 5 deletions(-) create mode 100644 gptme/cache.py create mode 100644 gptme/context.py create mode 100644 gptme/tools/rag.py create mode 100644 tests/test_cache.py create mode 100644 tests/test_context.py create mode 100644 tests/test_tools_rag.py diff --git a/docs/tools.rst b/docs/tools.rst index 05599798..8c4a4180 100644 --- a/docs/tools.rst +++ b/docs/tools.rst @@ -32,6 +32,10 @@ The tools can be grouped into the following categories: - `Chats`_ +- Context management + + - `RAG`_ + Shell ----- @@ -143,3 +147,111 @@ Example usage:: The tool automatically handles screen resolution scaling to ensure optimal performance with LLM vision capabilities. .. include:: computer-use-warning.rst + +RAG +--- + +.. automodule:: gptme.tools.rag + :members: + :noindex: + +The RAG (Retrieval-Augmented Generation) tool provides context-aware assistance by indexing and searching project documentation. + +Installation +^^^^^^^^^^^ + +The RAG tool requires the ``gptme-rag`` package. Install it with:: + + pip install "gptme[rag]" + +Configuration +^^^^^^^^^^^^ + +Configure RAG in your ``gptme.toml``:: + + [rag] + # Storage configuration + index_path = "~/.cache/gptme/rag" # Where to store the index + collection = "gptme_docs" # Collection name for documents + + # Context enhancement settings + max_tokens = 2000 # Maximum tokens for context window + auto_context = true # Enable automatic context enhancement + min_relevance = 0.5 # Minimum relevance score for including context + max_results = 5 # Maximum number of results to consider + + # Cache configuration + [rag.cache] + max_embeddings = 10000 # Maximum number of cached embeddings + max_searches = 1000 # Maximum number of cached search results + embedding_ttl = 86400 # Embedding cache TTL in seconds (24h) + search_ttl = 3600 # Search cache TTL in seconds (1h) + +Features +^^^^^^^^ + +1. Manual Search and Indexing + - Index project documentation with ``rag index`` + - Search indexed documents with ``rag search`` + - Check index status with ``rag status`` + +2. Automatic Context Enhancement + - Automatically adds relevant context to user messages + - Retrieves semantically similar documents + - Manages token budget to avoid context overflow + - Preserves conversation flow with hidden context messages + +3. Performance Optimization + - Intelligent caching system for embeddings and search results + - Configurable cache sizes and TTLs + - Automatic cache invalidation + - Memory-efficient storage + +Example Usage +^^^^^^^^^^^^ + +1. Index your project:: + + ```rag index ./docs ./src``` + +2. Search for specific information:: + + ```rag search python functions``` + +3. Automatic Context Enhancement: + + When you ask a question, gptme automatically: + - Searches for relevant documentation + - Adds context as hidden system messages + - Maintains conversation history + + Example conversation with automatic context:: + + User: How do I use the patch tool? + [Hidden context from patch.py docs added automatically] + Assistant: The patch tool allows you to modify files... + +Benefits +^^^^^^^^ + +- Better informed responses through relevant documentation +- Reduced need for manual context inclusion +- Automatic token management +- Seamless integration with conversation flow + +Usage +^^^^^ + +Index documents:: + + ```rag index ./docs``` + +Search the index:: + + ```rag search python functions``` + +Check index status:: + + ```rag status``` + +The tool automatically handles document processing, embedding generation, and semantic search to provide relevant context for your queries. diff --git a/gptme/cache.py b/gptme/cache.py new file mode 100644 index 00000000..61396efb --- /dev/null +++ b/gptme/cache.py @@ -0,0 +1,121 @@ +"""Caching system for RAG functionality.""" + +import logging +import time +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Optional + +from .config import get_project_config + +logger = logging.getLogger(__name__) + + +# Global cache instance +_cache: Optional["RAGCache"] = None + + +def get_cache() -> "RAGCache": + """Get the global RAG cache instance.""" + global _cache + if _cache is None: + _cache = RAGCache() + return _cache + + +@dataclass +class CacheEntry: + """Entry in the cache with metadata.""" + + data: Any + timestamp: float + ttl: float + + +class Cache: + """Simple cache with TTL and size limits.""" + + def __init__(self, max_size: int = 1000, default_ttl: float = 3600): + self.max_size = max_size + self.default_ttl = default_ttl + self._cache: dict[str, CacheEntry] = {} + + def get(self, key: str) -> Any | None: + """Get a value from the cache.""" + if key not in self._cache: + return None + + entry = self._cache[key] + if time.time() - entry.timestamp > entry.ttl: + # Entry expired + del self._cache[key] + return None + + return entry.data + + def set(self, key: str, value: Any, ttl: float | None = None) -> None: + """Set a value in the cache.""" + # Enforce size limit + if len(self._cache) >= self.max_size: + # Remove oldest entry + oldest_key = min(self._cache.items(), key=lambda x: x[1].timestamp)[0] + del self._cache[oldest_key] + + self._cache[key] = CacheEntry( + data=value, timestamp=time.time(), ttl=ttl or self.default_ttl + ) + + def clear(self) -> None: + """Clear the cache.""" + self._cache.clear() + + +class RAGCache: + """Cache for RAG functionality.""" + + def __init__(self): + config = get_project_config(Path.cwd()) + assert config + cache_config = config.rag.get("rag", {}).get("cache", {}) + + # Initialize caches with configured limits + self.embedding_cache = Cache( + max_size=cache_config.get("max_embeddings", 10000), + default_ttl=cache_config.get("embedding_ttl", 86400), # 24 hours + ) + self.search_cache = Cache( + max_size=cache_config.get("max_searches", 1000), + default_ttl=cache_config.get("search_ttl", 3600), # 1 hour + ) + + @staticmethod + def _make_search_key(query: str, n_results: int) -> str: + """Create a cache key for a search query.""" + return f"{query}::{n_results}" + + def get_embedding(self, text: str) -> list[float] | None: + """Get cached embedding for text.""" + return self.embedding_cache.get(text) + + def set_embedding(self, text: str, embedding: list[float]) -> None: + """Cache embedding for text.""" + self.embedding_cache.set(text, embedding) + + def get_search_results( + self, query: str, n_results: int + ) -> tuple[list[Any], dict[str, Any]] | None: + """Get cached search results.""" + key = self._make_search_key(query, n_results) + return self.search_cache.get(key) + + def set_search_results( + self, query: str, n_results: int, results: tuple[list[Any], dict[str, Any]] + ) -> None: + """Cache search results.""" + key = self._make_search_key(query, n_results) + self.search_cache.set(key, results) + + def clear(self) -> None: + """Clear all caches.""" + self.embedding_cache.clear() + self.search_cache.clear() diff --git a/gptme/config.py b/gptme/config.py index 4002a43d..edfaba5e 100644 --- a/gptme/config.py +++ b/gptme/config.py @@ -41,6 +41,7 @@ class ProjectConfig: """Project-level configuration, such as which files to include in the context by default.""" files: list[str] = field(default_factory=list) + rag: dict = field(default_factory=dict) ABOUT_ACTIVITYWATCH = """ActivityWatch is a free and open-source automated time-tracker that helps you track how you spend your time on your devices.""" diff --git a/gptme/context.py b/gptme/context.py new file mode 100644 index 00000000..92a039ce --- /dev/null +++ b/gptme/context.py @@ -0,0 +1,137 @@ +"""Context providers for enhancing messages with relevant context.""" + +import logging +from abc import ABC, abstractmethod +from dataclasses import dataclass +from pathlib import Path + +import gptme_rag + +from .cache import get_cache +from .config import get_project_config +from .message import Message + +logger = logging.getLogger(__name__) + + +@dataclass +class Context: + """Context information to be added to messages.""" + + content: str + source: str + relevance: float + + +class ContextProvider(ABC): + """Base class for context providers.""" + + @abstractmethod + def get_context(self, query: str, max_tokens: int = 1000) -> list[Context]: + """Get relevant context for a query.""" + pass + + +class RAGContextProvider(ContextProvider): + """Context provider using RAG.""" + + def __init__(self): + try: + self._has_rag = True + + config = get_project_config(Path.cwd()) + assert config + + # Storage configuration + self.indexer = gptme_rag.Indexer( + persist_directory=config.rag.get("index_path", "~/.cache/gptme/rag"), + collection_name=config.rag.get("collection", "gptme_docs"), + ) + + # Context enhancement configuration + self.context_assembler = gptme_rag.ContextAssembler( + max_tokens=config.rag.get("max_tokens", 2000) + ) + self.auto_context = config.rag.get("auto_context", True) + self.min_relevance = config.rag.get("min_relevance", 0.5) + self.max_results = config.rag.get("max_results", 5) + except ImportError: + logger.debug( + "gptme-rag not installed, RAG context provider will not be available" + ) + self._has_rag = False + + def get_context(self, query: str, max_tokens: int = 1000) -> list[Context]: + """Get relevant context using RAG.""" + if not self._has_rag or not self.auto_context: + return [] + + try: + # Check cache first + cache = get_cache() + cached_results = cache.get_search_results(query, self.max_results) + + if cached_results: + docs, results = cached_results + logger.debug(f"Using cached search results for query: {query}") + else: + # Search with configured limits + docs, results = self.indexer.search(query, n_results=self.max_results) + # Cache the results + cache.set_search_results(query, self.max_results, (docs, results)) + logger.debug(f"Cached search results for query: {query}") + + contexts = [] + for i, doc in enumerate(docs): + # Calculate relevance score (1 - distance) + relevance = 1.0 - results["distances"][0][i] + + # Skip if below minimum relevance + if relevance < self.min_relevance: + continue + + contexts.append( + Context( + content=doc.content, + source=doc.metadata.get("source", "unknown"), + relevance=relevance, + ) + ) + + # Sort by relevance + contexts.sort(key=lambda x: x.relevance, reverse=True) + + return contexts + except Exception as e: + logger.warning(f"Error getting RAG context: {e}") + return [] + + +def enhance_messages(messages: list[Message]) -> list[Message]: + """Enhance messages with context from available providers.""" + providers = [RAGContextProvider()] + enhanced_messages = [] + + for msg in messages: + if msg.role == "user": + # Get context from all providers + contexts = [] + for provider in providers: + try: + contexts.extend(provider.get_context(msg.content)) + except Exception as e: + logger.warning(f"Error getting context from provider: {e}") + + # Add context as a system message before the user message + if contexts: + context_msg = "Relevant context:\n\n" + for ctx in contexts: + context_msg += f"### {ctx.source}\n{ctx.content}\n\n" + + enhanced_messages.append( + Message(role="system", content=context_msg, hide=True) + ) + + enhanced_messages.append(msg) + + return enhanced_messages diff --git a/gptme/logmanager.py b/gptme/logmanager.py index 83f613b3..57462aa9 100644 --- a/gptme/logmanager.py +++ b/gptme/logmanager.py @@ -306,11 +306,17 @@ def to_dict(self, branches=False) -> dict: def prepare_messages(msgs: list[Message]) -> list[Message]: """Prepares the messages before sending to the LLM.""" - msgs_reduced = list(reduce_log(msgs)) + from .context import enhance_messages - if len_tokens(msgs) != len_tokens(msgs_reduced): + # First enhance messages with context + msgs_enhanced = enhance_messages(msgs) + + # Then reduce and limit as before + msgs_reduced = list(reduce_log(msgs_enhanced)) + + if len_tokens(msgs_enhanced) != len_tokens(msgs_reduced): logger.info( - f"Reduced log from {len_tokens(msgs)//1} to {len_tokens(msgs_reduced)//1} tokens" + f"Reduced log from {len_tokens(msgs_enhanced)//1} to {len_tokens(msgs_reduced)//1} tokens" ) msgs_limited = limit_log(msgs_reduced) if len(msgs_reduced) != len(msgs_limited): diff --git a/gptme/tools/__init__.py b/gptme/tools/__init__.py index 351adb84..24a015b7 100644 --- a/gptme/tools/__init__.py +++ b/gptme/tools/__init__.py @@ -19,6 +19,7 @@ from .tmux import tool as tmux_tool from .vision import tool as vision_tool from .youtube import tool as youtube_tool +from .rag import tool as rag_tool logger = logging.getLogger(__name__) @@ -45,6 +46,7 @@ screenshot_tool, vision_tool, computer_tool, + rag_tool, # python tool is loaded last to ensure all functions are registered python_tool, ] diff --git a/gptme/tools/base.py b/gptme/tools/base.py index 55df2ab3..c6bc52de 100644 --- a/gptme/tools/base.py +++ b/gptme/tools/base.py @@ -23,12 +23,19 @@ ConfirmFunc = Callable[[str], bool] -class ExecuteFunc(Protocol): +class ExecuteFuncGen(Protocol): def __call__( self, code: str, args: list[str], confirm: ConfirmFunc ) -> Generator[Message, None, None]: ... +class ExecuteFuncMsg(Protocol): + def __call__(self, code: str, args: list[str], confirm: ConfirmFunc) -> Message: ... + + +ExecuteFunc: TypeAlias = ExecuteFuncGen | ExecuteFuncMsg + + @dataclass(frozen=True, eq=False) class ToolSpec: """ @@ -99,7 +106,11 @@ def execute(self, confirm: ConfirmFunc) -> Generator[Message, None, None]: tool = get_tool(self.tool) if tool and tool.execute: try: - yield from tool.execute(self.content, self.args, confirm) + ex = tool.execute(self.content, self.args, confirm) + if isinstance(ex, Generator): + yield from ex + else: + yield ex except Exception as e: # if we are testing, raise the exception if "pytest" in globals(): diff --git a/gptme/tools/rag.py b/gptme/tools/rag.py new file mode 100644 index 00000000..fdb82e1b --- /dev/null +++ b/gptme/tools/rag.py @@ -0,0 +1,103 @@ +"""RAG tool for context-aware assistance.""" + +import logging +from pathlib import Path + +import gptme_rag + +from ..config import get_project_config +from ..message import Message +from .base import ConfirmFunc, ToolSpec + +logger = logging.getLogger(__name__) + +try: + _HAS_RAG = True +except ImportError: + logger.debug("gptme-rag not installed, RAG tool will not be available") + _HAS_RAG = False + +indexer: gptme_rag.Indexer | None = None + + +def execute_rag(code: str, args: list[str], confirm: ConfirmFunc) -> Message: + """Execute RAG commands.""" + assert indexer is not None, "RAG indexer not initialized" + command = args[0] if args else "help" + + if command == "help": + return Message("system", "Available commands: index, search, status") + elif command == "index": + paths = args[1:] or ["."] + for path in paths: + indexer.index_directory(Path(path)) + return Message("system", f"Indexed {len(paths)} paths") + elif command == "search": + query = " ".join(args[1:]) + docs = indexer.search(query) + return Message( + "system", + "\n\n".join( + f"### {doc.metadata['source']}\n{doc.content[:200]}..." for doc in docs + ), + ) + elif command == "status": + return Message( + "system", f"Index contains {indexer.collection.count()} documents" + ) + else: + return Message("system", f"Unknown command: {command}") + + +def init_rag() -> ToolSpec: + """Initialize the RAG tool.""" + if not _HAS_RAG: + return ToolSpec( + name="rag", + desc="RAG (Retrieval-Augmented Generation) for context-aware assistance", + available=False, + ) + + config = get_project_config(Path.cwd()) + if config: + # Initialize RAG with configuration + global indexer + indexer = gptme_rag.Indexer( + persist_directory=Path( + config.rag.get("index_path", "~/.cache/gptme/rag") + ).expanduser(), + # TODO: use a better default collection name? (e.g. project name) + collection_name=config.rag.get("collection", "gptme_docs"), + ) + + return ToolSpec( + name="rag", + desc="RAG (Retrieval-Augmented Generation) for context-aware assistance", + instructions="""Use RAG to index and search project documentation. + +Commands: +- index [paths...] - Index documents in specified paths +- search - Search indexed documents +- status - Show index status""", + examples="""User: Index the current directory +Assistant: Let me index the current directory with RAG. +```rag index``` +System: Indexed 1 paths + +User: Search for documentation about functions +Assistant: I'll search for function-related documentation. +```rag search function documentation``` +System: ### docs/api.md +Functions are documented using docstrings... + +User: Show index status +Assistant: I'll check the current status of the RAG index. +```rag status``` +System: Index contains 42 documents""", + block_types=["rag"], + execute=execute_rag, + available=True, + ) + + +tool = init_rag() diff --git a/pyproject.toml b/pyproject.toml index acd1857e..c6fb9b3f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,7 @@ bashlex = "^0.18" playwright = {version = "1.47.*", optional=true} # version constrained due to annoying to have to run `playwright install` on every update youtube_transcript_api = {version = "^0.6.1", optional = true} python-xlib = {version = "^0.33", optional = true} # for X11 interaction +gptme-rag = {version = "^0.1.0", optional = true} # for RAG functionality # providers openai = "^1.0" @@ -89,6 +90,7 @@ server = ["flask", "flask-cors"] browser = ["playwright"] datascience = ["matplotlib", "pandas", "numpy", "pillow"] computer = ["python-xlib", "pillow"] # pillow already in datascience but listed for clarity +rag = ["gptme-rag"] # RAG functionality all = [ # server "flask", "flask-cors", @@ -98,6 +100,8 @@ all = [ "matplotlib", "pandas", "numpy", "pillow", # computer "python-xlib", + # rag + "gptme-rag", ] [tool.ruff] diff --git a/tests/test_cache.py b/tests/test_cache.py new file mode 100644 index 00000000..c07c18c3 --- /dev/null +++ b/tests/test_cache.py @@ -0,0 +1,112 @@ +"""Tests for the caching system.""" + +import time +from unittest.mock import Mock, patch + +import pytest + +from gptme.cache import Cache, RAGCache, get_cache +from gptme.context import RAGContextProvider + + +def test_cache_basic_operations(): + """Test basic cache operations.""" + cache = Cache(max_size=2) + + # Set and get + cache.set("key1", "value1") + assert cache.get("key1") == "value1" + + # Missing key + assert cache.get("missing") is None + + # Clear + cache.clear() + assert cache.get("key1") is None + + +def test_cache_ttl(): + """Test cache TTL functionality.""" + cache = Cache(default_ttl=0.1) # 100ms TTL + + cache.set("key1", "value1") + assert cache.get("key1") == "value1" + + # Wait for TTL to expire + time.sleep(0.2) + assert cache.get("key1") is None + + +def test_cache_size_limit(): + """Test cache size limiting.""" + cache = Cache(max_size=2) + + cache.set("key1", "value1") + cache.set("key2", "value2") + cache.set("key3", "value3") # Should evict oldest entry + + assert cache.get("key1") is None # Evicted + assert cache.get("key2") == "value2" + assert cache.get("key3") == "value3" + + +def test_rag_cache_search_results(): + """Test RAG cache with search results.""" + cache = RAGCache() + + # Mock search results + docs = [Mock(content="test content")] + results = {"distances": [[0.1]]} + + # Cache results + cache.set_search_results("test query", 5, (docs, results)) + + # Get cached results + cached = cache.get_search_results("test query", 5) + assert cached is not None + cached_docs, cached_results = cached + + assert len(cached_docs) == 1 + assert cached_docs[0].content == "test content" + assert cached_results["distances"] == [[0.1]] + + +def test_rag_cache_embeddings(): + """Test RAG cache with embeddings.""" + cache = RAGCache() + + embedding = [0.1, 0.2, 0.3] + cache.set_embedding("test text", embedding) + + cached = cache.get_embedding("test text") + assert cached == embedding + + +def test_get_cache_singleton(): + """Test that get_cache returns a singleton instance.""" + cache1 = get_cache() + cache2 = get_cache() + assert cache1 is cache2 + + +@pytest.mark.asyncio +async def test_rag_context_provider_with_cache(): + """Test RAG context provider with caching.""" + with patch("gptme.context.RAGContextProvider._has_rag", True): + provider = RAGContextProvider() + + # Mock the indexer's search method + mock_docs = [Mock(content="test content", metadata={"source": "test.md"})] + mock_results = {"distances": [[0.1]]} + provider.indexer.search = Mock(return_value=(mock_docs, mock_results)) + + # First call should use indexer + contexts = provider.get_context("test query") + assert len(contexts) == 1 + assert provider.indexer.search.call_count == 1 + + # Second call should use cache + contexts = provider.get_context("test query") + assert len(contexts) == 1 + # Search should not be called again + assert provider.indexer.search.call_count == 1 diff --git a/tests/test_context.py b/tests/test_context.py new file mode 100644 index 00000000..698245af --- /dev/null +++ b/tests/test_context.py @@ -0,0 +1,101 @@ +"""Tests for context enhancement functionality.""" + +import pytest +from unittest.mock import Mock, patch + +from gptme.context import Context, RAGContextProvider, enhance_messages +from gptme.message import Message + + +@pytest.fixture +def mock_rag_provider(): + """Create a mock RAG provider that returns test contexts.""" + provider = Mock() + provider.get_context.return_value = [ + Context( + content="This is a test document about Python functions.", + source="doc1.md", + relevance=0.8, + ), + Context( + content="Documentation about testing practices.", + source="doc2.md", + relevance=0.6, + ), + ] + return provider + + +def test_enhance_messages_with_context(mock_rag_provider): + """Test that messages are enhanced with context.""" + with patch("gptme.context.RAGContextProvider", return_value=mock_rag_provider): + messages = [ + Message("system", "Initial system message"), + Message("user", "Tell me about Python functions"), + Message("assistant", "Here's what I know about functions..."), + ] + + enhanced = enhance_messages(messages) + + # Should have one extra message for the context + assert len(enhanced) == 4 + + # Check that context was added before the user message + assert enhanced[0].role == "system" # Original system message + assert enhanced[1].role == "system" # Added context + assert "Relevant context:" in enhanced[1].content + assert "doc1.md" in enhanced[1].content + assert "doc2.md" in enhanced[1].content + assert enhanced[1].hide is True # Context should be hidden + + # Original messages should remain unchanged + assert enhanced[2].role == "user" + assert enhanced[3].role == "assistant" + + +def test_enhance_messages_no_rag(): + """Test that enhancement works even without RAG available.""" + with patch("gptme.context.RAGContextProvider._has_rag", False): + messages = [ + Message("user", "Tell me about Python"), + Message("assistant", "Python is a programming language"), + ] + + enhanced = enhance_messages(messages) + + # Should be unchanged when RAG is not available + assert len(enhanced) == len(messages) + assert enhanced == messages + + +def test_enhance_messages_error_handling(mock_rag_provider): + """Test that errors in context providers are handled gracefully.""" + mock_rag_provider.get_context.side_effect = Exception("Test error") + + with patch("gptme.context.RAGContextProvider", return_value=mock_rag_provider): + messages = [ + Message("user", "Tell me about Python"), + Message("assistant", "Python is great"), + ] + + # Should not raise an exception + enhanced = enhance_messages(messages) + + # Messages should be unchanged when provider fails + assert len(enhanced) == len(messages) + assert enhanced == messages + + +def test_rag_provider_initialization(): + """Test RAG provider initialization with and without gptme-rag.""" + # Test when gptme-rag is not available + with patch("gptme.context.RAGContextProvider._has_rag", False): + provider = RAGContextProvider() + assert provider.get_context("test query") == [] + + # Test when gptme-rag is available + with patch("gptme.context.RAGContextProvider._has_rag", True): + with patch("gptme.context.gptme_rag") as mock_rag: + provider = RAGContextProvider() + assert provider._has_rag is True + mock_rag.Indexer.assert_called_once() diff --git a/tests/test_tools_rag.py b/tests/test_tools_rag.py new file mode 100644 index 00000000..403358b7 --- /dev/null +++ b/tests/test_tools_rag.py @@ -0,0 +1,105 @@ +"""Tests for the RAG tool.""" + +from collections.abc import Generator +from unittest.mock import patch + +import pytest +from gptme import Message +from gptme.tools.base import ToolSpec +from gptme.tools.rag import _HAS_RAG, init_rag + + +@pytest.fixture +def temp_docs(tmp_path): + """Create temporary test documents.""" + doc1 = tmp_path / "doc1.md" + doc1.write_text("# Test Document\nThis is a test document about Python functions.") + + doc2 = tmp_path / "doc2.md" + doc2.write_text("# Another Document\nThis document discusses testing practices.") + + return tmp_path + + +@pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") +def test_rag_tool_init(): + """Test RAG tool initialization.""" + tool = init_rag() + assert isinstance(tool, ToolSpec) + assert tool.name == "rag" + assert tool.available is True + + +def test_rag_tool_init_without_gptme_rag(): + """Test RAG tool initialization when gptme-rag is not available.""" + with patch("gptme.tools.rag._HAS_RAG", False): + tool = init_rag() + assert isinstance(tool, ToolSpec) + assert tool.name == "rag" + assert tool.available is False + + +def _m2str(tool_execute: Generator[Message, None, None] | Message) -> str: + """Convert a execute() call to a string.""" + if isinstance(tool_execute, Generator): + return tool_execute.send(None).content + elif isinstance(tool_execute, Message): + return tool_execute.content + + +def noconfirm(*args, **kwargs): + return True + + +@pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") +def test_rag_index_command(temp_docs): + """Test the index command.""" + tool = init_rag() + assert tool.execute + result = _m2str(tool.execute("", ["index", str(temp_docs)], noconfirm)) + assert "Indexed" in result + + # Check status after indexing + result = _m2str(tool.execute("", ["status"], noconfirm)) + assert "Index contains" in result + assert "2" in result # Should have indexed 2 documents + + +@pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") +def test_rag_search_command(temp_docs): + """Test the search command.""" + tool = init_rag() + assert tool.execute + # Index first + _m2str(tool.execute("", ["index", str(temp_docs)], noconfirm)) + + # Search for Python + result = _m2str(tool.execute("", ["search", "Python"], noconfirm)) + assert "doc1.md" in result + assert "Python functions" in result + + # Search for testing + result = _m2str(tool.execute("", ["search", "testing"], noconfirm)) + assert "doc2.md" in result + assert "testing practices" in result + + +@pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") +def test_rag_help_command(): + """Test the help command.""" + tool = init_rag() + assert tool.execute + result = _m2str(tool.execute("", ["help"], noconfirm)) + assert "Available commands" in result + assert "index" in result + assert "search" in result + assert "status" in result + + +@pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") +def test_rag_invalid_command(): + """Test invalid command handling.""" + tool = init_rag() + assert tool.execute + result = _m2str(tool.execute("", ["invalid"], noconfirm)) + assert "Unknown command" in result From 9961166293e205bd2901a05c85b60bea1a7917bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 15 Nov 2024 13:53:28 +0100 Subject: [PATCH 02/24] fix: fixed bugs in rag --- gptme/config.py | 14 ++++++++------ gptme/context.py | 4 +++- gptme/init.py | 4 ++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/gptme/config.py b/gptme/config.py index edfaba5e..b22ca4c2 100644 --- a/gptme/config.py +++ b/gptme/config.py @@ -1,6 +1,7 @@ import logging import os from dataclasses import dataclass, field +from functools import lru_cache from pathlib import Path import tomlkit @@ -73,12 +74,12 @@ class ProjectConfig: def get_config() -> Config: global _config if _config is None: - _config = load_config() + _config = _load_config() return _config -def load_config() -> Config: - config = _load_config() +def _load_config() -> Config: + config = _load_config_doc() assert "prompt" in config, "prompt key missing in config" assert "env" in config, "env key missing in config" prompt = config.pop("prompt") @@ -88,7 +89,7 @@ def load_config() -> Config: return Config(prompt=prompt, env=env) -def _load_config() -> tomlkit.TOMLDocument: +def _load_config_doc() -> tomlkit.TOMLDocument: # Check if the config file exists if not os.path.exists(config_path): # If not, create it and write some default settings @@ -106,7 +107,7 @@ def _load_config() -> tomlkit.TOMLDocument: def set_config_value(key: str, value: str) -> None: # pragma: no cover - doc: TOMLDocument | Container = _load_config() + doc: TOMLDocument | Container = _load_config_doc() # Set the value keypath = key.split(".") @@ -121,9 +122,10 @@ def set_config_value(key: str, value: str) -> None: # pragma: no cover # Reload config global _config - _config = load_config() + _config = _load_config() +@lru_cache def get_project_config(workspace: Path) -> ProjectConfig | None: project_config_paths = [ p diff --git a/gptme/context.py b/gptme/context.py index 92a039ce..082ff5b2 100644 --- a/gptme/context.py +++ b/gptme/context.py @@ -35,6 +35,8 @@ def get_context(self, query: str, max_tokens: int = 1000) -> list[Context]: class RAGContextProvider(ContextProvider): """Context provider using RAG.""" + # TODO: refactor this to share code with rag tool + def __init__(self): try: self._has_rag = True @@ -45,7 +47,7 @@ def __init__(self): # Storage configuration self.indexer = gptme_rag.Indexer( persist_directory=config.rag.get("index_path", "~/.cache/gptme/rag"), - collection_name=config.rag.get("collection", "gptme_docs"), + collection_name=config.rag.get("collection", "default"), ) # Context enhancement configuration diff --git a/gptme/init.py b/gptme/init.py index 03b6c778..72f0fcba 100644 --- a/gptme/init.py +++ b/gptme/init.py @@ -3,7 +3,7 @@ from dotenv import load_dotenv -from .config import config_path, load_config, set_config_value +from .config import config_path, get_config, set_config_value from .llm import init_llm from .models import ( PROVIDERS, @@ -30,7 +30,7 @@ def init(model: str | None, interactive: bool, tool_allowlist: list[str] | None) logger.debug("Started") load_dotenv() - config = load_config() + config = get_config() # get from config if not model: From e485c5a87d77b5889f7afcf4f5c926838b114a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 15 Nov 2024 15:47:30 +0100 Subject: [PATCH 03/24] fix: fixed tests after refactor --- tests/test_config.py | 6 +++--- tests/test_eval.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index 98b91801..dd5910ca 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,7 +1,7 @@ -from gptme.config import load_config +from gptme.config import get_config -def test_load_config(): - config = load_config() +def test_get_config(): + config = get_config() print(f"config: {config}") assert config diff --git a/tests/test_eval.py b/tests/test_eval.py index 8fd59f9b..b0ae8014 100644 --- a/tests/test_eval.py +++ b/tests/test_eval.py @@ -1,6 +1,6 @@ import pytest from click.testing import CliRunner -from gptme.config import load_config +from gptme.config import get_config from gptme.eval import execute, tests from gptme.eval.agents import GPTMe from gptme.eval.main import main @@ -9,7 +9,7 @@ def _detect_model(): # detect which model is configured # TODO: isn't there already a get_default_model() helper? - config = load_config() + config = get_config() if model := config.get_env("MODEL"): return model elif config.get_env("OPENAI_API_KEY"): From 6cf10be73bf0dc634155042d14c3ecc781861e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 15 Nov 2024 15:49:08 +0100 Subject: [PATCH 04/24] build(deps): updated dependencies --- poetry.lock | 1750 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 1693 insertions(+), 57 deletions(-) diff --git a/poetry.lock b/poetry.lock index 03d87082..d11711ee 100644 --- a/poetry.lock +++ b/poetry.lock @@ -87,6 +87,23 @@ doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] trio = ["trio (>=0.26.1)"] +[[package]] +name = "asgiref" +version = "3.8.1" +description = "ASGI specs, helper code, and adapters" +optional = true +python-versions = ">=3.8" +files = [ + {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"}, + {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} + +[package.extras] +tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] + [[package]] name = "astroid" version = "3.3.5" @@ -133,6 +150,17 @@ files = [ [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] +[[package]] +name = "backoff" +version = "2.2.1" +description = "Function decoration for backoff and retry" +optional = true +python-versions = ">=3.7,<4.0" +files = [ + {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, + {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, +] + [[package]] name = "bashlex" version = "0.18" @@ -144,6 +172,46 @@ files = [ {file = "bashlex-0.18.tar.gz", hash = "sha256:5bb03a01c6d5676338c36fd1028009c8ad07e7d61d8a1ce3f513b7fff52796ee"}, ] +[[package]] +name = "bcrypt" +version = "4.2.0" +description = "Modern password hashing for your software and your servers" +optional = true +python-versions = ">=3.7" +files = [ + {file = "bcrypt-4.2.0-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:096a15d26ed6ce37a14c1ac1e48119660f21b24cba457f160a4b830f3fe6b5cb"}, + {file = "bcrypt-4.2.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c02d944ca89d9b1922ceb8a46460dd17df1ba37ab66feac4870f6862a1533c00"}, + {file = "bcrypt-4.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d84cf6d877918620b687b8fd1bf7781d11e8a0998f576c7aa939776b512b98d"}, + {file = "bcrypt-4.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:1bb429fedbe0249465cdd85a58e8376f31bb315e484f16e68ca4c786dcc04291"}, + {file = "bcrypt-4.2.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:655ea221910bcac76ea08aaa76df427ef8625f92e55a8ee44fbf7753dbabb328"}, + {file = "bcrypt-4.2.0-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:1ee38e858bf5d0287c39b7a1fc59eec64bbf880c7d504d3a06a96c16e14058e7"}, + {file = "bcrypt-4.2.0-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:0da52759f7f30e83f1e30a888d9163a81353ef224d82dc58eb5bb52efcabc399"}, + {file = "bcrypt-4.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3698393a1b1f1fd5714524193849d0c6d524d33523acca37cd28f02899285060"}, + {file = "bcrypt-4.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:762a2c5fb35f89606a9fde5e51392dad0cd1ab7ae64149a8b935fe8d79dd5ed7"}, + {file = "bcrypt-4.2.0-cp37-abi3-win32.whl", hash = "sha256:5a1e8aa9b28ae28020a3ac4b053117fb51c57a010b9f969603ed885f23841458"}, + {file = "bcrypt-4.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:8f6ede91359e5df88d1f5c1ef47428a4420136f3ce97763e31b86dd8280fbdf5"}, + {file = "bcrypt-4.2.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:c52aac18ea1f4a4f65963ea4f9530c306b56ccd0c6f8c8da0c06976e34a6e841"}, + {file = "bcrypt-4.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3bbbfb2734f0e4f37c5136130405332640a1e46e6b23e000eeff2ba8d005da68"}, + {file = "bcrypt-4.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3413bd60460f76097ee2e0a493ccebe4a7601918219c02f503984f0a7ee0aebe"}, + {file = "bcrypt-4.2.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8d7bb9c42801035e61c109c345a28ed7e84426ae4865511eb82e913df18f58c2"}, + {file = "bcrypt-4.2.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3d3a6d28cb2305b43feac298774b997e372e56c7c7afd90a12b3dc49b189151c"}, + {file = "bcrypt-4.2.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:9c1c4ad86351339c5f320ca372dfba6cb6beb25e8efc659bedd918d921956bae"}, + {file = "bcrypt-4.2.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:27fe0f57bb5573104b5a6de5e4153c60814c711b29364c10a75a54bb6d7ff48d"}, + {file = "bcrypt-4.2.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8ac68872c82f1add6a20bd489870c71b00ebacd2e9134a8aa3f98a0052ab4b0e"}, + {file = "bcrypt-4.2.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:cb2a8ec2bc07d3553ccebf0746bbf3d19426d1c6d1adbd4fa48925f66af7b9e8"}, + {file = "bcrypt-4.2.0-cp39-abi3-win32.whl", hash = "sha256:77800b7147c9dc905db1cba26abe31e504d8247ac73580b4aa179f98e6608f34"}, + {file = "bcrypt-4.2.0-cp39-abi3-win_amd64.whl", hash = "sha256:61ed14326ee023917ecd093ee6ef422a72f3aec6f07e21ea5f10622b735538a9"}, + {file = "bcrypt-4.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:39e1d30c7233cfc54f5c3f2c825156fe044efdd3e0b9d309512cc514a263ec2a"}, + {file = "bcrypt-4.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f4f4acf526fcd1c34e7ce851147deedd4e26e6402369304220250598b26448db"}, + {file = "bcrypt-4.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:1ff39b78a52cf03fdf902635e4c81e544714861ba3f0efc56558979dd4f09170"}, + {file = "bcrypt-4.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:373db9abe198e8e2c70d12b479464e0d5092cc122b20ec504097b5f2297ed184"}, + {file = "bcrypt-4.2.0.tar.gz", hash = "sha256:cf69eaf5185fd58f268f805b505ce31f9b9fc2d64b376642164e9244540c1221"}, +] + +[package.extras] +tests = ["pytest (>=3.2.1,!=3.3.0)"] +typecheck = ["mypy"] + [[package]] name = "beautifulsoup4" version = "4.12.3" @@ -176,6 +244,42 @@ files = [ {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, ] +[[package]] +name = "build" +version = "1.2.2.post1" +description = "A simple, correct Python build frontend" +optional = true +python-versions = ">=3.8" +files = [ + {file = "build-1.2.2.post1-py3-none-any.whl", hash = "sha256:1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5"}, + {file = "build-1.2.2.post1.tar.gz", hash = "sha256:b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "os_name == \"nt\""} +importlib-metadata = {version = ">=4.6", markers = "python_full_version < \"3.10.2\""} +packaging = ">=19.1" +pyproject_hooks = "*" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["furo (>=2023.08.17)", "sphinx (>=7.0,<8.0)", "sphinx-argparse-cli (>=1.5)", "sphinx-autodoc-typehints (>=1.10)", "sphinx-issues (>=3.0.0)"] +test = ["build[uv,virtualenv]", "filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0)", "setuptools (>=56.0.0)", "setuptools (>=56.0.0)", "setuptools (>=67.8.0)", "wheel (>=0.36.0)"] +typing = ["build[uv]", "importlib-metadata (>=5.1)", "mypy (>=1.9.0,<1.10.0)", "tomli", "typing-extensions (>=3.7.4.3)"] +uv = ["uv (>=0.1.18)"] +virtualenv = ["virtualenv (>=20.0.35)"] + +[[package]] +name = "cachetools" +version = "5.5.0" +description = "Extensible memoizing collections and decorators" +optional = true +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, + {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, +] + [[package]] name = "certifi" version = "2024.8.30" @@ -312,6 +416,84 @@ files = [ {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] +[[package]] +name = "chroma-hnswlib" +version = "0.7.3" +description = "Chromas fork of hnswlib" +optional = true +python-versions = "*" +files = [ + {file = "chroma-hnswlib-0.7.3.tar.gz", hash = "sha256:b6137bedde49fffda6af93b0297fe00429fc61e5a072b1ed9377f909ed95a932"}, + {file = "chroma_hnswlib-0.7.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:59d6a7c6f863c67aeb23e79a64001d537060b6995c3eca9a06e349ff7b0998ca"}, + {file = "chroma_hnswlib-0.7.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d71a3f4f232f537b6152947006bd32bc1629a8686df22fd97777b70f416c127a"}, + {file = "chroma_hnswlib-0.7.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c92dc1ebe062188e53970ba13f6b07e0ae32e64c9770eb7f7ffa83f149d4210"}, + {file = "chroma_hnswlib-0.7.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49da700a6656fed8753f68d44b8cc8ae46efc99fc8a22a6d970dc1697f49b403"}, + {file = "chroma_hnswlib-0.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:108bc4c293d819b56476d8f7865803cb03afd6ca128a2a04d678fffc139af029"}, + {file = "chroma_hnswlib-0.7.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:11e7ca93fb8192214ac2b9c0943641ac0daf8f9d4591bb7b73be808a83835667"}, + {file = "chroma_hnswlib-0.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6f552e4d23edc06cdeb553cdc757d2fe190cdeb10d43093d6a3319f8d4bf1c6b"}, + {file = "chroma_hnswlib-0.7.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f96f4d5699e486eb1fb95849fe35ab79ab0901265805be7e60f4eaa83ce263ec"}, + {file = "chroma_hnswlib-0.7.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:368e57fe9ebae05ee5844840fa588028a023d1182b0cfdb1d13f607c9ea05756"}, + {file = "chroma_hnswlib-0.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:b7dca27b8896b494456db0fd705b689ac6b73af78e186eb6a42fea2de4f71c6f"}, + {file = "chroma_hnswlib-0.7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:70f897dc6218afa1d99f43a9ad5eb82f392df31f57ff514ccf4eeadecd62f544"}, + {file = "chroma_hnswlib-0.7.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aef10b4952708f5a1381c124a29aead0c356f8d7d6e0b520b778aaa62a356f4"}, + {file = "chroma_hnswlib-0.7.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ee2d8d1529fca3898d512079144ec3e28a81d9c17e15e0ea4665697a7923253"}, + {file = "chroma_hnswlib-0.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:a4021a70e898783cd6f26e00008b494c6249a7babe8774e90ce4766dd288c8ba"}, + {file = "chroma_hnswlib-0.7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a8f61fa1d417fda848e3ba06c07671f14806a2585272b175ba47501b066fe6b1"}, + {file = "chroma_hnswlib-0.7.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d7563be58bc98e8f0866907368e22ae218d6060601b79c42f59af4eccbbd2e0a"}, + {file = "chroma_hnswlib-0.7.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51b8d411486ee70d7b66ec08cc8b9b6620116b650df9c19076d2d8b6ce2ae914"}, + {file = "chroma_hnswlib-0.7.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d706782b628e4f43f1b8a81e9120ac486837fbd9bcb8ced70fe0d9b95c72d77"}, + {file = "chroma_hnswlib-0.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:54f053dedc0e3ba657f05fec6e73dd541bc5db5b09aa8bc146466ffb734bdc86"}, + {file = "chroma_hnswlib-0.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e607c5a71c610a73167a517062d302c0827ccdd6e259af6e4869a5c1306ffb5d"}, + {file = "chroma_hnswlib-0.7.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2358a795870156af6761890f9eb5ca8cade57eb10c5f046fe94dae1faa04b9e"}, + {file = "chroma_hnswlib-0.7.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cea425df2e6b8a5e201fff0d922a1cc1d165b3cfe762b1408075723c8892218"}, + {file = "chroma_hnswlib-0.7.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:454df3dd3e97aa784fba7cf888ad191e0087eef0fd8c70daf28b753b3b591170"}, + {file = "chroma_hnswlib-0.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:df587d15007ca701c6de0ee7d5585dd5e976b7edd2b30ac72bc376b3c3f85882"}, +] + +[package.dependencies] +numpy = "*" + +[[package]] +name = "chromadb" +version = "0.4.24" +description = "Chroma." +optional = true +python-versions = ">=3.8" +files = [ + {file = "chromadb-0.4.24-py3-none-any.whl", hash = "sha256:3a08e237a4ad28b5d176685bd22429a03717fe09d35022fb230d516108da01da"}, + {file = "chromadb-0.4.24.tar.gz", hash = "sha256:a5c80b4e4ad9b236ed2d4899a5b9e8002b489293f2881cb2cadab5b199ee1c72"}, +] + +[package.dependencies] +bcrypt = ">=4.0.1" +build = ">=1.0.3" +chroma-hnswlib = "0.7.3" +fastapi = ">=0.95.2" +grpcio = ">=1.58.0" +importlib-resources = "*" +kubernetes = ">=28.1.0" +mmh3 = ">=4.0.1" +numpy = ">=1.22.5" +onnxruntime = ">=1.14.1" +opentelemetry-api = ">=1.2.0" +opentelemetry-exporter-otlp-proto-grpc = ">=1.2.0" +opentelemetry-instrumentation-fastapi = ">=0.41b0" +opentelemetry-sdk = ">=1.2.0" +orjson = ">=3.9.12" +overrides = ">=7.3.1" +posthog = ">=2.4.0" +pulsar-client = ">=3.1.0" +pydantic = ">=1.9" +pypika = ">=0.48.9" +PyYAML = ">=6.0.0" +requests = ">=2.28" +tenacity = ">=8.2.3" +tokenizers = ">=0.13.2" +tqdm = ">=4.65.0" +typer = ">=0.9.0" +typing-extensions = ">=4.5.0" +uvicorn = {version = ">=0.18.3", extras = ["standard"]} + [[package]] name = "click" version = "8.1.7" @@ -337,6 +519,23 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "coloredlogs" +version = "15.0.1" +description = "Colored terminal output for Python's logging module" +optional = true +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934"}, + {file = "coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"}, +] + +[package.dependencies] +humanfriendly = ">=9.1" + +[package.extras] +cron = ["capturer (>=2.4)"] + [[package]] name = "contourpy" version = "1.3.0" @@ -535,6 +734,23 @@ files = [ {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, ] +[[package]] +name = "deprecated" +version = "1.2.15" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +optional = true +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +files = [ + {file = "Deprecated-1.2.15-py2.py3-none-any.whl", hash = "sha256:353bc4a8ac4bfc96800ddab349d89c25dec1079f65fd53acdcc1e0b975b21320"}, + {file = "deprecated-1.2.15.tar.gz", hash = "sha256:683e561a90de76239796e6b6feac66b99030d2dd3fcf61ef996330f14bbb9b0d"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "jinja2 (>=3.0.3,<3.1.0)", "setuptools", "sphinx (<2)", "tox"] + [[package]] name = "dill" version = "0.3.9" @@ -583,6 +799,17 @@ files = [ {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, ] +[[package]] +name = "durationpy" +version = "0.9" +description = "Module for converting between datetime.timedelta and Go's Duration strings." +optional = true +python-versions = "*" +files = [ + {file = "durationpy-0.9-py3-none-any.whl", hash = "sha256:e65359a7af5cedad07fb77a2dd3f390f8eb0b74cb845589fa6c057086834dd38"}, + {file = "durationpy-0.9.tar.gz", hash = "sha256:fd3feb0a69a0057d582ef643c355c40d2fa1c942191f914d12203b1a01ac722a"}, +] + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -625,6 +852,26 @@ files = [ [package.extras] tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] +[[package]] +name = "fastapi" +version = "0.115.5" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = true +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.115.5-py3-none-any.whl", hash = "sha256:596b95adbe1474da47049e802f9a65ab2ffa9c2b07e7efee70eb8a66c9f2f796"}, + {file = "fastapi-0.115.5.tar.gz", hash = "sha256:0e7a4d0dc0d01c68df21887cce0945e72d3c48b9f4f79dfe7a7d53aa08fbb289"}, +] + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +starlette = ">=0.40.0,<0.42.0" +typing-extensions = ">=4.8.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] + [[package]] name = "filelock" version = "3.16.1" @@ -677,6 +924,17 @@ files = [ [package.dependencies] Flask = ">=0.9" +[[package]] +name = "flatbuffers" +version = "24.3.25" +description = "The FlatBuffers serialization format for Python" +optional = true +python-versions = "*" +files = [ + {file = "flatbuffers-24.3.25-py2.py3-none-any.whl", hash = "sha256:8dbdec58f935f3765e4f7f3cf635ac3a77f83568138d6a2311f524ec96364812"}, + {file = "flatbuffers-24.3.25.tar.gz", hash = "sha256:de2ec5b203f21441716617f38443e0a8ebf3d25bf0d9c0bb0ce68fa00ad546a4"}, +] + [[package]] name = "fonttools" version = "4.54.1" @@ -787,6 +1045,46 @@ test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe, test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] tqdm = ["tqdm"] +[[package]] +name = "google-auth" +version = "2.36.0" +description = "Google Authentication Library" +optional = true +python-versions = ">=3.7" +files = [ + {file = "google_auth-2.36.0-py2.py3-none-any.whl", hash = "sha256:51a15d47028b66fd36e5c64a82d2d57480075bccc7da37cde257fc94177a61fb"}, + {file = "google_auth-2.36.0.tar.gz", hash = "sha256:545e9618f2df0bcbb7dcbc45a546485b1212624716975a1ea5ae8149ce769ab1"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography", "pyopenssl"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.66.0" +description = "Common protobufs used in Google APIs" +optional = true +python-versions = ">=3.7" +files = [ + {file = "googleapis_common_protos-1.66.0-py2.py3-none-any.whl", hash = "sha256:d7abcd75fabb2e0ec9f74466401f6c119a0b498e27370e9be4c94cb7e382b8ed"}, + {file = "googleapis_common_protos-1.66.0.tar.gz", hash = "sha256:c3e7b33d15fdca5374cc0a7346dd92ffa847425cc4ea941d970f13680052ec8c"}, +] + +[package.dependencies] +protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + [[package]] name = "gprof2dot" version = "2024.6.6" @@ -798,6 +1096,26 @@ files = [ {file = "gprof2dot-2024.6.6.tar.gz", hash = "sha256:fa1420c60025a9eb7734f65225b4da02a10fc6dd741b37fa129bc6b41951e5ab"}, ] +[[package]] +name = "gptme-rag" +version = "0.1.4" +description = "RAG implementation for gptme context management" +optional = true +python-versions = "<4.0,>=3.10" +files = [ + {file = "gptme_rag-0.1.4-py3-none-any.whl", hash = "sha256:047acd2aa5d7aefea467a6b15a4b6c416e5260eaf39d66043275c3b8dd1504d6"}, + {file = "gptme_rag-0.1.4.tar.gz", hash = "sha256:a972a9de96fbe6d5a0674c7dd9f5443e2159b74316ec210be71744cb02799073"}, +] + +[package.dependencies] +chromadb = ">=0.4.22,<0.5.0" +click = "*" +numpy = "<2.0.0" +psutil = ">=6.1.0,<7.0.0" +rich = "*" +tiktoken = ">=0.7" +watchdog = ">=3.0.0,<4.0.0" + [[package]] name = "greenlet" version = "3.0.3" @@ -869,6 +1187,73 @@ files = [ docs = ["Sphinx", "furo"] test = ["objgraph", "psutil"] +[[package]] +name = "grpcio" +version = "1.68.0" +description = "HTTP/2-based RPC framework" +optional = true +python-versions = ">=3.8" +files = [ + {file = "grpcio-1.68.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:619b5d0f29f4f5351440e9343224c3e19912c21aeda44e0c49d0d147a8d01544"}, + {file = "grpcio-1.68.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:a59f5822f9459bed098ffbceb2713abbf7c6fd13f2b9243461da5c338d0cd6c3"}, + {file = "grpcio-1.68.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:c03d89df516128febc5a7e760d675b478ba25802447624edf7aa13b1e7b11e2a"}, + {file = "grpcio-1.68.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44bcbebb24363d587472089b89e2ea0ab2e2b4df0e4856ba4c0b087c82412121"}, + {file = "grpcio-1.68.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79f81b7fbfb136247b70465bd836fa1733043fdee539cd6031cb499e9608a110"}, + {file = "grpcio-1.68.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:88fb2925789cfe6daa20900260ef0a1d0a61283dfb2d2fffe6194396a354c618"}, + {file = "grpcio-1.68.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:99f06232b5c9138593ae6f2e355054318717d32a9c09cdc5a2885540835067a1"}, + {file = "grpcio-1.68.0-cp310-cp310-win32.whl", hash = "sha256:a6213d2f7a22c3c30a479fb5e249b6b7e648e17f364598ff64d08a5136fe488b"}, + {file = "grpcio-1.68.0-cp310-cp310-win_amd64.whl", hash = "sha256:15327ab81131ef9b94cb9f45b5bd98803a179c7c61205c8c0ac9aff9d6c4e82a"}, + {file = "grpcio-1.68.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:3b2b559beb2d433129441783e5f42e3be40a9e1a89ec906efabf26591c5cd415"}, + {file = "grpcio-1.68.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e46541de8425a4d6829ac6c5d9b16c03c292105fe9ebf78cb1c31e8d242f9155"}, + {file = "grpcio-1.68.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:c1245651f3c9ea92a2db4f95d37b7597db6b246d5892bca6ee8c0e90d76fb73c"}, + {file = "grpcio-1.68.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f1931c7aa85be0fa6cea6af388e576f3bf6baee9e5d481c586980c774debcb4"}, + {file = "grpcio-1.68.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b0ff09c81e3aded7a183bc6473639b46b6caa9c1901d6f5e2cba24b95e59e30"}, + {file = "grpcio-1.68.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8c73f9fbbaee1a132487e31585aa83987ddf626426d703ebcb9a528cf231c9b1"}, + {file = "grpcio-1.68.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6b2f98165ea2790ea159393a2246b56f580d24d7da0d0342c18a085299c40a75"}, + {file = "grpcio-1.68.0-cp311-cp311-win32.whl", hash = "sha256:e1e7ed311afb351ff0d0e583a66fcb39675be112d61e7cfd6c8269884a98afbc"}, + {file = "grpcio-1.68.0-cp311-cp311-win_amd64.whl", hash = "sha256:e0d2f68eaa0a755edd9a47d40e50dba6df2bceda66960dee1218da81a2834d27"}, + {file = "grpcio-1.68.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8af6137cc4ae8e421690d276e7627cfc726d4293f6607acf9ea7260bd8fc3d7d"}, + {file = "grpcio-1.68.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4028b8e9a3bff6f377698587d642e24bd221810c06579a18420a17688e421af7"}, + {file = "grpcio-1.68.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f60fa2adf281fd73ae3a50677572521edca34ba373a45b457b5ebe87c2d01e1d"}, + {file = "grpcio-1.68.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e18589e747c1e70b60fab6767ff99b2d0c359ea1db8a2cb524477f93cdbedf5b"}, + {file = "grpcio-1.68.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0d30f3fee9372796f54d3100b31ee70972eaadcc87314be369360248a3dcffe"}, + {file = "grpcio-1.68.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7e0a3e72c0e9a1acab77bef14a73a416630b7fd2cbd893c0a873edc47c42c8cd"}, + {file = "grpcio-1.68.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a831dcc343440969aaa812004685ed322cdb526cd197112d0db303b0da1e8659"}, + {file = "grpcio-1.68.0-cp312-cp312-win32.whl", hash = "sha256:5a180328e92b9a0050958ced34dddcb86fec5a8b332f5a229e353dafc16cd332"}, + {file = "grpcio-1.68.0-cp312-cp312-win_amd64.whl", hash = "sha256:2bddd04a790b69f7a7385f6a112f46ea0b34c4746f361ebafe9ca0be567c78e9"}, + {file = "grpcio-1.68.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:fc05759ffbd7875e0ff2bd877be1438dfe97c9312bbc558c8284a9afa1d0f40e"}, + {file = "grpcio-1.68.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:15fa1fe25d365a13bc6d52fcac0e3ee1f9baebdde2c9b3b2425f8a4979fccea1"}, + {file = "grpcio-1.68.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:32a9cb4686eb2e89d97022ecb9e1606d132f85c444354c17a7dbde4a455e4a3b"}, + {file = "grpcio-1.68.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dba037ff8d284c8e7ea9a510c8ae0f5b016004f13c3648f72411c464b67ff2fb"}, + {file = "grpcio-1.68.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0efbbd849867e0e569af09e165363ade75cf84f5229b2698d53cf22c7a4f9e21"}, + {file = "grpcio-1.68.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:4e300e6978df0b65cc2d100c54e097c10dfc7018b9bd890bbbf08022d47f766d"}, + {file = "grpcio-1.68.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:6f9c7ad1a23e1047f827385f4713b5b8c6c7d325705be1dd3e31fb00dcb2f665"}, + {file = "grpcio-1.68.0-cp313-cp313-win32.whl", hash = "sha256:3ac7f10850fd0487fcce169c3c55509101c3bde2a3b454869639df2176b60a03"}, + {file = "grpcio-1.68.0-cp313-cp313-win_amd64.whl", hash = "sha256:afbf45a62ba85a720491bfe9b2642f8761ff348006f5ef67e4622621f116b04a"}, + {file = "grpcio-1.68.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:f8f695d9576ce836eab27ba7401c60acaf9ef6cf2f70dfe5462055ba3df02cc3"}, + {file = "grpcio-1.68.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9fe1b141cda52f2ca73e17d2d3c6a9f3f3a0c255c216b50ce616e9dca7e3441d"}, + {file = "grpcio-1.68.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:4df81d78fd1646bf94ced4fb4cd0a7fe2e91608089c522ef17bc7db26e64effd"}, + {file = "grpcio-1.68.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46a2d74d4dd8993151c6cd585594c082abe74112c8e4175ddda4106f2ceb022f"}, + {file = "grpcio-1.68.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a17278d977746472698460c63abf333e1d806bd41f2224f90dbe9460101c9796"}, + {file = "grpcio-1.68.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:15377bce516b1c861c35e18eaa1c280692bf563264836cece693c0f169b48829"}, + {file = "grpcio-1.68.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cc5f0a4f5904b8c25729a0498886b797feb817d1fd3812554ffa39551112c161"}, + {file = "grpcio-1.68.0-cp38-cp38-win32.whl", hash = "sha256:def1a60a111d24376e4b753db39705adbe9483ef4ca4761f825639d884d5da78"}, + {file = "grpcio-1.68.0-cp38-cp38-win_amd64.whl", hash = "sha256:55d3b52fd41ec5772a953612db4e70ae741a6d6ed640c4c89a64f017a1ac02b5"}, + {file = "grpcio-1.68.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:0d230852ba97654453d290e98d6aa61cb48fa5fafb474fb4c4298d8721809354"}, + {file = "grpcio-1.68.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:50992f214264e207e07222703c17d9cfdcc2c46ed5a1ea86843d440148ebbe10"}, + {file = "grpcio-1.68.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:14331e5c27ed3545360464a139ed279aa09db088f6e9502e95ad4bfa852bb116"}, + {file = "grpcio-1.68.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f84890b205692ea813653ece4ac9afa2139eae136e419231b0eec7c39fdbe4c2"}, + {file = "grpcio-1.68.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0cf343c6f4f6aa44863e13ec9ddfe299e0be68f87d68e777328bff785897b05"}, + {file = "grpcio-1.68.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fd2c2d47969daa0e27eadaf15c13b5e92605c5e5953d23c06d0b5239a2f176d3"}, + {file = "grpcio-1.68.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:18668e36e7f4045820f069997834e94e8275910b1f03e078a6020bd464cb2363"}, + {file = "grpcio-1.68.0-cp39-cp39-win32.whl", hash = "sha256:2af76ab7c427aaa26aa9187c3e3c42f38d3771f91a20f99657d992afada2294a"}, + {file = "grpcio-1.68.0-cp39-cp39-win_amd64.whl", hash = "sha256:e694b5928b7b33ca2d3b4d5f9bf8b5888906f181daff6b406f4938f3a997a490"}, + {file = "grpcio-1.68.0.tar.gz", hash = "sha256:7e7483d39b4a4fddb9906671e9ea21aaad4f031cdfc349fec76bdfa1e404543a"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.68.0)"] + [[package]] name = "h11" version = "0.14.0" @@ -901,6 +1286,61 @@ http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] trio = ["trio (>=0.22.0,<1.0)"] +[[package]] +name = "httptools" +version = "0.6.4" +description = "A collection of framework independent HTTP protocol utils." +optional = true +python-versions = ">=3.8.0" +files = [ + {file = "httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0"}, + {file = "httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da"}, + {file = "httptools-0.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deee0e3343f98ee8047e9f4c5bc7cedbf69f5734454a94c38ee829fb2d5fa3c1"}, + {file = "httptools-0.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca80b7485c76f768a3bc83ea58373f8db7b015551117375e4918e2aa77ea9b50"}, + {file = "httptools-0.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90d96a385fa941283ebd231464045187a31ad932ebfa541be8edf5b3c2328959"}, + {file = "httptools-0.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59e724f8b332319e2875efd360e61ac07f33b492889284a3e05e6d13746876f4"}, + {file = "httptools-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:c26f313951f6e26147833fc923f78f95604bbec812a43e5ee37f26dc9e5a686c"}, + {file = "httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069"}, + {file = "httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a"}, + {file = "httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975"}, + {file = "httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636"}, + {file = "httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721"}, + {file = "httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988"}, + {file = "httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17"}, + {file = "httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2"}, + {file = "httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44"}, + {file = "httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1"}, + {file = "httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2"}, + {file = "httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81"}, + {file = "httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f"}, + {file = "httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970"}, + {file = "httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660"}, + {file = "httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083"}, + {file = "httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3"}, + {file = "httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071"}, + {file = "httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5"}, + {file = "httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0"}, + {file = "httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8"}, + {file = "httptools-0.6.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d3f0d369e7ffbe59c4b6116a44d6a8eb4783aae027f2c0b366cf0aa964185dba"}, + {file = "httptools-0.6.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:94978a49b8f4569ad607cd4946b759d90b285e39c0d4640c6b36ca7a3ddf2efc"}, + {file = "httptools-0.6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40dc6a8e399e15ea525305a2ddba998b0af5caa2566bcd79dcbe8948181eeaff"}, + {file = "httptools-0.6.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab9ba8dcf59de5181f6be44a77458e45a578fc99c31510b8c65b7d5acc3cf490"}, + {file = "httptools-0.6.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:fc411e1c0a7dcd2f902c7c48cf079947a7e65b5485dea9decb82b9105ca71a43"}, + {file = "httptools-0.6.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d54efd20338ac52ba31e7da78e4a72570cf729fac82bc31ff9199bedf1dc7440"}, + {file = "httptools-0.6.4-cp38-cp38-win_amd64.whl", hash = "sha256:df959752a0c2748a65ab5387d08287abf6779ae9165916fe053e68ae1fbdc47f"}, + {file = "httptools-0.6.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:85797e37e8eeaa5439d33e556662cc370e474445d5fab24dcadc65a8ffb04003"}, + {file = "httptools-0.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:db353d22843cf1028f43c3651581e4bb49374d85692a85f95f7b9a130e1b2cab"}, + {file = "httptools-0.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1ffd262a73d7c28424252381a5b854c19d9de5f56f075445d33919a637e3547"}, + {file = "httptools-0.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c346571fa50d2e9856a37d7cd9435a25e7fd15e236c397bf224afaa355fe9"}, + {file = "httptools-0.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aafe0f1918ed07b67c1e838f950b1c1fabc683030477e60b335649b8020e1076"}, + {file = "httptools-0.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0e563e54979e97b6d13f1bbc05a96109923e76b901f786a5eae36e99c01237bd"}, + {file = "httptools-0.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:b799de31416ecc589ad79dd85a0b2657a8fe39327944998dea368c1d4c9e55e6"}, + {file = "httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c"}, +] + +[package.extras] +test = ["Cython (>=0.29.24)"] + [[package]] name = "httpx" version = "0.27.2" @@ -960,6 +1400,20 @@ testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gr torch = ["safetensors[torch]", "torch"] typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] +[[package]] +name = "humanfriendly" +version = "10.0" +description = "Human friendly output for text interfaces using Python" +optional = true +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"}, + {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"}, +] + +[package.dependencies] +pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_version >= \"3.8\""} + [[package]] name = "identify" version = "2.6.1" @@ -999,6 +1453,48 @@ files = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] +[[package]] +name = "importlib-metadata" +version = "8.5.0" +description = "Read metadata from Python packages" +optional = true +python-versions = ">=3.8" +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"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +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)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + +[[package]] +name = "importlib-resources" +version = "6.4.5" +description = "Read resources from Python packages" +optional = true +python-versions = ">=3.8" +files = [ + {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"}, + {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"}, +] + +[package.extras] +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 = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "zipp (>=3.17)"] +type = ["pytest-mypy"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -1314,6 +1810,33 @@ files = [ {file = "kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60"}, ] +[[package]] +name = "kubernetes" +version = "31.0.0" +description = "Kubernetes python client" +optional = true +python-versions = ">=3.6" +files = [ + {file = "kubernetes-31.0.0-py2.py3-none-any.whl", hash = "sha256:bf141e2d380c8520eada8b351f4e319ffee9636328c137aa432bc486ca1200e1"}, + {file = "kubernetes-31.0.0.tar.gz", hash = "sha256:28945de906c8c259c1ebe62703b56a03b714049372196f854105afe4e6d014c0"}, +] + +[package.dependencies] +certifi = ">=14.05.14" +durationpy = ">=0.7" +google-auth = ">=1.0.1" +oauthlib = ">=3.2.2" +python-dateutil = ">=2.5.3" +pyyaml = ">=5.4.1" +requests = "*" +requests-oauthlib = "*" +six = ">=1.9.0" +urllib3 = ">=1.24.2" +websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.dev0 || >=0.43.dev0" + +[package.extras] +adal = ["adal (>=1.0.2)"] + [[package]] name = "lxml" version = "5.3.0" @@ -1680,6 +2203,147 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "mmh3" +version = "5.0.1" +description = "Python extension for MurmurHash (MurmurHash3), a set of fast and robust hash functions." +optional = true +python-versions = ">=3.8" +files = [ + {file = "mmh3-5.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f0a4b4bf05778ed77d820d6e7d0e9bd6beb0c01af10e1ce9233f5d2f814fcafa"}, + {file = "mmh3-5.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac7a391039aeab95810c2d020b69a94eb6b4b37d4e2374831e92db3a0cdf71c6"}, + {file = "mmh3-5.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3a2583b5521ca49756d8d8bceba80627a9cc295f255dcab4e3df7ccc2f09679a"}, + {file = "mmh3-5.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:081a8423fe53c1ac94f87165f3e4c500125d343410c1a0c5f1703e898a3ef038"}, + {file = "mmh3-5.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8b4d72713799755dc8954a7d36d5c20a6c8de7b233c82404d122c7c7c1707cc"}, + {file = "mmh3-5.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:389a6fd51efc76d3182d36ec306448559c1244f11227d2bb771bdd0e6cc91321"}, + {file = "mmh3-5.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39f4128edaa074bff721b1d31a72508cba4d2887ee7867f22082e1fe9d4edea0"}, + {file = "mmh3-5.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d5d23a94d91aabba3386b3769048d5f4210fdfef80393fece2f34ba5a7b466c"}, + {file = "mmh3-5.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:16347d038361f8b8f24fd2b7ef378c9b68ddee9f7706e46269b6e0d322814713"}, + {file = "mmh3-5.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6e299408565af7d61f2d20a5ffdd77cf2ed902460fe4e6726839d59ba4b72316"}, + {file = "mmh3-5.0.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42050af21ddfc5445ee5a66e73a8fc758c71790305e3ee9e4a85a8e69e810f94"}, + {file = "mmh3-5.0.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2ae9b1f5ef27ec54659920f0404b7ceb39966e28867c461bfe83a05e8d18ddb0"}, + {file = "mmh3-5.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:50c2495a02045f3047d71d4ae9cdd7a15efc0bcbb7ff17a18346834a8e2d1d19"}, + {file = "mmh3-5.0.1-cp310-cp310-win32.whl", hash = "sha256:c028fa77cddf351ca13b4a56d43c1775652cde0764cadb39120b68f02a23ecf6"}, + {file = "mmh3-5.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:c5e741e421ec14400c4aae30890515c201f518403bdef29ae1e00d375bb4bbb5"}, + {file = "mmh3-5.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:b17156d56fabc73dbf41bca677ceb6faed435cc8544f6566d72ea77d8a17e9d0"}, + {file = "mmh3-5.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a6d5a9b1b923f1643559ba1fc0bf7a5076c90cbb558878d3bf3641ce458f25d"}, + {file = "mmh3-5.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3349b968be555f7334bbcce839da98f50e1e80b1c615d8e2aa847ea4a964a012"}, + {file = "mmh3-5.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1bd3c94b110e55db02ab9b605029f48a2f7f677c6e58c09d44e42402d438b7e1"}, + {file = "mmh3-5.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47ba84d48608f79adbb10bb09986b6dc33eeda5c2d1bd75d00820081b73bde9"}, + {file = "mmh3-5.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c0217987a8b8525c8d9170f66d036dec4ab45cfbd53d47e8d76125791ceb155e"}, + {file = "mmh3-5.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2797063a34e78d1b61639a98b0edec1c856fa86ab80c7ec859f1796d10ba429"}, + {file = "mmh3-5.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8bba16340adcbd47853a2fbe5afdb397549e8f2e79324ff1dced69a3f8afe7c3"}, + {file = "mmh3-5.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:282797957c9f60b51b9d768a602c25f579420cc9af46feb77d457a27823d270a"}, + {file = "mmh3-5.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e4fb670c29e63f954f9e7a2cdcd57b36a854c2538f579ef62681ccbaa1de2b69"}, + {file = "mmh3-5.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ee7d85438dc6aff328e19ab052086a3c29e8a9b632998a49e5c4b0034e9e8d6"}, + {file = "mmh3-5.0.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7fb5db231f3092444bc13901e6a8d299667126b00636ffbad4a7b45e1051e2f"}, + {file = "mmh3-5.0.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c100dd441703da5ec136b1d9003ed4a041d8a1136234c9acd887499796df6ad8"}, + {file = "mmh3-5.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:71f3b765138260fd7a7a2dba0ea5727dabcd18c1f80323c9cfef97a7e86e01d0"}, + {file = "mmh3-5.0.1-cp311-cp311-win32.whl", hash = "sha256:9a76518336247fd17689ce3ae5b16883fd86a490947d46a0193d47fb913e26e3"}, + {file = "mmh3-5.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:336bc4df2e44271f1c302d289cc3d78bd52d3eed8d306c7e4bff8361a12bf148"}, + {file = "mmh3-5.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:af6522722fbbc5999aa66f7244d0986767a46f1fb05accc5200f75b72428a508"}, + {file = "mmh3-5.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f2730bb263ed9c388e8860438b057a53e3cc701134a6ea140f90443c4c11aa40"}, + {file = "mmh3-5.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6246927bc293f6d56724536400b85fb85f5be26101fa77d5f97dd5e2a4c69bf2"}, + {file = "mmh3-5.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fbca322519a6e6e25b6abf43e940e1667cf8ea12510e07fb4919b48a0cd1c411"}, + {file = "mmh3-5.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eae8c19903ed8a1724ad9e67e86f15d198a7a1271a4f9be83d47e38f312ed672"}, + {file = "mmh3-5.0.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a09fd6cc72c07c0c07c3357714234b646d78052487c4a3bd5f7f6e08408cff60"}, + {file = "mmh3-5.0.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ff8551fee7ae3b11c5d986b6347ade0dccaadd4670ffdb2b944dee120ffcc84"}, + {file = "mmh3-5.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e39694c73a5a20c8bf36dfd8676ed351e5234d55751ba4f7562d85449b21ef3f"}, + {file = "mmh3-5.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eba6001989a92f72a89c7cf382fda831678bd780707a66b4f8ca90239fdf2123"}, + {file = "mmh3-5.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0771f90c9911811cc606a5c7b7b58f33501c9ee896ed68a6ac22c7d55878ecc0"}, + {file = "mmh3-5.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:09b31ed0c0c0920363e96641fac4efde65b1ab62b8df86293142f35a254e72b4"}, + {file = "mmh3-5.0.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5cf4a8deda0235312db12075331cb417c4ba163770edfe789bde71d08a24b692"}, + {file = "mmh3-5.0.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:41f7090a95185ef20ac018581a99337f0cbc84a2135171ee3290a9c0d9519585"}, + {file = "mmh3-5.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b97b5b368fb7ff22194ec5854f5b12d8de9ab67a0f304728c7f16e5d12135b76"}, + {file = "mmh3-5.0.1-cp312-cp312-win32.whl", hash = "sha256:842516acf04da546f94fad52db125ee619ccbdcada179da51c326a22c4578cb9"}, + {file = "mmh3-5.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:d963be0dbfd9fca209c17172f6110787ebf78934af25e3694fe2ba40e55c1e2b"}, + {file = "mmh3-5.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:a5da292ceeed8ce8e32b68847261a462d30fd7b478c3f55daae841404f433c15"}, + {file = "mmh3-5.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:673e3f1c8d4231d6fb0271484ee34cb7146a6499fc0df80788adb56fd76842da"}, + {file = "mmh3-5.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f795a306bd16a52ad578b663462cc8e95500b3925d64118ae63453485d67282b"}, + {file = "mmh3-5.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5ed57a5e28e502a1d60436cc25c76c3a5ba57545f250f2969af231dc1221e0a5"}, + {file = "mmh3-5.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:632c28e7612e909dbb6cbe2fe496201ada4695b7715584005689c5dc038e59ad"}, + {file = "mmh3-5.0.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53fd6bd525a5985e391c43384672d9d6b317fcb36726447347c7fc75bfed34ec"}, + {file = "mmh3-5.0.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dceacf6b0b961a0e499836af3aa62d60633265607aef551b2a3e3c48cdaa5edd"}, + {file = "mmh3-5.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f0738d478fdfb5d920f6aff5452c78f2c35b0eff72caa2a97dfe38e82f93da2"}, + {file = "mmh3-5.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e70285e7391ab88b872e5bef632bad16b9d99a6d3ca0590656a4753d55988af"}, + {file = "mmh3-5.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:27e5fc6360aa6b828546a4318da1a7da6bf6e5474ccb053c3a6aa8ef19ff97bd"}, + {file = "mmh3-5.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7989530c3c1e2c17bf5a0ec2bba09fd19819078ba90beedabb1c3885f5040b0d"}, + {file = "mmh3-5.0.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:cdad7bee649950da7ecd3cbbbd12fb81f1161072ecbdb5acfa0018338c5cb9cf"}, + {file = "mmh3-5.0.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e143b8f184c1bb58cecd85ab4a4fd6dc65a2d71aee74157392c3fddac2a4a331"}, + {file = "mmh3-5.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5eb12e886f3646dd636f16b76eb23fc0c27e8ff3c1ae73d4391e50ef60b40f6"}, + {file = "mmh3-5.0.1-cp313-cp313-win32.whl", hash = "sha256:16e6dddfa98e1c2d021268e72c78951234186deb4df6630e984ac82df63d0a5d"}, + {file = "mmh3-5.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:d3ffb792d70b8c4a2382af3598dad6ae0c5bd9cee5b7ffcc99aa2f5fd2c1bf70"}, + {file = "mmh3-5.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:122fa9ec148383f9124292962bda745f192b47bfd470b2af5fe7bb3982b17896"}, + {file = "mmh3-5.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b12bad8c75e6ff5d67319794fb6a5e8c713826c818d47f850ad08b4aa06960c6"}, + {file = "mmh3-5.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e5bbb066538c1048d542246fc347bb7994bdda29a3aea61c22f9f8b57111ce69"}, + {file = "mmh3-5.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:eee6134273f64e2a106827cc8fd77e70cc7239a285006fc6ab4977d59b015af2"}, + {file = "mmh3-5.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d04d9aa19d48e4c7bbec9cabc2c4dccc6ff3b2402f856d5bf0de03e10f167b5b"}, + {file = "mmh3-5.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79f37da1eed034d06567a69a7988456345c7f29e49192831c3975b464493b16e"}, + {file = "mmh3-5.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:242f77666743337aa828a2bf2da71b6ba79623ee7f93edb11e009f69237c8561"}, + {file = "mmh3-5.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffd943fff690463945f6441a2465555b3146deaadf6a5e88f2590d14c655d71b"}, + {file = "mmh3-5.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:565b15f8d7df43acb791ff5a360795c20bfa68bca8b352509e0fbabd06cc48cd"}, + {file = "mmh3-5.0.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:fc6aafb867c2030df98ac7760ff76b500359252867985f357bd387739f3d5287"}, + {file = "mmh3-5.0.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:32898170644d45aa27c974ab0d067809c066205110f5c6d09f47d9ece6978bfe"}, + {file = "mmh3-5.0.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:42865567838d2193eb64e0ef571f678bf361a254fcdef0c5c8e73243217829bd"}, + {file = "mmh3-5.0.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:5ff5c1f301c4a8b6916498969c0fcc7e3dbc56b4bfce5cfe3fe31f3f4609e5ae"}, + {file = "mmh3-5.0.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:be74c2dda8a6f44a504450aa2c3507f8067a159201586fc01dd41ab80efc350f"}, + {file = "mmh3-5.0.1-cp38-cp38-win32.whl", hash = "sha256:5610a842621ff76c04b20b29cf5f809b131f241a19d4937971ba77dc99a7f330"}, + {file = "mmh3-5.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:de15739ac50776fe8aa1ef13f1be46a6ee1fbd45f6d0651084097eb2be0a5aa4"}, + {file = "mmh3-5.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:48e84cf3cc7e8c41bc07de72299a73b92d9e3cde51d97851420055b1484995f7"}, + {file = "mmh3-5.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6dd9dc28c2d168c49928195c2e29b96f9582a5d07bd690a28aede4cc07b0e696"}, + {file = "mmh3-5.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2771a1c56a3d4bdad990309cff5d0a8051f29c8ec752d001f97d6392194ae880"}, + {file = "mmh3-5.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5ff2a8322ba40951a84411550352fba1073ce1c1d1213bb7530f09aed7f8caf"}, + {file = "mmh3-5.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a16bd3ec90682c9e0a343e6bd4c778c09947c8c5395cdb9e5d9b82b2559efbca"}, + {file = "mmh3-5.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d45733a78d68b5b05ff4a823aea51fa664df1d3bf4929b152ff4fd6dea2dd69b"}, + {file = "mmh3-5.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:904285e83cedebc8873b0838ed54c20f7344120be26e2ca5a907ab007a18a7a0"}, + {file = "mmh3-5.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac4aeb1784e43df728034d0ed72e4b2648db1a69fef48fa58e810e13230ae5ff"}, + {file = "mmh3-5.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:cb3d4f751a0b8b4c8d06ef1c085216c8fddcc8b8c8d72445976b5167a40c6d1e"}, + {file = "mmh3-5.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:8021851935600e60c42122ed1176399d7692df338d606195cd599d228a04c1c6"}, + {file = "mmh3-5.0.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6182d5924a5efc451900f864cbb021d7e8ad5d524816ca17304a0f663bc09bb5"}, + {file = "mmh3-5.0.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:5f30b834552a4f79c92e3d266336fb87fd92ce1d36dc6813d3e151035890abbd"}, + {file = "mmh3-5.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cd4383f35e915e06d077df27e04ffd3be7513ec6a9de2d31f430393f67e192a7"}, + {file = "mmh3-5.0.1-cp39-cp39-win32.whl", hash = "sha256:1455fb6b42665a97db8fc66e89a861e52b567bce27ed054c47877183f86ea6e3"}, + {file = "mmh3-5.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:9e26a0f4eb9855a143f5938a53592fa14c2d3b25801c2106886ab6c173982780"}, + {file = "mmh3-5.0.1-cp39-cp39-win_arm64.whl", hash = "sha256:0d0a35a69abdad7549c4030a714bb4ad07902edb3bbe61e1bbc403ded5d678be"}, + {file = "mmh3-5.0.1.tar.gz", hash = "sha256:7dab080061aeb31a6069a181f27c473a1f67933854e36a3464931f2716508896"}, +] + +[package.extras] +benchmark = ["pymmh3 (==0.0.5)", "pyperf (==2.7.0)", "xxhash (==3.5.0)"] +docs = ["myst-parser (==4.0.0)", "shibuya (==2024.8.30)", "sphinx (==8.0.2)", "sphinx-copybutton (==0.5.2)"] +lint = ["black (==24.8.0)", "clang-format (==18.1.8)", "isort (==5.13.2)", "pylint (==3.2.7)"] +plot = ["matplotlib (==3.9.2)", "pandas (==2.2.2)"] +test = ["pytest (==8.3.3)", "pytest-sugar (==1.0.0)"] +type = ["mypy (==1.11.2)"] + +[[package]] +name = "monotonic" +version = "1.6" +description = "An implementation of time.monotonic() for Python 2 & < 3.3" +optional = true +python-versions = "*" +files = [ + {file = "monotonic-1.6-py2.py3-none-any.whl", hash = "sha256:68687e19a14f11f26d140dd5c86f3dba4bf5df58003000ed467e0e2a69bca96c"}, + {file = "monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7"}, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +description = "Python library for arbitrary-precision floating-point arithmetic" +optional = true +python-versions = "*" +files = [ + {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, + {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, +] + +[package.extras] +develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] +docs = ["sphinx"] +gmpy = ["gmpy2 (>=2.1.0a4)"] +tests = ["pytest (>=4.6)"] + [[package]] name = "multiprocessing-logging" version = "0.3.4" @@ -1792,66 +2456,103 @@ files = [ [[package]] name = "numpy" -version = "2.1.2" +version = "1.26.4" description = "Fundamental package for array computing in Python" optional = true -python-versions = ">=3.10" +python-versions = ">=3.9" files = [ - {file = "numpy-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30d53720b726ec36a7f88dc873f0eec8447fbc93d93a8f079dfac2629598d6ee"}, - {file = "numpy-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8d3ca0a72dd8846eb6f7dfe8f19088060fcb76931ed592d29128e0219652884"}, - {file = "numpy-2.1.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:fc44e3c68ff00fd991b59092a54350e6e4911152682b4782f68070985aa9e648"}, - {file = "numpy-2.1.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:7c1c60328bd964b53f8b835df69ae8198659e2b9302ff9ebb7de4e5a5994db3d"}, - {file = "numpy-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cdb606a7478f9ad91c6283e238544451e3a95f30fb5467fbf715964341a8a86"}, - {file = "numpy-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d666cb72687559689e9906197e3bec7b736764df6a2e58ee265e360663e9baf7"}, - {file = "numpy-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c6eef7a2dbd0abfb0d9eaf78b73017dbfd0b54051102ff4e6a7b2980d5ac1a03"}, - {file = "numpy-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:12edb90831ff481f7ef5f6bc6431a9d74dc0e5ff401559a71e5e4611d4f2d466"}, - {file = "numpy-2.1.2-cp310-cp310-win32.whl", hash = "sha256:a65acfdb9c6ebb8368490dbafe83c03c7e277b37e6857f0caeadbbc56e12f4fb"}, - {file = "numpy-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:860ec6e63e2c5c2ee5e9121808145c7bf86c96cca9ad396c0bd3e0f2798ccbe2"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b42a1a511c81cc78cbc4539675713bbcf9d9c3913386243ceff0e9429ca892fe"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:faa88bc527d0f097abdc2c663cddf37c05a1c2f113716601555249805cf573f1"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:c82af4b2ddd2ee72d1fc0c6695048d457e00b3582ccde72d8a1c991b808bb20f"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:13602b3174432a35b16c4cfb5de9a12d229727c3dd47a6ce35111f2ebdf66ff4"}, - {file = "numpy-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ebec5fd716c5a5b3d8dfcc439be82a8407b7b24b230d0ad28a81b61c2f4659a"}, - {file = "numpy-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2b49c3c0804e8ecb05d59af8386ec2f74877f7ca8fd9c1e00be2672e4d399b1"}, - {file = "numpy-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2cbba4b30bf31ddbe97f1c7205ef976909a93a66bb1583e983adbd155ba72ac2"}, - {file = "numpy-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8e00ea6fc82e8a804433d3e9cedaa1051a1422cb6e443011590c14d2dea59146"}, - {file = "numpy-2.1.2-cp311-cp311-win32.whl", hash = "sha256:5006b13a06e0b38d561fab5ccc37581f23c9511879be7693bd33c7cd15ca227c"}, - {file = "numpy-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:f1eb068ead09f4994dec71c24b2844f1e4e4e013b9629f812f292f04bd1510d9"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7bf0a4f9f15b32b5ba53147369e94296f5fffb783db5aacc1be15b4bf72f43b"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b1d0fcae4f0949f215d4632be684a539859b295e2d0cb14f78ec231915d644db"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f751ed0a2f250541e19dfca9f1eafa31a392c71c832b6bb9e113b10d050cb0f1"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:bd33f82e95ba7ad632bc57837ee99dba3d7e006536200c4e9124089e1bf42426"}, - {file = "numpy-2.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b8cde4f11f0a975d1fd59373b32e2f5a562ade7cde4f85b7137f3de8fbb29a0"}, - {file = "numpy-2.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d95f286b8244b3649b477ac066c6906fbb2905f8ac19b170e2175d3d799f4df"}, - {file = "numpy-2.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ab4754d432e3ac42d33a269c8567413bdb541689b02d93788af4131018cbf366"}, - {file = "numpy-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e585c8ae871fd38ac50598f4763d73ec5497b0de9a0ab4ef5b69f01c6a046142"}, - {file = "numpy-2.1.2-cp312-cp312-win32.whl", hash = "sha256:9c6c754df29ce6a89ed23afb25550d1c2d5fdb9901d9c67a16e0b16eaf7e2550"}, - {file = "numpy-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:456e3b11cb79ac9946c822a56346ec80275eaf2950314b249b512896c0d2505e"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a84498e0d0a1174f2b3ed769b67b656aa5460c92c9554039e11f20a05650f00d"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4d6ec0d4222e8ffdab1744da2560f07856421b367928026fb540e1945f2eeeaf"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:259ec80d54999cc34cd1eb8ded513cb053c3bf4829152a2e00de2371bd406f5e"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:675c741d4739af2dc20cd6c6a5c4b7355c728167845e3c6b0e824e4e5d36a6c3"}, - {file = "numpy-2.1.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b2d4e667895cc55e3ff2b56077e4c8a5604361fc21a042845ea3ad67465aa8"}, - {file = "numpy-2.1.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43cca367bf94a14aca50b89e9bc2061683116cfe864e56740e083392f533ce7a"}, - {file = "numpy-2.1.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:76322dcdb16fccf2ac56f99048af32259dcc488d9b7e25b51e5eca5147a3fb98"}, - {file = "numpy-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:32e16a03138cabe0cb28e1007ee82264296ac0983714094380b408097a418cfe"}, - {file = "numpy-2.1.2-cp313-cp313-win32.whl", hash = "sha256:242b39d00e4944431a3cd2db2f5377e15b5785920421993770cddb89992c3f3a"}, - {file = "numpy-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f2ded8d9b6f68cc26f8425eda5d3877b47343e68ca23d0d0846f4d312ecaa445"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ffef621c14ebb0188a8633348504a35c13680d6da93ab5cb86f4e54b7e922b5"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ad369ed238b1959dfbade9018a740fb9392c5ac4f9b5173f420bd4f37ba1f7a0"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d82075752f40c0ddf57e6e02673a17f6cb0f8eb3f587f63ca1eaab5594da5b17"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:1600068c262af1ca9580a527d43dc9d959b0b1d8e56f8a05d830eea39b7c8af6"}, - {file = "numpy-2.1.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a26ae94658d3ba3781d5e103ac07a876b3e9b29db53f68ed7df432fd033358a8"}, - {file = "numpy-2.1.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13311c2db4c5f7609b462bc0f43d3c465424d25c626d95040f073e30f7570e35"}, - {file = "numpy-2.1.2-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:2abbf905a0b568706391ec6fa15161fad0fb5d8b68d73c461b3c1bab6064dd62"}, - {file = "numpy-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ef444c57d664d35cac4e18c298c47d7b504c66b17c2ea91312e979fcfbdfb08a"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bdd407c40483463898b84490770199d5714dcc9dd9b792f6c6caccc523c00952"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:da65fb46d4cbb75cb417cddf6ba5e7582eb7bb0b47db4b99c9fe5787ce5d91f5"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c193d0b0238638e6fc5f10f1b074a6993cb13b0b431f64079a509d63d3aa8b7"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a7d80b2e904faa63068ead63107189164ca443b42dd1930299e0d1cb041cec2e"}, - {file = "numpy-2.1.2.tar.gz", hash = "sha256:13532a088217fa624c99b843eeb54640de23b3414b14aa66d023805eb731066c"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +] + +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +optional = true +python-versions = ">=3.6" +files = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, ] +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + +[[package]] +name = "onnxruntime" +version = "1.20.0" +description = "ONNX Runtime is a runtime accelerator for Machine Learning models" +optional = true +python-versions = "*" +files = [ + {file = "onnxruntime-1.20.0-cp310-cp310-macosx_13_0_universal2.whl", hash = "sha256:2ac38bc6cbf7bb8527ded58711af6ef2c8c59d070f0fde58f83824422526922a"}, + {file = "onnxruntime-1.20.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cfd5a22abc11b273ec76fa773e22db19b749e27bf1ed05dd50d207f1817aae1"}, + {file = "onnxruntime-1.20.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b5daee2d03909b589f1a9ab24c325cc3c33ab7f736228158784fb1a97a92308"}, + {file = "onnxruntime-1.20.0-cp310-cp310-win32.whl", hash = "sha256:e1eb08c13f91f830eb8df4f4e17a2a2652d1165f50bbed4f28f2afbf425c55d7"}, + {file = "onnxruntime-1.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:cfcc1d21a12076bcc213441b405c48e1f21dedb36943e31eb93cb7a12b34678e"}, + {file = "onnxruntime-1.20.0-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:3398354e9145c68edc09dbc72265401150027e76716ae758e8d9b52e6a7ddca0"}, + {file = "onnxruntime-1.20.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a831b720d0a7be8241a230cb06f592e8bb66652d7cea54ce02d83769651fdee"}, + {file = "onnxruntime-1.20.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:041fefe60af844ebd90f344c84f908201490555cd0a6d78dd0a7acdc27b59972"}, + {file = "onnxruntime-1.20.0-cp311-cp311-win32.whl", hash = "sha256:83da64d2824809d0f6977db8bfc5091f742c26f09dfd66a3934e673780f5f87a"}, + {file = "onnxruntime-1.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:bfa390046332f5fca6f8af8c9d17164621ac52e66b11518e187278b19364800c"}, + {file = "onnxruntime-1.20.0-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:97c2b91bfea063f9c3457422d28a336bfd2859001cd880645adfa7184e29dd79"}, + {file = "onnxruntime-1.20.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51e7b34e398089c4ed8d0f50722d7a64a4d5f11b38c4a42576458a03c6dbc72e"}, + {file = "onnxruntime-1.20.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e259378ff2843321e0bf4552adcbee48822c91d77d42dde78b87dcdf10ad01f"}, + {file = "onnxruntime-1.20.0-cp312-cp312-win32.whl", hash = "sha256:428abc1f7d8eb425887e2b7726044f2af7b5a098359455e7d2d92343f04ad0ff"}, + {file = "onnxruntime-1.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:d5f23cbfeb546e16ffea81c28d2e796a53197fdc6c92540648e2aa53a7c7a637"}, + {file = "onnxruntime-1.20.0-cp313-cp313-macosx_13_0_universal2.whl", hash = "sha256:95b91126bc3e1754868da1d3d2d08a7a10279b8ff5cea5e34e92fbe3fd691dcf"}, + {file = "onnxruntime-1.20.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d57c10d7729347d6663f32b3f569f33d69a95e150d37ff6af4be9b9ab1ffdc25"}, + {file = "onnxruntime-1.20.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9c38735dac127d0eeb957ec312c8f1ae90ecae2779a55b2fa279aa7bd116cbd"}, + {file = "onnxruntime-1.20.0-cp313-cp313-win_amd64.whl", hash = "sha256:25514cec4ea251d492aa1e38a7395d8801e64a4c940a154aef84cfad97ae4628"}, + {file = "onnxruntime-1.20.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:640ad9ea72d322f0325a51544eddb54f4fa843c4348573c88a9cb44f46678f3f"}, + {file = "onnxruntime-1.20.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc4e7c10c98c1f407835448c26a7e14ebff3234f131e1fbc53bd9500c828df89"}, +] + +[package.dependencies] +coloredlogs = "*" +flatbuffers = "*" +numpy = ">=1.21.6" +packaging = "*" +protobuf = "*" +sympy = "*" + [[package]] name = "openai" version = "1.51.2" @@ -1876,6 +2577,248 @@ typing-extensions = ">=4.11,<5" [package.extras] datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] +[[package]] +name = "opentelemetry-api" +version = "1.28.1" +description = "OpenTelemetry Python API" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_api-1.28.1-py3-none-any.whl", hash = "sha256:bfe86c95576cf19a914497f439fd79c9553a38de0adbdc26f7cfc46b0c00b16c"}, + {file = "opentelemetry_api-1.28.1.tar.gz", hash = "sha256:6fa7295a12c707f5aebef82da3d9ec5afe6992f3e42bfe7bec0339a44b3518e7"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +importlib-metadata = ">=6.0,<=8.5.0" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.28.1" +description = "OpenTelemetry Protobuf encoding" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_exporter_otlp_proto_common-1.28.1-py3-none-any.whl", hash = "sha256:56ea6cf28c90f767733f046a54525dc7271a25faff86b1955e5252b55f4e007f"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.28.1.tar.gz", hash = "sha256:6e55e7f5d59296cc87a74c08b8e0ddf87403f73a62302ec7ee042c1a1f4a8f70"}, +] + +[package.dependencies] +opentelemetry-proto = "1.28.1" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.28.1" +description = "OpenTelemetry Collector Protobuf over gRPC Exporter" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_exporter_otlp_proto_grpc-1.28.1-py3-none-any.whl", hash = "sha256:fd494b9dd7869975138cef68d52ed45b9ca584c1fa31bef2d01ecfd537445dfa"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.28.1.tar.gz", hash = "sha256:9c84a103734d0c9cf9a4ba973d9c15c21996a554ab2bbd6208b3925873912642"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +googleapis-common-protos = ">=1.52,<2.0" +grpcio = ">=1.63.2,<2.0.0" +opentelemetry-api = ">=1.15,<2.0" +opentelemetry-exporter-otlp-proto-common = "1.28.1" +opentelemetry-proto = "1.28.1" +opentelemetry-sdk = ">=1.28.1,<1.29.0" + +[[package]] +name = "opentelemetry-instrumentation" +version = "0.49b1" +description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_instrumentation-0.49b1-py3-none-any.whl", hash = "sha256:0a9d3821736104013693ef3b8a9d29b41f2f3a81ee2d8c9288b52d62bae5747c"}, + {file = "opentelemetry_instrumentation-0.49b1.tar.gz", hash = "sha256:2d0e41181b7957ba061bb436b969ad90545ac3eba65f290830009b4264d2824e"}, +] + +[package.dependencies] +opentelemetry-api = ">=1.4,<2.0" +opentelemetry-semantic-conventions = "0.49b1" +packaging = ">=18.0" +wrapt = ">=1.0.0,<2.0.0" + +[[package]] +name = "opentelemetry-instrumentation-asgi" +version = "0.49b1" +description = "ASGI instrumentation for OpenTelemetry" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_instrumentation_asgi-0.49b1-py3-none-any.whl", hash = "sha256:8dcbc438cb138789fcb20ae38b6e7f23088e066d77b54bae205c5744856603c6"}, + {file = "opentelemetry_instrumentation_asgi-0.49b1.tar.gz", hash = "sha256:d1a2b4cb76490be28bcad3c0f562c4b3c84157148c922ca298bb04ed9e36c005"}, +] + +[package.dependencies] +asgiref = ">=3.0,<4.0" +opentelemetry-api = ">=1.12,<2.0" +opentelemetry-instrumentation = "0.49b1" +opentelemetry-semantic-conventions = "0.49b1" +opentelemetry-util-http = "0.49b1" + +[package.extras] +instruments = ["asgiref (>=3.0,<4.0)"] + +[[package]] +name = "opentelemetry-instrumentation-fastapi" +version = "0.49b1" +description = "OpenTelemetry FastAPI Instrumentation" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_instrumentation_fastapi-0.49b1-py3-none-any.whl", hash = "sha256:3398940102c8ef613b9c55fc4f179cc92413de456f6bec6eeb1995270de2b087"}, + {file = "opentelemetry_instrumentation_fastapi-0.49b1.tar.gz", hash = "sha256:13d9d4d70b4bb831468b8e40807353731cad7fbfaeedde0070d93bcb2c417b07"}, +] + +[package.dependencies] +opentelemetry-api = ">=1.12,<2.0" +opentelemetry-instrumentation = "0.49b1" +opentelemetry-instrumentation-asgi = "0.49b1" +opentelemetry-semantic-conventions = "0.49b1" +opentelemetry-util-http = "0.49b1" + +[package.extras] +instruments = ["fastapi (>=0.58,<1.0)"] + +[[package]] +name = "opentelemetry-proto" +version = "1.28.1" +description = "OpenTelemetry Python Proto" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_proto-1.28.1-py3-none-any.whl", hash = "sha256:cb406ec69f1d11439e60fb43c6b744783fc8ee4deecdab61b3e29f112b0602f9"}, + {file = "opentelemetry_proto-1.28.1.tar.gz", hash = "sha256:6f9e9d9958822ab3e3cdcd2a24806d62aa10282349fd4338aafe32c69c87fc15"}, +] + +[package.dependencies] +protobuf = ">=5.0,<6.0" + +[[package]] +name = "opentelemetry-sdk" +version = "1.28.1" +description = "OpenTelemetry Python SDK" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_sdk-1.28.1-py3-none-any.whl", hash = "sha256:72aad7f5fcbe37113c4ab4899f6cdeb6ac77ed3e62f25a85e3627b12583dad0f"}, + {file = "opentelemetry_sdk-1.28.1.tar.gz", hash = "sha256:100fa371b2046ffba6a340c18f0b2a0463acad7461e5177e126693b613a6ca57"}, +] + +[package.dependencies] +opentelemetry-api = "1.28.1" +opentelemetry-semantic-conventions = "0.49b1" +typing-extensions = ">=3.7.4" + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.49b1" +description = "OpenTelemetry Semantic Conventions" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_semantic_conventions-0.49b1-py3-none-any.whl", hash = "sha256:dd6f3ac8169d2198c752e1a63f827e5f5e110ae9b0ce33f2aad9a3baf0739743"}, + {file = "opentelemetry_semantic_conventions-0.49b1.tar.gz", hash = "sha256:91817883b159ffb94c2ca9548509c4fe0aafce7c24f437aa6ac3fc613aa9a758"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +opentelemetry-api = "1.28.1" + +[[package]] +name = "opentelemetry-util-http" +version = "0.49b1" +description = "Web util for OpenTelemetry" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_util_http-0.49b1-py3-none-any.whl", hash = "sha256:0290b942f7888b6310df6803e52e12f4043b8f224db0659f62dc7b70059eb94f"}, + {file = "opentelemetry_util_http-0.49b1.tar.gz", hash = "sha256:6c2bc6f7e20e286dbdfcccb9d895fa290ec9d7c596cdf2e06bf1d8e434b2edd0"}, +] + +[[package]] +name = "orjson" +version = "3.10.11" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = true +python-versions = ">=3.8" +files = [ + {file = "orjson-3.10.11-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6dade64687f2bd7c090281652fe18f1151292d567a9302b34c2dbb92a3872f1f"}, + {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82f07c550a6ccd2b9290849b22316a609023ed851a87ea888c0456485a7d196a"}, + {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd9a187742d3ead9df2e49240234d728c67c356516cf4db018833a86f20ec18c"}, + {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77b0fed6f209d76c1c39f032a70df2d7acf24b1812ca3e6078fd04e8972685a3"}, + {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63fc9d5fe1d4e8868f6aae547a7b8ba0a2e592929245fff61d633f4caccdcdd6"}, + {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65cd3e3bb4fbb4eddc3c1e8dce10dc0b73e808fcb875f9fab40c81903dd9323e"}, + {file = "orjson-3.10.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6f67c570602300c4befbda12d153113b8974a3340fdcf3d6de095ede86c06d92"}, + {file = "orjson-3.10.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1f39728c7f7d766f1f5a769ce4d54b5aaa4c3f92d5b84817053cc9995b977acc"}, + {file = "orjson-3.10.11-cp310-none-win32.whl", hash = "sha256:1789d9db7968d805f3d94aae2c25d04014aae3a2fa65b1443117cd462c6da647"}, + {file = "orjson-3.10.11-cp310-none-win_amd64.whl", hash = "sha256:5576b1e5a53a5ba8f8df81872bb0878a112b3ebb1d392155f00f54dd86c83ff6"}, + {file = "orjson-3.10.11-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1444f9cb7c14055d595de1036f74ecd6ce15f04a715e73f33bb6326c9cef01b6"}, + {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdec57fe3b4bdebcc08a946db3365630332dbe575125ff3d80a3272ebd0ddafe"}, + {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4eed32f33a0ea6ef36ccc1d37f8d17f28a1d6e8eefae5928f76aff8f1df85e67"}, + {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80df27dd8697242b904f4ea54820e2d98d3f51f91e97e358fc13359721233e4b"}, + {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:705f03cee0cb797256d54de6695ef219e5bc8c8120b6654dd460848d57a9af3d"}, + {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03246774131701de8e7059b2e382597da43144a9a7400f178b2a32feafc54bd5"}, + {file = "orjson-3.10.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8b5759063a6c940a69c728ea70d7c33583991c6982915a839c8da5f957e0103a"}, + {file = "orjson-3.10.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:677f23e32491520eebb19c99bb34675daf5410c449c13416f7f0d93e2cf5f981"}, + {file = "orjson-3.10.11-cp311-none-win32.whl", hash = "sha256:a11225d7b30468dcb099498296ffac36b4673a8398ca30fdaec1e6c20df6aa55"}, + {file = "orjson-3.10.11-cp311-none-win_amd64.whl", hash = "sha256:df8c677df2f9f385fcc85ab859704045fa88d4668bc9991a527c86e710392bec"}, + {file = "orjson-3.10.11-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:360a4e2c0943da7c21505e47cf6bd725588962ff1d739b99b14e2f7f3545ba51"}, + {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:496e2cb45de21c369079ef2d662670a4892c81573bcc143c4205cae98282ba97"}, + {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7dfa8db55c9792d53c5952900c6a919cfa377b4f4534c7a786484a6a4a350c19"}, + {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51f3382415747e0dbda9dade6f1e1a01a9d37f630d8c9049a8ed0e385b7a90c0"}, + {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f35a1b9f50a219f470e0e497ca30b285c9f34948d3c8160d5ad3a755d9299433"}, + {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f3b7c5803138e67028dde33450e054c87e0703afbe730c105f1fcd873496d5"}, + {file = "orjson-3.10.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f91d9eb554310472bd09f5347950b24442600594c2edc1421403d7610a0998fd"}, + {file = "orjson-3.10.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dfbb2d460a855c9744bbc8e36f9c3a997c4b27d842f3d5559ed54326e6911f9b"}, + {file = "orjson-3.10.11-cp312-none-win32.whl", hash = "sha256:d4a62c49c506d4d73f59514986cadebb7e8d186ad510c518f439176cf8d5359d"}, + {file = "orjson-3.10.11-cp312-none-win_amd64.whl", hash = "sha256:f1eec3421a558ff7a9b010a6c7effcfa0ade65327a71bb9b02a1c3b77a247284"}, + {file = "orjson-3.10.11-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c46294faa4e4d0eb73ab68f1a794d2cbf7bab33b1dda2ac2959ffb7c61591899"}, + {file = "orjson-3.10.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52e5834d7d6e58a36846e059d00559cb9ed20410664f3ad156cd2cc239a11230"}, + {file = "orjson-3.10.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2fc947e5350fdce548bfc94f434e8760d5cafa97fb9c495d2fef6757aa02ec0"}, + {file = "orjson-3.10.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0efabbf839388a1dab5b72b5d3baedbd6039ac83f3b55736eb9934ea5494d258"}, + {file = "orjson-3.10.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3f29634260708c200c4fe148e42b4aae97d7b9fee417fbdd74f8cfc265f15b0"}, + {file = "orjson-3.10.11-cp313-none-win32.whl", hash = "sha256:1a1222ffcee8a09476bbdd5d4f6f33d06d0d6642df2a3d78b7a195ca880d669b"}, + {file = "orjson-3.10.11-cp313-none-win_amd64.whl", hash = "sha256:bc274ac261cc69260913b2d1610760e55d3c0801bb3457ba7b9004420b6b4270"}, + {file = "orjson-3.10.11-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:19b3763e8bbf8ad797df6b6b5e0fc7c843ec2e2fc0621398534e0c6400098f87"}, + {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1be83a13312e5e58d633580c5eb8d0495ae61f180da2722f20562974188af205"}, + {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:afacfd1ab81f46dedd7f6001b6d4e8de23396e4884cd3c3436bd05defb1a6446"}, + {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cb4d0bea56bba596723d73f074c420aec3b2e5d7d30698bc56e6048066bd560c"}, + {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96ed1de70fcb15d5fed529a656df29f768187628727ee2788344e8a51e1c1350"}, + {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bfb30c891b530f3f80e801e3ad82ef150b964e5c38e1fb8482441c69c35c61c"}, + {file = "orjson-3.10.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d496c74fc2b61341e3cefda7eec21b7854c5f672ee350bc55d9a4997a8a95204"}, + {file = "orjson-3.10.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:655a493bac606655db9a47fe94d3d84fc7f3ad766d894197c94ccf0c5408e7d3"}, + {file = "orjson-3.10.11-cp38-none-win32.whl", hash = "sha256:b9546b278c9fb5d45380f4809e11b4dd9844ca7aaf1134024503e134ed226161"}, + {file = "orjson-3.10.11-cp38-none-win_amd64.whl", hash = "sha256:b592597fe551d518f42c5a2eb07422eb475aa8cfdc8c51e6da7054b836b26782"}, + {file = "orjson-3.10.11-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c95f2ecafe709b4e5c733b5e2768ac569bed308623c85806c395d9cca00e08af"}, + {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80c00d4acded0c51c98754fe8218cb49cb854f0f7eb39ea4641b7f71732d2cb7"}, + {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:461311b693d3d0a060439aa669c74f3603264d4e7a08faa68c47ae5a863f352d"}, + {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52ca832f17d86a78cbab86cdc25f8c13756ebe182b6fc1a97d534051c18a08de"}, + {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c57ea78a753812f528178aa2f1c57da633754c91d2124cb28991dab4c79a54"}, + {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7fcfc6f7ca046383fb954ba528587e0f9336828b568282b27579c49f8e16aad"}, + {file = "orjson-3.10.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:86b9dd983857970c29e4c71bb3e95ff085c07d3e83e7c46ebe959bac07ebd80b"}, + {file = "orjson-3.10.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4d83f87582d223e54efb2242a79547611ba4ebae3af8bae1e80fa9a0af83bb7f"}, + {file = "orjson-3.10.11-cp39-none-win32.whl", hash = "sha256:9fd0ad1c129bc9beb1154c2655f177620b5beaf9a11e0d10bac63ef3fce96950"}, + {file = "orjson-3.10.11-cp39-none-win_amd64.whl", hash = "sha256:10f416b2a017c8bd17f325fb9dee1fb5cdd7a54e814284896b7c3f2763faa017"}, + {file = "orjson-3.10.11.tar.gz", hash = "sha256:e35b6d730de6384d5b2dab5fd23f0d76fae8bbc8c353c2f78210aa5fa4beb3ef"}, +] + +[[package]] +name = "overrides" +version = "7.7.0" +description = "A decorator to automatically detect mismatch when overriding a method." +optional = true +python-versions = ">=3.6" +files = [ + {file = "overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49"}, + {file = "overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a"}, +] + [[package]] name = "packaging" version = "24.1" @@ -2159,6 +3102,29 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "posthog" +version = "3.7.0" +description = "Integrate PostHog into any python application." +optional = true +python-versions = "*" +files = [ + {file = "posthog-3.7.0-py2.py3-none-any.whl", hash = "sha256:3555161c3a9557b5666f96d8e1f17f410ea0f07db56e399e336a1656d4e5c722"}, + {file = "posthog-3.7.0.tar.gz", hash = "sha256:b095d4354ba23f8b346ab5daed8ecfc5108772f922006982dfe8b2d29ebc6e0e"}, +] + +[package.dependencies] +backoff = ">=1.10.0" +monotonic = ">=1.5" +python-dateutil = ">2.1" +requests = ">=2.7,<3.0" +six = ">=1.5" + +[package.extras] +dev = ["black", "flake8", "flake8-print", "isort", "pre-commit"] +sentry = ["django", "sentry-sdk"] +test = ["coverage", "django", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint", "pytest", "pytest-timeout"] + [[package]] name = "pre-commit" version = "4.0.1" @@ -2191,6 +3157,56 @@ files = [ [package.dependencies] wcwidth = "*" +[[package]] +name = "protobuf" +version = "5.28.3" +description = "" +optional = true +python-versions = ">=3.8" +files = [ + {file = "protobuf-5.28.3-cp310-abi3-win32.whl", hash = "sha256:0c4eec6f987338617072592b97943fdbe30d019c56126493111cf24344c1cc24"}, + {file = "protobuf-5.28.3-cp310-abi3-win_amd64.whl", hash = "sha256:91fba8f445723fcf400fdbe9ca796b19d3b1242cd873907979b9ed71e4afe868"}, + {file = "protobuf-5.28.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a3f6857551e53ce35e60b403b8a27b0295f7d6eb63d10484f12bc6879c715687"}, + {file = "protobuf-5.28.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:3fa2de6b8b29d12c61911505d893afe7320ce7ccba4df913e2971461fa36d584"}, + {file = "protobuf-5.28.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:712319fbdddb46f21abb66cd33cb9e491a5763b2febd8f228251add221981135"}, + {file = "protobuf-5.28.3-cp38-cp38-win32.whl", hash = "sha256:3e6101d095dfd119513cde7259aa703d16c6bbdfae2554dfe5cfdbe94e32d548"}, + {file = "protobuf-5.28.3-cp38-cp38-win_amd64.whl", hash = "sha256:27b246b3723692bf1068d5734ddaf2fccc2cdd6e0c9b47fe099244d80200593b"}, + {file = "protobuf-5.28.3-cp39-cp39-win32.whl", hash = "sha256:135658402f71bbd49500322c0f736145731b16fc79dc8f367ab544a17eab4535"}, + {file = "protobuf-5.28.3-cp39-cp39-win_amd64.whl", hash = "sha256:70585a70fc2dd4818c51287ceef5bdba6387f88a578c86d47bb34669b5552c36"}, + {file = "protobuf-5.28.3-py3-none-any.whl", hash = "sha256:cee1757663fa32a1ee673434fcf3bf24dd54763c79690201208bafec62f19eed"}, + {file = "protobuf-5.28.3.tar.gz", hash = "sha256:64badbc49180a5e401f373f9ce7ab1d18b63f7dd4a9cdc43c92b9f0b481cef7b"}, +] + +[[package]] +name = "psutil" +version = "6.1.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = true +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "psutil-6.1.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ff34df86226c0227c52f38b919213157588a678d049688eded74c76c8ba4a5d0"}, + {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c0e0c00aa18ca2d3b2b991643b799a15fc8f0563d2ebb6040f64ce8dc027b942"}, + {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:000d1d1ebd634b4efb383f4034437384e44a6d455260aaee2eca1e9c1b55f047"}, + {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5cd2bcdc75b452ba2e10f0e8ecc0b57b827dd5d7aaffbc6821b2a9a242823a76"}, + {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:045f00a43c737f960d273a83973b2511430d61f283a44c96bf13a6e829ba8fdc"}, + {file = "psutil-6.1.0-cp27-none-win32.whl", hash = "sha256:9118f27452b70bb1d9ab3198c1f626c2499384935aaf55388211ad982611407e"}, + {file = "psutil-6.1.0-cp27-none-win_amd64.whl", hash = "sha256:a8506f6119cff7015678e2bce904a4da21025cc70ad283a53b099e7620061d85"}, + {file = "psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688"}, + {file = "psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e"}, + {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38"}, + {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b"}, + {file = "psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a"}, + {file = "psutil-6.1.0-cp36-cp36m-win32.whl", hash = "sha256:6d3fbbc8d23fcdcb500d2c9f94e07b1342df8ed71b948a2649b5cb060a7c94ca"}, + {file = "psutil-6.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1209036fbd0421afde505a4879dee3b2fd7b1e14fee81c0069807adcbbcca747"}, + {file = "psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e"}, + {file = "psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be"}, + {file = "psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a"}, +] + +[package.extras] +dev = ["black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "wheel"] +test = ["pytest", "pytest-xdist", "setuptools"] + [[package]] name = "ptyprocess" version = "0.7.0" @@ -2202,6 +3218,53 @@ files = [ {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] +[[package]] +name = "pulsar-client" +version = "3.5.0" +description = "Apache Pulsar Python client library" +optional = true +python-versions = "*" +files = [ + {file = "pulsar_client-3.5.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:c18552edb2f785de85280fe624bc507467152bff810fc81d7660fa2dfa861f38"}, + {file = "pulsar_client-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18d438e456c146f01be41ef146f649dedc8f7bc714d9eaef94cff2e34099812b"}, + {file = "pulsar_client-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18a26a0719841103c7a89eb1492c4a8fedf89adaa386375baecbb4fa2707e88f"}, + {file = "pulsar_client-3.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ab0e1605dc5f44a126163fd06cd0a768494ad05123f6e0de89a2c71d6e2d2319"}, + {file = "pulsar_client-3.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdef720891b97656fdce3bf5913ea7729b2156b84ba64314f432c1e72c6117fa"}, + {file = "pulsar_client-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:a42544e38773191fe550644a90e8050579476bb2dcf17ac69a4aed62a6cb70e7"}, + {file = "pulsar_client-3.5.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:fd94432ea5d398ea78f8f2e09a217ec5058d26330c137a22690478c031e116da"}, + {file = "pulsar_client-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6252ae462e07ece4071213fdd9c76eab82ca522a749f2dc678037d4cbacd40b"}, + {file = "pulsar_client-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03b4d440b2d74323784328b082872ee2f206c440b5d224d7941eb3c083ec06c6"}, + {file = "pulsar_client-3.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f60af840b8d64a2fac5a0c1ce6ae0ddffec5f42267c6ded2c5e74bad8345f2a1"}, + {file = "pulsar_client-3.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2277a447c3b7f6571cb1eb9fc5c25da3fdd43d0b2fb91cf52054adfadc7d6842"}, + {file = "pulsar_client-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:f20f3e9dd50db2a37059abccad42078b7a4754b8bc1d3ae6502e71c1ad2209f0"}, + {file = "pulsar_client-3.5.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:d61f663d85308e12f44033ba95af88730f581a7e8da44f7a5c080a3aaea4878d"}, + {file = "pulsar_client-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1ba0be25b6f747bcb28102b7d906ec1de48dc9f1a2d9eacdcc6f44ab2c9e17"}, + {file = "pulsar_client-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a181e3e60ac39df72ccb3c415d7aeac61ad0286497a6e02739a560d5af28393a"}, + {file = "pulsar_client-3.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3c72895ff7f51347e4f78b0375b2213fa70dd4790bbb78177b4002846f1fd290"}, + {file = "pulsar_client-3.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:547dba1b185a17eba915e51d0a3aca27c80747b6187e5cd7a71a3ca33921decc"}, + {file = "pulsar_client-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:443b786eed96bc86d2297a6a42e79f39d1abf217ec603e0bd303f3488c0234af"}, + {file = "pulsar_client-3.5.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:15b58f5d759dd6166db8a2d90ed05a38063b05cda76c36d190d86ef5c9249397"}, + {file = "pulsar_client-3.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af34bfe813dddf772a8a298117fa0a036ee963595d8bc8f00d969a0329ae6ed9"}, + {file = "pulsar_client-3.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27a0fec1dd74e1367d3742ce16679c1807994df60f5e666f440cf39323938fad"}, + {file = "pulsar_client-3.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbcd26ef9c03f96fb9cd91baec3bbd3c4b997834eb3556670d31f41cc25b5f64"}, + {file = "pulsar_client-3.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:afea1d0b6e793fd56e56463145751ff3aa79fdcd5b26e90d0da802a1bbabe07e"}, + {file = "pulsar_client-3.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:da1ab2fb1bef64b966e9403a0a186ebc90368d99e054ce2cae5b1128478f4ef4"}, + {file = "pulsar_client-3.5.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:9ad5dcc0eb8d2a7c0fb8e1fa146a0c6d4bdaf934f1169080b2c64b2f0573e086"}, + {file = "pulsar_client-3.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5870c6805b1a57962ed908d1173e97e13470415998393925c86a43694420389"}, + {file = "pulsar_client-3.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29cb5fedb969895b78301dc00a979133e69940812b8332e4de948bb0ad3db7cb"}, + {file = "pulsar_client-3.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e53c74bfa59b20c66adea95023169060f5048dd8d843e6ef9cd3b8ee2d23e93b"}, + {file = "pulsar_client-3.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99dbadb13967f1add57010971ed36b5a77d24afcdaea01960d0e55e56cf4ba6f"}, + {file = "pulsar_client-3.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:058887661d438796f42307dcc8054c84dea88a37683dae36498b95d7e1c39b37"}, +] + +[package.dependencies] +certifi = "*" + +[package.extras] +all = ["apache-bookkeeper-client (>=4.16.1)", "fastavro (>=1.9.2)", "grpcio (>=1.60.0)", "prometheus-client", "protobuf (>=3.6.1,<=3.20.3)", "ratelimit"] +avro = ["fastavro (>=1.9.2)"] +functions = ["apache-bookkeeper-client (>=4.16.1)", "grpcio (>=1.60.0)", "prometheus-client", "protobuf (>=3.6.1,<=3.20.3)", "ratelimit"] + [[package]] name = "pure-eval" version = "0.2.3" @@ -2216,6 +3279,31 @@ files = [ [package.extras] tests = ["pytest"] +[[package]] +name = "pyasn1" +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" +files = [ + {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, + {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.1" +description = "A collection of ASN.1-based protocols modules" +optional = true +python-versions = ">=3.8" +files = [ + {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"}, + {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.7.0" + [[package]] name = "pydantic" version = "2.9.2" @@ -2442,6 +3530,41 @@ files = [ [package.extras] diagrams = ["jinja2", "railroad-diagrams"] +[[package]] +name = "pypika" +version = "0.48.9" +description = "A SQL query builder API for Python" +optional = true +python-versions = "*" +files = [ + {file = "PyPika-0.48.9.tar.gz", hash = "sha256:838836a61747e7c8380cd1b7ff638694b7a7335345d0f559b04b2cd832ad5378"}, +] + +[[package]] +name = "pyproject-hooks" +version = "1.2.0" +description = "Wrappers to call pyproject.toml-based build backend hooks." +optional = true +python-versions = ">=3.7" +files = [ + {file = "pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913"}, + {file = "pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8"}, +] + +[[package]] +name = "pyreadline3" +version = "3.5.4" +description = "A python implementation of GNU readline." +optional = true +python-versions = ">=3.8" +files = [ + {file = "pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6"}, + {file = "pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7"}, +] + +[package.extras] +dev = ["build", "flake8", "mypy", "pytest", "twine"] + [[package]] name = "pytest" version = "8.3.3" @@ -2823,6 +3946,24 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "requests-oauthlib" +version = "2.0.0" +description = "OAuthlib authentication support for Requests." +optional = true +python-versions = ">=3.4" +files = [ + {file = "requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9"}, + {file = "requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36"}, +] + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + [[package]] name = "rich" version = "13.9.2" @@ -2842,6 +3983,20 @@ typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.1 [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = true +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + [[package]] name = "ruff" version = "0.6.9" @@ -2869,6 +4024,17 @@ files = [ {file = "ruff-0.6.9.tar.gz", hash = "sha256:b076ef717a8e5bc819514ee1d602bbdca5b4420ae13a9cf61a0c0a4f53a2baa2"}, ] +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = true +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + [[package]] name = "six" version = "1.16.0" @@ -3124,6 +4290,40 @@ pure-eval = "*" [package.extras] tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] +[[package]] +name = "starlette" +version = "0.41.2" +description = "The little ASGI library that shines." +optional = true +python-versions = ">=3.8" +files = [ + {file = "starlette-0.41.2-py3-none-any.whl", hash = "sha256:fbc189474b4731cf30fcef52f18a8d070e3f3b46c6a04c97579e85e6ffca942d"}, + {file = "starlette-0.41.2.tar.gz", hash = "sha256:9834fd799d1a87fd346deb76158668cfa0b0d56f85caefe8268e2d97c3468b62"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + +[[package]] +name = "sympy" +version = "1.13.3" +description = "Computer algebra system (CAS) in Python" +optional = true +python-versions = ">=3.8" +files = [ + {file = "sympy-1.13.3-py3-none-any.whl", hash = "sha256:54612cf55a62755ee71824ce692986f23c88ffa77207b30c1368eda4a7060f73"}, + {file = "sympy-1.13.3.tar.gz", hash = "sha256:b27fd2c6530e0ab39e275fc9b683895367e51d5da91baa8d3d64db2565fec4d9"}, +] + +[package.dependencies] +mpmath = ">=1.1.0,<1.4" + +[package.extras] +dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"] + [[package]] name = "tabulate" version = "0.9.0" @@ -3138,6 +4338,21 @@ files = [ [package.extras] widechars = ["wcwidth"] +[[package]] +name = "tenacity" +version = "9.0.0" +description = "Retry code until it succeeds" +optional = true +python-versions = ">=3.8" +files = [ + {file = "tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539"}, + {file = "tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b"}, +] + +[package.extras] +doc = ["reno", "sphinx"] +test = ["pytest", "tornado (>=4.5)", "typeguard"] + [[package]] name = "tiktoken" version = "0.8.0" @@ -3359,6 +4574,23 @@ files = [ docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] +[[package]] +name = "typer" +version = "0.13.0" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = true +python-versions = ">=3.7" +files = [ + {file = "typer-0.13.0-py3-none-any.whl", hash = "sha256:d85fe0b777b2517cc99c8055ed735452f2659cd45e451507c76f48ce5c1d00e2"}, + {file = "typer-0.13.0.tar.gz", hash = "sha256:f1c7198347939361eec90139ffa0fd8b3df3a2259d5852a0f7400e476d95985c"}, +] + +[package.dependencies] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" + [[package]] name = "types-beautifulsoup4" version = "4.12.0.20240907" @@ -3453,6 +4685,83 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "uvicorn" +version = "0.32.0" +description = "The lightning-fast ASGI server." +optional = true +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.32.0-py3-none-any.whl", hash = "sha256:60b8f3a5ac027dcd31448f411ced12b5ef452c646f76f02f8cc3f25d8d26fd82"}, + {file = "uvicorn-0.32.0.tar.gz", hash = "sha256:f78b36b143c16f54ccdb8190d0a26b5f1901fe5a3c777e1ab29f26391af8551e"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +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" +version = "0.21.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = true +python-versions = ">=3.8.0" +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"}, + {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f38b2e090258d051d68a5b14d1da7203a3c3677321cf32a95a6f4db4dd8b6f26"}, + {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c43e0f13022b998eb9b973b5e97200c8b90823454d4bc06ab33829e09fb9bb"}, + {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10d66943def5fcb6e7b37310eb6b5639fd2ccbc38df1177262b0640c3ca68c1f"}, + {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c"}, + {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8"}, + {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0"}, + {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e"}, + {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb"}, + {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6"}, + {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d"}, + {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c"}, + {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2"}, + {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d"}, + {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc"}, + {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb"}, + {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f"}, + {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281"}, + {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af"}, + {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6"}, + {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816"}, + {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc"}, + {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553"}, + {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:17df489689befc72c39a08359efac29bbee8eee5209650d4b9f34df73d22e414"}, + {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc09f0ff191e61c2d592a752423c767b4ebb2986daa9ed62908e2b1b9a9ae206"}, + {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0ce1b49560b1d2d8a2977e3ba4afb2414fb46b86a1b64056bc4ab929efdafbe"}, + {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e678ad6fe52af2c58d2ae3c73dc85524ba8abe637f134bf3564ed07f555c5e79"}, + {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:460def4412e473896ef179a1671b40c039c7012184b627898eea5072ef6f017a"}, + {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:10da8046cc4a8f12c91a1c39d1dd1585c41162a15caaef165c2174db9ef18bdc"}, + {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c097078b8031190c934ed0ebfee8cc5f9ba9642e6eb88322b9958b649750f72b"}, + {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:46923b0b5ee7fc0020bef24afe7836cb068f5050ca04caf6b487c513dc1a20b2"}, + {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53e420a3afe22cdcf2a0f4846e377d16e718bc70103d7088a4f7623567ba5fb0"}, + {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88cb67cdbc0e483da00af0b2c3cdad4b7c61ceb1ee0f33fe00e09c81e3a6cb75"}, + {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:221f4f2a1f46032b403bf3be628011caf75428ee3cc204a22addf96f586b19fd"}, + {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2d1f581393673ce119355d56da84fe1dd9d2bb8b3d13ce792524e1607139feff"}, + {file = "uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3"}, +] + +[package.extras] +dev = ["Cython (>=3.0,<4.0)", "setuptools (>=60)"] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["aiohttp (>=3.10.5)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + [[package]] name = "virtualenv" version = "20.26.6" @@ -3473,6 +4782,140 @@ platformdirs = ">=3.9.1,<5" 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)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +[[package]] +name = "watchdog" +version = "3.0.0" +description = "Filesystem events monitoring" +optional = true +python-versions = ">=3.7" +files = [ + {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41"}, + {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397"}, + {file = "watchdog-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96"}, + {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae"}, + {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9"}, + {file = "watchdog-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7"}, + {file = "watchdog-3.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674"}, + {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f"}, + {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc"}, + {file = "watchdog-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3"}, + {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3"}, + {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0"}, + {file = "watchdog-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8"}, + {file = "watchdog-3.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100"}, + {file = "watchdog-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346"}, + {file = "watchdog-3.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33"}, + {file = "watchdog-3.0.0-py3-none-win32.whl", hash = "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f"}, + {file = "watchdog-3.0.0-py3-none-win_amd64.whl", hash = "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c"}, + {file = "watchdog-3.0.0-py3-none-win_ia64.whl", hash = "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759"}, + {file = "watchdog-3.0.0.tar.gz", hash = "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9"}, +] + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + +[[package]] +name = "watchfiles" +version = "0.24.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = true +python-versions = ">=3.8" +files = [ + {file = "watchfiles-0.24.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:083dc77dbdeef09fa44bb0f4d1df571d2e12d8a8f985dccde71ac3ac9ac067a0"}, + {file = "watchfiles-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e94e98c7cb94cfa6e071d401ea3342767f28eb5a06a58fafdc0d2a4974f4f35c"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82ae557a8c037c42a6ef26c494d0631cacca040934b101d001100ed93d43f361"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:acbfa31e315a8f14fe33e3542cbcafc55703b8f5dcbb7c1eecd30f141df50db3"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74fdffce9dfcf2dc296dec8743e5b0332d15df19ae464f0e249aa871fc1c571"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:449f43f49c8ddca87c6b3980c9284cab6bd1f5c9d9a2b00012adaaccd5e7decd"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4abf4ad269856618f82dee296ac66b0cd1d71450fc3c98532d93798e73399b7a"}, + {file = "watchfiles-0.24.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f895d785eb6164678ff4bb5cc60c5996b3ee6df3edb28dcdeba86a13ea0465e"}, + {file = "watchfiles-0.24.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ae3e208b31be8ce7f4c2c0034f33406dd24fbce3467f77223d10cd86778471c"}, + {file = "watchfiles-0.24.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2efec17819b0046dde35d13fb8ac7a3ad877af41ae4640f4109d9154ed30a188"}, + {file = "watchfiles-0.24.0-cp310-none-win32.whl", hash = "sha256:6bdcfa3cd6fdbdd1a068a52820f46a815401cbc2cb187dd006cb076675e7b735"}, + {file = "watchfiles-0.24.0-cp310-none-win_amd64.whl", hash = "sha256:54ca90a9ae6597ae6dc00e7ed0a040ef723f84ec517d3e7ce13e63e4bc82fa04"}, + {file = "watchfiles-0.24.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:bdcd5538e27f188dd3c804b4a8d5f52a7fc7f87e7fd6b374b8e36a4ca03db428"}, + {file = "watchfiles-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2dadf8a8014fde6addfd3c379e6ed1a981c8f0a48292d662e27cabfe4239c83c"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6509ed3f467b79d95fc62a98229f79b1a60d1b93f101e1c61d10c95a46a84f43"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8360f7314a070c30e4c976b183d1d8d1585a4a50c5cb603f431cebcbb4f66327"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:316449aefacf40147a9efaf3bd7c9bdd35aaba9ac5d708bd1eb5763c9a02bef5"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73bde715f940bea845a95247ea3e5eb17769ba1010efdc938ffcb967c634fa61"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3770e260b18e7f4e576edca4c0a639f704088602e0bc921c5c2e721e3acb8d15"}, + {file = "watchfiles-0.24.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa0fd7248cf533c259e59dc593a60973a73e881162b1a2f73360547132742823"}, + {file = "watchfiles-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d7a2e3b7f5703ffbd500dabdefcbc9eafeff4b9444bbdd5d83d79eedf8428fab"}, + {file = "watchfiles-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d831ee0a50946d24a53821819b2327d5751b0c938b12c0653ea5be7dea9c82ec"}, + {file = "watchfiles-0.24.0-cp311-none-win32.whl", hash = "sha256:49d617df841a63b4445790a254013aea2120357ccacbed00253f9c2b5dc24e2d"}, + {file = "watchfiles-0.24.0-cp311-none-win_amd64.whl", hash = "sha256:d3dcb774e3568477275cc76554b5a565024b8ba3a0322f77c246bc7111c5bb9c"}, + {file = "watchfiles-0.24.0-cp311-none-win_arm64.whl", hash = "sha256:9301c689051a4857d5b10777da23fafb8e8e921bcf3abe6448a058d27fb67633"}, + {file = "watchfiles-0.24.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7211b463695d1e995ca3feb38b69227e46dbd03947172585ecb0588f19b0d87a"}, + {file = "watchfiles-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b8693502d1967b00f2fb82fc1e744df128ba22f530e15b763c8d82baee15370"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdab9555053399318b953a1fe1f586e945bc8d635ce9d05e617fd9fe3a4687d6"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34e19e56d68b0dad5cff62273107cf5d9fbaf9d75c46277aa5d803b3ef8a9e9b"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41face41f036fee09eba33a5b53a73e9a43d5cb2c53dad8e61fa6c9f91b5a51e"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5148c2f1ea043db13ce9b0c28456e18ecc8f14f41325aa624314095b6aa2e9ea"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e4bd963a935aaf40b625c2499f3f4f6bbd0c3776f6d3bc7c853d04824ff1c9f"}, + {file = "watchfiles-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c79d7719d027b7a42817c5d96461a99b6a49979c143839fc37aa5748c322f234"}, + {file = "watchfiles-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:32aa53a9a63b7f01ed32e316e354e81e9da0e6267435c7243bf8ae0f10b428ef"}, + {file = "watchfiles-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce72dba6a20e39a0c628258b5c308779b8697f7676c254a845715e2a1039b968"}, + {file = "watchfiles-0.24.0-cp312-none-win32.whl", hash = "sha256:d9018153cf57fc302a2a34cb7564870b859ed9a732d16b41a9b5cb2ebed2d444"}, + {file = "watchfiles-0.24.0-cp312-none-win_amd64.whl", hash = "sha256:551ec3ee2a3ac9cbcf48a4ec76e42c2ef938a7e905a35b42a1267fa4b1645896"}, + {file = "watchfiles-0.24.0-cp312-none-win_arm64.whl", hash = "sha256:b52a65e4ea43c6d149c5f8ddb0bef8d4a1e779b77591a458a893eb416624a418"}, + {file = "watchfiles-0.24.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3d2e3ab79a1771c530233cadfd277fcc762656d50836c77abb2e5e72b88e3a48"}, + {file = "watchfiles-0.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327763da824817b38ad125dcd97595f942d720d32d879f6c4ddf843e3da3fe90"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd82010f8ab451dabe36054a1622870166a67cf3fce894f68895db6f74bbdc94"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d64ba08db72e5dfd5c33be1e1e687d5e4fcce09219e8aee893a4862034081d4e"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1cf1f6dd7825053f3d98f6d33f6464ebdd9ee95acd74ba2c34e183086900a827"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43e3e37c15a8b6fe00c1bce2473cfa8eb3484bbeecf3aefbf259227e487a03df"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88bcd4d0fe1d8ff43675360a72def210ebad3f3f72cabfeac08d825d2639b4ab"}, + {file = "watchfiles-0.24.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:999928c6434372fde16c8f27143d3e97201160b48a614071261701615a2a156f"}, + {file = "watchfiles-0.24.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:30bbd525c3262fd9f4b1865cb8d88e21161366561cd7c9e1194819e0a33ea86b"}, + {file = "watchfiles-0.24.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:edf71b01dec9f766fb285b73930f95f730bb0943500ba0566ae234b5c1618c18"}, + {file = "watchfiles-0.24.0-cp313-none-win32.whl", hash = "sha256:f4c96283fca3ee09fb044f02156d9570d156698bc3734252175a38f0e8975f07"}, + {file = "watchfiles-0.24.0-cp313-none-win_amd64.whl", hash = "sha256:a974231b4fdd1bb7f62064a0565a6b107d27d21d9acb50c484d2cdba515b9366"}, + {file = "watchfiles-0.24.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:ee82c98bed9d97cd2f53bdb035e619309a098ea53ce525833e26b93f673bc318"}, + {file = "watchfiles-0.24.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fd92bbaa2ecdb7864b7600dcdb6f2f1db6e0346ed425fbd01085be04c63f0b05"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f83df90191d67af5a831da3a33dd7628b02a95450e168785586ed51e6d28943c"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fca9433a45f18b7c779d2bae7beeec4f740d28b788b117a48368d95a3233ed83"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b995bfa6bf01a9e09b884077a6d37070464b529d8682d7691c2d3b540d357a0c"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed9aba6e01ff6f2e8285e5aa4154e2970068fe0fc0998c4380d0e6278222269b"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5171ef898299c657685306d8e1478a45e9303ddcd8ac5fed5bd52ad4ae0b69b"}, + {file = "watchfiles-0.24.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4933a508d2f78099162da473841c652ad0de892719043d3f07cc83b33dfd9d91"}, + {file = "watchfiles-0.24.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95cf3b95ea665ab03f5a54765fa41abf0529dbaf372c3b83d91ad2cfa695779b"}, + {file = "watchfiles-0.24.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:01def80eb62bd5db99a798d5e1f5f940ca0a05986dcfae21d833af7a46f7ee22"}, + {file = "watchfiles-0.24.0-cp38-none-win32.whl", hash = "sha256:4d28cea3c976499475f5b7a2fec6b3a36208656963c1a856d328aeae056fc5c1"}, + {file = "watchfiles-0.24.0-cp38-none-win_amd64.whl", hash = "sha256:21ab23fdc1208086d99ad3f69c231ba265628014d4aed31d4e8746bd59e88cd1"}, + {file = "watchfiles-0.24.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b665caeeda58625c3946ad7308fbd88a086ee51ccb706307e5b1fa91556ac886"}, + {file = "watchfiles-0.24.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5c51749f3e4e269231510da426ce4a44beb98db2dce9097225c338f815b05d4f"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b2509f08761f29a0fdad35f7e1638b8ab1adfa2666d41b794090361fb8b855"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a60e2bf9dc6afe7f743e7c9b149d1fdd6dbf35153c78fe3a14ae1a9aee3d98b"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7d9b87c4c55e3ea8881dfcbf6d61ea6775fffed1fedffaa60bd047d3c08c430"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:78470906a6be5199524641f538bd2c56bb809cd4bf29a566a75051610bc982c3"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07cdef0c84c03375f4e24642ef8d8178e533596b229d32d2bbd69e5128ede02a"}, + {file = "watchfiles-0.24.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d337193bbf3e45171c8025e291530fb7548a93c45253897cd764a6a71c937ed9"}, + {file = "watchfiles-0.24.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ec39698c45b11d9694a1b635a70946a5bad066b593af863460a8e600f0dff1ca"}, + {file = "watchfiles-0.24.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e28d91ef48eab0afb939fa446d8ebe77e2f7593f5f463fd2bb2b14132f95b6e"}, + {file = "watchfiles-0.24.0-cp39-none-win32.whl", hash = "sha256:7138eff8baa883aeaa074359daabb8b6c1e73ffe69d5accdc907d62e50b1c0da"}, + {file = "watchfiles-0.24.0-cp39-none-win_amd64.whl", hash = "sha256:b3ef2c69c655db63deb96b3c3e587084612f9b1fa983df5e0c3379d41307467f"}, + {file = "watchfiles-0.24.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:632676574429bee8c26be8af52af20e0c718cc7f5f67f3fb658c71928ccd4f7f"}, + {file = "watchfiles-0.24.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a2a9891723a735d3e2540651184be6fd5b96880c08ffe1a98bae5017e65b544b"}, + {file = "watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7fa2bc0efef3e209a8199fd111b8969fe9db9c711acc46636686331eda7dd4"}, + {file = "watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01550ccf1d0aed6ea375ef259706af76ad009ef5b0203a3a4cce0f6024f9b68a"}, + {file = "watchfiles-0.24.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:96619302d4374de5e2345b2b622dc481257a99431277662c30f606f3e22f42be"}, + {file = "watchfiles-0.24.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:85d5f0c7771dcc7a26c7a27145059b6bb0ce06e4e751ed76cdf123d7039b60b5"}, + {file = "watchfiles-0.24.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:951088d12d339690a92cef2ec5d3cfd957692834c72ffd570ea76a6790222777"}, + {file = "watchfiles-0.24.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49fb58bcaa343fedc6a9e91f90195b20ccb3135447dc9e4e2570c3a39565853e"}, + {file = "watchfiles-0.24.0.tar.gz", hash = "sha256:afb72325b74fa7a428c009c1b8be4b4d7c2afedafb2982827ef2156646df2fe1"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + [[package]] name = "wcwidth" version = "0.2.13" @@ -3484,6 +4927,100 @@ files = [ {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] +[[package]] +name = "websocket-client" +version = "1.8.0" +description = "WebSocket client for Python with low level API options" +optional = true +python-versions = ">=3.8" +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"}, +] + +[package.extras] +docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "websockets" +version = "14.1" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = true +python-versions = ">=3.9" +files = [ + {file = "websockets-14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a0adf84bc2e7c86e8a202537b4fd50e6f7f0e4a6b6bf64d7ccb96c4cd3330b29"}, + {file = "websockets-14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90b5d9dfbb6d07a84ed3e696012610b6da074d97453bd01e0e30744b472c8179"}, + {file = "websockets-14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2177ee3901075167f01c5e335a6685e71b162a54a89a56001f1c3e9e3d2ad250"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f14a96a0034a27f9d47fd9788913924c89612225878f8078bb9d55f859272b0"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f874ba705deea77bcf64a9da42c1f5fc2466d8f14daf410bc7d4ceae0a9fcb0"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9607b9a442392e690a57909c362811184ea429585a71061cd5d3c2b98065c199"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bea45f19b7ca000380fbd4e02552be86343080120d074b87f25593ce1700ad58"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:219c8187b3ceeadbf2afcf0f25a4918d02da7b944d703b97d12fb01510869078"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad2ab2547761d79926effe63de21479dfaf29834c50f98c4bf5b5480b5838434"}, + {file = "websockets-14.1-cp310-cp310-win32.whl", hash = "sha256:1288369a6a84e81b90da5dbed48610cd7e5d60af62df9851ed1d1d23a9069f10"}, + {file = "websockets-14.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0744623852f1497d825a49a99bfbec9bea4f3f946df6eb9d8a2f0c37a2fec2e"}, + {file = "websockets-14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:449d77d636f8d9c17952628cc7e3b8faf6e92a17ec581ec0c0256300717e1512"}, + {file = "websockets-14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a35f704be14768cea9790d921c2c1cc4fc52700410b1c10948511039be824aac"}, + {file = "websockets-14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1f3628a0510bd58968c0f60447e7a692933589b791a6b572fcef374053ca280"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c3deac3748ec73ef24fc7be0b68220d14d47d6647d2f85b2771cb35ea847aa1"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7048eb4415d46368ef29d32133134c513f507fff7d953c18c91104738a68c3b3"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cf0ad281c979306a6a34242b371e90e891bce504509fb6bb5246bbbf31e7b6"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc1fc87428c1d18b643479caa7b15db7d544652e5bf610513d4a3478dbe823d0"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f95ba34d71e2fa0c5d225bde3b3bdb152e957150100e75c86bc7f3964c450d89"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9481a6de29105d73cf4515f2bef8eb71e17ac184c19d0b9918a3701c6c9c4f23"}, + {file = "websockets-14.1-cp311-cp311-win32.whl", hash = "sha256:368a05465f49c5949e27afd6fbe0a77ce53082185bbb2ac096a3a8afaf4de52e"}, + {file = "websockets-14.1-cp311-cp311-win_amd64.whl", hash = "sha256:6d24fc337fc055c9e83414c94e1ee0dee902a486d19d2a7f0929e49d7d604b09"}, + {file = "websockets-14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed907449fe5e021933e46a3e65d651f641975a768d0649fee59f10c2985529ed"}, + {file = "websockets-14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87e31011b5c14a33b29f17eb48932e63e1dcd3fa31d72209848652310d3d1f0d"}, + {file = "websockets-14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc6ccf7d54c02ae47a48ddf9414c54d48af9c01076a2e1023e3b486b6e72c707"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9777564c0a72a1d457f0848977a1cbe15cfa75fa2f67ce267441e465717dcf1a"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a655bde548ca98f55b43711b0ceefd2a88a71af6350b0c168aa77562104f3f45"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfff83ca578cada2d19e665e9c8368e1598d4e787422a460ec70e531dbdd58"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6a6c9bcf7cdc0fd41cc7b7944447982e8acfd9f0d560ea6d6845428ed0562058"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4b6caec8576e760f2c7dd878ba817653144d5f369200b6ddf9771d64385b84d4"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb6d38971c800ff02e4a6afd791bbe3b923a9a57ca9aeab7314c21c84bf9ff05"}, + {file = "websockets-14.1-cp312-cp312-win32.whl", hash = "sha256:1d045cbe1358d76b24d5e20e7b1878efe578d9897a25c24e6006eef788c0fdf0"}, + {file = "websockets-14.1-cp312-cp312-win_amd64.whl", hash = "sha256:90f4c7a069c733d95c308380aae314f2cb45bd8a904fb03eb36d1a4983a4993f"}, + {file = "websockets-14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3630b670d5057cd9e08b9c4dab6493670e8e762a24c2c94ef312783870736ab9"}, + {file = "websockets-14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36ebd71db3b89e1f7b1a5deaa341a654852c3518ea7a8ddfdf69cc66acc2db1b"}, + {file = "websockets-14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5b918d288958dc3fa1c5a0b9aa3256cb2b2b84c54407f4813c45d52267600cd3"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00fe5da3f037041da1ee0cf8e308374e236883f9842c7c465aa65098b1c9af59"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8149a0f5a72ca36720981418eeffeb5c2729ea55fa179091c81a0910a114a5d2"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77569d19a13015e840b81550922056acabc25e3f52782625bc6843cfa034e1da"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf5201a04550136ef870aa60ad3d29d2a59e452a7f96b94193bee6d73b8ad9a9"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:88cf9163ef674b5be5736a584c999e98daf3aabac6e536e43286eb74c126b9c7"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:836bef7ae338a072e9d1863502026f01b14027250a4545672673057997d5c05a"}, + {file = "websockets-14.1-cp313-cp313-win32.whl", hash = "sha256:0d4290d559d68288da9f444089fd82490c8d2744309113fc26e2da6e48b65da6"}, + {file = "websockets-14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8621a07991add373c3c5c2cf89e1d277e49dc82ed72c75e3afc74bd0acc446f0"}, + {file = "websockets-14.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01bb2d4f0a6d04538d3c5dfd27c0643269656c28045a53439cbf1c004f90897a"}, + {file = "websockets-14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:414ffe86f4d6f434a8c3b7913655a1a5383b617f9bf38720e7c0799fac3ab1c6"}, + {file = "websockets-14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fda642151d5affdee8a430bd85496f2e2517be3a2b9d2484d633d5712b15c56"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd7c11968bc3860d5c78577f0dbc535257ccec41750675d58d8dc66aa47fe52c"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a032855dc7db987dff813583d04f4950d14326665d7e714d584560b140ae6b8b"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7e7ea2f782408c32d86b87a0d2c1fd8871b0399dd762364c731d86c86069a78"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:39450e6215f7d9f6f7bc2a6da21d79374729f5d052333da4d5825af8a97e6735"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ceada5be22fa5a5a4cdeec74e761c2ee7db287208f54c718f2df4b7e200b8d4a"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3fc753451d471cff90b8f467a1fc0ae64031cf2d81b7b34e1811b7e2691bc4bc"}, + {file = "websockets-14.1-cp39-cp39-win32.whl", hash = "sha256:14839f54786987ccd9d03ed7f334baec0f02272e7ec4f6e9d427ff584aeea8b4"}, + {file = "websockets-14.1-cp39-cp39-win_amd64.whl", hash = "sha256:d9fd19ecc3a4d5ae82ddbfb30962cf6d874ff943e56e0c81f5169be2fda62979"}, + {file = "websockets-14.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5dc25a9dbd1a7f61eca4b7cb04e74ae4b963d658f9e4f9aad9cd00b688692c8"}, + {file = "websockets-14.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:04a97aca96ca2acedf0d1f332c861c5a4486fdcba7bcef35873820f940c4231e"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df174ece723b228d3e8734a6f2a6febbd413ddec39b3dc592f5a4aa0aff28098"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:034feb9f4286476f273b9a245fb15f02c34d9586a5bc936aff108c3ba1b21beb"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c308dabd2b380807ab64b62985eaccf923a78ebc572bd485375b9ca2b7dc7"}, + {file = "websockets-14.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a42d3ecbb2db5080fc578314439b1d79eef71d323dc661aa616fb492436af5d"}, + {file = "websockets-14.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddaa4a390af911da6f680be8be4ff5aaf31c4c834c1a9147bc21cbcbca2d4370"}, + {file = "websockets-14.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a4c805c6034206143fbabd2d259ec5e757f8b29d0a2f0bf3d2fe5d1f60147a4a"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:205f672a6c2c671a86d33f6d47c9b35781a998728d2c7c2a3e1cf3333fcb62b7"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef440054124728cc49b01c33469de06755e5a7a4e83ef61934ad95fc327fbb0"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7591d6f440af7f73c4bd9404f3772bfee064e639d2b6cc8c94076e71b2471c1"}, + {file = "websockets-14.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:25225cc79cfebc95ba1d24cd3ab86aaa35bcd315d12fa4358939bd55e9bd74a5"}, + {file = "websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e"}, + {file = "websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8"}, +] + [[package]] name = "werkzeug" version = "3.0.4" @@ -3524,6 +5061,85 @@ files = [ {file = "windows_curses-2.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:441189fb71c455ed0575ffb1b840d5857c01c4dda441cbbbd34a6216e7207bfa"}, ] +[[package]] +name = "wrapt" +version = "1.16.0" +description = "Module for decorators, wrappers and monkey patching." +optional = true +python-versions = ">=3.6" +files = [ + {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, + {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, + {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, + {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, + {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, + {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, + {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, + {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, + {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, + {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, +] + [[package]] name = "youtube-transcript-api" version = "0.6.2" @@ -3538,14 +5154,34 @@ files = [ [package.dependencies] requests = "*" +[[package]] +name = "zipp" +version = "3.21.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = true +python-versions = ">=3.9" +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)"] +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", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + [extras] -all = ["flask", "flask-cors", "matplotlib", "numpy", "pandas", "pillow", "playwright", "python-xlib"] +all = ["flask", "flask-cors", "gptme-rag", "matplotlib", "numpy", "pandas", "pillow", "playwright", "python-xlib"] browser = ["playwright"] computer = ["pillow", "python-xlib"] datascience = ["matplotlib", "numpy", "pandas", "pillow"] +rag = ["gptme-rag"] server = ["flask", "flask-cors"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "fc6e6adb9c74703fa2abf164f432eb9006fc2da17b9ab1179836395439492823" +content-hash = "cab733cb45c80054b8008b207e70544389fd59ff91341aa877dca538e00f9d0a" From 82e4ce12827bda2431c854c6c306641349689d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 15 Nov 2024 16:20:32 +0100 Subject: [PATCH 05/24] build(deps): updated gptme-rag --- poetry.lock | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index d11711ee..f34040c4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -5184,4 +5184,4 @@ server = ["flask", "flask-cors"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "cab733cb45c80054b8008b207e70544389fd59ff91341aa877dca538e00f9d0a" +content-hash = "1e79823f341ef0399ceb2e2a391ae0f1e74d37c58944865ef882e610aa70bbc3" diff --git a/pyproject.toml b/pyproject.toml index c6fb9b3f..2203de1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ bashlex = "^0.18" playwright = {version = "1.47.*", optional=true} # version constrained due to annoying to have to run `playwright install` on every update youtube_transcript_api = {version = "^0.6.1", optional = true} python-xlib = {version = "^0.33", optional = true} # for X11 interaction -gptme-rag = {version = "^0.1.0", optional = true} # for RAG functionality +gptme-rag = {version = "^0.1.4", optional = true} # for RAG functionality # providers openai = "^1.0" From 43452b4f73e1579118541c8a01e15a46871059f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 15 Nov 2024 16:30:56 +0100 Subject: [PATCH 06/24] fix: made rag support optional --- gptme/tools/rag.py | 91 ++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/gptme/tools/rag.py b/gptme/tools/rag.py index fdb82e1b..d7b702db 100644 --- a/gptme/tools/rag.py +++ b/gptme/tools/rag.py @@ -3,8 +3,6 @@ import logging from pathlib import Path -import gptme_rag - from ..config import get_project_config from ..message import Message from .base import ConfirmFunc, ToolSpec @@ -12,12 +10,41 @@ logger = logging.getLogger(__name__) try: + import gptme_rag # fmt: skip + _HAS_RAG = True except ImportError: logger.debug("gptme-rag not installed, RAG tool will not be available") _HAS_RAG = False -indexer: gptme_rag.Indexer | None = None +indexer: "gptme_rag.Indexer | None" = None + +instructions = """ +Use RAG to index and search project documentation. + +Commands: +- index [paths...] - Index documents in specified paths +- search - Search indexed documents +- status - Show index status +""" + +examples = """ +User: Index the current directory +Assistant: Let me index the current directory with RAG. +```rag index``` +System: Indexed 1 paths + +User: Search for documentation about functions +Assistant: I'll search for function-related documentation. +```rag search function documentation``` +System: ### docs/api.md +Functions are documented using docstrings... + +User: Show index status +Assistant: I'll check the current status of the RAG index. +```rag status``` +System: Index contains 42 documents +""" def execute_rag(code: str, args: list[str], confirm: ConfirmFunc) -> Message: @@ -34,7 +61,7 @@ def execute_rag(code: str, args: list[str], confirm: ConfirmFunc) -> Message: return Message("system", f"Indexed {len(paths)} paths") elif command == "search": query = " ".join(args[1:]) - docs = indexer.search(query) + docs, _ = indexer.search(query) return Message( "system", "\n\n".join( @@ -49,19 +76,14 @@ def execute_rag(code: str, args: list[str], confirm: ConfirmFunc) -> Message: return Message("system", f"Unknown command: {command}") -def init_rag() -> ToolSpec: +def init() -> ToolSpec: """Initialize the RAG tool.""" - if not _HAS_RAG: - return ToolSpec( - name="rag", - desc="RAG (Retrieval-Augmented Generation) for context-aware assistance", - available=False, - ) - config = get_project_config(Path.cwd()) if config: # Initialize RAG with configuration global indexer + import gptme_rag # fmt: skip + indexer = gptme_rag.Indexer( persist_directory=Path( config.rag.get("index_path", "~/.cache/gptme/rag") @@ -69,35 +91,16 @@ def init_rag() -> ToolSpec: # TODO: use a better default collection name? (e.g. project name) collection_name=config.rag.get("collection", "gptme_docs"), ) - - return ToolSpec( - name="rag", - desc="RAG (Retrieval-Augmented Generation) for context-aware assistance", - instructions="""Use RAG to index and search project documentation. - -Commands: -- index [paths...] - Index documents in specified paths -- search - Search indexed documents -- status - Show index status""", - examples="""User: Index the current directory -Assistant: Let me index the current directory with RAG. -```rag index``` -System: Indexed 1 paths - -User: Search for documentation about functions -Assistant: I'll search for function-related documentation. -```rag search function documentation``` -System: ### docs/api.md -Functions are documented using docstrings... - -User: Show index status -Assistant: I'll check the current status of the RAG index. -```rag status``` -System: Index contains 42 documents""", - block_types=["rag"], - execute=execute_rag, - available=True, - ) - - -tool = init_rag() + return tool + + +tool = ToolSpec( + name="rag", + desc="RAG (Retrieval-Augmented Generation) for context-aware assistance", + instructions=instructions, + examples=examples, + block_types=["rag"], + execute=execute_rag, + available=_HAS_RAG, + init=init, +) From b1d54080fe5ca1c70d4ec1547a2f402a5c33bc03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sun, 17 Nov 2024 16:53:15 +0100 Subject: [PATCH 07/24] test: fixed tests --- tests/test_tools_rag.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_tools_rag.py b/tests/test_tools_rag.py index 403358b7..7e15306e 100644 --- a/tests/test_tools_rag.py +++ b/tests/test_tools_rag.py @@ -6,7 +6,8 @@ import pytest from gptme import Message from gptme.tools.base import ToolSpec -from gptme.tools.rag import _HAS_RAG, init_rag +from gptme.tools.rag import _HAS_RAG +from gptme.tools.rag import init as init_rag @pytest.fixture From f2bd6a4512901d68f36c1bc0809d1ecd326d54bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sun, 17 Nov 2024 17:33:13 +0100 Subject: [PATCH 08/24] docs: fixed docs --- docs/tools.rst | 101 --------------------------------------------- gptme/tools/rag.py | 72 +++++++++++++++++++++++++++++--- 2 files changed, 66 insertions(+), 107 deletions(-) diff --git a/docs/tools.rst b/docs/tools.rst index 8c4a4180..0efbfe95 100644 --- a/docs/tools.rst +++ b/docs/tools.rst @@ -154,104 +154,3 @@ RAG .. automodule:: gptme.tools.rag :members: :noindex: - -The RAG (Retrieval-Augmented Generation) tool provides context-aware assistance by indexing and searching project documentation. - -Installation -^^^^^^^^^^^ - -The RAG tool requires the ``gptme-rag`` package. Install it with:: - - pip install "gptme[rag]" - -Configuration -^^^^^^^^^^^^ - -Configure RAG in your ``gptme.toml``:: - - [rag] - # Storage configuration - index_path = "~/.cache/gptme/rag" # Where to store the index - collection = "gptme_docs" # Collection name for documents - - # Context enhancement settings - max_tokens = 2000 # Maximum tokens for context window - auto_context = true # Enable automatic context enhancement - min_relevance = 0.5 # Minimum relevance score for including context - max_results = 5 # Maximum number of results to consider - - # Cache configuration - [rag.cache] - max_embeddings = 10000 # Maximum number of cached embeddings - max_searches = 1000 # Maximum number of cached search results - embedding_ttl = 86400 # Embedding cache TTL in seconds (24h) - search_ttl = 3600 # Search cache TTL in seconds (1h) - -Features -^^^^^^^^ - -1. Manual Search and Indexing - - Index project documentation with ``rag index`` - - Search indexed documents with ``rag search`` - - Check index status with ``rag status`` - -2. Automatic Context Enhancement - - Automatically adds relevant context to user messages - - Retrieves semantically similar documents - - Manages token budget to avoid context overflow - - Preserves conversation flow with hidden context messages - -3. Performance Optimization - - Intelligent caching system for embeddings and search results - - Configurable cache sizes and TTLs - - Automatic cache invalidation - - Memory-efficient storage - -Example Usage -^^^^^^^^^^^^ - -1. Index your project:: - - ```rag index ./docs ./src``` - -2. Search for specific information:: - - ```rag search python functions``` - -3. Automatic Context Enhancement: - - When you ask a question, gptme automatically: - - Searches for relevant documentation - - Adds context as hidden system messages - - Maintains conversation history - - Example conversation with automatic context:: - - User: How do I use the patch tool? - [Hidden context from patch.py docs added automatically] - Assistant: The patch tool allows you to modify files... - -Benefits -^^^^^^^^ - -- Better informed responses through relevant documentation -- Reduced need for manual context inclusion -- Automatic token management -- Seamless integration with conversation flow - -Usage -^^^^^ - -Index documents:: - - ```rag index ./docs``` - -Search the index:: - - ```rag search python functions``` - -Check index status:: - - ```rag status``` - -The tool automatically handles document processing, embedding generation, and semantic search to provide relevant context for your queries. diff --git a/gptme/tools/rag.py b/gptme/tools/rag.py index d7b702db..decaf5bd 100644 --- a/gptme/tools/rag.py +++ b/gptme/tools/rag.py @@ -1,11 +1,69 @@ -"""RAG tool for context-aware assistance.""" +""" +RAG (Retrieval-Augmented Generation) tool for context-aware assistance. + +The RAG tool provides context-aware assistance by indexing and searching project documentation. + +Installation +------------ +The RAG tool requires the ``gptme-rag`` package. Install it with:: + + pip install "gptme[rag]" + +Configuration +------------- +Configure RAG in your ``gptme.toml``:: + + [rag] + # Storage configuration + index_path = "~/.cache/gptme/rag" # Where to store the index + collection = "gptme_docs" # Collection name for documents + + # Context enhancement settings + max_tokens = 2000 # Maximum tokens for context window + auto_context = true # Enable automatic context enhancement + min_relevance = 0.5 # Minimum relevance score for including context + max_results = 5 # Maximum number of results to consider + + # Cache configuration + [rag.cache] + max_embeddings = 10000 # Maximum number of cached embeddings + max_searches = 1000 # Maximum number of cached search results + embedding_ttl = 86400 # Embedding cache TTL in seconds (24h) + search_ttl = 3600 # Search cache TTL in seconds (1h) + +Features +-------- +1. Manual Search and Indexing + - Index project documentation with ``rag index`` + - Search indexed documents with ``rag search`` + - Check index status with ``rag status`` + +2. Automatic Context Enhancement + - Automatically adds relevant context to user messages + - Retrieves semantically similar documents + - Manages token budget to avoid context overflow + - Preserves conversation flow with hidden context messages + +3. Performance Optimization + - Intelligent caching system for embeddings and search results + - Configurable cache sizes and TTLs + - Automatic cache invalidation + - Memory-efficient storage + +Benefits +-------- +- Better informed responses through relevant documentation +- Reduced need for manual context inclusion +- Automatic token management +- Seamless integration with conversation flow +""" import logging from pathlib import Path from ..config import get_project_config from ..message import Message -from .base import ConfirmFunc, ToolSpec +from .base import ConfirmFunc, ToolSpec, ToolUse logger = logging.getLogger(__name__) @@ -28,21 +86,21 @@ - status - Show index status """ -examples = """ +examples = f""" User: Index the current directory Assistant: Let me index the current directory with RAG. -```rag index``` +{ToolUse("rag", ["index"], "").to_output()} System: Indexed 1 paths User: Search for documentation about functions Assistant: I'll search for function-related documentation. -```rag search function documentation``` +{ToolUse("rag", ["search", "function", "documentation"], "").to_output()} System: ### docs/api.md Functions are documented using docstrings... User: Show index status Assistant: I'll check the current status of the RAG index. -```rag status``` +{ToolUse("rag", ["status"], "").to_output()} System: Index contains 42 documents """ @@ -104,3 +162,5 @@ def init() -> ToolSpec: available=_HAS_RAG, init=init, ) + +__doc__ = tool.get_doc(__doc__) From 31cfbc21c4c94094de27a8ccec04e6e82cd37e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sun, 17 Nov 2024 17:36:19 +0100 Subject: [PATCH 09/24] docs: fixed docs for computer tool --- docs/tools.rst | 26 -------------- gptme/tools/computer.py | 76 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 72 insertions(+), 30 deletions(-) diff --git a/docs/tools.rst b/docs/tools.rst index 0efbfe95..ea93dbf1 100644 --- a/docs/tools.rst +++ b/docs/tools.rst @@ -120,32 +120,6 @@ Computer :members: :noindex: -The computer tool provides direct interaction with the desktop environment through X11, allowing for: - -- Keyboard input simulation -- Mouse control (movement, clicks, dragging) -- Screen capture with automatic scaling -- Cursor position tracking - -To use the computer tool, see the instructions for :doc:`server`. - -Example usage:: - - # Type text - computer(action="type", text="Hello, World!") - - # Move mouse and click - computer(action="mouse_move", coordinate=(100, 100)) - computer(action="left_click") - - # Take screenshot - computer(action="screenshot") - - # Send keyboard shortcuts - computer(action="key", text="Control_L+c") - -The tool automatically handles screen resolution scaling to ensure optimal performance with LLM vision capabilities. - .. include:: computer-use-warning.rst RAG diff --git a/gptme/tools/computer.py b/gptme/tools/computer.py index b98c2446..88c5de38 100644 --- a/gptme/tools/computer.py +++ b/gptme/tools/computer.py @@ -1,6 +1,56 @@ """ Tool for computer interaction through X11, including screen capture, keyboard, and mouse control. + +The computer tool provides direct interaction with the desktop environment through X11. Similar to Anthropic's computer use demo, but integrated with gptme's architecture. + +Features +-------- +- Keyboard input simulation +- Mouse control (movement, clicks, dragging) +- Screen capture with automatic scaling +- Cursor position tracking + +Installation +------------ +Requires X11 and xdotool:: + + # On Debian/Ubuntu + sudo apt install xdotool + + # On Arch Linux + sudo pacman -S xdotool + +Configuration +------------- +The tool uses these environment variables: + +- DISPLAY: X11 display to use (default: ":1") +- WIDTH: Screen width (default: 1024) +- HEIGHT: Screen height (default: 768) + +Usage +----- +The tool supports these actions: + +Keyboard: + - key: Send key sequence (e.g., "Return", "Control_L+c") + - type: Type text with realistic delays + +Mouse: + - mouse_move: Move mouse to coordinates + - left_click: Click left mouse button + - right_click: Click right mouse button + - middle_click: Click middle mouse button + - double_click: Double click left mouse button + - left_click_drag: Click and drag to coordinates + +Screen: + - screenshot: Take and view a screenshot + - cursor_position: Get current mouse position + +The tool automatically handles screen resolution scaling to ensure optimal performance +with LLM vision capabilities. """ import os @@ -208,11 +258,27 @@ def computer( """ examples = f""" -#### View a screenshot -> User: What do you see on the screen? -> Assistant: +User: Take a screenshot of the desktop +Assistant: I'll capture the current screen. {ToolUse("ipython", [], 'computer("screenshot")').to_output()} -> System: Viewing image... +System: Viewing image... + +User: Type "Hello, World!" into the active window +Assistant: I'll type the text with realistic delays. +{ToolUse("ipython", [], 'computer("type", text="Hello, World!")').to_output()} +System: Typed text: Hello, World! + +User: Move the mouse to coordinates (100, 200) and click +Assistant: I'll move the mouse and perform a left click. +{ToolUse("ipython", [], 'computer("mouse_move", coordinate=(100, 200))').to_output()} +System: Moved mouse to 100,200 +{ToolUse("ipython", [], 'computer("left_click")').to_output()} +System: Performed left_click + +User: Press Ctrl+C +Assistant: I'll send the Control+C key sequence. +{ToolUse("ipython", [], 'computer("key", text="Control_L+c")').to_output()} +System: Sent key sequence: Control_L+c """ tool = ToolSpec( @@ -222,3 +288,5 @@ def computer( examples=examples, functions=[computer], ) + +__doc__ = tool.get_doc(__doc__) From 075c765abf23b6ee05112e6fc038d7f12cab6135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sun, 17 Nov 2024 17:44:43 +0100 Subject: [PATCH 10/24] docs: fixed docs, made some funcs private to hide them from autodocs --- docs/tools.rst | 4 +-- gptme/tools/computer.py | 64 ++++++++++++++++++++--------------------- gptme/tools/rag.py | 16 +++++------ 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/docs/tools.rst b/docs/tools.rst index ea93dbf1..65eff546 100644 --- a/docs/tools.rst +++ b/docs/tools.rst @@ -116,12 +116,12 @@ Chats Computer -------- +.. include:: computer-use-warning.rst + .. automodule:: gptme.tools.computer :members: :noindex: -.. include:: computer-use-warning.rst - RAG --- diff --git a/gptme/tools/computer.py b/gptme/tools/computer.py index 88c5de38..572e5e9b 100644 --- a/gptme/tools/computer.py +++ b/gptme/tools/computer.py @@ -4,15 +4,15 @@ The computer tool provides direct interaction with the desktop environment through X11. Similar to Anthropic's computer use demo, but integrated with gptme's architecture. -Features --------- +.. rubric:: Features + - Keyboard input simulation - Mouse control (movement, clicks, dragging) - Screen capture with automatic scaling - Cursor position tracking -Installation ------------- +.. rubric:: Installation + Requires X11 and xdotool:: # On Debian/Ubuntu @@ -21,16 +21,16 @@ # On Arch Linux sudo pacman -S xdotool -Configuration -------------- +.. rubric:: Configuration + The tool uses these environment variables: - DISPLAY: X11 display to use (default: ":1") - WIDTH: Screen width (default: 1024) - HEIGHT: Screen height (default: 768) -Usage ------ +.. rubric:: Usage + The tool supports these actions: Keyboard: @@ -85,31 +85,31 @@ ] -class Resolution(TypedDict): +class _Resolution(TypedDict): width: int height: int # Recommended maximum resolutions for LLM vision -MAX_SCALING_TARGETS: dict[str, Resolution] = { - "XGA": Resolution(width=1024, height=768), # 4:3 - "WXGA": Resolution(width=1280, height=800), # 16:10 - "FWXGA": Resolution(width=1366, height=768), # ~16:9 +MAX_SCALING_TARGETS: dict[str, _Resolution] = { + "XGA": _Resolution(width=1024, height=768), # 4:3 + "WXGA": _Resolution(width=1280, height=800), # 16:10 + "FWXGA": _Resolution(width=1366, height=768), # ~16:9 } -class ScalingSource(Enum): +class _ScalingSource(Enum): COMPUTER = "computer" API = "api" -def chunks(s: str, chunk_size: int) -> list[str]: +def _chunks(s: str, chunk_size: int) -> list[str]: """Split string into chunks for typing simulation.""" return [s[i : i + chunk_size] for i in range(0, len(s), chunk_size)] -def scale_coordinates( - source: ScalingSource, x: int, y: int, current_width: int, current_height: int +def _scale_coordinates( + source: _ScalingSource, x: int, y: int, current_width: int, current_height: int ) -> tuple[int, int]: """Scale coordinates to/from recommended resolutions.""" ratio = current_width / current_height @@ -127,7 +127,7 @@ def scale_coordinates( x_scaling_factor = target_dimension["width"] / current_width y_scaling_factor = target_dimension["height"] / current_height - if source == ScalingSource.API: + if source == _ScalingSource.API: if x > current_width or y > current_height: raise ValueError(f"Coordinates {x}, {y} are out of bounds") # Scale up @@ -136,7 +136,7 @@ def scale_coordinates( return round(x * x_scaling_factor), round(y * y_scaling_factor) -def run_xdotool(cmd: str, display: str | None = None) -> str: +def _run_xdotool(cmd: str, display: str | None = None) -> str: """Run an xdotool command with optional display setting and wait for completion.""" env = os.environ.copy() if display: @@ -173,14 +173,14 @@ def computer( if action in ("mouse_move", "left_click_drag"): if not coordinate: raise ValueError(f"coordinate is required for {action}") - x, y = scale_coordinates( - ScalingSource.API, coordinate[0], coordinate[1], width, height + x, y = _scale_coordinates( + _ScalingSource.API, coordinate[0], coordinate[1], width, height ) if action == "mouse_move": - run_xdotool(f"mousemove --sync {x} {y}", display) + _run_xdotool(f"mousemove --sync {x} {y}", display) else: # left_click_drag - run_xdotool(f"mousedown 1 mousemove --sync {x} {y} mouseup 1", display) + _run_xdotool(f"mousedown 1 mousemove --sync {x} {y} mouseup 1", display) print(f"Moved mouse to {x},{y}") return None @@ -189,11 +189,11 @@ def computer( raise ValueError(f"text is required for {action}") if action == "key": - run_xdotool(f"key -- {text}", display) + _run_xdotool(f"key -- {text}", display) print(f"Sent key sequence: {text}") else: # type - for chunk in chunks(text, TYPING_GROUP_SIZE): - run_xdotool( + for chunk in _chunks(text, TYPING_GROUP_SIZE): + _run_xdotool( f"type --delay {TYPING_DELAY_MS} -- {shlex.quote(chunk)}", display, ) @@ -206,7 +206,7 @@ def computer( "middle_click": "2", "double_click": "--repeat 2 --delay 500 1", }[action] - run_xdotool(f"click {click_arg}", display) + _run_xdotool(f"click {click_arg}", display) print(f"Performed {action}") return None elif action == "screenshot": @@ -217,7 +217,7 @@ def computer( if shutil.which("gnome-screenshot"): # FIXME: incorrect call to xdotool - run_xdotool(f"gnome-screenshot -f {path} -p", display) + _run_xdotool(f"gnome-screenshot -f {path} -p", display) elif os.name == "posix": path = _screenshot(path) # Use existing screenshot function else: @@ -225,8 +225,8 @@ def computer( # Scale if needed if path.exists(): - x, y = scale_coordinates( - ScalingSource.COMPUTER, width, height, width, height + x, y = _scale_coordinates( + _ScalingSource.COMPUTER, width, height, width, height ) subprocess.run( f"convert {path} -resize {x}x{y}! {path}", shell=True, check=True @@ -236,10 +236,10 @@ def computer( print("Error: Screenshot failed") return None elif action == "cursor_position": - output = run_xdotool("getmouselocation --shell", display) + output = _run_xdotool("getmouselocation --shell", display) x = int(output.split("X=")[1].split("\n")[0]) y = int(output.split("Y=")[1].split("\n")[0]) - x, y = scale_coordinates(ScalingSource.COMPUTER, x, y, width, height) + x, y = _scale_coordinates(_ScalingSource.COMPUTER, x, y, width, height) print(f"Cursor position: X={x},Y={y}") return None raise ValueError(f"Invalid action: {action}") diff --git a/gptme/tools/rag.py b/gptme/tools/rag.py index decaf5bd..7c7e7929 100644 --- a/gptme/tools/rag.py +++ b/gptme/tools/rag.py @@ -3,14 +3,14 @@ The RAG tool provides context-aware assistance by indexing and searching project documentation. -Installation ------------- +.. rubric:: Installation + The RAG tool requires the ``gptme-rag`` package. Install it with:: pip install "gptme[rag]" -Configuration -------------- +.. rubric:: Configuration + Configure RAG in your ``gptme.toml``:: [rag] @@ -31,8 +31,8 @@ embedding_ttl = 86400 # Embedding cache TTL in seconds (24h) search_ttl = 3600 # Search cache TTL in seconds (1h) -Features --------- +.. rubric:: Features + 1. Manual Search and Indexing - Index project documentation with ``rag index`` - Search indexed documents with ``rag search`` @@ -50,8 +50,8 @@ - Automatic cache invalidation - Memory-efficient storage -Benefits --------- +.. rubric:: Benefits + - Better informed responses through relevant documentation - Reduced need for manual context inclusion - Automatic token management From b2d41b53648334d423eaa1db848d6f127331f2fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sun, 17 Nov 2024 18:00:27 +0100 Subject: [PATCH 11/24] test: fixed tests --- gptme/context.py | 3 ++- gptme/tools/rag.py | 3 +++ tests/test_cli.py | 2 +- tests/test_tools_rag.py | 32 ++++++++++++++++++++++---------- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/gptme/context.py b/gptme/context.py index 082ff5b2..67da6b11 100644 --- a/gptme/context.py +++ b/gptme/context.py @@ -35,8 +35,9 @@ def get_context(self, query: str, max_tokens: int = 1000) -> list[Context]: class RAGContextProvider(ContextProvider): """Context provider using RAG.""" - # TODO: refactor this to share code with rag tool + _has_rag = True # Class attribute for testing + # TODO: refactor this to share code with rag tool def __init__(self): try: self._has_rag = True diff --git a/gptme/tools/rag.py b/gptme/tools/rag.py index 7c7e7929..88037ddf 100644 --- a/gptme/tools/rag.py +++ b/gptme/tools/rag.py @@ -136,6 +136,9 @@ def execute_rag(code: str, args: list[str], confirm: ConfirmFunc) -> Message: def init() -> ToolSpec: """Initialize the RAG tool.""" + if not _HAS_RAG: + return tool + config = get_project_config(Path.cwd()) if config: # Initialize RAG with configuration diff --git a/tests/test_cli.py b/tests/test_cli.py index c0d0d070..d2677ef1 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -129,7 +129,7 @@ def test_command_rename(args: list[str], runner: CliRunner, name: str): args.append("/rename auto") print(f"running: gptme {' '.join(args)}") result = runner.invoke(gptme.cli.main, args) - assert result.exit_code == 0 + assert result.exit_code == 0, (result.output, result.exception) @pytest.mark.slow diff --git a/tests/test_tools_rag.py b/tests/test_tools_rag.py index 7e15306e..43082d45 100644 --- a/tests/test_tools_rag.py +++ b/tests/test_tools_rag.py @@ -1,6 +1,7 @@ """Tests for the RAG tool.""" from collections.abc import Generator +from dataclasses import replace from unittest.mock import patch import pytest @@ -33,7 +34,12 @@ def test_rag_tool_init(): def test_rag_tool_init_without_gptme_rag(): """Test RAG tool initialization when gptme-rag is not available.""" - with patch("gptme.tools.rag._HAS_RAG", False): + + tool = init_rag() + with ( + patch("gptme.tools.rag._HAS_RAG", False), + patch("gptme.tools.rag.tool", replace(tool, available=False)), + ): tool = init_rag() assert isinstance(tool, ToolSpec) assert tool.name == "rag" @@ -53,17 +59,23 @@ def noconfirm(*args, **kwargs): @pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") -def test_rag_index_command(temp_docs): +def test_rag_index_command(temp_docs, tmp_path): """Test the index command.""" - tool = init_rag() - assert tool.execute - result = _m2str(tool.execute("", ["index", str(temp_docs)], noconfirm)) - assert "Indexed" in result + with patch("gptme.tools.rag.get_project_config") as mock_config: + mock_config.return_value.rag = { + "index_path": str(tmp_path), + "collection": "test", + } - # Check status after indexing - result = _m2str(tool.execute("", ["status"], noconfirm)) - assert "Index contains" in result - assert "2" in result # Should have indexed 2 documents + tool = init_rag() + assert tool.execute + result = _m2str(tool.execute("", ["index", str(temp_docs)], noconfirm)) + assert "Indexed" in result + + # Check status after indexing + result = _m2str(tool.execute("", ["status"], noconfirm)) + assert "Index contains" in result + assert "2" in result # Should have indexed 2 documents @pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") From 19a7d33914471f519cd7c85414167d6849b763e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sun, 17 Nov 2024 18:07:34 +0100 Subject: [PATCH 12/24] Apply suggestions from code review --- tests/test_cache.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_cache.py b/tests/test_cache.py index c07c18c3..2423db46 100644 --- a/tests/test_cache.py +++ b/tests/test_cache.py @@ -89,8 +89,7 @@ def test_get_cache_singleton(): assert cache1 is cache2 -@pytest.mark.asyncio -async def test_rag_context_provider_with_cache(): +def test_rag_context_provider_with_cache(): """Test RAG context provider with caching.""" with patch("gptme.context.RAGContextProvider._has_rag", True): provider = RAGContextProvider() From b64e805fd6484ca4f9a5469190e17fdc9e0dc747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sun, 17 Nov 2024 18:56:55 +0100 Subject: [PATCH 13/24] fix: fixed when running in a directory without gptme.toml --- gptme/context.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/gptme/context.py b/gptme/context.py index 67da6b11..7061c60b 100644 --- a/gptme/context.py +++ b/gptme/context.py @@ -40,10 +40,24 @@ class RAGContextProvider(ContextProvider): # TODO: refactor this to share code with rag tool def __init__(self): try: - self._has_rag = True + # Check if gptme-rag is installed + import importlib.util + + if importlib.util.find_spec("gptme_rag") is None: + logger.debug( + "gptme-rag not installed, RAG context provider will not be available" + ) + self._has_rag = False + return + # Check if we have a valid config config = get_project_config(Path.cwd()) - assert config + if not config or not hasattr(config, "rag"): + logger.debug("No RAG configuration found in gptme.toml") + self._has_rag = False + return + + self._has_rag = True # Storage configuration self.indexer = gptme_rag.Indexer( @@ -58,10 +72,8 @@ def __init__(self): self.auto_context = config.rag.get("auto_context", True) self.min_relevance = config.rag.get("min_relevance", 0.5) self.max_results = config.rag.get("max_results", 5) - except ImportError: - logger.debug( - "gptme-rag not installed, RAG context provider will not be available" - ) + except Exception as e: + logger.debug(f"Failed to initialize RAG context provider: {e}") self._has_rag = False def get_context(self, query: str, max_tokens: int = 1000) -> list[Context]: From 573514fe7183ec9c37aa990c5d1f9f56eab7530a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sun, 17 Nov 2024 19:01:36 +0100 Subject: [PATCH 14/24] fix: fixed lint --- tests/test_cache.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_cache.py b/tests/test_cache.py index 2423db46..c0877145 100644 --- a/tests/test_cache.py +++ b/tests/test_cache.py @@ -3,7 +3,6 @@ import time from unittest.mock import Mock, patch -import pytest from gptme.cache import Cache, RAGCache, get_cache from gptme.context import RAGContextProvider From 72bc9761c0926eb922f72b165532a310ae2d49d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sun, 17 Nov 2024 19:21:59 +0100 Subject: [PATCH 15/24] test: fixed tests for rag --- gptme/interrupt.py | 6 +++++- tests/conftest.py | 20 ++++++++++++++++++++ tests/test_tools_rag.py | 1 + 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/conftest.py diff --git a/gptme/interrupt.py b/gptme/interrupt.py index 73d0cf74..dcd9c7a6 100644 --- a/gptme/interrupt.py +++ b/gptme/interrupt.py @@ -2,6 +2,7 @@ Sets up a KeyboardInterrupt handler to handle Ctrl-C during the chat loop. """ +import os import time from .util import console @@ -18,7 +19,10 @@ def handle_keyboard_interrupt(signum, frame): # pragma: no cover global last_interrupt_time current_time = time.time() - if interruptible: + # if testing with pytest + testing = bool(os.getenv("PYTEST_CURRENT_TEST")) + + if interruptible or testing: raise KeyboardInterrupt # if current_time - last_interrupt_time <= timeout: diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..cebcc5fc --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,20 @@ +"""Test configuration and shared fixtures.""" + +from gptme.tools.rag import _HAS_RAG + + +def pytest_sessionstart(session): + # Download the embedding model before running tests. + download_model() + + +def download_model(): + if not _HAS_RAG: + return + + # downloads the model if it doesn't exist + from chromadb.utils import embedding_functions # fmt: skip + + ef = embedding_functions.DefaultEmbeddingFunction() + if ef: + ef._download_model_if_not_exists() # type: ignore diff --git a/tests/test_tools_rag.py b/tests/test_tools_rag.py index 43082d45..1972561d 100644 --- a/tests/test_tools_rag.py +++ b/tests/test_tools_rag.py @@ -23,6 +23,7 @@ def temp_docs(tmp_path): return tmp_path +@pytest.mark.timeout(func_only=True) @pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") def test_rag_tool_init(): """Test RAG tool initialization.""" From aa57b672ece60331394ba52cfabdbaad4729abf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sun, 17 Nov 2024 20:15:59 +0100 Subject: [PATCH 16/24] build(deps): updated gptme-rag --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index f34040c4..c953d38b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1098,13 +1098,13 @@ files = [ [[package]] name = "gptme-rag" -version = "0.1.4" +version = "0.1.5" description = "RAG implementation for gptme context management" optional = true python-versions = "<4.0,>=3.10" files = [ - {file = "gptme_rag-0.1.4-py3-none-any.whl", hash = "sha256:047acd2aa5d7aefea467a6b15a4b6c416e5260eaf39d66043275c3b8dd1504d6"}, - {file = "gptme_rag-0.1.4.tar.gz", hash = "sha256:a972a9de96fbe6d5a0674c7dd9f5443e2159b74316ec210be71744cb02799073"}, + {file = "gptme_rag-0.1.5-py3-none-any.whl", hash = "sha256:0d93a13d8ffb3d1c4e9938c7c4469e0907c2c51d048816ba8b9a9e2b6ef5c5b1"}, + {file = "gptme_rag-0.1.5.tar.gz", hash = "sha256:a93f5643556e5b0ded10cad81e80affe87c24ed92cc31d7ac260e96285b0e22e"}, ] [package.dependencies] @@ -5184,4 +5184,4 @@ server = ["flask", "flask-cors"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "1e79823f341ef0399ceb2e2a391ae0f1e74d37c58944865ef882e610aa70bbc3" +content-hash = "24319f2d1054169eb45f031cc6b34fda546e04a6f29b033024c55348f7dfa281" diff --git a/pyproject.toml b/pyproject.toml index 2203de1e..562d9c80 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ bashlex = "^0.18" playwright = {version = "1.47.*", optional=true} # version constrained due to annoying to have to run `playwright install` on every update youtube_transcript_api = {version = "^0.6.1", optional = true} python-xlib = {version = "^0.33", optional = true} # for X11 interaction -gptme-rag = {version = "^0.1.4", optional = true} # for RAG functionality +gptme-rag = {version = "^0.1.5", optional = true} # for RAG functionality # providers openai = "^1.0" From 2af98f14830b706803c9b33cf04ddc3a6b8c08cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sun, 17 Nov 2024 20:30:17 +0100 Subject: [PATCH 17/24] fix: add get_project_dir helper function --- gptme/prompts.py | 18 +++++------------- gptme/tools/rag.py | 7 ++++++- gptme/util/__init__.py | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/gptme/prompts.py b/gptme/prompts.py index 376235d3..3695eeae 100644 --- a/gptme/prompts.py +++ b/gptme/prompts.py @@ -7,7 +7,6 @@ import glob import logging -import os import platform import subprocess from collections.abc import Generator, Iterable @@ -17,7 +16,7 @@ from .__version__ import __version__ from .config import get_config, get_project_config from .message import Message -from .util import document_prompt_function +from .util import document_prompt_function, get_project_dir PromptType = Literal["full", "short"] @@ -173,19 +172,12 @@ def prompt_project() -> Generator[Message, None, None]: """ Generate the project-specific prompt based on the current Git repository. """ - config_prompt = get_config().prompt - try: - projectdir = subprocess.run( - ["git", "rev-parse", "--show-toplevel"], - capture_output=True, - text=True, - check=True, - ).stdout.strip() - project = os.path.basename(projectdir) - except subprocess.CalledProcessError: - logger.debug("Unable to determine Git repository root.") + projectdir = get_project_dir() + if not projectdir: return + config_prompt = get_config().prompt + project = projectdir.name project_info = config_prompt.get("project", {}).get( project, "No specific project context provided." ) diff --git a/gptme/tools/rag.py b/gptme/tools/rag.py index 88037ddf..4c4a057f 100644 --- a/gptme/tools/rag.py +++ b/gptme/tools/rag.py @@ -63,6 +63,7 @@ from ..config import get_project_config from ..message import Message +from ..util import get_project_dir from .base import ConfirmFunc, ToolSpec, ToolUse logger = logging.getLogger(__name__) @@ -139,7 +140,11 @@ def init() -> ToolSpec: if not _HAS_RAG: return tool - config = get_project_config(Path.cwd()) + project_dir = get_project_dir() + if not project_dir: + return tool + + config = get_project_config(project_dir) if config: # Initialize RAG with configuration global indexer diff --git a/gptme/util/__init__.py b/gptme/util/__init__.py index 3e6d33cb..3fea65e7 100644 --- a/gptme/util/__init__.py +++ b/gptme/util/__init__.py @@ -8,6 +8,7 @@ import random import re import shutil +import subprocess import sys import termios import textwrap @@ -368,3 +369,17 @@ def get_installed_programs(candidates: tuple[str, ...]) -> set[str]: if shutil.which(candidate) is not None: installed.add(candidate) return installed + + +def get_project_dir() -> Path | None: + try: + projectdir = subprocess.run( + ["git", "rev-parse", "--show-toplevel"], + capture_output=True, + text=True, + check=True, + ).stdout.strip() + return Path(projectdir) + except subprocess.CalledProcessError: + logger.debug("Unable to determine Git repository root.") + return None From 33467674f6cb42a1ab5a02182dac2750207a19c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sun, 17 Nov 2024 21:23:54 +0100 Subject: [PATCH 18/24] fix: changed rag tool to use functions instead of execute --- gptme/tools/rag.py | 92 +++++++++++++++------------------- gptme/util/cli.py | 16 +++--- pyproject.toml | 1 + tests/test_tools_rag.py | 107 +++++++++++++++++++--------------------- tests/test_util_cli.py | 8 ++- 5 files changed, 103 insertions(+), 121 deletions(-) diff --git a/gptme/tools/rag.py b/gptme/tools/rag.py index 4c4a057f..bc7b5899 100644 --- a/gptme/tools/rag.py +++ b/gptme/tools/rag.py @@ -62,9 +62,8 @@ from pathlib import Path from ..config import get_project_config -from ..message import Message from ..util import get_project_dir -from .base import ConfirmFunc, ToolSpec, ToolUse +from .base import ToolSpec, ToolUse logger = logging.getLogger(__name__) @@ -80,59 +79,50 @@ instructions = """ Use RAG to index and search project documentation. - -Commands: -- index [paths...] - Index documents in specified paths -- search - Search indexed documents -- status - Show index status """ examples = f""" User: Index the current directory Assistant: Let me index the current directory with RAG. -{ToolUse("rag", ["index"], "").to_output()} +{ToolUse("ipython", [], "rag_index()").to_output()} System: Indexed 1 paths User: Search for documentation about functions Assistant: I'll search for function-related documentation. -{ToolUse("rag", ["search", "function", "documentation"], "").to_output()} +{ToolUse("ipython", [], 'rag_search("function documentation")').to_output()} System: ### docs/api.md Functions are documented using docstrings... User: Show index status Assistant: I'll check the current status of the RAG index. -{ToolUse("rag", ["status"], "").to_output()} +{ToolUse("ipython", [], "rag_status()").to_output()} System: Index contains 42 documents """ -def execute_rag(code: str, args: list[str], confirm: ConfirmFunc) -> Message: - """Execute RAG commands.""" +def rag_index(*paths: str, glob: str | None = None) -> str: + """Index documents in specified paths.""" + assert indexer is not None, "RAG indexer not initialized" + paths = paths or (".",) + kwargs = {"glob_pattern": glob} if glob else {} + for path in paths: + indexer.index_directory(Path(path), **kwargs) + return f"Indexed {len(paths)} paths" + + +def rag_search(query: str) -> str: + """Search indexed documents.""" + assert indexer is not None, "RAG indexer not initialized" + docs, _ = indexer.search(query) + return "\n\n".join( + f"### {doc.metadata['source']}\n{doc.content[:200]}..." for doc in docs + ) + + +def rag_status() -> str: + """Show index status.""" assert indexer is not None, "RAG indexer not initialized" - command = args[0] if args else "help" - - if command == "help": - return Message("system", "Available commands: index, search, status") - elif command == "index": - paths = args[1:] or ["."] - for path in paths: - indexer.index_directory(Path(path)) - return Message("system", f"Indexed {len(paths)} paths") - elif command == "search": - query = " ".join(args[1:]) - docs, _ = indexer.search(query) - return Message( - "system", - "\n\n".join( - f"### {doc.metadata['source']}\n{doc.content[:200]}..." for doc in docs - ), - ) - elif command == "status": - return Message( - "system", f"Index contains {indexer.collection.count()} documents" - ) - else: - return Message("system", f"Unknown command: {command}") + return f"Index contains {indexer.collection.count()} documents" def init() -> ToolSpec: @@ -141,22 +131,19 @@ def init() -> ToolSpec: return tool project_dir = get_project_dir() - if not project_dir: - return tool + index_path = Path("~/.cache/gptme/rag").expanduser() + collection = "default" + if project_dir and (config := get_project_config(project_dir)): + index_path = Path(config.rag.get("index_path", index_path)).expanduser() + collection = config.rag.get("collection", project_dir.name) + + import gptme_rag # fmt: skip - config = get_project_config(project_dir) - if config: - # Initialize RAG with configuration - global indexer - import gptme_rag # fmt: skip - - indexer = gptme_rag.Indexer( - persist_directory=Path( - config.rag.get("index_path", "~/.cache/gptme/rag") - ).expanduser(), - # TODO: use a better default collection name? (e.g. project name) - collection_name=config.rag.get("collection", "gptme_docs"), - ) + global indexer + indexer = gptme_rag.Indexer( + persist_directory=index_path, + collection_name=collection, + ) return tool @@ -165,8 +152,7 @@ def init() -> ToolSpec: desc="RAG (Retrieval-Augmented Generation) for context-aware assistance", instructions=instructions, examples=examples, - block_types=["rag"], - execute=execute_rag, + functions=[rag_index, rag_search, rag_status], available=_HAS_RAG, init=init, ) diff --git a/gptme/util/cli.py b/gptme/util/cli.py index 3bdc52b3..eea61c04 100644 --- a/gptme/util/cli.py +++ b/gptme/util/cli.py @@ -103,14 +103,14 @@ def context(): @context.command("generate") -@click.argument("path", type=click.Path(exists=True)) -def context_generate(_path: str): - """Generate context from a directory.""" - pass - # from ..context import generate_context # fmt: skip - - # ctx = generate_context(path) - # print(ctx) +@click.argument("query") +def context_generate(query: str): + """Retrieve context for a given query.""" + from ..context import RAGContextProvider # fmt: skip + + provider = RAGContextProvider() + ctx = provider.get_context(query) + print(ctx) @main.group() diff --git a/pyproject.toml b/pyproject.toml index 562d9c80..fe688887 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ include = ["gptme/server/static/**/*", "media/logo.png"] gptme = "gptme.cli:main" gptme-server = "gptme.server.cli:main" gptme-eval = "gptme.eval.main:main" +gptme-util = "gptme.util.cli:main" gptme-nc = "gptme.ncurses:main" [tool.poetry.dependencies] diff --git a/tests/test_tools_rag.py b/tests/test_tools_rag.py index 1972561d..1d509161 100644 --- a/tests/test_tools_rag.py +++ b/tests/test_tools_rag.py @@ -1,14 +1,13 @@ """Tests for the RAG tool.""" -from collections.abc import Generator from dataclasses import replace from unittest.mock import patch import pytest -from gptme import Message from gptme.tools.base import ToolSpec from gptme.tools.rag import _HAS_RAG from gptme.tools.rag import init as init_rag +from gptme.tools.rag import rag_index, rag_search, rag_status @pytest.fixture @@ -35,7 +34,6 @@ def test_rag_tool_init(): def test_rag_tool_init_without_gptme_rag(): """Test RAG tool initialization when gptme-rag is not available.""" - tool = init_rag() with ( patch("gptme.tools.rag._HAS_RAG", False), @@ -47,73 +45,72 @@ def test_rag_tool_init_without_gptme_rag(): assert tool.available is False -def _m2str(tool_execute: Generator[Message, None, None] | Message) -> str: - """Convert a execute() call to a string.""" - if isinstance(tool_execute, Generator): - return tool_execute.send(None).content - elif isinstance(tool_execute, Message): - return tool_execute.content - - -def noconfirm(*args, **kwargs): - return True - - @pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") -def test_rag_index_command(temp_docs, tmp_path): - """Test the index command.""" +def test_rag_index_function(temp_docs, tmp_path): + """Test the index function.""" with patch("gptme.tools.rag.get_project_config") as mock_config: mock_config.return_value.rag = { "index_path": str(tmp_path), "collection": "test", } - tool = init_rag() - assert tool.execute - result = _m2str(tool.execute("", ["index", str(temp_docs)], noconfirm)) - assert "Indexed" in result + # Initialize RAG + init_rag() - # Check status after indexing - result = _m2str(tool.execute("", ["status"], noconfirm)) - assert "Index contains" in result - assert "2" in result # Should have indexed 2 documents + # Test indexing with specific path + result = rag_index(str(temp_docs)) + assert "Indexed 1 paths" in result + + # Test indexing with default path + # FIXME: this is really slow in the gptme directory, + # since it contains a lot of files (which are in gitignore, but not respected) + result = rag_index(glob="**/*.py") + assert "Indexed 1 paths" in result @pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") -def test_rag_search_command(temp_docs): - """Test the search command.""" - tool = init_rag() - assert tool.execute - # Index first - _m2str(tool.execute("", ["index", str(temp_docs)], noconfirm)) +def test_rag_search_function(temp_docs, tmp_path): + """Test the search function.""" + with patch("gptme.tools.rag.get_project_config") as mock_config: + mock_config.return_value.rag = { + "index_path": str(tmp_path), + "collection": "test", + } + + # Initialize RAG and index documents + init_rag() + rag_index(str(temp_docs)) - # Search for Python - result = _m2str(tool.execute("", ["search", "Python"], noconfirm)) - assert "doc1.md" in result - assert "Python functions" in result + # Search for Python + result = rag_search("Python") + assert "doc1.md" in result + assert "Python functions" in result - # Search for testing - result = _m2str(tool.execute("", ["search", "testing"], noconfirm)) - assert "doc2.md" in result - assert "testing practices" in result + # Search for testing + result = rag_search("testing") + assert "doc2.md" in result + assert "testing practices" in result @pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") -def test_rag_help_command(): - """Test the help command.""" - tool = init_rag() - assert tool.execute - result = _m2str(tool.execute("", ["help"], noconfirm)) - assert "Available commands" in result - assert "index" in result - assert "search" in result - assert "status" in result +def test_rag_status_function(temp_docs, tmp_path): + """Test the status function.""" + with patch("gptme.tools.rag.get_project_config") as mock_config: + mock_config.return_value.rag = { + "index_path": str(tmp_path), + "collection": "test", + } + # Initialize RAG + init_rag() -@pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") -def test_rag_invalid_command(): - """Test invalid command handling.""" - tool = init_rag() - assert tool.execute - result = _m2str(tool.execute("", ["invalid"], noconfirm)) - assert "Unknown command" in result + # Check initial status + result = rag_status() + assert "Index contains" in result + assert "0" in result + + # Index documents and check status again + rag_index(str(temp_docs)) + result = rag_status() + assert "Index contains" in result + assert "2" in result # Should have indexed 2 documents diff --git a/tests/test_util_cli.py b/tests/test_util_cli.py index bff9df3d..46156240 100644 --- a/tests/test_util_cli.py +++ b/tests/test_util_cli.py @@ -1,9 +1,10 @@ """Tests for the gptme-util CLI.""" +import time from pathlib import Path -from click.testing import CliRunner -import pytest +from click.testing import CliRunner +from gptme.logmanager import ConversationMeta from gptme.util.cli import main @@ -64,8 +65,6 @@ def test_chats_list(tmp_path, mocker): ) # Create ConversationMeta objects for our test conversations - from gptme.logmanager import ConversationMeta - import time conv1 = ConversationMeta( name="2024-01-01-chat-one", @@ -96,7 +95,6 @@ def test_chats_list(tmp_path, mocker): assert "Messages: 2" in result.output # Second chat has 2 messages -@pytest.mark.skip("Waiting for context module PR") def test_context_generate(tmp_path): """Test the context generate command.""" # Create a test file From 5e053f09f7e8cb760002331ded0092c6196b5da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sun, 17 Nov 2024 21:26:55 +0100 Subject: [PATCH 19/24] Apply suggestions from code review --- gptme/tools/rag.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gptme/tools/rag.py b/gptme/tools/rag.py index bc7b5899..cbf813fb 100644 --- a/gptme/tools/rag.py +++ b/gptme/tools/rag.py @@ -34,9 +34,9 @@ .. rubric:: Features 1. Manual Search and Indexing - - Index project documentation with ``rag index`` - - Search indexed documents with ``rag search`` - - Check index status with ``rag status`` + - Index project documentation with ``rag_index`` + - Search indexed documents with ``rag_search`` + - Check index status with ``rag_status`` 2. Automatic Context Enhancement - Automatically adds relevant context to user messages From 26aeced690cfc3874afd55c0762568c45423f366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Mon, 18 Nov 2024 16:47:46 +0100 Subject: [PATCH 20/24] fix: more refactoring of rag, made typechecking of imports stricter, fixed tests --- gptme/cache.py | 121 -------------------- gptme/context.py | 152 ------------------------- gptme/init.py | 8 +- gptme/logmanager.py | 11 +- gptme/tools/_rag_context.py | 189 +++++++++++++++++++++++++++++++ gptme/tools/rag.py | 35 ++---- gptme/tools/youtube.py | 3 +- gptme/util/cli.py | 19 ++-- poetry.lock | 20 ++-- pyproject.toml | 9 +- scripts/summarize_project.py | 2 +- scripts/train/collect.py | 4 +- tests/conftest.py | 2 +- tests/test_cache.py | 110 ------------------ tests/test_context.py | 101 ----------------- tests/test_tools_rag.py | 209 ++++++++++++++++++++++++++++++++++- tests/test_util_cli.py | 14 ++- 17 files changed, 467 insertions(+), 542 deletions(-) delete mode 100644 gptme/cache.py delete mode 100644 gptme/context.py create mode 100644 gptme/tools/_rag_context.py delete mode 100644 tests/test_cache.py delete mode 100644 tests/test_context.py diff --git a/gptme/cache.py b/gptme/cache.py deleted file mode 100644 index 61396efb..00000000 --- a/gptme/cache.py +++ /dev/null @@ -1,121 +0,0 @@ -"""Caching system for RAG functionality.""" - -import logging -import time -from dataclasses import dataclass -from pathlib import Path -from typing import Any, Optional - -from .config import get_project_config - -logger = logging.getLogger(__name__) - - -# Global cache instance -_cache: Optional["RAGCache"] = None - - -def get_cache() -> "RAGCache": - """Get the global RAG cache instance.""" - global _cache - if _cache is None: - _cache = RAGCache() - return _cache - - -@dataclass -class CacheEntry: - """Entry in the cache with metadata.""" - - data: Any - timestamp: float - ttl: float - - -class Cache: - """Simple cache with TTL and size limits.""" - - def __init__(self, max_size: int = 1000, default_ttl: float = 3600): - self.max_size = max_size - self.default_ttl = default_ttl - self._cache: dict[str, CacheEntry] = {} - - def get(self, key: str) -> Any | None: - """Get a value from the cache.""" - if key not in self._cache: - return None - - entry = self._cache[key] - if time.time() - entry.timestamp > entry.ttl: - # Entry expired - del self._cache[key] - return None - - return entry.data - - def set(self, key: str, value: Any, ttl: float | None = None) -> None: - """Set a value in the cache.""" - # Enforce size limit - if len(self._cache) >= self.max_size: - # Remove oldest entry - oldest_key = min(self._cache.items(), key=lambda x: x[1].timestamp)[0] - del self._cache[oldest_key] - - self._cache[key] = CacheEntry( - data=value, timestamp=time.time(), ttl=ttl or self.default_ttl - ) - - def clear(self) -> None: - """Clear the cache.""" - self._cache.clear() - - -class RAGCache: - """Cache for RAG functionality.""" - - def __init__(self): - config = get_project_config(Path.cwd()) - assert config - cache_config = config.rag.get("rag", {}).get("cache", {}) - - # Initialize caches with configured limits - self.embedding_cache = Cache( - max_size=cache_config.get("max_embeddings", 10000), - default_ttl=cache_config.get("embedding_ttl", 86400), # 24 hours - ) - self.search_cache = Cache( - max_size=cache_config.get("max_searches", 1000), - default_ttl=cache_config.get("search_ttl", 3600), # 1 hour - ) - - @staticmethod - def _make_search_key(query: str, n_results: int) -> str: - """Create a cache key for a search query.""" - return f"{query}::{n_results}" - - def get_embedding(self, text: str) -> list[float] | None: - """Get cached embedding for text.""" - return self.embedding_cache.get(text) - - def set_embedding(self, text: str, embedding: list[float]) -> None: - """Cache embedding for text.""" - self.embedding_cache.set(text, embedding) - - def get_search_results( - self, query: str, n_results: int - ) -> tuple[list[Any], dict[str, Any]] | None: - """Get cached search results.""" - key = self._make_search_key(query, n_results) - return self.search_cache.get(key) - - def set_search_results( - self, query: str, n_results: int, results: tuple[list[Any], dict[str, Any]] - ) -> None: - """Cache search results.""" - key = self._make_search_key(query, n_results) - self.search_cache.set(key, results) - - def clear(self) -> None: - """Clear all caches.""" - self.embedding_cache.clear() - self.search_cache.clear() diff --git a/gptme/context.py b/gptme/context.py deleted file mode 100644 index 7061c60b..00000000 --- a/gptme/context.py +++ /dev/null @@ -1,152 +0,0 @@ -"""Context providers for enhancing messages with relevant context.""" - -import logging -from abc import ABC, abstractmethod -from dataclasses import dataclass -from pathlib import Path - -import gptme_rag - -from .cache import get_cache -from .config import get_project_config -from .message import Message - -logger = logging.getLogger(__name__) - - -@dataclass -class Context: - """Context information to be added to messages.""" - - content: str - source: str - relevance: float - - -class ContextProvider(ABC): - """Base class for context providers.""" - - @abstractmethod - def get_context(self, query: str, max_tokens: int = 1000) -> list[Context]: - """Get relevant context for a query.""" - pass - - -class RAGContextProvider(ContextProvider): - """Context provider using RAG.""" - - _has_rag = True # Class attribute for testing - - # TODO: refactor this to share code with rag tool - def __init__(self): - try: - # Check if gptme-rag is installed - import importlib.util - - if importlib.util.find_spec("gptme_rag") is None: - logger.debug( - "gptme-rag not installed, RAG context provider will not be available" - ) - self._has_rag = False - return - - # Check if we have a valid config - config = get_project_config(Path.cwd()) - if not config or not hasattr(config, "rag"): - logger.debug("No RAG configuration found in gptme.toml") - self._has_rag = False - return - - self._has_rag = True - - # Storage configuration - self.indexer = gptme_rag.Indexer( - persist_directory=config.rag.get("index_path", "~/.cache/gptme/rag"), - collection_name=config.rag.get("collection", "default"), - ) - - # Context enhancement configuration - self.context_assembler = gptme_rag.ContextAssembler( - max_tokens=config.rag.get("max_tokens", 2000) - ) - self.auto_context = config.rag.get("auto_context", True) - self.min_relevance = config.rag.get("min_relevance", 0.5) - self.max_results = config.rag.get("max_results", 5) - except Exception as e: - logger.debug(f"Failed to initialize RAG context provider: {e}") - self._has_rag = False - - def get_context(self, query: str, max_tokens: int = 1000) -> list[Context]: - """Get relevant context using RAG.""" - if not self._has_rag or not self.auto_context: - return [] - - try: - # Check cache first - cache = get_cache() - cached_results = cache.get_search_results(query, self.max_results) - - if cached_results: - docs, results = cached_results - logger.debug(f"Using cached search results for query: {query}") - else: - # Search with configured limits - docs, results = self.indexer.search(query, n_results=self.max_results) - # Cache the results - cache.set_search_results(query, self.max_results, (docs, results)) - logger.debug(f"Cached search results for query: {query}") - - contexts = [] - for i, doc in enumerate(docs): - # Calculate relevance score (1 - distance) - relevance = 1.0 - results["distances"][0][i] - - # Skip if below minimum relevance - if relevance < self.min_relevance: - continue - - contexts.append( - Context( - content=doc.content, - source=doc.metadata.get("source", "unknown"), - relevance=relevance, - ) - ) - - # Sort by relevance - contexts.sort(key=lambda x: x.relevance, reverse=True) - - return contexts - except Exception as e: - logger.warning(f"Error getting RAG context: {e}") - return [] - - -def enhance_messages(messages: list[Message]) -> list[Message]: - """Enhance messages with context from available providers.""" - providers = [RAGContextProvider()] - enhanced_messages = [] - - for msg in messages: - if msg.role == "user": - # Get context from all providers - contexts = [] - for provider in providers: - try: - contexts.extend(provider.get_context(msg.content)) - except Exception as e: - logger.warning(f"Error getting context from provider: {e}") - - # Add context as a system message before the user message - if contexts: - context_msg = "Relevant context:\n\n" - for ctx in contexts: - context_msg += f"### {ctx.source}\n{ctx.content}\n\n" - - enhanced_messages.append( - Message(role="system", content=context_msg, hide=True) - ) - - enhanced_messages.append(msg) - - return enhanced_messages diff --git a/gptme/init.py b/gptme/init.py index 72f0fcba..f899f549 100644 --- a/gptme/init.py +++ b/gptme/init.py @@ -2,6 +2,7 @@ from typing import cast from dotenv import load_dotenv +from rich.logging import RichHandler from .config import config_path, get_config, set_config_value from .llm import init_llm @@ -77,7 +78,12 @@ def init(model: str | None, interactive: bool, tool_allowlist: list[str] | None) def init_logging(verbose): # log init - logging.basicConfig(level=logging.DEBUG if verbose else logging.INFO) + logging.basicConfig( + level=logging.DEBUG if verbose else logging.INFO, + format="%(message)s", + datefmt="[%X]", + handlers=[RichHandler()], + ) # set httpx logging to WARNING logging.getLogger("httpx").setLevel(logging.WARNING) diff --git a/gptme/logmanager.py b/gptme/logmanager.py index 57462aa9..d6d2656b 100644 --- a/gptme/logmanager.py +++ b/gptme/logmanager.py @@ -306,17 +306,18 @@ def to_dict(self, branches=False) -> dict: def prepare_messages(msgs: list[Message]) -> list[Message]: """Prepares the messages before sending to the LLM.""" - from .context import enhance_messages + from .tools._rag_context import _HAS_RAG, enhance_messages # fmt: skip # First enhance messages with context - msgs_enhanced = enhance_messages(msgs) + if _HAS_RAG: + msgs = enhance_messages(msgs) # Then reduce and limit as before - msgs_reduced = list(reduce_log(msgs_enhanced)) + msgs_reduced = list(reduce_log(msgs)) - if len_tokens(msgs_enhanced) != len_tokens(msgs_reduced): + if len_tokens(msgs) != len_tokens(msgs_reduced): logger.info( - f"Reduced log from {len_tokens(msgs_enhanced)//1} to {len_tokens(msgs_reduced)//1} tokens" + f"Reduced log from {len_tokens(msgs)//1} to {len_tokens(msgs_reduced)//1} tokens" ) msgs_limited = limit_log(msgs_reduced) if len(msgs_reduced) != len(msgs_limited): diff --git a/gptme/tools/_rag_context.py b/gptme/tools/_rag_context.py new file mode 100644 index 00000000..8e52591c --- /dev/null +++ b/gptme/tools/_rag_context.py @@ -0,0 +1,189 @@ +"""Shared RAG context functionality.""" + +import logging +from abc import ABC, abstractmethod +from dataclasses import dataclass +from pathlib import Path +from typing import ( + Any, +) + +from ..config import get_project_config +from ..message import Message + +logger = logging.getLogger(__name__) + +try: + import gptme_rag # type: ignore # fmt: skip + + _HAS_RAG = True +except ImportError: + logger.debug("gptme-rag not installed, RAG functionality will not be available") + _HAS_RAG = False + + +# Simple in-memory cache for search results +_search_cache: dict[str, tuple[list[Any], dict]] = {} + + +def _get_search_results(query: str, n_results: int) -> tuple[list[Any], dict] | None: + """Get cached search results.""" + return _search_cache.get(f"{query}::{n_results}") + + +def _set_search_results( + query: str, n_results: int, results: tuple[list[Any], dict] +) -> None: + """Cache search results.""" + _search_cache[f"{query}::{n_results}"] = results + + +def _clear_cache() -> None: + """Clear the search cache.""" + _search_cache.clear() + + +@dataclass +class Context: + """Context information to be added to messages.""" + + content: str + source: str + relevance: float + + +class ContextProvider(ABC): + """Base class for context providers.""" + + @abstractmethod + def get_context(self, query: str, max_tokens: int = 1000) -> list[Context]: + """Get relevant context for a query.""" + pass + + +class RAGManager: + """Manages RAG functionality for both context enhancement and tool use.""" + + def __init__( + self, index_path: Path = Path("~/.cache/gptme/rag"), collection: str = "default" + ): + if not _HAS_RAG: + raise ImportError("gptme-rag not installed") + + self.index_path = index_path.expanduser() + self.collection = collection + + # Initialize the indexer + self.indexer = gptme_rag.Indexer( + persist_directory=self.index_path, + collection_name=self.collection, + ) + + # Load config + config = get_project_config(Path.cwd()) + self.config = config.rag if config and config.rag else {} + + # Context enhancement configuration + self.context_assembler = gptme_rag.ContextAssembler( + max_tokens=self.config.get("max_tokens", 2000) + ) + self.auto_context = self.config.get("auto_context", True) + self.min_relevance = self.config.get("min_relevance", 0.5) + self.max_results = self.config.get("max_results", 5) + + def search( + self, query: str, n_results: int | None = None + ) -> tuple[list[Any], dict]: + """Search the index with caching.""" + n_results = n_results or self.max_results + + # Check cache + if cached := _get_search_results(query, n_results): + logger.debug(f"Using cached search results for query: {query}") + return cached + + # Perform search + docs, results = self.indexer.search(query, n_results=n_results) + + # Cache results + _set_search_results(query, n_results, (docs, results)) + logger.debug(f"Cached search results for query: {query}") + + return docs, results + + def get_context(self, query: str, max_tokens: int = 1000) -> list[Context]: + """Get relevant context using RAG.""" + if not self.auto_context: + return [] + + try: + docs, results = self.search(query) + contexts = [] + + for i, doc in enumerate(docs): + # Calculate relevance score (1 - distance) + relevance = 1.0 - results["distances"][0][i] + + # Skip if below minimum relevance + if relevance < self.min_relevance: + continue + + contexts.append( + Context( + content=doc.content, + source=doc.metadata.get("source", "unknown"), + relevance=relevance, + ) + ) + + # Sort by relevance + contexts.sort(key=lambda x: x.relevance, reverse=True) + return contexts + + except Exception as e: + logger.warning(f"Error getting RAG context: {e}") + return [] + + def index_directory(self, path: Path, **kwargs) -> int: + """Index a directory and return number of documents indexed.""" + return self.indexer.index_directory(path, **kwargs) + + def get_document_count(self) -> int: + """Get the total number of documents in the index.""" + return self.indexer.collection.count() + + +def enhance_messages(messages: list[Message]) -> list[Message]: + """Enhance messages with context from available providers.""" + if not _HAS_RAG: + return messages + + try: + rag_manager = RAGManager() + except Exception as e: + logger.warning(f"Failed to initialize RAG manager: {e}") + return messages + + enhanced_messages = [] + + for msg in messages: + if msg.role == "user": + # Get context from RAG + try: + contexts = rag_manager.get_context(msg.content) + + # Add context as a system message before the user message + if contexts: + context_msg = "Relevant context:\n\n" + for ctx in contexts: + context_msg += f"### {ctx.source}\n{ctx.content}\n\n" + + enhanced_messages.append( + Message(role="system", content=context_msg, hide=True) + ) + except Exception as e: + logger.warning(f"Error getting context: {e}") + + enhanced_messages.append(msg) + + return enhanced_messages diff --git a/gptme/tools/rag.py b/gptme/tools/rag.py index cbf813fb..d256c6b1 100644 --- a/gptme/tools/rag.py +++ b/gptme/tools/rag.py @@ -64,18 +64,11 @@ from ..config import get_project_config from ..util import get_project_dir from .base import ToolSpec, ToolUse +from ._rag_context import RAGManager, _HAS_RAG logger = logging.getLogger(__name__) -try: - import gptme_rag # fmt: skip - - _HAS_RAG = True -except ImportError: - logger.debug("gptme-rag not installed, RAG tool will not be available") - _HAS_RAG = False - -indexer: "gptme_rag.Indexer | None" = None +rag_manager: RAGManager | None = None instructions = """ Use RAG to index and search project documentation. @@ -102,18 +95,19 @@ def rag_index(*paths: str, glob: str | None = None) -> str: """Index documents in specified paths.""" - assert indexer is not None, "RAG indexer not initialized" + assert rag_manager is not None, "RAG manager not initialized" paths = paths or (".",) kwargs = {"glob_pattern": glob} if glob else {} + total_docs = 0 for path in paths: - indexer.index_directory(Path(path), **kwargs) - return f"Indexed {len(paths)} paths" + total_docs += rag_manager.index_directory(Path(path), **kwargs) + return f"Indexed {len(paths)} paths ({total_docs} documents)" def rag_search(query: str) -> str: """Search indexed documents.""" - assert indexer is not None, "RAG indexer not initialized" - docs, _ = indexer.search(query) + assert rag_manager is not None, "RAG manager not initialized" + docs, _ = rag_manager.search(query) return "\n\n".join( f"### {doc.metadata['source']}\n{doc.content[:200]}..." for doc in docs ) @@ -121,8 +115,8 @@ def rag_search(query: str) -> str: def rag_status() -> str: """Show index status.""" - assert indexer is not None, "RAG indexer not initialized" - return f"Index contains {indexer.collection.count()} documents" + assert rag_manager is not None, "RAG manager not initialized" + return f"Index contains {rag_manager.get_document_count()} documents" def init() -> ToolSpec: @@ -137,13 +131,8 @@ def init() -> ToolSpec: index_path = Path(config.rag.get("index_path", index_path)).expanduser() collection = config.rag.get("collection", project_dir.name) - import gptme_rag # fmt: skip - - global indexer - indexer = gptme_rag.Indexer( - persist_directory=index_path, - collection_name=collection, - ) + global rag_manager + rag_manager = RAGManager(index_path=index_path, collection=collection) return tool diff --git a/gptme/tools/youtube.py b/gptme/tools/youtube.py index 20973779..12fb937f 100644 --- a/gptme/tools/youtube.py +++ b/gptme/tools/youtube.py @@ -5,8 +5,7 @@ logger = logging.getLogger(__name__) try: - # noreorder - from youtube_transcript_api import YouTubeTranscriptApi # fmt: skip + from youtube_transcript_api import YouTubeTranscriptApi # type: ignore # fmt: skip except ImportError: YouTubeTranscriptApi = None diff --git a/gptme/util/cli.py b/gptme/util/cli.py index eea61c04..1a669a24 100644 --- a/gptme/util/cli.py +++ b/gptme/util/cli.py @@ -103,14 +103,17 @@ def context(): @context.command("generate") -@click.argument("query") -def context_generate(query: str): - """Retrieve context for a given query.""" - from ..context import RAGContextProvider # fmt: skip - - provider = RAGContextProvider() - ctx = provider.get_context(query) - print(ctx) +@click.argument("path", type=click.Path(exists=True)) +def context_generate(path: str): + """Index a file or directory for context retrieval.""" + from ..tools.rag import init, rag_index # fmt: skip + + # Initialize RAG + init() + + # Index the file/directory + n_docs = rag_index(path) + print(f"Indexed {n_docs} documents") @main.group() diff --git a/poetry.lock b/poetry.lock index c953d38b..e8083173 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1101,20 +1101,22 @@ name = "gptme-rag" version = "0.1.5" description = "RAG implementation for gptme context management" optional = true -python-versions = "<4.0,>=3.10" -files = [ - {file = "gptme_rag-0.1.5-py3-none-any.whl", hash = "sha256:0d93a13d8ffb3d1c4e9938c7c4469e0907c2c51d048816ba8b9a9e2b6ef5c5b1"}, - {file = "gptme_rag-0.1.5.tar.gz", hash = "sha256:a93f5643556e5b0ded10cad81e80affe87c24ed92cc31d7ac260e96285b0e22e"}, -] +python-versions = "^3.10" +files = [] +develop = false [package.dependencies] -chromadb = ">=0.4.22,<0.5.0" +chromadb = "^0.4.22" click = "*" numpy = "<2.0.0" -psutil = ">=6.1.0,<7.0.0" +psutil = "^6.1.0" rich = "*" tiktoken = ">=0.7" -watchdog = ">=3.0.0,<4.0.0" +watchdog = "^3.0.0" + +[package.source] +type = "directory" +url = "../gptme-rag" [[package]] name = "greenlet" @@ -5184,4 +5186,4 @@ server = ["flask", "flask-cors"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "24319f2d1054169eb45f031cc6b34fda546e04a6f29b033024c55348f7dfa281" +content-hash = "dfc95fcbe92d66d9563d628075bc5a4bf52a51e35e1987885823c025b7b722c3" diff --git a/pyproject.toml b/pyproject.toml index fe688887..b1f84fad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,8 @@ bashlex = "^0.18" playwright = {version = "1.47.*", optional=true} # version constrained due to annoying to have to run `playwright install` on every update youtube_transcript_api = {version = "^0.6.1", optional = true} python-xlib = {version = "^0.33", optional = true} # for X11 interaction -gptme-rag = {version = "^0.1.5", optional = true} # for RAG functionality +#gptme-rag = {version = "^0.1.5", optional = true} # for RAG functionality +gptme-rag = {path = "../gptme-rag", optional = true} # for RAG functionality # providers openai = "^1.0" @@ -118,8 +119,12 @@ ignore = ["E402", "E501", "B905"] #unfixable = ["B"] [tool.mypy] -ignore_missing_imports = true check_untyped_defs = true +disable_error_code = "import-untyped" + +[[tool.mypy.overrides]] +module = "langchain.*" +ignore_missing_imports = true [tool.pytest.ini_options] markers = [ diff --git a/scripts/summarize_project.py b/scripts/summarize_project.py index f451e74c..a7c55161 100644 --- a/scripts/summarize_project.py +++ b/scripts/summarize_project.py @@ -1,4 +1,4 @@ -import git +import git # type: ignore from fnmatch import fnmatch from pathlib import Path diff --git a/scripts/train/collect.py b/scripts/train/collect.py index 3677369d..d95c3ba0 100755 --- a/scripts/train/collect.py +++ b/scripts/train/collect.py @@ -11,9 +11,9 @@ from pathlib import Path import click -import torch +import torch # type: ignore from gptme.util import is_generated_name -from transformers import pipeline +from transformers import pipeline # type: ignore logger = logging.getLogger(__name__) diff --git a/tests/conftest.py b/tests/conftest.py index cebcc5fc..499bb82e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,7 +13,7 @@ def download_model(): return # downloads the model if it doesn't exist - from chromadb.utils import embedding_functions # fmt: skip + from chromadb.utils import embedding_functions # type: ignore # fmt: skip ef = embedding_functions.DefaultEmbeddingFunction() if ef: diff --git a/tests/test_cache.py b/tests/test_cache.py deleted file mode 100644 index c0877145..00000000 --- a/tests/test_cache.py +++ /dev/null @@ -1,110 +0,0 @@ -"""Tests for the caching system.""" - -import time -from unittest.mock import Mock, patch - - -from gptme.cache import Cache, RAGCache, get_cache -from gptme.context import RAGContextProvider - - -def test_cache_basic_operations(): - """Test basic cache operations.""" - cache = Cache(max_size=2) - - # Set and get - cache.set("key1", "value1") - assert cache.get("key1") == "value1" - - # Missing key - assert cache.get("missing") is None - - # Clear - cache.clear() - assert cache.get("key1") is None - - -def test_cache_ttl(): - """Test cache TTL functionality.""" - cache = Cache(default_ttl=0.1) # 100ms TTL - - cache.set("key1", "value1") - assert cache.get("key1") == "value1" - - # Wait for TTL to expire - time.sleep(0.2) - assert cache.get("key1") is None - - -def test_cache_size_limit(): - """Test cache size limiting.""" - cache = Cache(max_size=2) - - cache.set("key1", "value1") - cache.set("key2", "value2") - cache.set("key3", "value3") # Should evict oldest entry - - assert cache.get("key1") is None # Evicted - assert cache.get("key2") == "value2" - assert cache.get("key3") == "value3" - - -def test_rag_cache_search_results(): - """Test RAG cache with search results.""" - cache = RAGCache() - - # Mock search results - docs = [Mock(content="test content")] - results = {"distances": [[0.1]]} - - # Cache results - cache.set_search_results("test query", 5, (docs, results)) - - # Get cached results - cached = cache.get_search_results("test query", 5) - assert cached is not None - cached_docs, cached_results = cached - - assert len(cached_docs) == 1 - assert cached_docs[0].content == "test content" - assert cached_results["distances"] == [[0.1]] - - -def test_rag_cache_embeddings(): - """Test RAG cache with embeddings.""" - cache = RAGCache() - - embedding = [0.1, 0.2, 0.3] - cache.set_embedding("test text", embedding) - - cached = cache.get_embedding("test text") - assert cached == embedding - - -def test_get_cache_singleton(): - """Test that get_cache returns a singleton instance.""" - cache1 = get_cache() - cache2 = get_cache() - assert cache1 is cache2 - - -def test_rag_context_provider_with_cache(): - """Test RAG context provider with caching.""" - with patch("gptme.context.RAGContextProvider._has_rag", True): - provider = RAGContextProvider() - - # Mock the indexer's search method - mock_docs = [Mock(content="test content", metadata={"source": "test.md"})] - mock_results = {"distances": [[0.1]]} - provider.indexer.search = Mock(return_value=(mock_docs, mock_results)) - - # First call should use indexer - contexts = provider.get_context("test query") - assert len(contexts) == 1 - assert provider.indexer.search.call_count == 1 - - # Second call should use cache - contexts = provider.get_context("test query") - assert len(contexts) == 1 - # Search should not be called again - assert provider.indexer.search.call_count == 1 diff --git a/tests/test_context.py b/tests/test_context.py deleted file mode 100644 index 698245af..00000000 --- a/tests/test_context.py +++ /dev/null @@ -1,101 +0,0 @@ -"""Tests for context enhancement functionality.""" - -import pytest -from unittest.mock import Mock, patch - -from gptme.context import Context, RAGContextProvider, enhance_messages -from gptme.message import Message - - -@pytest.fixture -def mock_rag_provider(): - """Create a mock RAG provider that returns test contexts.""" - provider = Mock() - provider.get_context.return_value = [ - Context( - content="This is a test document about Python functions.", - source="doc1.md", - relevance=0.8, - ), - Context( - content="Documentation about testing practices.", - source="doc2.md", - relevance=0.6, - ), - ] - return provider - - -def test_enhance_messages_with_context(mock_rag_provider): - """Test that messages are enhanced with context.""" - with patch("gptme.context.RAGContextProvider", return_value=mock_rag_provider): - messages = [ - Message("system", "Initial system message"), - Message("user", "Tell me about Python functions"), - Message("assistant", "Here's what I know about functions..."), - ] - - enhanced = enhance_messages(messages) - - # Should have one extra message for the context - assert len(enhanced) == 4 - - # Check that context was added before the user message - assert enhanced[0].role == "system" # Original system message - assert enhanced[1].role == "system" # Added context - assert "Relevant context:" in enhanced[1].content - assert "doc1.md" in enhanced[1].content - assert "doc2.md" in enhanced[1].content - assert enhanced[1].hide is True # Context should be hidden - - # Original messages should remain unchanged - assert enhanced[2].role == "user" - assert enhanced[3].role == "assistant" - - -def test_enhance_messages_no_rag(): - """Test that enhancement works even without RAG available.""" - with patch("gptme.context.RAGContextProvider._has_rag", False): - messages = [ - Message("user", "Tell me about Python"), - Message("assistant", "Python is a programming language"), - ] - - enhanced = enhance_messages(messages) - - # Should be unchanged when RAG is not available - assert len(enhanced) == len(messages) - assert enhanced == messages - - -def test_enhance_messages_error_handling(mock_rag_provider): - """Test that errors in context providers are handled gracefully.""" - mock_rag_provider.get_context.side_effect = Exception("Test error") - - with patch("gptme.context.RAGContextProvider", return_value=mock_rag_provider): - messages = [ - Message("user", "Tell me about Python"), - Message("assistant", "Python is great"), - ] - - # Should not raise an exception - enhanced = enhance_messages(messages) - - # Messages should be unchanged when provider fails - assert len(enhanced) == len(messages) - assert enhanced == messages - - -def test_rag_provider_initialization(): - """Test RAG provider initialization with and without gptme-rag.""" - # Test when gptme-rag is not available - with patch("gptme.context.RAGContextProvider._has_rag", False): - provider = RAGContextProvider() - assert provider.get_context("test query") == [] - - # Test when gptme-rag is available - with patch("gptme.context.RAGContextProvider._has_rag", True): - with patch("gptme.context.gptme_rag") as mock_rag: - provider = RAGContextProvider() - assert provider._has_rag is True - mock_rag.Indexer.assert_called_once() diff --git a/tests/test_tools_rag.py b/tests/test_tools_rag.py index 1d509161..0a1093f6 100644 --- a/tests/test_tools_rag.py +++ b/tests/test_tools_rag.py @@ -1,13 +1,25 @@ -"""Tests for the RAG tool.""" +"""Tests for the RAG tool and context enhancement functionality.""" from dataclasses import replace -from unittest.mock import patch +from pathlib import Path +from unittest.mock import Mock, patch import pytest from gptme.tools.base import ToolSpec from gptme.tools.rag import _HAS_RAG from gptme.tools.rag import init as init_rag from gptme.tools.rag import rag_index, rag_search, rag_status +from gptme.tools._rag_context import ( + Context, + RAGManager, + enhance_messages, + _get_search_results, + _clear_cache, +) +from gptme.message import Message + + +# Fixtures @pytest.fixture @@ -22,6 +34,64 @@ def temp_docs(tmp_path): return tmp_path +@pytest.fixture +def mock_rag_manager(): + """Create a mock RAG manager that returns test contexts.""" + with patch("gptme.tools._rag_context.gptme_rag"): + manager = RAGManager( + index_path=Path("~/.cache/gptme/rag"), + collection="test", + ) + # Create mock documents + mock_docs = [ + Mock( + content="This is a test document about Python functions.", + metadata={"source": "doc1.md"}, + ), + Mock( + content="Documentation about testing practices.", + metadata={"source": "doc2.md"}, + ), + ] + mock_results = {"distances": [[0.2, 0.4]]} # 1 - distance = relevance + + # Mock the indexer's search method + manager.indexer.search = Mock(return_value=(mock_docs, mock_results)) + + # Mock get_context to return actual Context objects + test_contexts = [ + Context( + content="This is a test document about Python functions.", + source="doc1.md", + relevance=0.8, + ), + Context( + content="Documentation about testing practices.", + source="doc2.md", + relevance=0.6, + ), + ] + manager.get_context = Mock(return_value=test_contexts) # type: ignore + + return manager + + +@pytest.fixture +def mock_rag_manager_no_context(mock_rag_manager): + """Create a RAG manager that returns no context.""" + mock_rag_manager.get_context = Mock(return_value=[]) + return mock_rag_manager + + +@pytest.fixture(autouse=True) +def clear_cache(): + """Clear the search cache before each test.""" + _clear_cache() + + +# RAG Tool Tests + + @pytest.mark.timeout(func_only=True) @pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") def test_rag_tool_init(): @@ -114,3 +184,138 @@ def test_rag_status_function(temp_docs, tmp_path): result = rag_status() assert "Index contains" in result assert "2" in result # Should have indexed 2 documents + + +# Context Enhancement Tests + + +def test_search_caching(mock_rag_manager): + """Test that search results are properly cached.""" + query = "test query" + n_results = 5 + + # First search should use the manager + docs, results = mock_rag_manager.search(query, n_results) + assert mock_rag_manager.indexer.search.call_count == 1 + + # Cache should be populated + cached = _get_search_results(query, n_results) + assert cached is not None + assert cached == (docs, results) + + # Second search should use cache + docs2, results2 = mock_rag_manager.search(query, n_results) + assert mock_rag_manager.indexer.search.call_count == 1 # No additional calls + assert (docs2, results2) == (docs, results) + + +def test_enhance_messages_with_context(mock_rag_manager): + """Test that messages are enhanced with context.""" + with patch("gptme.tools._rag_context.RAGManager", return_value=mock_rag_manager): + messages = [ + Message("system", "Initial system message"), + Message("user", "Tell me about Python functions"), + Message("assistant", "Here's what I know about functions..."), + ] + + enhanced = enhance_messages(messages) + + # Should have one extra message for the context + assert len(enhanced) == 4 + + # Check that context was added before the user message + assert enhanced[0].role == "system" # Original system message + assert enhanced[1].role == "system" # Added context + assert "Relevant context:" in enhanced[1].content + assert "doc1.md" in enhanced[1].content + assert "doc2.md" in enhanced[1].content + assert enhanced[1].hide is True # Context should be hidden + + # Original messages should remain unchanged + assert enhanced[2].role == "user" + assert enhanced[3].role == "assistant" + + +def test_enhance_messages_no_rag(): + """Test that enhancement works even without RAG available.""" + with patch("gptme.tools._rag_context._HAS_RAG", False): + messages = [ + Message("user", "Tell me about Python"), + Message("assistant", "Python is a programming language"), + ] + + enhanced = enhance_messages(messages) + + # Should be unchanged when RAG is not available + assert len(enhanced) == len(messages) + assert enhanced == messages + + +def test_enhance_messages_error_handling(mock_rag_manager): + """Test that errors in context enhancement are handled gracefully.""" + mock_rag_manager.get_context.side_effect = Exception("Test error") + + with patch("gptme.tools._rag_context.RAGManager", return_value=mock_rag_manager): + messages = [ + Message("user", "Tell me about Python"), + Message("assistant", "Python is great"), + ] + + # Should not raise an exception + enhanced = enhance_messages(messages) + + # Messages should be unchanged when enhancement fails + assert len(enhanced) == len(messages) + assert enhanced == messages + + +def test_rag_manager_initialization(): + """Test RAG manager initialization with and without gptme-rag.""" + # Test when gptme-rag is not available + with patch("gptme.tools._rag_context._HAS_RAG", False): + with pytest.raises(ImportError): + RAGManager() + + # Test when gptme-rag is available + with patch("gptme.tools._rag_context._HAS_RAG", True): + with patch("gptme.tools._rag_context.gptme_rag") as mock_rag: + manager = RAGManager() + assert isinstance(manager, RAGManager) + mock_rag.Indexer.assert_called_once() + + +def test_get_context_with_relevance_filter(mock_rag_manager): + """Test that get_context properly filters by relevance.""" + with patch("gptme.tools._rag_context.RAGManager", return_value=mock_rag_manager): + # Create test contexts with different relevance scores + contexts = [ + Context(content="High relevance", source="high.md", relevance=0.8), + Context(content="Low relevance", source="low.md", relevance=0.4), + ] + + # Mock get_context directly instead of search + mock_rag_manager.get_context = Mock( + return_value=[ctx for ctx in contexts if ctx.relevance >= 0.7] + ) + + messages = [Message("user", "test query")] + enhanced = enhance_messages(messages) + + # Should only include the high relevance context + assert len(enhanced) == 2 # Original message + context message + assert "high.md" in enhanced[0].content + assert "low.md" not in enhanced[0].content + + +def test_auto_context_disabled(mock_rag_manager): + """Test that context enhancement respects auto_context setting.""" + mock_rag_manager.auto_context = False + mock_rag_manager.get_context = Mock(return_value=[]) # Should not be called + + messages = [Message("user", "Tell me about Python")] + enhanced = enhance_messages(messages) + + # No context should be added when auto_context is False + assert len(enhanced) == 1 + assert enhanced[0].role == "user" + assert not mock_rag_manager.get_context.called diff --git a/tests/test_util_cli.py b/tests/test_util_cli.py index 46156240..16b7f4b7 100644 --- a/tests/test_util_cli.py +++ b/tests/test_util_cli.py @@ -95,16 +95,26 @@ def test_chats_list(tmp_path, mocker): assert "Messages: 2" in result.output # Second chat has 2 messages -def test_context_generate(tmp_path): +def test_context_generate(tmp_path, mocker): """Test the context generate command.""" # Create a test file test_file = tmp_path / "test.txt" test_file.write_text("Hello, world!") + # Mock RAG dependencies + mocker.patch("gptme.tools._rag_context._HAS_RAG", True) + mocker.patch("gptme.tools.rag.init") # Mock the init function + + # Mock the rag_index function + mock_index = mocker.patch("gptme.tools.rag.rag_index") + mock_index.return_value = 1 + runner = CliRunner() result = runner.invoke(main, ["context", "generate", str(test_file)]) + assert result.exit_code == 0 - assert "Hello, world!" in result.output + mock_index.assert_called_once_with(str(test_file)) + assert "Indexed 1" in result.output def test_tools_list(): From d403e54fcd424b3e15d85b43e6d7afa592c4e419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Tue, 19 Nov 2024 11:46:47 +0100 Subject: [PATCH 21/24] fix: fixed rag tool tests --- gptme/tools/_rag_context.py | 19 +++---- poetry.lock | 24 ++++----- pyproject.toml | 6 ++- tests/test_tools_rag.py | 103 +++++++++++++++++++++++++----------- 4 files changed, 98 insertions(+), 54 deletions(-) diff --git a/gptme/tools/_rag_context.py b/gptme/tools/_rag_context.py index 8e52591c..3ac94d5c 100644 --- a/gptme/tools/_rag_context.py +++ b/gptme/tools/_rag_context.py @@ -64,14 +64,19 @@ def get_context(self, query: str, max_tokens: int = 1000) -> list[Context]: class RAGManager: """Manages RAG functionality for both context enhancement and tool use.""" - def __init__( - self, index_path: Path = Path("~/.cache/gptme/rag"), collection: str = "default" - ): + def __init__(self, index_path: Path | None = None, collection: str | None = None): if not _HAS_RAG: raise ImportError("gptme-rag not installed") - self.index_path = index_path.expanduser() - self.collection = collection + # Load config + config = get_project_config(Path.cwd()) + self.config = config.rag if config and config.rag else {} + + # Use config values if not overridden by parameters + self.index_path = Path( + index_path or self.config.get("index_path", "~/.cache/gptme/rag") + ).expanduser() + self.collection = collection or self.config.get("collection", "default") # Initialize the indexer self.indexer = gptme_rag.Indexer( @@ -79,10 +84,6 @@ def __init__( collection_name=self.collection, ) - # Load config - config = get_project_config(Path.cwd()) - self.config = config.rag if config and config.rag else {} - # Context enhancement configuration self.context_assembler = gptme_rag.ContextAssembler( max_tokens=self.config.get("max_tokens", 2000) diff --git a/poetry.lock b/poetry.lock index e8083173..4109441a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "accessible-pygments" @@ -1098,25 +1098,23 @@ files = [ [[package]] name = "gptme-rag" -version = "0.1.5" +version = "0.2.0" description = "RAG implementation for gptme context management" optional = true -python-versions = "^3.10" -files = [] -develop = false +python-versions = "<4.0,>=3.10" +files = [ + {file = "gptme_rag-0.2.0-py3-none-any.whl", hash = "sha256:6880230b3d8f894fd4f8255e8b58e4ea181d835fac32f334c7920f72a85a2024"}, + {file = "gptme_rag-0.2.0.tar.gz", hash = "sha256:6da5bc11c6c2529167602a21d9c6f20ee98955069c5aa9007635a1ac4648c546"}, +] [package.dependencies] -chromadb = "^0.4.22" +chromadb = ">=0.4.22,<0.5.0" click = "*" numpy = "<2.0.0" -psutil = "^6.1.0" +psutil = ">=6.1.0,<7.0.0" rich = "*" tiktoken = ">=0.7" -watchdog = "^3.0.0" - -[package.source] -type = "directory" -url = "../gptme-rag" +watchdog = ">=3.0.0,<4.0.0" [[package]] name = "greenlet" @@ -5186,4 +5184,4 @@ server = ["flask", "flask-cors"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "dfc95fcbe92d66d9563d628075bc5a4bf52a51e35e1987885823c025b7b722c3" +content-hash = "089a59c15be55bb13874467345cbf1a394b09cfea84c9bcb76832e34350ca584" diff --git a/pyproject.toml b/pyproject.toml index b1f84fad..6bfbbc74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,8 +37,10 @@ bashlex = "^0.18" playwright = {version = "1.47.*", optional=true} # version constrained due to annoying to have to run `playwright install` on every update youtube_transcript_api = {version = "^0.6.1", optional = true} python-xlib = {version = "^0.33", optional = true} # for X11 interaction -#gptme-rag = {version = "^0.1.5", optional = true} # for RAG functionality -gptme-rag = {path = "../gptme-rag", optional = true} # for RAG functionality + +# RAG +gptme-rag = {version = "^0.2.0", optional = true} +#gptme-rag = {path = "../gptme-rag", optional = true, develop = true} # providers openai = "^1.0" diff --git a/tests/test_tools_rag.py b/tests/test_tools_rag.py index 0a1093f6..ac9c9974 100644 --- a/tests/test_tools_rag.py +++ b/tests/test_tools_rag.py @@ -1,28 +1,36 @@ """Tests for the RAG tool and context enhancement functionality.""" +import shutil from dataclasses import replace -from pathlib import Path from unittest.mock import Mock, patch import pytest -from gptme.tools.base import ToolSpec -from gptme.tools.rag import _HAS_RAG -from gptme.tools.rag import init as init_rag -from gptme.tools.rag import rag_index, rag_search, rag_status +from gptme.message import Message from gptme.tools._rag_context import ( Context, RAGManager, - enhance_messages, - _get_search_results, _clear_cache, + _get_search_results, + enhance_messages, ) -from gptme.message import Message +from gptme.tools.base import ToolSpec +from gptme.tools.rag import _HAS_RAG +from gptme.tools.rag import init as init_rag +from gptme.tools.rag import rag_index, rag_search, rag_status +pytest.importorskip("gptme_rag") # Fixtures -@pytest.fixture +@pytest.fixture(scope="function") +def index_path(tmp_path): + """Create a temporary index path.""" + yield tmp_path + shutil.rmtree(tmp_path, ignore_errors=True) + + +@pytest.fixture(scope="function") def temp_docs(tmp_path): """Create temporary test documents.""" doc1 = tmp_path / "doc1.md" @@ -31,15 +39,16 @@ def temp_docs(tmp_path): doc2 = tmp_path / "doc2.md" doc2.write_text("# Another Document\nThis document discusses testing practices.") - return tmp_path + yield tmp_path + shutil.rmtree(tmp_path, ignore_errors=True) @pytest.fixture -def mock_rag_manager(): +def mock_rag_manager(index_path): """Create a mock RAG manager that returns test contexts.""" with patch("gptme.tools._rag_context.gptme_rag"): manager = RAGManager( - index_path=Path("~/.cache/gptme/rag"), + index_path=index_path, collection="test", ) # Create mock documents @@ -115,14 +124,26 @@ def test_rag_tool_init_without_gptme_rag(): assert tool.available is False +@pytest.mark.slow @pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") -def test_rag_index_function(temp_docs, tmp_path): +def test_rag_index_function(temp_docs, index_path, tmp_path): """Test the index function.""" - with patch("gptme.tools.rag.get_project_config") as mock_config: - mock_config.return_value.rag = { - "index_path": str(tmp_path), - "collection": "test", - } + with ( + patch("gptme.tools.rag.get_project_config") as mock_config, + patch("gptme.tools.rag.get_project_dir") as mock_project_dir, + ): + # Mock project dir to return the temp path + mock_project_dir.return_value = tmp_path + + # Mock config to return an object with a proper .get method + class MockConfig: + def __init__(self): + self.rag = {"index_path": str(index_path), "collection": tmp_path.name} + + def get(self, key, default=None): + return self.rag.get(key, default) + + mock_config.return_value = MockConfig() # Initialize RAG init_rag() @@ -139,13 +160,24 @@ def test_rag_index_function(temp_docs, tmp_path): @pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") -def test_rag_search_function(temp_docs, tmp_path): +def test_rag_search_function(temp_docs, index_path, tmp_path): """Test the search function.""" - with patch("gptme.tools.rag.get_project_config") as mock_config: - mock_config.return_value.rag = { - "index_path": str(tmp_path), - "collection": "test", - } + with ( + patch("gptme.tools.rag.get_project_config") as mock_config, + patch("gptme.tools.rag.get_project_dir") as mock_project_dir, + ): + # Mock project dir to return the temp path + mock_project_dir.return_value = tmp_path + + # Mock config to return an object with a proper .get method + class MockConfig: + def __init__(self): + self.rag = {"index_path": str(index_path), "collection": tmp_path.name} + + def get(self, key, default=None): + return self.rag.get(key, default) + + mock_config.return_value = MockConfig() # Initialize RAG and index documents init_rag() @@ -163,13 +195,24 @@ def test_rag_search_function(temp_docs, tmp_path): @pytest.mark.skipif(not _HAS_RAG, reason="gptme-rag not installed") -def test_rag_status_function(temp_docs, tmp_path): +def test_rag_status_function(temp_docs, index_path, tmp_path): """Test the status function.""" - with patch("gptme.tools.rag.get_project_config") as mock_config: - mock_config.return_value.rag = { - "index_path": str(tmp_path), - "collection": "test", - } + with ( + patch("gptme.tools.rag.get_project_config") as mock_config, + patch("gptme.tools.rag.get_project_dir") as mock_project_dir, + ): + # Mock project dir to return the temp path + mock_project_dir.return_value = tmp_path + + # Mock config to return an object with a proper .get method + class MockConfig: + def __init__(self): + self.rag = {"index_path": str(index_path), "collection": tmp_path.name} + + def get(self, key, default=None): + return self.rag.get(key, default) + + mock_config.return_value = MockConfig() # Initialize RAG init_rag() From 75787da8da5f32f10a8035fe7315124d6a071830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Tue, 19 Nov 2024 11:55:05 +0100 Subject: [PATCH 22/24] ci: fixed typechecking --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 46d25ab9..42bf1757 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -182,7 +182,7 @@ jobs: - name: Install dependencies run: | make build - poetry install + poetry install -E server -E browser - name: Typecheck run: | make typecheck From ea0bc6d22cbb1d299ec537b134af7699f6e95609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Tue, 19 Nov 2024 12:07:07 +0100 Subject: [PATCH 23/24] fix: hopefully finally fixed rag tests now --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- tests/test_tools_rag.py | 7 ++----- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4109441a..88383237 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1098,13 +1098,13 @@ files = [ [[package]] name = "gptme-rag" -version = "0.2.0" +version = "0.2.1" description = "RAG implementation for gptme context management" optional = true python-versions = "<4.0,>=3.10" files = [ - {file = "gptme_rag-0.2.0-py3-none-any.whl", hash = "sha256:6880230b3d8f894fd4f8255e8b58e4ea181d835fac32f334c7920f72a85a2024"}, - {file = "gptme_rag-0.2.0.tar.gz", hash = "sha256:6da5bc11c6c2529167602a21d9c6f20ee98955069c5aa9007635a1ac4648c546"}, + {file = "gptme_rag-0.2.1-py3-none-any.whl", hash = "sha256:ada534d91200bdaf7341e24de7ca82bf5f76071e9c155003b3dd0abe16290598"}, + {file = "gptme_rag-0.2.1.tar.gz", hash = "sha256:bae2b60e14e3a7a4c71dd2a9dac13abc7306a00902f8cd28d11395395fc82adc"}, ] [package.dependencies] @@ -5184,4 +5184,4 @@ server = ["flask", "flask-cors"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "089a59c15be55bb13874467345cbf1a394b09cfea84c9bcb76832e34350ca584" +content-hash = "fdb4c7c81ec59e0a80bb1e0f36a9d49934b460cc335dcbe613f37d2ed9e7e5e2" diff --git a/pyproject.toml b/pyproject.toml index 6bfbbc74..2a33f00c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ youtube_transcript_api = {version = "^0.6.1", optional = true} python-xlib = {version = "^0.33", optional = true} # for X11 interaction # RAG -gptme-rag = {version = "^0.2.0", optional = true} +gptme-rag = {version = "^0.2.1", optional = true} #gptme-rag = {path = "../gptme-rag", optional = true, develop = true} # providers diff --git a/tests/test_tools_rag.py b/tests/test_tools_rag.py index ac9c9974..7196c49b 100644 --- a/tests/test_tools_rag.py +++ b/tests/test_tools_rag.py @@ -1,6 +1,5 @@ """Tests for the RAG tool and context enhancement functionality.""" -import shutil from dataclasses import replace from unittest.mock import Mock, patch @@ -26,8 +25,7 @@ @pytest.fixture(scope="function") def index_path(tmp_path): """Create a temporary index path.""" - yield tmp_path - shutil.rmtree(tmp_path, ignore_errors=True) + return tmp_path @pytest.fixture(scope="function") @@ -39,8 +37,7 @@ def temp_docs(tmp_path): doc2 = tmp_path / "doc2.md" doc2.write_text("# Another Document\nThis document discusses testing practices.") - yield tmp_path - shutil.rmtree(tmp_path, ignore_errors=True) + return tmp_path @pytest.fixture From 0e1596d56f85bf79b00ed028de7dcdf56cfe5782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Tue, 19 Nov 2024 12:25:54 +0100 Subject: [PATCH 24/24] test: made test_chain more reliable for gpt-4o-mini --- tests/test_cli.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index d2677ef1..c790c3b8 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -266,22 +266,29 @@ def test_stdin(args: list[str], runner: CliRunner): def test_chain(args: list[str], runner: CliRunner): """tests that the "-" argument works to chain commands, executing after the agent has exhausted the previous command""" # first command needs to be something requiring two tools, so we can check both are ran before the next chained command - args.append("write a test.txt file, then patch it") + args.append("we are testing. write a test.txt file with the save tool") + args.append("-") + args.append("patch it to contain emojis") args.append("-") args.append("read the contents") + args.extend(["--tools", "save,patch,shell,read"]) result = runner.invoke(gptme.cli.main, args) print(result.output) # check that outputs came in expected order user1_loc = result.output.index("User:") user2_loc = result.output.index("User:", user1_loc + 1) + user3_loc = result.output.index("User:", user2_loc + 1) save_loc = result.output.index("```save") patch_loc = result.output.index("```patch") print_loc = result.output.rindex("cat test.txt") - print(f"{user1_loc=} {save_loc=} {patch_loc=} {user2_loc=} {print_loc=}") - assert user1_loc < user2_loc - assert save_loc < patch_loc - assert patch_loc < user2_loc - assert user2_loc < print_loc + print( + f"{user1_loc=} {save_loc=} {user2_loc=} {patch_loc=} {user3_loc=} {print_loc=}" + ) + assert user1_loc < save_loc + assert save_loc < user2_loc + assert user2_loc < patch_loc + assert patch_loc < user3_loc + assert user3_loc < print_loc assert result.exit_code == 0