diff --git a/AI-Travel-Agent/requirements.txt b/AI-Travel-Agent/requirements.txt index 885361c..a90e34b 100644 --- a/AI-Travel-Agent/requirements.txt +++ b/AI-Travel-Agent/requirements.txt @@ -2,10 +2,10 @@ amadeus==12.0.0 ipykernel==7.0.1 jupyter==1.1.1 langchain==1.0.2 -langchain-community==0.3.27 +langchain-community==0.4.1 streamlit==1.51.0 huggingface-hub==1.1.4 pydantic==2.12.3 wikipedia==1.4.0 google-search-results==2.4.2 -hf-xet==1.2.0 +hf-xet==1.2.0 \ No newline at end of file diff --git a/LLM/rag/requirements.txt b/LLM/rag/requirements.txt index 1fceec8..e3ba24b 100644 --- a/LLM/rag/requirements.txt +++ b/LLM/rag/requirements.txt @@ -1,12 +1,12 @@ -langchain==0.3.24 +langchain==0.3.27 langchain-community==0.3.27 -langchain-core==0.3.56 +langchain-core==0.3.72 langchain-huggingface==0.1.2 -huggingface-hub==0.29.1 +huggingface-hub>=0.30.0 sentence-transformers==3.4.1 chromadb==0.6.3 transformers==4.53.1 pypdf==6.1.3 torch==2.7.1 langchain-chroma==0.2.2 -beautifulsoup4==4.13.3 +beautifulsoup4==4.13.3 \ No newline at end of file diff --git a/LLM/src/README.md b/LLM/src/README.md new file mode 100644 index 0000000..c9c1422 --- /dev/null +++ b/LLM/src/README.md @@ -0,0 +1,150 @@ +# RAG Chat with Ollama + +A Streamlit-based Retrieval-Augmented Generation (RAG) chat application powered by Ollama on Intel® Core™ Ultra Processors. + +## Overview + +This application demonstrates a RAG (Retrieval-Augmented Generation) system that allows you to chat with documents using Ollama's language models. Upload your documents, and the system will create embeddings and enable semantic search to provide context-aware responses. + +## Prerequisites + +- Windows 11 or Ubuntu 20.04+ +- Intel® Core™ Ultra Processors or Intel Arc™ Graphics +- 16GB+ RAM recommended + +## Setup + +### 1. Install Ollama + +Download and install Ollama: +```powershell +winget install Ollama.Ollama +``` + +Or download from [https://ollama.com/download](https://ollama.com/download) + +### 2. Building Ollama with GPU Support (Vulkan) + +For advanced users who want to build Ollama from source with Vulkan GPU acceleration on Windows: + +**a. Install Vulkan SDK** +- Download from: [https://vulkan.lunarg.com/sdk/home](https://vulkan.lunarg.com/sdk/home) + +**b. Install TDM-GCC** +- Download from: [https://github.com/jmeubank/tdm-gcc/releases/tag/v10.3.0-tdm64-2](https://github.com/jmeubank/tdm-gcc/releases/tag/v10.3.0-tdm64-2) + +**c. Install Go SDK** +- Download Go v1.24.9: [https://go.dev/dl/go1.24.9.windows-amd64.msi](https://go.dev/dl/go1.24.9.windows-amd64.msi) + +**d. Build Ollama with Vulkan** +```powershell +# Set environment variables +set CGO_ENABLED=1 +set CGO_CFLAGS=-IC:\VulkanSDK\1.4.321.1\Include + +# Build with CMake +cmake -B build +cmake --build build --config Release -j14 + +# Build Go binary +go build + +# Run Ollama server (Terminal 1) +go run . serve + +# Test with a model (Terminal 2) +ollama run gemma3:270m +``` + +**Note:** This is for advanced users. The pre-built Ollama installation works fine for most users. + +### 3. Pull Language Models + +Pull the models you want to use: +```bash +ollama pull llama3.2 +ollama pull qwen2.5 +ollama pull mistral +``` + +### 4. Install Python Dependencies + +Using pip: +```bash +pip install streamlit ollama chromadb sentence-transformers pypdf +``` + +Using uv (recommended): +```bash +uv pip install streamlit ollama chromadb sentence-transformers pypdf +``` + +## Running the Application + +### 1. Start Ollama Server + +If not already running: +```bash +ollama serve +``` + +### 2. Run the Streamlit App + +```bash +# Using Python directly +streamlit run st_rag_chat.py + +# Or using uv +uv run streamlit run st_rag_chat.py +``` + +### 3. Access the App + +Open your browser and navigate to: +``` +http://localhost:8501 +``` + +## Usage + +1. **Upload Documents**: Use the sidebar to upload PDF or text files +2. **Select Model**: Choose your preferred Ollama model from the dropdown +3. **Process Documents**: Click "Process Documents" to create embeddings +4. **Chat**: Ask questions about your documents in the chat interface +5. **View Sources**: See which document sections were used to answer your questions + +## Features + +- 📄 **Multi-format Support**: Upload PDF and text documents +- 🤖 **Model Selection**: Choose from available Ollama models +- 🔍 **Semantic Search**: Find relevant context using vector embeddings +- 💬 **Context-Aware Chat**: Get answers based on your documents +- 📚 **Source Attribution**: See which parts of documents were used +- 💾 **Persistent Storage**: ChromaDB vector database for efficient retrieval + +## Troubleshooting + +**Ollama Connection Error:** +- Ensure Ollama is running: `ollama serve` +- Check if models are installed: `ollama list` + +**Memory Issues:** +- Use smaller models like `llama3.2:1b` or `qwen2.5:3b` +- Reduce the number of documents processed at once + +**Slow Performance:** +- Ensure GPU drivers are up to date +- Use GPU-accelerated Ollama build (Vulkan) +- Try smaller, faster models + +## Technical Stack + +- **Ollama**: Local LLM runtime +- **Streamlit**: Web interface +- **ChromaDB**: Vector database +- **Sentence Transformers**: Text embeddings +- **Intel Hardware**: Optimized for Intel Core™ Ultra Processors + +## License + +This project is licensed under the MIT License. diff --git a/LLM/src/st_ollama.py b/LLM/src/st_ollama.py index f704eb1..90335a5 100644 --- a/LLM/src/st_ollama.py +++ b/LLM/src/st_ollama.py @@ -13,7 +13,8 @@ def load_models(): list: A list of model names if successful, otherwise an empty list. """ try: - model_list = [model["name"] for model in ollama.list()["models"]] + response = ollama.list() + model_list = [model.model for model in response.models] return model_list except Exception as e: st.error(f"Error loading models: {e}") diff --git a/LLM/src/st_rag_chat.py b/LLM/src/st_rag_chat.py new file mode 100644 index 0000000..5b2b69f --- /dev/null +++ b/LLM/src/st_rag_chat.py @@ -0,0 +1,431 @@ +from langchain.chains import RetrievalQA +from langchain.text_splitter import RecursiveCharacterTextSplitter +from langchain_core.prompts import PromptTemplate +from langchain_community.embeddings.fastembed import FastEmbedEmbeddings +from langchain_community import document_loaders, embeddings, vectorstores, llms +from langchain_community.document_loaders import DirectoryLoader, TextLoader +from langchain_community.document_loaders.pdf import PyPDFLoader +import streamlit as st +import time +import os +import warnings +import ollama +import hashlib + +warnings.filterwarnings("ignore") + +OLLAMA_BASE_URL = "http://localhost:11434" + +st.header("LLM RAG Chat Interface 🐻‍❄️💬") + +# Initialize session state for chat history +if "messages" not in st.session_state: + st.session_state.messages = [] +if "vector_store" not in st.session_state: + st.session_state.vector_store = None +if "current_document" not in st.session_state: + st.session_state.current_document = None + +response = ollama.list() +models = [model.model for model in response.models] +model = st.selectbox("Choose a model from the list", models) + +# Select source type +source_type = st.selectbox( + "Select document source:", + ("URL", "Local File from Data Folder"), + key="source_type" +) + +if source_type == "URL": + # Input text to load the document from URL + source_path = st.text_input("Enter the URL to load for RAG:", key="url_path") +else: + # Show available files in data folder + current_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Go up to LLM folder + data_dir = os.path.join(current_dir, "data") + if os.path.exists(data_dir): + files = [f for f in os.listdir(data_dir) if f.endswith(('.txt', '.pdf', '.md'))] + if files: + st.write("Available files in data folder:") + source_path = st.selectbox("Select a file:", files, key="file_selection") + st.info(f"Selected file: {source_path}") + else: + st.warning("No supported files (.txt, .pdf, .md) found in data folder") + source_path = None + else: + st.error("Data folder not found. Please create a 'data' folder in the parent directory and add your files.") + source_path = None + +# Select embedding type +embedding_type = st.selectbox( + "Please select an embedding type", + ("ollama", + "huggingface", + "nomic", + "fastembed"), + index=1) + + +def load_document(source_path, source_type="URL"): + """ + Load the document from the specified URL or local file. + + Args: + source_path (str): The URL or filename of the document to load. + source_type (str): Either "URL" or "Local File from Data Folder" + + Returns: + Document: The loaded document. + """ + if source_type == "URL": + print("Loading document from URL...") + st.markdown(''' :green[Loading document from URL...] ''') + loader = document_loaders.WebBaseLoader(source_path) + return loader.load() + else: + # Load from local file in data folder + current_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Go up to LLM folder + data_dir = os.path.join(current_dir, "data") + full_path = os.path.join(data_dir, source_path) + + if not os.path.exists(full_path): + raise FileNotFoundError(f"File not found: {full_path}") + + print(f"Loading document from: {full_path}") + st.markdown(f''' :green[Loading document from: {full_path}] ''') + + if source_path.endswith('.pdf'): + loader = PyPDFLoader(full_path) + else: + loader = TextLoader(full_path, encoding='utf-8') + + return loader.load() + + +def split_document(text, chunk_size=3000, overlap=200): + """ + Split the document into multiple chunks. + + Args: + text (str): The text of the document to split. + chunk_size (int): The size of each chunk. + overlap (int): The overlap between chunks. + + Returns: + list: A list of document chunks. + """ + print("Splitting document into chunks...") + st.markdown(''' :green[Splitting document into chunks...] ''') + text_splitter_instance = RecursiveCharacterTextSplitter( + chunk_size=chunk_size, chunk_overlap=overlap) + return text_splitter_instance.split_documents(text) + + +def initialize_embedding_fn( + embedding_type="huggingface", + model_name="sentence-transformers/all-MiniLM-l6-v2"): + """ + Initialize the embedding function based on the specified type. + + Args: + embedding_type (str): The type of embedding to use. + model_name (str): The name of the model to use for embeddings. + + Returns: + Embeddings: The initialized embedding function. + """ + print(f"Initializing {embedding_type} model with {model_name}...") + st.write(f"Initializing {embedding_type} model with {model_name}...") + if embedding_type == "ollama": + model_name = chat_model + return embeddings.OllamaEmbeddings( + model=model_name, base_url=OLLAMA_BASE_URL) + elif embedding_type == "huggingface": + model_name = "sentence-transformers/paraphrase-MiniLM-L3-v2" + return embeddings.HuggingFaceEmbeddings(model_name=model_name) + elif embedding_type == "nomic": + return embeddings.NomicEmbeddings(model_name=model_name) + elif embedding_type == "fastembed": + return FastEmbedEmbeddings(threads=16) + else: + raise ValueError(f"Unsupported embedding type: {embedding_type}") + + +def get_or_create_embeddings(document_url, source_type, embedding_fn): + """ + Create embeddings for the document chunks and store them in a vector database. + Uses persistent storage with improved caching that considers embedding type. + + Args: + document_url (str): The URL of the document. + source_type (str): The type of source (URL or local file). + embedding_fn (Embeddings): The embedding function to use. + + Returns: + VectorStore: The created or loaded vector store. + """ + # Create a more specific hash for caching that includes embedding type + embedding_type_name = embedding_fn.__class__.__name__ + cache_key = f"{document_url}_{source_type}_{embedding_type_name}" + source_hash = hashlib.md5(cache_key.encode()).hexdigest() + persist_directory = f"./chroma_db/{source_hash}" + + print(f"Cache key: {cache_key}") + print(f"Cache directory: {persist_directory}") + + # Check if embeddings already exist and are for the same document + if os.path.exists(persist_directory): + # Check if there's a metadata file to verify this is the right document + metadata_file = os.path.join(persist_directory, "document_info.txt") + if os.path.exists(metadata_file): + with open(metadata_file, 'r', encoding='utf-8') as f: + cached_info = f.read().strip() + if cached_info == cache_key: + print(f"Loading existing embeddings from: {persist_directory}") + st.markdown(f''' :orange[Loading cached embeddings for: {source_type}: {document_url}] ''') + try: + vector_store = vectorstores.Chroma( + persist_directory=persist_directory, + embedding_function=embedding_fn + ) + return vector_store + except Exception as e: + print(f"Error loading cached embeddings: {e}") + st.warning("Error loading cached embeddings, creating new ones...") + else: + print(f"Cache mismatch. Expected: {cache_key}, Found: {cached_info}") + st.warning("Cache mismatch detected, creating new embeddings...") + else: + print("No metadata file found, creating new embeddings...") + st.warning("Cache metadata missing, creating new embeddings...") + + # Create new embeddings + start_time = time.time() + print(f"Creating new embeddings for: {document_url}") + st.markdown(f''' :green[Creating new embeddings for: {source_type}: {document_url}] ''') + + # Clean up the directory if it exists but has issues + if os.path.exists(persist_directory): + import shutil + shutil.rmtree(persist_directory) + + document = load_document(document_url, source_type) + documents = split_document(document) + + vector_store = vectorstores.Chroma.from_documents( + documents=documents, + embedding=embedding_fn, + persist_directory=persist_directory + ) + + # Save metadata to verify cache validity + os.makedirs(persist_directory, exist_ok=True) + metadata_file = os.path.join(persist_directory, "document_info.txt") + with open(metadata_file, 'w', encoding='utf-8') as f: + f.write(cache_key) + + print(f"Embedding time: {time.time() - start_time:.2f} seconds") + st.write(f"Embedding time: {time.time() - start_time:.2f} seconds") + return vector_store + + +def get_chat_context(): + """ + Build context from previous messages for continuity + """ + if not st.session_state.messages: + return "" + + # Get last few messages for context (limit to avoid token overflow) + recent_messages = st.session_state.messages[-6:] # Last 3 Q&A pairs + context_parts = [] + + for msg in recent_messages: + if msg["role"] == "user": + context_parts.append(f"Previous Question: {msg['content']}") + else: + context_parts.append(f"Previous Answer: {msg['content']}") + + return "\n".join(context_parts) + + +def handle_chat_query(vector_store, chat_model, question): + """ + Handle chat query with context awareness + """ + # Get conversation context + chat_context = get_chat_context() + + # Create enhanced question with chat context if available + if chat_context: + enhanced_question = f""" +Previous conversation context: +{chat_context} + +Current question: {question} +""" + else: + enhanced_question = question + + # Simple prompt template that only uses context and question + prompt_template = """ + Use the following pieces of context to answer the question at the end. + If you do not know the answer, answer 'I don't know', limit your response to the answer and nothing more. + + {context} + + Question: {question} + """ + + prompt = PromptTemplate( + template=prompt_template, + input_variables=["context", "question"] + ) + + chain_type_kwargs = {"prompt": prompt} + retriever = vector_store.as_retriever(search_kwargs={"k": 4}) + + qachain = RetrievalQA.from_chain_type( + llm=chat_model, + retriever=retriever, + chain_type="stuff", + chain_type_kwargs=chain_type_kwargs + ) + + start_time = time.time() + answer = qachain.invoke({"query": enhanced_question}) + print(f"Response time: {time.time() - start_time:.2f} seconds") + + return answer['result'] + + +# Load document section +st.write("### 📄 Document Loading") +load_button = st.button("Load Document", type="primary") + +if load_button: + if not source_path or not source_path.strip(): + st.error("Please select/enter a valid source.") + else: + with st.spinner("Loading document and creating embeddings..."): + try: + embedding_fn = initialize_embedding_fn(embedding_type) + vector_store = get_or_create_embeddings(source_path, source_type, embedding_fn) + + # Proper warmup: initialize the model and retriever chain + st.markdown(''' :green[Initializing RAG system...] ''') + chat_model_instance = llms.Ollama(base_url=OLLAMA_BASE_URL, model=model) + + # Warmup the retriever + retriever = vector_store.as_retriever(search_kwargs={"k": 4}) + warmup_docs = retriever.get_relevant_documents("document content summary") + + # Warmup the QA chain with actual query + prompt_template = """ + Use the following pieces of context to answer the question at the end. + If you do not know the answer, answer 'I don't know', limit your response to the answer and nothing more. + + {context} + + Question: {question} + """ + prompt = PromptTemplate( + template=prompt_template, + input_variables=["context", "question"] + ) + chain_type_kwargs = {"prompt": prompt} + + warmup_chain = RetrievalQA.from_chain_type( + llm=chat_model_instance, + retriever=retriever, + chain_type="stuff", + chain_type_kwargs=chain_type_kwargs + ) + + # Execute warmup query + warmup_chain.invoke({"query": "What is this document about?"}) + + st.session_state.vector_store = vector_store + st.session_state.current_document = f"{source_type}: {source_path}" + + # Clear previous chat and add initial summary request + st.session_state.messages = [] + st.session_state.messages.append({ + "role": "user", + "content": "Summarize this document" + }) + + # Generate the summary automatically + st.markdown(''' :green[Generating document summary...] ''') + summary = handle_chat_query( + st.session_state.vector_store, + chat_model_instance, + "Summarize this document" + ) + st.session_state.messages.append({ + "role": "assistant", + "content": summary + }) + + st.success(f"✅ Document loaded successfully!") + st.info(f"Loaded: {st.session_state.current_document}") + st.rerun() + except Exception as e: + st.error(f"Error loading document: {e}") + +# Display current document status +if st.session_state.current_document: + st.write(f"📄 **Current Document**: {st.session_state.current_document}") +else: + st.warning("⚠️ No document loaded. Please load a document first.") + +# Chat Interface +st.write("### 💬 Chat Interface") + +# Display chat messages +for message in st.session_state.messages: + with st.chat_message(message["role"]): + st.markdown(message["content"]) + +# Chat input +if question := st.chat_input("Ask a question about the document..."): + if not st.session_state.vector_store: + st.error("Please load a document first!") + else: + # Add user message to chat history + st.session_state.messages.append({"role": "user", "content": question}) + + # Display user message + with st.chat_message("user"): + st.markdown(question) + + # Generate response + with st.chat_message("assistant"): + with st.spinner("Thinking..."): + try: + chat_model_instance = llms.Ollama( + base_url=OLLAMA_BASE_URL, model=model) + response = handle_chat_query( + st.session_state.vector_store, + chat_model_instance, + question + ) + st.markdown(response) + + # Add assistant response to chat history + st.session_state.messages.append({"role": "assistant", "content": response}) + + except Exception as e: + error_msg = f"Error generating response: {str(e)}" + st.error(error_msg) + st.session_state.messages.append({"role": "assistant", "content": error_msg}) + +# Clear chat button +if st.button("🗑️ Clear Chat History"): + st.session_state.messages = [] + st.rerun() + +# Display chat statistics +if st.session_state.messages: + st.write(f"💬 **Chat History**: {len(st.session_state.messages)} messages") \ No newline at end of file diff --git a/LLM/src/st_rag_chromadb.py b/LLM/src/st_rag_chromadb.py deleted file mode 100644 index 0b41f56..0000000 --- a/LLM/src/st_rag_chromadb.py +++ /dev/null @@ -1,219 +0,0 @@ -from langchain import chains, text_splitter, PromptTemplate -from langchain_community.embeddings.fastembed import FastEmbedEmbeddings -from langchain_community import document_loaders, embeddings, vectorstores, llms -import streamlit as st -import time -import os -import warnings -import ollama - -warnings.filterwarnings("ignore") - -OLLAMA_BASE_URL = "http://localhost:11434" -VECTOR_DB_DIR = "vector_dbs" - -st.header("LLM Rag 🐻‍❄️") - -models = [model["name"] for model in ollama.list()["models"]] -model = st.selectbox("Choose a model from the list", models) - -# Input text to load the document -url_path = st.text_input("Enter the URL to load for RAG:", key="url_path") - -# Select embedding type -embedding_type = st.selectbox( - "Please select an embedding type", - ("ollama", - "huggingface", - "nomic", - "fastembed"), - index=1) - -# Input for RAG -question = st.text_input( - "Enter the question for RAG:", - value="What is this about", - key="question") - - -def load_document(url): - """ - Load the document from the specified URL. - - Args: - url (str): The URL of the document to load. - - Returns: - Document: The loaded document. - """ - print("Loading document from URL...") - st.markdown(''' :green[Loading document from URL...] ''') - loader = document_loaders.WebBaseLoader(url) - return loader.load() - - -def split_document(text, chunk_size=3000, overlap=200): - """ - Split the document into multiple chunks. - - Args: - text (str): The text of the document to split. - chunk_size (int): The size of each chunk. - overlap (int): The overlap between chunks. - - Returns: - list: A list of document chunks. - """ - print("Splitting document into chunks...") - st.markdown(''' :green[Splitting document into chunks...] ''') - text_splitter_instance = text_splitter.RecursiveCharacterTextSplitter( - chunk_size=chunk_size, chunk_overlap=overlap) - return text_splitter_instance.split_documents(text) - - -def initialize_embedding_fn( - embedding_type="huggingface", - model_name="sentence-transformers/all-MiniLM-l6-v2"): - """ - Initialize the embedding function based on the specified type. - - Args: - embedding_type (str): The type of embedding to use. - model_name (str): The name of the model to use for embeddings. - - Returns: - Embeddings: The initialized embedding function. - """ - print(f"Initializing {embedding_type} model with {model_name}...") - st.write(f"Initializing {embedding_type} model with {model_name}...") - if embedding_type == "ollama": - model_name = chat_model - return embeddings.OllamaEmbeddings( - model=model_name, base_url=OLLAMA_BASE_URL) - elif embedding_type == "huggingface": - model_name = "sentence-transformers/paraphrase-MiniLM-L3-v2" - return embeddings.HuggingFaceEmbeddings(model_name=model_name) - elif embedding_type == "nomic": - return embeddings.NomicEmbeddings(model_name=model_name) - elif embedding_type == "fastembed": - return FastEmbedEmbeddings(threads=16) - else: - raise ValueError(f"Unsupported embedding type: {embedding_type}") - - -def get_or_create_embeddings( - document_url, - embedding_fn, - persist_dir=VECTOR_DB_DIR): - """ - Create embeddings for the document chunks and store them in a vector database. - - Args: - document_url (str): The URL of the document. - embedding_fn (Embeddings): The embedding function to use. - persist_dir (str): The directory to persist the vector database. - - Returns: - VectorStore: The created vector store. - """ - vector_store_path = os.path.join(os.getcwd(), persist_dir) - start_time = time.time() - print("No existing vector store found. Creating new one...") - st.markdown( - ''' :green[No existing vector store found. Creating new one......] ''') - document = load_document(document_url) - documents = split_document(document) - vector_store = vectorstores.Chroma.from_documents( - documents=documents, - embedding=embedding_fn, - persist_directory=persist_dir - ) - vector_store.persist() - print(f"Embedding time: {time.time() - start_time:.2f} seconds") - st.write(f"Embedding time: {time.time() - start_time:.2f} seconds") - return vector_store - - -def handle_user_interaction(vector_store, chat_model): - """ - Handle user interaction by generating a response based on the user's question. - - Args: - vector_store (VectorStore): The vector store containing document embeddings. - chat_model (LLM): The language model to generate responses. - - Returns: - str: The generated response. - """ - prompt_template = """ - Use the following pieces of context to answer the question at the end. - If you do not know the answer, answer 'I don't know', limit your response to the answer and nothing more. - - {context} - - Question: {question} - """ - prompt = PromptTemplate( - template=prompt_template, - input_variables=[ - "context", - "question"]) - chain_type_kwargs = {"prompt": prompt} - st.markdown( - ''' :green[Using retrievers to retrieve the data from the database...] ''') - retriever = vector_store.as_retriever(search_kwargs={"k": 4}) - st.markdown(''' :green[Answering the query...] ''') - qachain = chains.RetrievalQA.from_chain_type( - llm=chat_model, - retriever=retriever, - chain_type="stuff", - chain_type_kwargs=chain_type_kwargs) - qachain.invoke({"query": "what is this about?"}) - print(f"Model warmup complete...") - st.markdown(''' :green[Model warmup complete...] ''') - - start_time = time.time() - answer = qachain.invoke({"query": question}) - print(f"Answer: {answer['result']}") - print(f"Response time: {time.time() - start_time:.2f} seconds") - st.write(f"Response time: {time.time() - start_time:.2f} seconds") - - return answer['result'] - - -def getfinalresponse(document_url, embedding_type, chat_model): - """ - Main function to load the document, initialize the embeddings, create the vector database, and invoke the model. - - Args: - document_url (str): The URL of the document. - embedding_type (str): The type of embedding to use. - chat_model (str): The name of the chat model to use. - - Returns: - str: The final response generated by the model. - """ - try: - document_url = url_path - chat_model = model - - embedding_fn = initialize_embedding_fn(embedding_type) - vector_store = get_or_create_embeddings(document_url, embedding_fn) - chat_model_instance = llms.Ollama( - base_url=OLLAMA_BASE_URL, model=chat_model) - return handle_user_interaction(vector_store, chat_model_instance) - except Exception as e: - st.error(f"An error occurred: {e}") - return None - - -submit = st.button("Generate") - -if submit: - if not url_path.strip(): - st.error("Please enter a valid URL.") - elif not question.strip(): - st.error("Please enter a valid question.") - else: - with st.spinner("Loading document....🐎"): - st.write(getfinalresponse(url_path, embedding_type, model)) diff --git a/Video-Description-Generation-Query-Retrieval/Readme.md b/Video-Description-Generation-Query-Retrieval/Readme.md index 13f976e..16fd9d7 100644 --- a/Video-Description-Generation-Query-Retrieval/Readme.md +++ b/Video-Description-Generation-Query-Retrieval/Readme.md @@ -1,10 +1,17 @@ -# Video Description Generation and Query Retrieval +# Video Description Generation and Query Retrieval with Ollama ## Introduction -This sample demonstrates how to generate video descriptions using the [**Qwen 2.5 Vision-Language model**](https://github.com/QwenLM/Qwen2.5-VL) and store their embeddings in [**ChromaDB**](https://www.trychroma.com/) for efficient semantic search on **Intel® Core™ Ultra Processors**. The Qwen 2.5 Vision-Language model is loaded using the [**PyTorch XPU backend**](https://docs.pytorch.org/docs/stable/notes/get_start_xpu.html) to leverage Intel hardware acceleration.\ -For each video, a description is generated and stored as an embedding in ChromaDB. When a user submits a query, cosine similarity search is performed in ChromaDB to retrieve the most relevant video description. The matching video is then displayed inline.\ +This sample demonstrates how to generate video descriptions using [**Ollama**](https://ollama.ai) vision models and store their embeddings in [**ChromaDB**](https://www.trychroma.com/) for efficient semantic search on **Intel® Core™ Ultra Processors** and **Intel® Arc™ Graphics**. + +Ollama provides an optimized runtime for vision-language models (Qwen 2.5 VL, Llama 3.2 Vision, LLaVA) with simplified deployment on Intel GPUs. For each video, a description is generated and stored as an embedding in ChromaDB. When a user submits a query, cosine similarity search is performed in ChromaDB to retrieve the most relevant video description. + This sample uses the videos from the [**stepfun-ai/Step-Video-T2V-Eval**](https://huggingface.co/datasets/stepfun-ai/Step-Video-T2V-Eval) Hugging Face dataset. For more information on the dataset and citation requirements, please refer to the [**Step-Video-T2V Technical Report paper**](https://arxiv.org/abs/2502.10248). +### Available Interfaces: + +- `Video_RAG_Ollama.ipynb` - Jupyter notebook implementation +- `st_video_rag_demo.py` - Streamlit web interface + --- ## Table of Contents @@ -25,12 +32,59 @@ This sample uses the videos from the [**stepfun-ai/Step-Video-T2V-Eval**](https: ## Architecture -- During the initial data load, videos from the [Step-Video-T2V-Eval](https://huggingface.co/datasets/stepfun-ai/Step-Video-T2V-Eval) Hugging Face dataset is fed into the [Qwen 2.5 Vision-Language model](https://github.com/QwenLM/Qwen2.5-VL). -- Here, the [Qwen2.5-VL-3B-Instruct](https://huggingface.co/Qwen/Qwen2.5-VL-3B-Instruct) model variant is used to process these videos and generate descriptions. The Qwen 2.5 Vision-Language model is loaded using the [PyTorch XPU backend](https://docs.pytorch.org/docs/stable/notes/get_start_xpu.html) to leverage Intel hardware acceleration. -- Next, the generated video descriptions are converted into embeddings using [Sentence Transformers](https://sbert.net/), with the [all-MiniLM-L6-v2 model](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2). -- These embeddings, along with the descriptions and video metadata, are stored in a persistent local [ChromaDB](https://www.trychroma.com/) collection. This is a one-time operation; since ChromaDB is local and persistent, it does not need to be repeated unless new videos are added. -- When a user submits a query, the text is similarly encoded into an embedding, which is then used to perform a semantic search (via cosine similarity) over the ChromaDB collection. -- The final result will be the most relevant video description and its associated video file name, and the video is displayed directly in the notebook. +### System Workflow + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ VIDEO PROCESSING PIPELINE │ +└─────────────────────────────────────────────────────────────────────┘ + +Video Files ──► Frame Extraction ──► Ollama Vision Model ──► Text Descriptions + │ + │ (Accelerated by Intel GPUs) + ▼ + Sentence Transformer ──► ChromaDB Storage + │ +┌─────────────────────────────────────────────────────────────┘ +│ SEARCH PIPELINE +└─────────────────────────────────────────────────────────────────────┐ + │ +User Query ──► Query Embedding ──► Cosine Similarity ◄────────────────┘ + │ + ▼ + Ranked Results ──► Display Videos + +┌─────────────────────────────────────────────────────────────────────┐ +│ Hardware: Intel Arc™ Graphics | Core™ Ultra | Iris® Xe Graphics │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +### Workflow Details + +**Video Processing Pipeline:** + +1. **Video Input**: Videos from [Step-Video-T2V-Eval](https://huggingface.co/datasets/stepfun-ai/Step-Video-T2V-Eval) dataset +2. **Frame Extraction**: Representative frames extracted using OpenCV +3. **Vision Model**: Ollama runtime with Qwen 2.5 VL, Llama 3.2 Vision, or LLaVA on Intel GPU +4. **Description Generation**: Detailed text descriptions of video content +5. **Embedding Creation**: Text → vector embeddings via [Sentence Transformers](https://sbert.net/) ([all-MiniLM-L6-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)) +6. **Storage**: Embeddings + metadata stored in [ChromaDB](https://www.trychroma.com/) vector database + +**Search Pipeline:** + +1. **User Query**: Natural language text input (e.g., "person playing basketball") +2. **Query Embedding**: Convert query to vector using same embedding model +3. **Similarity Search**: Cosine similarity search over ChromaDB collection +4. **Ranked Results**: Return top matching videos with similarity scores +5. **Display**: Show videos with descriptions and match quality + +**Intel Hardware Optimization:** + +- **Intel Arc™ Graphics**: GPU acceleration for vision model inference +- **Intel Core™ Ultra Processors**: Optimized CPU performance +- **Intel Iris® Xe Graphics**: Integrated graphics acceleration +- **Local Processing**: No cloud dependency, all computation on-device +- **Ollama Runtime**: Optimized for Intel hardware ![How it works](./assets/Video_description_generation_and_query_retrieval_workflow.png) @@ -39,14 +93,15 @@ This sample uses the videos from the [**stepfun-ai/Step-Video-T2V-Eval**](https: ## Project Structure Video-Description-Generation-Query-Retrieval/ # Project Sample folder - ├── assets/ # Assets folder which contains the images and diagrams - │ ├── Generating_video_descriptions_using_Pytorch_XPU.png # Output screenshot image 1 - │ ├── Video_description_generation_and_query_retrieval_workflow.jpg # Workflow image - │ └── Video_display.png # Output screenshot image 2 - ├── Readme.md # Readme file which contains all the details and instructions about the project sample - ├── Video_Description_Generation_Query_Retrieval.ipynb # Notebook file to excute the project sample - ├── pyproject.toml # Requirements for the project sample - └── uv.lock # File which captures the packages installed for the project sample + ├── assets/ # Assets folder with images and diagrams + │ ├── Generating_video_descriptions_using_Pytorch_XPU.png # PyTorch XPU output screenshot + │ ├── Video_description_generation_and_query_retrieval_workflow.jpg # Workflow diagram + │ └── Video_display.png # Video display screenshot + ├── Readme.md # Main README + ├── Video_RAG_Ollama.ipynb # Ollama-based notebook + ├── st_video_rag_demo.py # Streamlit web interface + ├── pyproject.toml # UV/pip requirements + └── uv.lock # UV lock file --- @@ -81,33 +136,79 @@ Please find the some of the keywords below used in the prompts across 11 differe ### For Windows: To install any software using commands, Open the Command Prompt as an administrator by right-clicking the terminal icon and selecting `Run as administrator`. + 1. **GPU Drivers installation**\ Download and install the Intel® Graphics Driver for Intel® Arc™ B-Series, A-Series, Intel® Iris® Xe Graphics, and Intel® Core™ Ultra Processors with Intel® Arc™ Graphics from [here](https://www.intel.com/content/www/us/en/download/785597/intel-arc-iris-xe-graphics-windows.html)\ **IMPORTANT:** Reboot the system after the installation. -2. **Git for Windows**\ +2. **Ollama Installation**\ + Download and install Ollama from [https://ollama.com/download](https://ollama.com/download) or use the command: + ```powershell + winget install Ollama.Ollama + ``` + + **Building Ollama with GPU Support (Vulkan)**\ + For advanced users who want to build Ollama from source with Vulkan GPU acceleration: + + a. **Install Vulkan SDK** + - Download and install from [https://vulkan.lunarg.com/sdk/home](https://vulkan.lunarg.com/sdk/home) + + b. **Install TDM-GCC** + - Download and install from [https://github.com/jmeubank/tdm-gcc/releases/tag/v10.3.0-tdm64-2](https://github.com/jmeubank/tdm-gcc/releases/tag/v10.3.0-tdm64-2) + + c. **Install Go SDK** + - Download and install Go v1.24.9 from [https://go.dev/dl/go1.24.9.windows-amd64.msi](https://go.dev/dl/go1.24.9.windows-amd64.msi) + + d. **Build Ollama with Vulkan** + ```powershell + # Set environment variables + set CGO_ENABLED=1 + set CGO_CFLAGS=-IC:\VulkanSDK\1.4.321.1\Include + + # Build with CMake + cmake -B build + cmake --build build --config Release -j14 + + # Build Go binary + go build + + # Run Ollama server (Terminal 1) + go run . serve + + # Test with a model (Terminal 2) + ollama run gemma3:270m + ``` + +3. **Git for Windows**\ Download and install Git from [here](https://git-scm.com/downloads/win) -3. **uv for Windows**\ +4. **uv for Windows**\ Steps to install `uv` in the Command Prompt are as follows. Please refer to the [documentation](https://docs.astral.sh/uv/getting-started/installation/) for more information. - ``` + ```powershell powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" ``` **NOTE:** Close and reopen the Command Prompt to recognize uv. ### For Linux: To install any software using commands, Open a new terminal window by right-clicking the terminal and selecting `New Window`. + 1. **GPU Drivers installation**\ Download and install the GPU drivers from [here](https://dgpu-docs.intel.com/driver/client/overview.html) -2. **Dependencies on Linux**\ +2. **Ollama Installation**\ + Install Ollama using the official script: + ```bash + curl -fsSL https://ollama.com/install.sh | sh + ``` + +3. **Dependencies on Linux**\ Install Curl, Wget, Git using the following commands: - For Debian/Ubuntu-based systems: - ``` + ```bash sudo apt update && sudo apt -y install curl wget git ``` - For RHEL/CentOS-based systems: - ``` + ```bash sudo dnf update && sudo dnf -y install curl wget git ``` @@ -125,50 +226,89 @@ To install any software using commands, Open a new terminal window by right-clic --- -## Running the Sample && execution output - -1. In the Command Prompt/terminal, navigate to `Video-Description-Generation-Query-Retrieval` folder after cloning the sample: - ``` +## Running the Sample + +### Quick Start with Streamlit (Recommended) + +1. **Navigate to project folder:** + ```bash cd ``` - -2. Log in to Hugging Face, generate a token, and download the required models and datasets:\ - `huggingface-cli` lets you interact directly with the Hugging Face Hub from a terminal. Log in to [Huggingface](https://huggingface.co/) with your credentials. You need a [User Access Token](https://huggingface.co/docs/hub/security-tokens) from your [Settings page](https://huggingface.co/settings/tokens). The User Access Token is used to authenticate your identity to the Hub.\ - Once you have your token, run the following command in your terminal. + +2. **Pull an Ollama vision model:** + ```bash + ollama pull qwen2.5-vl + # OR + ollaa pull qwen3-vl:latest + # OR + ollama pull llama3.2-vision + # OR + ollama pull llava ``` + +3. **Download the video dataset:** + + Log in to [Huggingface](https://huggingface.co/) and get a [User Access Token](https://huggingface.co/docs/hub/security-tokens) from your [Settings page](https://huggingface.co/settings/tokens). + + ```bash uv run huggingface-cli login - ``` - This command will prompt you for a token. Copy-paste yours and press Enter. - ``` - uv run huggingface-cli download Qwen/Qwen2.5-VL-3B-Instruct - uv run huggingface-cli download sentence-transformers/all-MiniLM-L6-v2 uv run huggingface-cli download stepfun-ai/Step-Video-T2V-Eval --repo-type dataset --local-dir ./Step-Video-T2V-Eval ``` -3. Launch Jupyter Lab and Run the notebook:\ - Open the [Video Description Generation Query Retrieval](./Video_Description_Generation_Query_Retrieval.ipynb) notebook in the Jupyter Lab. - - In the Jupyter Lab go to the kernel menu in the top-right corner of the notebook interface and choose default kernel i.e. `Python 3 (ipykernel)` from the available kernels list and run the code cells one by one in the notebook. - ``` - uv run jupyter lab - ``` - - If you are running the sample in the [Intel Tiber AI Cloud(ITAC)](https://ai.cloud.intel.com/), follow these steps in a new terminal session. Create and select the `uv_env` Jupyter kernel to get access to required python packages in the notebook. +4. **Launch the Streamlit application:** + ```bash + uv run streamlit run st_video_rag_demo.py ``` + + The app will open in your browser at `http://localhost:8501` + +5. **Use the application:** + - Go to "Process Videos" tab + - Configure settings (model, dataset folder, max videos) + - Click "Start Processing" + - Once complete, go to "Search Videos" tab + - Enter natural language queries to find videos + +### Alternative: Jupyter Notebook + +1. **Install dependencies:** + ```bash uv sync - uv run python -m ipykernel install --user --name=uv_env --display-name="uv_env" ``` -4. GPU utilization can be seen in the Task Manager while generating video descriptions for videos which are processing on Intel XPUs. - ![Generating_video_descriptions_using_Pytorch_XPU](./assets/Generating_video_descriptions_using_Pytorch_XPU.png) +2. **Launch Jupyter Lab:** + ```bash + uv run jupyter lab + ``` + + Open `Video_RAG_Ollama.ipynb` and run cells sequentially. -5. Relevant video will be displayed based on user query. - ![Video_display](./assets/Video_display.png) +3. **Expected Output:** + + - GPU utilization can be monitored in Task Manager (Windows) or nvidia-smi/intel_gpu_top (Linux) + - Processing 128 videos typically takes 10-60 minutes depending on hardware + - Search queries return results in < 100ms + - Videos displayed with similarity scores and descriptions --- ## Troubleshooting -- **Dependency Issues:** Run `uv clean` and then `uv sync`. -- **File Access Issues:** Restart the kernel and run the cells again. +**Ollama Issues:** +- **Ollama not accessible:** Check if Ollama is running with `ollama list`. Start with `ollama serve` if needed. +- **No vision model:** Pull a vision model: `ollama pull qwen2.5-vl` or `ollama pull llama3.2-vision` +- **Model too slow:** Try a smaller model like `llava` for faster inference + +**Application Issues:** +- **Dependency Issues:** Run `uv clean` and then `uv sync` +- **No videos found:** Ensure video folder path is correct and contains .mp4/.avi/.mov files +- **Database errors:** Delete `Video_descriptions_database_ollama` folder and reprocess +- **Poor search results:** Check that descriptions are detailed (view in "View All Descriptions" section) + +**Performance:** +- **Slow processing:** Reduce number of videos or use a faster model +- **Out of memory:** Process fewer videos at once or use a smaller model +- **GPU not utilized:** Ensure Intel GPU drivers are installed and Ollama is using GPU --- diff --git a/Video-Description-Generation-Query-Retrieval/Video_Description_Generation_Query_Retrieval.ipynb b/Video-Description-Generation-Query-Retrieval/Video_Description_Generation_Query_Retrieval.ipynb deleted file mode 100644 index 1fa010d..0000000 --- a/Video-Description-Generation-Query-Retrieval/Video_Description_Generation_Query_Retrieval.ipynb +++ /dev/null @@ -1,4823 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "b584ead1-9a08-44eb-8c03-4ab98eb0f34a", - "metadata": {}, - "source": [ - "# Video Description Generation and Query Retrieval" - ] - }, - { - "cell_type": "markdown", - "id": "a50c359d-093d-49f2-8a53-43ae428355d1", - "metadata": {}, - "source": [ - "## Overview" - ] - }, - { - "cell_type": "markdown", - "id": "c84bdadd-ac84-4232-baf5-3394e51b6d3b", - "metadata": {}, - "source": [ - "This notebook demonstrates how to generate video descriptions using the [**Qwen 2.5 Vision-Language model**](https://github.com/QwenLM/Qwen2.5-VL) and store their embeddings in [**ChromaDB**](https://www.trychroma.com/) for efficient semantic search on **Intel® Core™ Ultra Processors**. The Qwen 2.5 Vision-Language model is loaded using the [**PyTorch XPU backend**](https://docs.pytorch.org/docs/stable/notes/get_start_xpu.html) to leverage Intel hardware acceleration.\\\n", - "For each video, a description is generated and stored as an embedding in ChromaDB. When a user submits a query, cosine similarity search is performed in ChromaDB to retrieve the most relevant video description. The matching video is then displayed inline.\\\n", - "This sample uses the videos from the [**stepfun-ai/Step-Video-T2V-Eval**](https://huggingface.co/datasets/stepfun-ai/Step-Video-T2V-Eval) Hugging Face dataset. For more information on the dataset and citation requirements, please refer to the [**Step-Video-T2V Technical Report paper**](https://arxiv.org/abs/2502.10248)." - ] - }, - { - "cell_type": "markdown", - "id": "4066302a-a785-4a4d-882d-26cba0f73f6c", - "metadata": {}, - "source": [ - "## Workflow" - ] - }, - { - "cell_type": "markdown", - "id": "087121d4-116e-4954-a84d-a8790774ec3c", - "metadata": {}, - "source": [ - "- During the initial data load, videos from the [*Step-Video-T2V-Eval*](https://huggingface.co/datasets/stepfun-ai/Step-Video-T2V-Eval) Hugging Face dataset is fed into the [*Qwen 2.5 Vision-Language model*](https://github.com/QwenLM/Qwen2.5-VL).\n", - "- Here, the [*Qwen2.5-VL-3B-Instruct*](https://huggingface.co/Qwen/Qwen2.5-VL-3B-Instruct) model variant is used to process these videos and generate descriptions. The Qwen 2.5 Vision-Language model is loaded using the [*PyTorch XPU backend*](https://docs.pytorch.org/docs/stable/notes/get_start_xpu.html) to leverage Intel hardware acceleration.\n", - "- Next, the generated video descriptions are converted into embeddings using [*Sentence Transformers*](https://sbert.net/), with the [*all-MiniLM-L6-v2 model*](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2).\n", - "- These embeddings, along with the descriptions and video metadata, are stored in a persistent local [*ChromaDB*](https://www.trychroma.com/) collection. This is a one-time operation; since ChromaDB is local and persistent, it does not need to be repeated unless new videos are added.\n", - "- When a user submits a query, the text is similarly encoded into an embedding, which is then used to perform a semantic search (via cosine similarity) over the ChromaDB collection.\n", - "- The final result will be the most relevant video description and its associated video file name, and the video is displayed directly in the notebook." - ] - }, - { - "attachments": { - "0ed8e566-5603-4a27-a8ea-906c82934c53.png": { - "image/png": "/9j/4AAQSkZJRgABAQEAeAB4AAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAJ9BdcDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9U6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAEzjk8CorS8gv4Fntp47iFs4khcMpwcHBFZ3i/R38ReE9Z0qOVoZL2zmtlkQ4Kl0Kgj86+S/wDgn74unsbjxb4Gv2ZJ7aQXsML/AMLA+XMPzCfrXqUMD9YwlXExlrTtp5PqeZXxnsMVSw8o6Tvr5rofZdQT31vazQRTXEUUs7FYo5HCtIQMkKD1OPSp6+GviJcXfxu/bW0jQ7K4kXT/AA/PGhkiY/u1hPmzMPQl/kz7CjL8D9dnNSlyxhFyb9Ax2M+pxhyx5pSkopep9y1DcXkFmqtcTxwKzbVMjhQSegGe9TV8R/tkfBX4jePvilaahoumXevaJJaxw2sdu4K2rjhwwJG3Jw248HPXijLsJTxtf2VWoqate7/LdDzDFVMHQ9rTpubvsv6Z9uUVx3wf8Pax4T+GHhrR9fuPtWsWdkkVxJv34YdF3d9owue+2uxrz6kVCcoxd0nv38zupyc4Rk1ZtbdvIKqatqlvoml3mo3knlWlpC88z4ztRVLMfyBq3XGfGiRovhF40dThho93g/8AbJqd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47jwhqUcaqAXiuY3PA9CB/OvrDQtXi8QaLp+qQJJHBe28dzGky7XVXUMAw7HBqNfDekKwZdKsgQcgi3Tj9K0q8nGVsJWt9WouHf3r3PTwlHFUr/Waqn2921gooorzD0gooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvmn40/s6eJPHn7QnhHxlpclkukWZtvtjTSlZI/JlLnC4+bIIAx364619LUV24TGVcFUdSlu018mceKwtPGQVOrsmn9wUUUVxHYFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABWZ4j8N6X4u0W60jWrGHUtNuV2y21wu5WGcj6EHBBHIIyK06KqMnFqUXZoUoqScZK6ZneHfDum+E9FtNI0ezi0/TbRPLgt4RhUX+p7knkk5rRooolJyblJ3bCKUUklZIKKKKkYUUUUAFFFFABRRRQAV8g658AfGHxO/axuNc8V6f8A8UbYSpLbzmRTFNBGMxQqM5yW5YY/veoz9fUV6GDx1TAucqSV5Jq/a/bzODF4OnjVCNVu0Wnbvbv5CUtFFeed4UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAB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y0yzs727Obi5t7dEkmPq7AZb8a0a+RfhbZn4jeG9B+LmvfE7VPDfiG68VSILeXU3GnxwLdPbrpAtDIsOXVVBYqZN53e1c54413VNS+H/AMbfiVd+LNc0vxn4R8R3FhoVla6vPDa2qwNELa3azVxFL9o3Dd5iMW83g4AoA+3a8yh/aI8HR/BuT4n6nc3Wi+E4mkWWa7t2eWPZOYMlIt5OXHGM8EZxzXlHiRZ/iT8bfFuj+MfHGveCNM0PwzY3+mWOi61JpIJmWQ3N47oymXy3QJh8ouOQd1cDpNi0/wDwTr0nxFpvi7xBpN/oOjahf2t1oeqNbNdSrLKQZtv+tGRkg+poA+2tPvoNUsba8tn8y2uI1mifBG5WAIODyOD3qxXzh+0Fcaz4mX4I6Fo3jDVvDC6/rqw3uqaPceXPJEthPIyZ6Ett4yDg4bGQK4288d+I/gv4L+Peh6b4l1PUpvDWo6cNJ1HxBePqFxYW97FArSNJKSzrG7SuAxPTGMcEA+waK+dfAunx/D39pTS/CGn+NPEHiSO68JXOo6lDrmuz6gxmW5gWGcRyOUhLB5eI1VSBwMCvoqgAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK4bwr8I9K8F+PPEXiXR7/UrOPxA3n3+iCVDp73WADdKhTekrKoDbXCt1Kk8128kiQxtJIyoigszMcAAdSTXl3hH9pz4c+OvEllomja3cXN1ftOlhcSaZdw2d80IJlFvcvEIpioBPyOcgEjIoA0PDP7PPwz8G+LpvFGi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igAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAPnLXvAHiD4k/tqaJresaTdW3gfwDobT6VczL+5vdTuiVd0OcHZGAMHlWQH+IVh/Df4PXXjDWf2o/D/iLS7my0nxVrTQW1xcQsqTI9moEsZP3gpZTkdx6ivqmuO+H/xW0L4lah4rs9Ga4M/hnVpdF1AXEWwC4jClthydy4YYNAHFfsfzeKo/2e/C2leNNJvNI8R6HHJo1xHexMjSrbO0UUq7gCytGqEP/FyRkcn2aiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvnX+xdQ/4bnOp/Yp/wCzv+EV8v7V5Z8rd5n3d3TPtX0VRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXiH7N/w18Q/D/xP8ZbvXLJbSDxB4zudV01hKj+favFHtk+UnbzkYbByp46E+31558HfjFafGD/AITf7Jp02nf8It4ovvDE3nOG8+S22bpVx0Vt4wDzxQB6HRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAeU/Frx94ltPG3hPwF4LfT7PxBr8d1ezarqls9zDp9nbhA8ghV4zI7PLGqguoGSTnGKzPhp8cLiG41nw38Q7uxg8S6V4kj8NRXunWssdvqUk0Cz27rGS5iLRvghmKhlOGwQKyf2iPDvjzRvHHh74ieAdGbxHqGnaPqWjTafEYvNjNwI3huFSR0WQLLCoZd4O1sjOK8Z8JaD4v+LGgz/FO18O/aPEsPxGtNYv8Awjb3kSXdtHZWws5IC0pRBKDukCswBVl+Y5FAH1/c/ErwzZal4jsLnV4LW58O2kV9qvnho0tIJFdkkZ2AXBEbngnG3nFYvgT47+DPiNqk+m6NqF2moRWi34tdT0y6sJZrVjgTxLcRoZYif40yORzyK+dvFvwn+J3xgk+O11d+FJfB0nifQdKt9HtbjU7edp5LaSZzHIyFkR2GFI+ZRvHzNzXYfA/wfqtv4ov7ufwH4ugvptIltbvxh8QNat7i+MhIK2trDBI8aQ5ySUES/KvDUAd74Z/ax+FPjHWdD0rRvFseoX2tSGGzSGzuSplCs/lSP5e2KQqjMEkKsRggEEZ6T/hdfgr/AIVnP8QRrsZ8IQK7Saj5MnGyQxsvl7d+7eCu3bnPauV/Zp+FMPgz4D/DzRdf8NWdjrmi26zvDNDHI9vekOskysMgSNvfLA5w5Ga8Yh+Gmo6x+0xq/wALrWaG4+GFlqcHxA1G3QnMFzIWKWDDGNj3CG5x6DpzQB7B44+Obad8cPA3gXRdZ0O3fUJXOq2er2V8tzLGYTJELSVI/I34ViQ7dB0zXQfEb9or4f8Awo8RW+g+Jtdey1q4tftkGnwWNxdTzRbymUSGNy5yDkAEgAkgAZrgPjjqPjjVPiZ4CuNC+GPiPWdN8K649/c3sF1pqRXkT2c0P7kSXavuDTD76p91vbPR6H4P1fUP2ln8cX3h+Sz0y58F29jFLeNC8tpc/ankltzsdgGKsu4qSp2dTQB1Oo/HTwLpfw/0rxtceIYf+Eb1YR/2fdRRSyyXjSfcSKFVMryHB+RVLcHjg1reAfiPoHxM0u51Dw/dXFxDa3LWdxHd2U9nPBMoBZHhnRJFOGU8qMggivmTwx8PPHPwx0f4Taxc+CtQ8Rx+D9T1+2uNG0ua3e6WC5lkFrdxLJIiN8uARuBCynj09F/ZN1rUvE0nxY1jVNHfQbq78ZXGdPkmjmeHZa2se1njJUuNnzBSQGyMnGaAPY/GPi7SfAPhXVfEeu3X2HRtLt3uru48t5PLjUZZtqAseOwBNcn4I/aF+HvxI8VSeHfDXiSLV9US1a9Cw28whkhV1R2jmKCOTazBSEYkHggYNVf2nPCOq+PfgB468P6HaNf6tqGmSQ29qrhDM2QdgJIAyARye9dI3gHSn8PRQWml2elajHo7aTa3UNugls4WQAxIwGVQFVO0cZUelAGB4f8A2jPAHifxTaaBp+szSXd7NNb2NzLp1zFZX0sWfMjt7p4xDMy7WyI3Y/KfQ1W1/wDac+HHhvxXJ4du9dmk1KG9g065az026uba1uZnCRRTXEcbRROWZRtdwRkZxXgf7PHwl8SeBdS8CaXr3gDxbr2ueHyLWTWvEmuWv9h6REsbIZtOghc72YYC7oRJhzukHNM8SeCvHF5461SLwV4C8Y+CfE9z4gN3dXX9tW914RvITOpN7LDMz7pHjUMY4okcPj5gRmgD6pPxO8MrD4tlOqDy/CbFdZPkyf6IRAs5/h+f926t8m7rjrxXlf7R3ivxLpfgnw5448EeObzSbG4vdLt1sI7C1lt7+O7u4Yw7tNC0qfu5T90r24zXGeMvB/xI8MXnx20jQfAE/imDx45urDVo9Utba2hD6elvIkod/NDq0eVCxsG3D5l5Id8UdF+I9x8EPAPgfTfhrq+u3Wnw6Dd3uoW+paekcUlrNDJNDtluFdnAhIyBtJYfN1wAel/tJftDaf8As/6X4YubsoZNW1m2smWS0nmC2xkUXEg8pTh1VsqDyTwA3SvQvAvj7QviVoK6z4dvWv8ATmcxiVreWA7gASCkiqw6jqK8r+Mlr4r+I/w48Ma3p3gjVLTV9C8TWOsy+G765sxeTQQSHeI2SZ4dxViygyDOMcEivUfAfiy+8ZaO9/feFdY8Iv5pjjstcNv9odQB8+IJZVUEkjBbPy5xgigDpKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigA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w54lXxBZ3ehx6nqRtJpo5baFg8CtIvmhp/NzgN82QfSvtAWsIjjjEMYjiIMa7RhMdMDtio7jTbS6uYbia1hmuIf9VLJGGdP90kZH4UAWa+V/jNL4Gb9pqOP4yyaIvgdPCe/Q4/FBjGnveG4Iuiol+QziPycfxhSdvU19UVXvNPtdRjWO7toblFYOqzRhwGHQgHvQB8K6D4XuvH3w7/Zj0PxDeavaaVea/qb20qXEtveNYrb3bWcZlBDrut9q5BDbDwR1r63+E3wX8NfBWx1ux8LQzWenarqL6k1kzgxW8jRohWIADav7sHnJySc13DwxyNGzxqzRnchYAlTjGR6cE/nT6ACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArlvhr8MfDXwg8JW/hnwlpo0nQ7eWWWK1EryhGkkaR8M7FsbmPGeOldDqFz9j0+5uOnlRNJ+QJr57/wCCeOqXutfsefD+81C8uL+7kF+HuLmVpJGC6hcquWYknAAA9gBQB9GUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXJfFD4naN8JfCj69rS3c0JnitLe0sIDNcXVxKwSKGJB1dmIAyQPUgc11tebfHzw3r3irwMtjofhnw34zQ3cT3+geKBiC9thkskblWWOUHaVdlIBH40AJ4R+NUnijTvFcs/gTxXomo+HlR5dIv7aA3V0Hj8xPI8qZ43JHGN4w3BxXFfBj4xXsfwf+IHjPxXquua0NB1LUpZrTVNEg028sYoED/ZBHFIyOVHRy2STzjFVfgT8KPHfwn0jxhqFppGk6UmomA6L4BHiG7uNP07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ls5pLoNJGCUIKZBJHHSgD6borzSz+N1jqXx2vPhnZ6fNc3djpq6heagjDyoNxwkZHXJr0ugAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKK4v4gfGjwH8KkU+L/F+jeHXZN6Q396kc0i+qRk7m/AGvLl/bw+Et9/yBbzxB4l5wp0fw1fzK3+6xhAI9wcUAfQtFfPf/DbHhL/AKEz4jf+Ehef/E0f8NseEv8AoTPiN/4SF5/8TQB9CUV89/8ADbHhL/oTPiN/4SF5/wDE0f8ADbHhL/oTPiN/4SF5/wDE0AfQlFfPf/DbHhL/AKEz4jf+Ehef/E0f8NseEv8AoTPiN/4SF5/8TQB9CUV89/8ADbHhL/oTPiN/4SF5/wDE0f8ADbHhL/oTPiN/4SF5/wDE0AfQlFfPf/DbHhL/AKEz4jf+Ehef/E0f8NseEv8AoTPiN/4SF5/8TQB75qOoW+k6fdX13KILS2iaaaVgcIigljx6AGs7wf4w0fx94Z07xDoF8mpaNqMQntbuNWVZUPAIDAEdO4r50+In7YHh7xB8P/E+l6d4K+IjahfaXdW1ur+ErtVMjxMqAnbwMkc1zP7Of7TmifC/4F+CPCmseCviEmq6TpcVtdLD4Tu3QSAfNhtvIyetAH2VRXz3/wANseEv+hM+I3/hIXn/AMTR/wANseEv+hM+I3/hIXn/AMTQB9CUV89/8NseEv8AoTPiN/4SF5/8TR/w2x4S/wChM+I3/hIXn/xNAH0JRXz3/wANseEv+hM+I3/hIXn/AMTR/wANseEv+hM+I3/hIXn/AMTQB9CUV89/8NseEv8AoTPiN/4SF5/8TR/w2x4S/wChM+I3/hIXn/xNAH0JRXz3/wANseEv+hM+I3/hIXn/AMTR/wANseEv+hM+I3/hIXn/AMTQB9CUV89/8NseEv8AoTPiN/4SF5/8TR/w2x4S7+DfiMB6/wDCIXn/AMTQB9CUV89x/t5fBy3lSLV9b1Xw3Ix2j+2tAvrZQfQuYdq/iRXr3gf4m+EfiXZPd+E/E2k+JLePHmPpd7HceXnoHCklT7HBoA6aiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA4L47fDiD4s/CXxP4WmXLX1m6wt3WUDKEeh3AV5L+zb+0D4e1L4J+Gn8YajZ2nijS/P0qWG5I81p7ZSH2ZGQSq5r6Xr8kv21vhrr/g39qC28PeH1eLTvGd4t9ZiIEeXPKBFNt9OMmgD7M/Yg0u48U2Xjb4r6krG98ZatJJbMxyVs4ztiA9jjNfUNc58OfBtr8PfAmg+G7JFjt9Ms47ZQvTKqMn8Tk10dABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXz34w+Ini/wCNXjrWPh98L7+PQtF0WUWvifx1gSvazEZaysUI2tcBcb5DkRbum7Art/2kviTffCn4M+Idd0iIXGvskdhpMJx897cSLBBweoDyKxHcKa0vgd8K7P4L/C3QPCNq/wBolsYM3l5zuu7pzvnnYnkl5GY88gEDtQBi/DX9mH4c/C8yXOneH4tU1ydvMutf1w/b9RuZO7vPLlsn0XaPavVOnApaKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAGyRpNG0ciq6MCrKwyCD1BFeOfEP9k7wD44vo9a06xk8D+L7cE2viXwo/2C8jbr82wBZRnGVkDZHHFey0UAeEfCf4teKvDnjxfhX8WWsj4rkga50HxFZJ5Vr4it04chOkdygwXiBxg7lG3k+714z+1h8N7vx98JbrUNDKweMvCsyeItAutuWS7tv3gT3EihoyOh3DPSu++Fvjy1+KHw38MeLrNPKt9b06C/WHduMRkQMUJ9VJKn3FAHUUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAV4z8Sv2j4fDvieXwZ4G8OXnxK8exqGn0jSpUit9OVvute3TfJAD2U5Y/wB3BBpP2lviNrvh/T/DngjwVPHb+PfHF62mabcuu/8As+BUL3V8V/iEMfIH95k68iuz+Evwj8N/BXwfb+HvDdn5MKnzLq7lO+5vpyPnnnk6vIx5JP0GAAAAeXN4J/aP8bRpJqnxJ8K/DpGG42nhjQTqUqj+6012+3PYsqY9PWlH7NPj68+fUv2hvHEs3rY21jap/wB8rCa+hKKAPnv/AIZd8W/9HA/Eb/v9Z/8Axij/AIZd8W/9HA/Eb/v9Z/8AxivoSigD57/4Zd8W/wDRwPxG/wC/1n/8Yo/4Zd8W/wDRwPxG/wC/1n/8Yr6EooA+e/8Ahl3xb/0cD8Rv+/1n/wDGKP8Ahl3xb/0cD8Rv+/1n/wDGK+hKKAPnv/hl3xb/ANHA/Eb/AL/Wf/xij/hl3xb/ANHA/Eb/AL/Wf/xivoSigD57/wCGXfFv/RwPxG/7/Wf/AMYo/wCGXfFv/RwPxG/7/Wf/AMYr6EooA+e/+GXfFv8A0cD8Rv8Av9Z//GK5/Xv2G5/FGvaRrWr/ABl8c6jq2kOZNPvLj7E0lsx6lCYOK+pKKAPnv/hl3xb/ANHA/Eb/AL/Wf/xij/hl3xb/ANHA/Eb/AL/Wf/xivoSigD57/wCGXfFv/RwPxG/7/Wf/AMYo/wCGXfFv/RwPxG/7/Wf/AMYr6EooA+e/+GXfFv8A0cD8Rv8Av9Z//GKP+GXfFv8A0cD8Rv8Av9Z//GK+hKKAPnv/AIZd8W/9HA/Eb/v9Z/8Axij/AIZd8W/9HA/Eb/v9Z/8AxivoSigD57/4Zd8W/wDRwPxG/wC/1n/8Yo/4Zd8W/wDRwPxG/wC/1n/8Yr6EooA+e/8Ahl3xb/0cD8Rv+/1n/wDGKD+zJ44t/nsv2hPH0c3ZrpLKdP8AvkwivoSigD57j+Gf7Q3hMmbR/jHoXjMfw2Pi3wyluo9vOtHVvzU49+lWPC/7Td74f8RWXhb4xeFZPhvrd5J5Fjq/2kXOh6lL2SK6AHluwBIjlAPbJPFe+Vh+NfBGg/Ebwzf+HvEulW2s6LfJ5c9ndJuVh2I7hgeQwwQQCCCKANyivnr9nfxFrPw/8d+Ifgd4p1CTVJ9AtI9T8M6tcn9/qGjOxRVkP8UkDgRM3G4bTjqT9C0AFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB8/wD7UmdU8ZfAjQDzBeeObe9lj/vi1t551B9g6o3/AAEV9AV4F8ff+S/fs7Dt/bupf+myevfaACiivKP2rNY1Lw/+zn4/1LSL2407ULXS5Joru0lMUsWCCzKwIIIXPTmgD1eivGPjn4h1DTU+EUmnahdWi3/jDTre4NvKyefC8cpaN8H5lOBlTxwK5nwX+1lqPxG8fLpPh3wlpt3o8WtSaReeZ4jiGs2qIzo91JpyxlkhDpj5pAxBB2igD6Nor5q+IX7ZEfg/xJrVtp2habq2l6LqkejzrNri2+q6jdEp5sen2XlMbgx+YucumSCB0zXUeOP2gte0X4tS/D/wx8PrrxZqq6FFrXnjUEtIYQ8rpsnZkbyxhOCodmJwEwCwAPbaK5v4b+KL/wAbeA9C17VNBuvDGo6haR3Fxo14SZrN2GTGxKqSQfVQfYdKw/jJ8Vz8LdH0n7Fo8niPxFrmoR6Vo+jxTrB9puGVm+eUgiONUR2Z8HAXoaAPQKK+ebr9qLWvC/h74hXPinwZZ2WqeD77TrKWDS9aN1b3JuzFt2ytAjqyiVSQY8HjB9O+8X/GzTvBHxFPhvVLbybKPw1eeJJ9T87/AFcVvIiOnl7eThy2d3bGOc0AekUV88/Cz9qy5+IXxC0Hw3c+G9Ptk1ywk1GAaTri6jeabEqq8f8AaUCxKLUyKwxh3G47c96r+Ev2p/FvjgQXui/CLUr3w+viR9AutVTUVIVVumg+0wxrEXlRQodywjVckB22kgA+jqK8VuP2mtP0r4f/ABK8Q6tpEmnah4I1G40240j7QHkuZBtNrsbaP+PgSRFRg4L45xmu11r4hah4d0HwneXXhHWL6+1q7tbO5stIjFydMaZcvJM3y4ijIIZ8enHNAHa0UUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAIyhlIIyDwQa+f/2Gd1j8BI9DJJj0HXNX0mJT1WOK+m2L+CkD6AV9A14F+xv/AMih8QR2HxA8QAf+BjUAe+0UUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB8/eB0j8cftkfEjXJlEkfgzRNO8OWTHlVlud93csvo2DApPoMetfQNeA/szr9o+JP7QF83+sfxr9mP+7FY2wX/0I179QB8yy+GJ/jt8RPi9LrfiDX9MsPClzFo+hW+jaxc2CWkws455LsiB182QvMAPMDABMY5Odz9m39oC9+I9n4O8P6ta+frV14KtPEV3qgfYJZHlMDL5W0YyUL7ge/QVa8VfAvxpF4o8Z3vgPxvp3hjTfGflyatFf6O17PbTrCIWntXE6KrPGqAiRWAK5HXFQt+zRqfhHXPDuo/DjxdB4UfTfDcfhaYahpA1EyWscgeORP3sYWUHdywdTu+7xyAcL8WP2h/GXib4X/D/AMR+CPDjxzal40TS54Y9bWBiYL14hbs3l8pOIn3ZGEHUNXo/xI+NnjXwB8LNO1+58AxQeKb3W7LSIfD51aOZZPPmVMicBVBILAbgADgnjisbS/2WdS0X4H6P4MtvGCyeIdF8Qt4kstcudPDRvcC7kuFWaBXXKkSFW2svXIxgCsf9pTTfHEPwT0f/AISLVtAv/FA8ZaJJZXGm6bPbWUBN7CqB42nd3GSSTvXIbAxjNAG1qv7UmreC9I+Iq+MfA8Wi+I/COjQ66mn2Ws/bLe/tpSyrsn8hCjCRGRgYzjggkGvQdd+LR0jx94N8LRaT9quPE2m31/BK10I/La3SJxFjac7/ADfvZGNucHtwF1+zLq3xAh+Id98QvEllea94s0RPD0cmhWDW9tptohd12JLJIzuZH3sWYDgAY61b8N/ATxqvxW8GeNPFvxBs9f8A+Easruyg02w0IWMO2aJI94JmkcudmWJYrwAqrySAc5+yn8Uvih4yTWV8ReDWfRn8SarF/bMniGO4azCXEi/ZxEUDMkbL5asDyOQAK9z1DxpLY/EHSPC48P6vcxahZz3ba3Db7rC2MZUCKWTPyu275RjnB98cF8O/hN44+GXia/tNK8U6HP4CvdYu9YexvNHlbUo2uJGlkhWdbgR7d7EhjESAcYPWu+1LwXLqHj/RvEw8Qaxaw6daT2raJb3O2wujIVxLNHj5nTadpzxk0AdNRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB89ftOxx+EfiN8EfiFGgSfTvFKaBdyjjNpqETwNu9VWQRNg9DyK+ha8A/bgUR/A2K9/jsfEeiXKf7w1GBf5Ma9/oAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDwL4+/wDJfv2dv+w7qf8A6bJ699rwL4+/8l+/Z2/7Dup/+myevfaACue+IXgnT/iT4G17wrqpkXTtYspbKdoW2uqupUsp9RnI+ldDRQB4b4e+B/jfUde8Iz+PvHGneINK8IyefpdtpejtZy3VyImijubt2mkDMiu5Cxqq7myegFc0/wCzP438ZeLvDV5498V+GtYs/Depx6ja61p/h5bfXb3y3LxxTXO8rEgO0MIlG8Lg4ya+l6KAPnO4/Zk8SeHPiB4v17wF4h8M+Gz4pvGvbrV7vw0LvWbFnVRKltceaqlGKlgsiMFLNweK9N0b4YzaT8ZNW8cHVTcx6hoNno8lrJEA++CWZ/NLjA+YTEbQo5H4V39FABXm3xq+FV/8SLfw1qGh6vDofifwzqi6tpd1eWxubYyCN43imjDoWR0kZcqwI4I6V6TRQB8geC/hhrXxjm/aL8K+KtWsrXWrvWtNU6ro9iyW0U8NnbyxMkMsjswUrHkF/m5xtyMd6P2avEfirx9d+JPH/jm28SR3nhq+8Nyadp2jCwhiiuGjJaPMsjZwjZ3s2SRjaBive7extrOa4lgt4oZbh/MmeNArSsFC7mI6nAAyewAq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1FcB8Nvjz4F+Lup6np/hPXf7UvNOjjmnja0ngzE5YJLGZUUSxsVbDoWU468iu/oAKKKKACiiigAooooA8B/bk/5N5v8A/sNaL/6c7avfq8B/bk/5N5v/APsNaL/6c7avfqACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA8C+Pv/ACX79nb/ALDup/8Apsnr32vAvj7/AMl+/Z2/7Dup/wDpsnr32gArP8QW2p3mi3kOjX1vpmqSRlba8urU3MUL9maIOhcD03L9a0KKAPAPg/4u+LPi74keMtJ1zxD4TfSfCerx6dcLZeHriKa+R7aOcOjteuIj+9AwVf7vvx2lv+0Z8P7rxdF4dj1qY3k1++lRXbadcrYS3i53WyXhj8hpRgjYJCcgjrxVD4V+A9Y8N/FL4x6pfWslrpviDVLS50+cyo3nKtlFE7BVJK4dSPmAPHpXz/8ABn4MeJ/h/qXhvQ9c8C+LvGWq6Lq7NBqGs65ax+GdPh892+228Mb7nmKPuAkhaQOT8ygZoA+nL/48eAdL0jxTqd34ltbe18MXf2HV/MSQSWs5ICxmPbvYsWG3aDvz8ua4L4k/taeG/hl8bvD3gvVrn7Np17pVxe3lydOu5ZYZQ0P2dV2IQVZXk3cHbtGSvQ+ffET4KePPGnxYm+MMHhfS1vfDV8kOm+Drh18zX7WBnH2meYPsWfLlrcMCEwNxy3y+j/FC18TeH/jN4I+IuneDdW8U6Za6HfaVfadpElt9utpJ5LeRG2Syxo6/umU7X4PPIoA9p0fVrTXtKtNSsZfPsruJZ4ZdpXcjDIOCARwe4q5VDQdRuNX0WyvbvTbnRrm4iWSTT7xo2mt2IzscxsyFh32sR71foAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvAf2N/8AkUfiH/2UDxB/6WNXv1eA/sb/APIo/EP/ALKB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UUUAFFFFABRRRQB4F8Bv+JT+0D+0Hoj/ACyHV9N1iNf70dxYRruH/A4HU+6177Xzp8ULpvg/+1J4I8eTOsfhnxjZjwZq0jfKlvdq7z2EzH/bYyxZPAyK+i6AG7lyRkZ+tEkixKWdgijqWOBXxl4s+B/hr4ufFL9pHUPEOnyapqmm2dpDpUEs0ghtnOmbkuEQHHm78gPglQOMZOce48a+CfGniT4XX3xnvrSTwUvw7t9Y02218j7Ff6mxVbhijHE06x7dqEMf3hIGTQB9yedGGVS6hmBZVyMkDGSPzH506vy2/sHR/Gvwl8Jz3sV0lto/gfxlc2ljNJJEba4gvU8qNo85AiyhVG4UondRXoWvabf+AfhL8Y7Dw4JIrY6t4bk1mW6vZ1EsM1rbG+mlnw0gWTJ8yQAnaXPagD9Bo5EkXKMrjplTmnV8s/sh6d4T0vxt4v8A7C8T+FdQ1O8s7Wa80H4c2rJ4d05VLqhEgLJJcuPvNlGZVHyDGa+pqACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA8B/bIP9oeDfAnh9fmn17xxolkkf94Lcidz9AkLMfpXv1fOWtXTfGL9sTQtJs3WTw98LbKTVNSkX5lk1W8jaK3gPvHD5kuR0LAGvo2gAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACuL+K3wh8MfGjwwdE8T2JuIkcT2l5A5iurGcfcnglHzRyKeQR9CCCRXaUUAfOtr4U/aE+EqrbaD4i0L4veH4/uQeKi+navGo6ILqJWjl93kQGr0n7QHxN0uMf2r+z14oWQff8A7L1jTr1P+AkSqSPqoPsK98ooA+e/+GovFv8A0b98Rv8AvzZ//H6P+GovFv8A0b98Rv8AvzZ//H6+hKKAPnv/AIai8W/9G/fEb/vzZ/8Ax+j/AIai8W/9G/fEb/vzZ/8Ax+vo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AUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAH/9k=" - } - }, - "cell_type": "markdown", - "id": "59f92287-32bd-4d39-85ef-7cab033a4259", - "metadata": {}, - "source": [ - "![Video_description_generation_and_query_retrieval_workflow.png](attachment:0ed8e566-5603-4a27-a8ea-906c82934c53.png)" - ] - }, - { - "cell_type": "markdown", - "id": "214bbd89-9b6d-48e8-904d-d08a7450fae0", - "metadata": {}, - "source": [ - "## Import necessary packages\n", - "Import all the necessary packages and libraries" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "d24b9f9f-c73b-4f91-91f9-cae38eb75e34", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import torch\n", - "import random\n", - "import shutil\n", - "import zipfile\n", - "import logging\n", - "import chromadb\n", - "import warnings\n", - "from tqdm import tqdm\n", - "from IPython.display import Video, display\n", - "from huggingface_hub import notebook_login\n", - "from qwen_vl_utils import process_vision_info\n", - "from sentence_transformers import SentenceTransformer\n", - "from transformers import Qwen2_5_VLForConditionalGeneration, AutoTokenizer, AutoProcessor\n", - "\n", - "logging.basicConfig(level=logging.INFO)\n", - "warnings.filterwarnings(\"ignore\")" - ] - }, - { - "cell_type": "markdown", - "id": "6fd9cf5b-3465-43d0-87bd-b1df7831d5c5", - "metadata": {}, - "source": [ - "## Login to Huggingface to download the models" - ] - }, - { - "cell_type": "markdown", - "id": "f306fbcb-82d7-482a-8b53-b1486f81d639", - "metadata": {}, - "source": [ - "- Log in to [Huggingface](https://huggingface.co/) using your credentials.\n", - "- You’ll need a [User Access Token](https://huggingface.co/docs/hub/security-tokens), which you can generate from your [Settings page](https://huggingface.co/settings/tokens). This token is used to authenticate your identity with the Hugging Face Hub.\n", - "- Once you've generated the token, copy it and keep it secure. Then, run the cell below and paste your access token when prompted." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "36f2f1da-9918-412e-b6f1-41eaf41946ed", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "e1cf3cc1e5304bc893397a834433b6d6", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(HTML(value='
\n", - " Generating video descriptions and updating the database. This may take some time.\n", - "" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "829fedae-16d5-47f8-8d2a-447e78d3e3bf", - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 0%| | 0/128 [00:00\n", - " Your browser does not support the video element.\n", - " " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "display_video(results)" - ] - }, - { - "cell_type": "markdown", - "id": "c55db9d3-b2e7-4826-8c1b-a1acb288ec9d", - "metadata": { - "scrolled": true - }, - "source": [ - "## Remove the database\n", - "Remove the local database if it is not needed." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "c9a08316-779e-4c35-bb83-242f6a9e0a37", - "metadata": {}, - "outputs": [], - "source": [ - "def delete_database():\n", - " \"\"\"\n", - " Delete the database directory.\n", - "\n", - " Raises:\n", - " Exception: Raises an exception if there is any error while deleting the database.\n", - " \"\"\"\n", - " database_folder = \"Video_descriptions_database\"\n", - " if os.path.exists(database_folder):\n", - " logging.info(\"You are about to delete the database. Once deleted, it will no longer be available, and you will need to regenerate and store the video descriptions again.\")\n", - " database_deletion = 'no'\n", - " if database_deletion == 'yes':\n", - " try:\n", - " shutil.rmtree(database_folder)\n", - " logging.info(\"Database deleted!\")\n", - " except Exception as e:\n", - " logging.exception(f\"Error while deleting the database: {str(e)}\")\n", - " else:\n", - " logging.info(\"Database not deleted\")\n", - " else:\n", - " logging.info(\"Database is not available\")" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "47d4486c-9165-4ebe-85be-7ed036634624", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:root:You are about to delete the database. Once deleted, it will no longer be available, and you will need to regenerate and store the video descriptions again.\n", - "INFO:root:Database not deleted\n" - ] - } - ], - "source": [ - "delete_database()" - ] - }, - { - "cell_type": "markdown", - "id": "fe8a1e8e-aefa-40fb-bc31-18320c6b21f5", - "metadata": {}, - "source": [ - "## Dataset Citations" - ] - }, - { - "cell_type": "markdown", - "id": "03fb3224-6818-4124-a97b-ec568c67ef3e", - "metadata": {}, - "source": [ - " @misc{ma2025stepvideot2vtechnicalreportpractice, \n", - " title={Step-Video-T2V Technical Report: The Practice, Challenges, and Future of Video Foundation Model}, \n", - " author={Guoqing Ma and Haoyang Huang and Kun Yan and Liangyu Chen and Nan Duan and Shengming Yin and Changyi Wan and Ranchen Ming and Xiaoniu Song and Xing Chen and Yu Zhou and Deshan Sun and Deyu Zhou and Jian Zhou and Kaijun Tan and Kang An and Mei Chen and Wei Ji and Qiling Wu and Wen Sun and Xin Han and Yanan Wei and Zheng Ge and Aojie Li and Bin Wang and Bizhu Huang and Bo Wang and Brian Li and Changxing Miao and Chen Xu and Chenfei Wu and Chenguang Yu and Dapeng Shi and Dingyuan Hu and Enle Liu and Gang Yu and Ge Yang and Guanzhe Huang and Gulin Yan and Haiyang Feng and Hao Nie and Haonan Jia and Hanpeng Hu and Hanqi Chen and Haolong Yan and Heng Wang and Hongcheng Guo and Huilin Xiong and Huixin Xiong and Jiahao Gong and Jianchang Wu and Jiaoren Wu and Jie Wu and Jie Yang and Jiashuai Liu and Jiashuo Li and Jingyang Zhang and Junjing Guo and Junzhe Lin and Kaixiang Li and Lei Liu and Lei Xia and Liang Zhao and Liguo Tan and Liwen Huang and Liying Shi and Ming Li and Mingliang Li and Muhua Cheng and Na Wang and Qiaohui Chen and Qinglin He and Qiuyan Liang and Quan Sun and Ran Sun and Rui Wang and Shaoliang Pang and Shiliang Yang and Sitong Liu and Siqi Liu and Shuli Gao and Tiancheng Cao and Tianyu Wang and Weipeng Ming and Wenqing He and Xu Zhao and Xuelin Zhang and Xianfang Zeng and Xiaojia Liu and Xuan Yang and Yaqi Dai and Yanbo Yu and Yang Li and Yineng Deng and Yingming Wang and Yilei Wang and Yuanwei Lu and Yu Chen and Yu Luo and Yuchu Luo and Yuhe Yin and Yuheng Feng and Yuxiang Yang and Zecheng Tang and Zekai Zhang and Zidong Yang and Binxing Jiao and Jiansheng Chen and Jing Li and Shuchang Zhou and Xiangyu Zhang and Xinhao Zhang and Yibo Zhu and Heung-Yeung Shum and Daxin Jiang},\n", - " year={2025},\n", - " eprint={2502.10248},\n", - " archivePrefix={arXiv},\n", - " primaryClass={cs.CV},\n", - " url={https://arxiv.org/abs/2502.10248}, \n", - " }" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.10" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "05a1e4abf5b046c29125e02d004059a0": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "description_width": "", - "font_size": null, - "text_color": null - } - }, - "06788d02ed4a4696affe2a2753bc88da": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "layout": "IPY_MODEL_a8b4bfd9c16f471992060cc05d45d22d", - "style": "IPY_MODEL_9cb93838bc474e28af02ee42b4bd7b6c", - "value": "\nPro Tip: If you don't already have one, you can create a dedicated\n'notebooks' token with 'write' access, that you can then easily reuse for all\nnotebooks.
" - } - }, - "0d018534eeb949a187ce8fbbd9bfa941": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": {} - }, - "1cd422a26899462c816a8bd827e8a94c": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "VBoxModel", - "state": { - "children": [ - "IPY_MODEL_de408595e9464d8882c9163de8c250fa", - "IPY_MODEL_dae4f862127b41468caa1aab709bc1eb", - "IPY_MODEL_36b84737080741b386a5e67a7e71098e", - "IPY_MODEL_78cf341e3b764c219fa49e431355ca86", - "IPY_MODEL_06788d02ed4a4696affe2a2753bc88da" - ], - "layout": "IPY_MODEL_a1d2eba2d2984f7cafd0d461628cf4c4" - } - }, - "1d729736fbcb482dac67802542714c5e": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "FloatProgressModel", - "state": { - "bar_style": "success", - "layout": "IPY_MODEL_8dd3c7571a7d4e02a1cf571167aa5e00", - "max": 2, - "style": "IPY_MODEL_eb7a88fd11554b84b5c848501000af91", - "value": 2 - } - }, - "1f74aa3526654d0da63f0cb3463386fb": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "CheckboxStyleModel", - "state": { - "description_width": "" - } - }, - "2a6ddf66b0a34b4da63c64e9df859465": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": {} - }, - "36b84737080741b386a5e67a7e71098e": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "CheckboxModel", - "state": { - "description": "Add token as git credential?", - "disabled": false, - "layout": "IPY_MODEL_c38387ffede745e68b3be6ec9f61a1eb", - "style": "IPY_MODEL_1f74aa3526654d0da63f0cb3463386fb", - "value": true - } - }, - "6714fabaf6854f379881bd8ac800a18d": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": {} - }, - "78cf341e3b764c219fa49e431355ca86": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "ButtonModel", - "state": { - "description": "Login", - "layout": "IPY_MODEL_c282851109ab4a219209d4ef7669bb0d", - "style": "IPY_MODEL_94bd57adb50c49b79486e45acac66f90", - "tooltip": null - } - }, - "8dd3c7571a7d4e02a1cf571167aa5e00": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": {} - }, - "90f2f0e3475c4743a832dcd3f061c1fa": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "TextStyleModel", - "state": { - "description_width": "", - "font_size": null, - "text_color": null - } - }, - "94bd57adb50c49b79486e45acac66f90": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "ButtonStyleModel", - "state": { - "font_family": null, - "font_size": null, - "font_style": null, - "font_variant": null, - "font_weight": null, - "text_color": null, - "text_decoration": null - } - }, - "9cb93838bc474e28af02ee42b4bd7b6c": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "description_width": "", - "font_size": null, - "text_color": null - } - }, - "a1d2eba2d2984f7cafd0d461628cf4c4": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "align_items": "center", - "display": "flex", - "flex_flow": "column", - "width": "50%" - } - }, - "a8b4bfd9c16f471992060cc05d45d22d": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": {} - }, - "bbe8609ac6354ab0b9405922f151f441": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "layout": "IPY_MODEL_0d018534eeb949a187ce8fbbd9bfa941", - "style": "IPY_MODEL_05a1e4abf5b046c29125e02d004059a0", - "value": " 2/2 [00:44<00:00, 22.01s/it]" - } - }, - "c282851109ab4a219209d4ef7669bb0d": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": {} - }, - "c38387ffede745e68b3be6ec9f61a1eb": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": {} - }, - "cebd89176601477fa21d9184db415d90": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "description_width": "", - "font_size": null, - "text_color": null - } - }, - "ced14b38c76c4b81b27d403077e95272": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "description_width": "", - "font_size": null, - "text_color": null - } - }, - "dac567963bd54bdead3f259b41438c6b": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": {} - }, - "dae4f862127b41468caa1aab709bc1eb": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "PasswordModel", - "state": { - "description": "Token:", - "layout": "IPY_MODEL_6714fabaf6854f379881bd8ac800a18d", - "style": "IPY_MODEL_90f2f0e3475c4743a832dcd3f061c1fa" - } - }, - "de408595e9464d8882c9163de8c250fa": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "layout": "IPY_MODEL_dac567963bd54bdead3f259b41438c6b", - "style": "IPY_MODEL_ced14b38c76c4b81b27d403077e95272", - "value": "

Copy a token from your Hugging Face\ntokens page and paste it below.
Immediately click login after copying\nyour token or it might be stored in plain text in this notebook file.
" - } - }, - "e633a8f0fc8d439abba63be9e41ab3c6": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "layout": "IPY_MODEL_ef7a0c0a6fe64d0a961772ac85c741c2", - "style": "IPY_MODEL_cebd89176601477fa21d9184db415d90", - "value": "Loading checkpoint shards: 100%" - } - }, - "eb7a88fd11554b84b5c848501000af91": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "ProgressStyleModel", - "state": { - "description_width": "" - } - }, - "ef7a0c0a6fe64d0a961772ac85c741c2": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": {} - }, - "ff01fa9425494c56a00236861a40e688": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HBoxModel", - "state": { - "children": [ - "IPY_MODEL_e633a8f0fc8d439abba63be9e41ab3c6", - "IPY_MODEL_1d729736fbcb482dac67802542714c5e", - "IPY_MODEL_bbe8609ac6354ab0b9405922f151f441" - ], - "layout": "IPY_MODEL_2a6ddf66b0a34b4da63c64e9df859465" - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/Video-Description-Generation-Query-Retrieval/Video_RAG_Ollama.ipynb b/Video-Description-Generation-Query-Retrieval/Video_RAG_Ollama.ipynb new file mode 100644 index 0000000..128785f --- /dev/null +++ b/Video-Description-Generation-Query-Retrieval/Video_RAG_Ollama.ipynb @@ -0,0 +1,767 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "79fcc40b-974c-4d09-9cb4-99f0b6706f33", + "metadata": {}, + "source": [ + "# Video Description Generation and Query Retrieval" + ] + }, + { + "cell_type": "markdown", + "id": "9ae88b40", + "metadata": {}, + "source": [ + "## Overview" + ] + }, + { + "cell_type": "markdown", + "id": "3a593030", + "metadata": {}, + "source": [ + "This notebook demonstrates how to generate video descriptions using the **Qwen2.5-VL (Qwen 2.5 Vision-Language model)** via **Ollama** and store their embeddings in **ChromaDB** for efficient semantic search on **Intel® Core™ Ultra Processors**. \n", + "\n", + "For each video, a description is generated using Ollama's vision model and stored as an embedding in ChromaDB. When a user submits a query, cosine similarity search is performed in ChromaDB to retrieve the most relevant video description. The matching video is then displayed inline.\n", + "\n", + "This sample uses the videos from the [**stepfun-ai/Step-Video-T2V-Eval**](https://huggingface.co/datasets/stepfun-ai/Step-Video-T2V-Eval) Hugging Face dataset.\n", + "\n", + "\n", + "- Uses Ollama as the GPU backend\n", + "- Simpler setup - no complex model loading required\n", + "- Uses Qwen2.5-VL vision model through Ollama\n", + "- ChromaDB and semantic search functionality" + ] + }, + { + "cell_type": "markdown", + "id": "ad8ec879", + "metadata": {}, + "source": [ + "## Workflow" + ] + }, + { + "cell_type": "markdown", + "id": "274819d8", + "metadata": {}, + "source": [ + "- During the initial data load, videos from the dataset are processed using **Ollama's Qwen2.5-VL vision model**\n", + "- The model generates descriptions for each video\n", + "- Generated video descriptions are converted into embeddings using **Sentence Transformers** (all-MiniLM-L6-v2 model)\n", + "- These embeddings, along with descriptions and video metadata, are stored in a persistent local **ChromaDB** collection\n", + "- When a user submits a query, the text is encoded into an embedding and used to perform semantic search (via cosine similarity) over the ChromaDB collection\n", + "- The most relevant video description and associated video file are returned and displayed" + ] + }, + { + "cell_type": "markdown", + "id": "0f4c8809", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "Before running this notebook, ensure you have:\n", + "1. **Ollama installed** and running locally\n", + "2. **Qwen2.5-VL vision model** pulled in Ollama: `ollama pull llava` or `ollama pull llama3.2-vision` (or any vision-capable model)\n", + "3. The video dataset downloaded" + ] + }, + { + "cell_type": "markdown", + "id": "e6ee3aa8-d8c6-44be-869b-422cc728d452", + "metadata": {}, + "source": [ + "## Setup: Install Dependencies\n", + "\n", + "Run this cell first to ensure all required packages are installed in the current environment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4ca1579a", + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "import subprocess\n", + "\n", + "# Install dependencies if not already installed\n", + "required_packages = [\n", + " \"ollama>=0.4.0\",\n", + " \"chromadb>=1.0.12\",\n", + " \"sentence-transformers>=4.1.0\",\n", + " \"opencv-python>=4.8.0\",\n", + " \"numpy>=1.24.0\",\n", + " \"tqdm>=4.65.0\",\n", + "]\n", + "\n", + "print(\"Checking and installing required packages...\")\n", + "for package in required_packages:\n", + " try:\n", + " package_name = package.split(\">=\")[0]\n", + " __import__(package_name.replace(\"-\", \"_\"))\n", + " print(f\"✓ {package_name} already installed\")\n", + " except ImportError:\n", + " print(f\"Installing {package}...\")\n", + " subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", package])\n", + " print(f\"✓ {package} installed successfully\")\n", + "\n", + "print(\"\\n✅ All dependencies are ready!\")" + ] + }, + { + "cell_type": "markdown", + "id": "3c8bc613", + "metadata": {}, + "source": [ + "## Building Ollama with GPU Support (Vulkan)\n", + "\n", + "For advanced users who want to build Ollama from source with Vulkan GPU acceleration on Windows:\n", + "\n", + "### Installation Steps\n", + "\n", + "1. **Install Vulkan SDK**\n", + " - Download from: https://vulkan.lunarg.com/sdk/home\n", + "\n", + "2. **Install TDM-GCC**\n", + " - Download from: https://github.com/jmeubank/tdm-gcc/releases/tag/v10.3.0-tdm64-2\n", + "\n", + "3. **Install Go SDK**\n", + " - Download Go v1.24.9: https://go.dev/dl/go1.24.9.windows-amd64.msi\n", + "\n", + "4. **Build Ollama**\n", + " ```bash\n", + " # Set environment variables\n", + " set CGO_ENABLED=1\n", + " set CGO_CFLAGS=-IC:\\VulkanSDK\\1.4.321.1\\Include\n", + " \n", + " # Build with CMake\n", + " cmake -B build\n", + " cmake --build build --config Release -j14\n", + " \n", + " # Build Go binary\n", + " go build\n", + " \n", + " # Run Ollama server (Terminal 1)\n", + " go run . serve\n", + " \n", + " # Test with a model (Terminal 2)\n", + " ollama run gemma3:270m\n", + " ```\n", + "\n", + "**Note:** This is for advanced users who want to compile Ollama from source. The pre-built Ollama installation works fine for most users." + ] + }, + { + "cell_type": "markdown", + "id": "90f262ea", + "metadata": {}, + "source": [ + "## Import necessary packages" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f012ada9", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import base64\n", + "import random\n", + "import shutil\n", + "import logging\n", + "import chromadb\n", + "import warnings\n", + "import ollama\n", + "from tqdm import tqdm\n", + "from IPython.display import Video, display\n", + "from sentence_transformers import SentenceTransformer\n", + "\n", + "logging.basicConfig(level=logging.INFO)\n", + "warnings.filterwarnings(\"ignore\")" + ] + }, + { + "cell_type": "markdown", + "id": "699f3cf3", + "metadata": {}, + "source": [ + "## Configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "600359d0", + "metadata": {}, + "outputs": [], + "source": [ + "# Ollama configuration\n", + "OLLAMA_BASE_URL = \"http://localhost:11434\"\n", + "VISION_MODEL = \"llama3.2-vision\" # or \"llava\" or other vision models\n", + "EMBEDDING_MODEL = \"all-MiniLM-L6-v2\"\n", + "\n", + "# Database configuration\n", + "DATABASE_PATH = \"./Video_descriptions_database_ollama\"\n", + "COLLECTION_NAME = \"Video_descriptions_ollama\"" + ] + }, + { + "cell_type": "markdown", + "id": "7bc20fa8", + "metadata": {}, + "source": [ + "## Get video file paths" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f30e37db", + "metadata": {}, + "outputs": [], + "source": [ + "def get_video_paths():\n", + " \"\"\"\n", + " Select the number of videos to process and return the selected video file paths.\n", + "\n", + " Returns:\n", + " list: Selected list of video files paths.\n", + " \"\"\"\n", + " try:\n", + " dataset_folder = \"Step-Video-T2V-Eval\"\n", + " max_videos_to_select = 128\n", + " video_extensions = ['.mp4', '.avi', '.mov', '.mkv', '.flv', '.wmv']\n", + " video_files = []\n", + " \n", + " for root, dirs, files in os.walk(dataset_folder):\n", + " video_files.extend([\n", + " os.path.join(root, f) for f in files \n", + " if any(f.lower().endswith(ext) for ext in video_extensions)\n", + " ])\n", + " \n", + " total_video_files = len(video_files)\n", + " num_videos_to_select = min(total_video_files, max_videos_to_select)\n", + " \n", + " random.seed(42)\n", + " selected_video_files = random.sample(video_files, num_videos_to_select)\n", + " \n", + " logging.info(f\" Total number of video files found: {total_video_files}\")\n", + " logging.info(f\" Selected {num_videos_to_select} video files\")\n", + " \n", + " return selected_video_files\n", + " except Exception as e:\n", + " logging.exception(f\" Error while extracting the video paths: {str(e)}\")\n", + " return []" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cef968d6", + "metadata": {}, + "outputs": [], + "source": [ + "selected_video_files = get_video_paths()" + ] + }, + { + "cell_type": "markdown", + "id": "3541e91c", + "metadata": {}, + "source": [ + "## Initialize models\n", + "Initialize the Sentence Transformer model for embeddings." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c252d8c", + "metadata": {}, + "outputs": [], + "source": [ + "def initialize_embedding_model():\n", + " \"\"\"\n", + " Initialize Sentence Transformer model for generating embeddings.\n", + "\n", + " Returns:\n", + " SentenceTransformer: The initialized embedding model.\n", + " \"\"\"\n", + " try:\n", + " logging.info(f\" Loading Sentence Transformer Model: {EMBEDDING_MODEL}\")\n", + " embedding_model = SentenceTransformer(EMBEDDING_MODEL)\n", + " return embedding_model\n", + " except Exception as e:\n", + " logging.exception(f\" Error while loading the embedding model: {str(e)}\")\n", + " return None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21f8648c", + "metadata": {}, + "outputs": [], + "source": [ + "embedding_model = initialize_embedding_model()" + ] + }, + { + "cell_type": "markdown", + "id": "2d7fe5a1", + "metadata": {}, + "source": [ + "## Encode video frame to base64\n", + "Utility function to encode video frames for Ollama API." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9666b01e", + "metadata": {}, + "outputs": [], + "source": [ + "def encode_video_frame(video_path, frame_time=0):\n", + " \"\"\"\n", + " Extract and encode a frame from the video as base64.\n", + " \n", + " Args:\n", + " video_path (str): Path to the video file.\n", + " frame_time (float): Time in seconds to extract the frame.\n", + " \n", + " Returns:\n", + " str: Base64 encoded image string or None if extraction fails.\n", + " \"\"\"\n", + " try:\n", + " import cv2\n", + " \n", + " cap = cv2.VideoCapture(video_path)\n", + " if not cap.isOpened():\n", + " logging.error(f\" Cannot open video: {video_path}\")\n", + " return None\n", + " \n", + " # Set position to specific time\n", + " fps = cap.get(cv2.CAP_PROP_FPS)\n", + " frame_number = int(frame_time * fps)\n", + " cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)\n", + " \n", + " ret, frame = cap.read()\n", + " cap.release()\n", + " \n", + " if not ret:\n", + " logging.error(f\" Cannot read frame from video: {video_path}\")\n", + " return None\n", + " \n", + " # Encode frame to base64\n", + " _, buffer = cv2.imencode('.jpg', frame)\n", + " frame_base64 = base64.b64encode(buffer).decode('utf-8')\n", + " \n", + " return frame_base64\n", + " except Exception as e:\n", + " logging.exception(f\" Error encoding video frame: {str(e)}\")\n", + " return None" + ] + }, + { + "cell_type": "markdown", + "id": "77ce50cd", + "metadata": {}, + "source": [ + "## Generate video description using Ollama" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "afb851d6", + "metadata": {}, + "outputs": [], + "source": [ + "def generate_video_description_ollama(video_path):\n", + " \"\"\"\n", + " Generate video description using Ollama vision model.\n", + " \n", + " Args:\n", + " video_path (str): Path to the video file.\n", + " \n", + " Returns:\n", + " str: Generated description of the video.\n", + " \"\"\"\n", + " try:\n", + " # Extract a frame from the middle of the video\n", + " frame_base64 = encode_video_frame(video_path, frame_time=2.0)\n", + " \n", + " if not frame_base64:\n", + " return \"Unable to process video\"\n", + " \n", + " # Use Ollama API to generate description\n", + " response = ollama.chat(\n", + " model=VISION_MODEL,\n", + " messages=[{\n", + " 'role': 'user',\n", + " 'content': 'Describe this sports video in detail. Focus on the main activity, people, objects, and setting.',\n", + " 'images': [frame_base64]\n", + " }]\n", + " )\n", + " \n", + " description = response['message']['content']\n", + " return description\n", + " \n", + " except Exception as e:\n", + " logging.exception(f\" Error generating description with Ollama: {str(e)}\")\n", + " return \"Error generating description\"" + ] + }, + { + "cell_type": "markdown", + "id": "d381f556", + "metadata": {}, + "source": [ + "## Get or create ChromaDB collection" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1e7cdef6", + "metadata": {}, + "outputs": [], + "source": [ + "def get_or_create_database():\n", + " \"\"\"\n", + " Connects to or creates a persistent ChromaDB collection.\n", + "\n", + " Returns:\n", + " tuple: (collection, existing_descriptions)\n", + " \"\"\"\n", + " try:\n", + " client = chromadb.PersistentClient(path=DATABASE_PATH)\n", + " collection = client.get_or_create_collection(\n", + " name=COLLECTION_NAME,\n", + " metadata={\"hnsw:space\": \"cosine\"}\n", + " )\n", + " \n", + " logging.info(\" Checking existing descriptions in database...\")\n", + " all_items = collection.get(include=[\"metadatas\", \"documents\"])\n", + " \n", + " existing_descriptions = {}\n", + " for metadata, doc in zip(all_items['metadatas'], all_items['documents']):\n", + " existing_descriptions[metadata['video_filename']] = doc\n", + " \n", + " logging.info(f\" Found {len(existing_descriptions)} existing descriptions\")\n", + " return collection, existing_descriptions\n", + " \n", + " except Exception as e:\n", + " logging.exception(f\" Error while checking database: {str(e)}\")\n", + " return None, {}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5d26017c", + "metadata": {}, + "outputs": [], + "source": [ + "collection, existing_descriptions = get_or_create_database()" + ] + }, + { + "cell_type": "markdown", + "id": "8438c801", + "metadata": {}, + "source": [ + "## Generate and store video descriptions\n", + "\n", + "Each video will be processed:\n", + "1. Check if description already exists in database\n", + "2. If not, generate description using Ollama\n", + "3. Create embedding and store in ChromaDB" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2e69a08", + "metadata": {}, + "outputs": [], + "source": [ + "def generate_and_store_video_descriptions(selected_video_files, collection, existing_descriptions, embedding_model):\n", + " \"\"\"\n", + " Generate and store video descriptions using Ollama.\n", + " \n", + " Args:\n", + " selected_video_files (list): List of video file paths.\n", + " collection: ChromaDB collection object.\n", + " existing_descriptions (dict): Already processed videos.\n", + " embedding_model: Sentence Transformer model.\n", + " \"\"\"\n", + " try:\n", + " video_descriptions = {}\n", + " \n", + " for video_file in tqdm(selected_video_files, desc=\"Processing videos\"):\n", + " video_filename = os.path.basename(video_file)\n", + " \n", + " # Skip if already processed\n", + " if video_filename in existing_descriptions:\n", + " logging.info(f\" Skipping {video_filename} - already in database\")\n", + " video_descriptions[video_file] = existing_descriptions[video_filename]\n", + " continue\n", + " \n", + " logging.info(f\"\\n Processing {video_filename} using Ollama...\")\n", + " \n", + " # Generate description using Ollama\n", + " description_text = generate_video_description_ollama(video_file)\n", + " video_descriptions[video_file] = description_text\n", + " \n", + " logging.info(f\" Generated description: {description_text}\\n\")\n", + " \n", + " # Create embedding\n", + " embedding = embedding_model.encode(description_text).tolist()\n", + " \n", + " # Store in ChromaDB\n", + " collection.add(\n", + " embeddings=[embedding],\n", + " documents=[description_text],\n", + " metadatas=[{\"video_filename\": video_filename}],\n", + " ids=[video_file]\n", + " )\n", + " \n", + " logging.info(f\" Added {video_filename} to database\\n\")\n", + " \n", + " logging.info(f\"\\n Processed {len(video_descriptions)} videos\")\n", + " logging.info(f\" Database now has {collection.count()} total descriptions\")\n", + " \n", + " except Exception as e:\n", + " logging.exception(f\" Error while generating and storing descriptions: {str(e)}\")" + ] + }, + { + "cell_type": "markdown", + "id": "07d2a1ea", + "metadata": {}, + "source": [ + "
\n", + " Generating video descriptions using Ollama. This may take some time.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d1290f57", + "metadata": {}, + "outputs": [], + "source": [ + "generate_and_store_video_descriptions(selected_video_files, collection, existing_descriptions, embedding_model)" + ] + }, + { + "cell_type": "markdown", + "id": "1bb8b66c", + "metadata": {}, + "source": [ + "## Query the database" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1d801454", + "metadata": {}, + "outputs": [], + "source": [ + "def query_videos_descriptions(query, collection, embedding_model):\n", + " \"\"\"\n", + " Query ChromaDB collection to find similar videos.\n", + " \n", + " Args:\n", + " query (str): User query.\n", + " collection: ChromaDB collection object.\n", + " embedding_model: Sentence Transformer model.\n", + " \n", + " Returns:\n", + " dict: Query results.\n", + " \"\"\"\n", + " try:\n", + " query_embedding = embedding_model.encode(query).tolist()\n", + " \n", + " results = collection.query(\n", + " query_embeddings=[query_embedding],\n", + " n_results=1,\n", + " include=[\"documents\", \"metadatas\", \"distances\"]\n", + " )\n", + " \n", + " logging.info(f\" Search results for: '{query}'\\n\")\n", + " \n", + " for doc, metadata, distance in zip(\n", + " results['documents'][0],\n", + " results['metadatas'][0],\n", + " results['distances'][0]\n", + " ):\n", + " similarity_score = 1 - distance\n", + " logging.info(f\" Video filename: {metadata['video_filename']}\")\n", + " logging.info(f\" Similarity score: {similarity_score:.3f}\")\n", + " logging.info(f\" Distance: {distance:.3f}\")\n", + " logging.info(f\" Video description: {doc}\\n\")\n", + " \n", + " return results\n", + " \n", + " except Exception as e:\n", + " logging.exception(f\" Error while querying video descriptions: {str(e)}\")\n", + " return None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ed608765", + "metadata": {}, + "outputs": [], + "source": [ + "query = \"Give me the video of the birds and blue sea\"\n", + "results = query_videos_descriptions(query, collection, embedding_model)" + ] + }, + { + "cell_type": "markdown", + "id": "9399bfd5", + "metadata": {}, + "source": [ + "## Display the video" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9e55e905", + "metadata": {}, + "outputs": [], + "source": [ + "def display_video(results):\n", + " \"\"\"\n", + " Display the video based on query results.\n", + " \n", + " Args:\n", + " results (dict): Query results.\n", + " \"\"\"\n", + " try:\n", + " if results and results['ids']:\n", + " video_path = results['ids'][0][0]\n", + " video = Video(video_path, width=600, height=400)\n", + " display(video)\n", + " else:\n", + " logging.info(\" No video found\")\n", + " except Exception as e:\n", + " logging.exception(f\" Error while displaying the video: {str(e)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf235da1", + "metadata": {}, + "outputs": [], + "source": [ + "display_video(results)" + ] + }, + { + "cell_type": "markdown", + "id": "fda2e217", + "metadata": {}, + "source": [ + "## Remove the database" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d6ba65c", + "metadata": {}, + "outputs": [], + "source": [ + "def delete_database():\n", + " \"\"\"\n", + " Delete the database directory.\n", + " \"\"\"\n", + " if os.path.exists(DATABASE_PATH):\n", + " logging.info(\"Database deletion option available.\")\n", + " database_deletion = 'no' # Change to 'yes' to delete\n", + " \n", + " if database_deletion == 'yes':\n", + " try:\n", + " shutil.rmtree(DATABASE_PATH)\n", + " logging.info(\" Database deleted!\")\n", + " except Exception as e:\n", + " logging.exception(f\" Error while deleting database: {str(e)}\")\n", + " else:\n", + " logging.info(\" Database not deleted\")\n", + " else:\n", + " logging.info(\" Database is not available\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b8070cae", + "metadata": {}, + "outputs": [], + "source": [ + "delete_database()" + ] + }, + { + "cell_type": "markdown", + "id": "4a2bba50", + "metadata": {}, + "source": [ + "## Dataset Citations" + ] + }, + { + "cell_type": "markdown", + "id": "7613a434", + "metadata": {}, + "source": [ + " @misc{ma2025stepvideot2vtechnicalreportpractice, \n", + " title={Step-Video-T2V Technical Report: The Practice, Challenges, and Future of Video Foundation Model}, \n", + " author={Guoqing Ma and Haoyang Huang and Kun Yan and Liangyu Chen and Nan Duan and Shengming Yin and Changyi Wan and Ranchen Ming and Xiaoniu Song and Xing Chen and Yu Zhou and Deshan Sun and Deyu Zhou and Jian Zhou and Kaijun Tan and Kang An and Mei Chen and Wei Ji and Qiling Wu and Wen Sun and Xin Han and Yanan Wei and Zheng Ge and Aojie Li and Bin Wang and Bizhu Huang and Bo Wang and Brian Li and Changxing Miao and Chen Xu and Chenfei Wu and Chenguang Yu and Dapeng Shi and Dingyuan Hu and Enle Liu and Gang Yu and Ge Yang and Guanzhe Huang and Gulin Yan and Haiyang Feng and Hao Nie and Haonan Jia and Hanpeng Hu and Hanqi Chen and Haolong Yan and Heng Wang and Hongcheng Guo and Huilin Xiong and Huixin Xiong and Jiahao Gong and Jianchang Wu and Jiaoren Wu and Jie Wu and Jie Yang and Jiashuai Liu and Jiashuo Li and Jingyang Zhang and Junjing Guo and Junzhe Lin and Kaixiang Li and Lei Liu and Lei Xia and Liang Zhao and Liguo Tan and Liwen Huang and Liying Shi and Ming Li and Mingliang Li and Muhua Cheng and Na Wang and Qiaohui Chen and Qinglin He and Qiuyan Liang and Quan Sun and Ran Sun and Rui Wang and Shaoliang Pang and Shiliang Yang and Sitong Liu and Siqi Liu and Shuli Gao and Tiancheng Cao and Tianyu Wang and Weipeng Ming and Wenqing He and Xu Zhao and Xuelin Zhang and Xianfang Zeng and Xiaojia Liu and Xuan Yang and Yaqi Dai and Yanbo Yu and Yang Li and Yineng Deng and Yingming Wang and Yilei Wang and Yuanwei Lu and Yu Chen and Yu Luo and Yuchu Luo and Yuhe Yin and Yuheng Feng and Yuxiang Yang and Zecheng Tang and Zekai Zhang and Zidong Yang and Binxing Jiao and Jiansheng Chen and Jing Li and Shuchang Zhou and Xiangyu Zhang and Xinhao Zhang and Yibo Zhu and Heung-Yeung Shum and Daxin Jiang},\n", + " year={2025},\n", + " eprint={2502.10248},\n", + " archivePrefix={arXiv},\n", + " primaryClass={cs.CV},\n", + " url={https://arxiv.org/abs/2502.10248}, \n", + " }" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "AI PC Samples", + "language": "python", + "name": "ai_pc_samples" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Video-Description-Generation-Query-Retrieval/Video_descriptions_database_ollama/3796f63e-da6e-45c5-bba9-dcb86401dc45/data_level0.bin b/Video-Description-Generation-Query-Retrieval/Video_descriptions_database_ollama/3796f63e-da6e-45c5-bba9-dcb86401dc45/data_level0.bin new file mode 100644 index 0000000..09cffb6 Binary files /dev/null and b/Video-Description-Generation-Query-Retrieval/Video_descriptions_database_ollama/3796f63e-da6e-45c5-bba9-dcb86401dc45/data_level0.bin differ diff --git a/Video-Description-Generation-Query-Retrieval/Video_descriptions_database_ollama/3796f63e-da6e-45c5-bba9-dcb86401dc45/header.bin b/Video-Description-Generation-Query-Retrieval/Video_descriptions_database_ollama/3796f63e-da6e-45c5-bba9-dcb86401dc45/header.bin new file mode 100644 index 0000000..e85f465 Binary files /dev/null and b/Video-Description-Generation-Query-Retrieval/Video_descriptions_database_ollama/3796f63e-da6e-45c5-bba9-dcb86401dc45/header.bin differ diff --git a/Video-Description-Generation-Query-Retrieval/Video_descriptions_database_ollama/3796f63e-da6e-45c5-bba9-dcb86401dc45/length.bin b/Video-Description-Generation-Query-Retrieval/Video_descriptions_database_ollama/3796f63e-da6e-45c5-bba9-dcb86401dc45/length.bin new file mode 100644 index 0000000..f79b7db Binary files /dev/null and b/Video-Description-Generation-Query-Retrieval/Video_descriptions_database_ollama/3796f63e-da6e-45c5-bba9-dcb86401dc45/length.bin differ diff --git a/Video-Description-Generation-Query-Retrieval/Video_descriptions_database_ollama/3796f63e-da6e-45c5-bba9-dcb86401dc45/link_lists.bin b/Video-Description-Generation-Query-Retrieval/Video_descriptions_database_ollama/3796f63e-da6e-45c5-bba9-dcb86401dc45/link_lists.bin new file mode 100644 index 0000000..e69de29 diff --git a/Video-Description-Generation-Query-Retrieval/Video_descriptions_database_ollama/chroma.sqlite3 b/Video-Description-Generation-Query-Retrieval/Video_descriptions_database_ollama/chroma.sqlite3 new file mode 100644 index 0000000..e412276 Binary files /dev/null and b/Video-Description-Generation-Query-Retrieval/Video_descriptions_database_ollama/chroma.sqlite3 differ diff --git a/Video-Description-Generation-Query-Retrieval/pyproject.toml b/Video-Description-Generation-Query-Retrieval/pyproject.toml index c845aed..ab1863e 100644 --- a/Video-Description-Generation-Query-Retrieval/pyproject.toml +++ b/Video-Description-Generation-Query-Retrieval/pyproject.toml @@ -1,33 +1,18 @@ [project] -name = "video-description-generation-query-retrieval" +name = "video-rag-ollama" version = "0.1.0" -description = "Add your description here" -readme = "README.md" +description = "Video RAG application using Ollama vision models for semantic video search" +readme = "README_Ollama.md" requires-python = ">=3.12.0" dependencies = [ - "torch>=2.7.0", - "torchvision>=0.22.0", - "pytorch-triton-xpu>=3.3.0 ; sys_platform == 'win32' or sys_platform == 'linux'", - "jupyter>=1.1.1", + "ollama>=0.4.0", + "streamlit>=1.30.0", "chromadb>=1.0.12", - "huggingface_hub[hf_xet]>=0.32.3", - "qwen-vl-utils[decord]>=0.0.11", "sentence-transformers>=4.1.0", - "transformers>=4.52.4", -] - -[tool.uv.sources] -torch = [ - { index = "pytorch-xpu", marker = "sys_platform == 'win32' or sys_platform == 'linux'" }, -] -torchvision = [ - { index = "pytorch-xpu", marker = "sys_platform == 'win32' or sys_platform == 'linux'" }, -] -pytorch-triton-xpu = [ - { index = "pytorch-xpu", marker = "sys_platform == 'win32' or sys_platform == 'linux'" }, + "opencv-python>=4.8.0", + "numpy>=1.24.0", + "tqdm>=4.65.0", + "jupyter>=1.1.1", + "ipykernel>=6.25.0", + "ipywidgets>=8.1.0", ] - -[[tool.uv.index]] -name = "pytorch-xpu" -url = "https://download.pytorch.org/whl/xpu" -explicit = true diff --git a/Video-Description-Generation-Query-Retrieval/st_video_rag_demo.py b/Video-Description-Generation-Query-Retrieval/st_video_rag_demo.py new file mode 100644 index 0000000..24417e1 --- /dev/null +++ b/Video-Description-Generation-Query-Retrieval/st_video_rag_demo.py @@ -0,0 +1,731 @@ +import os +import base64 +import shutil +import logging +import chromadb +import warnings +import ollama +import cv2 +import streamlit as st +from sentence_transformers import SentenceTransformer + +warnings.filterwarnings("ignore") +logging.basicConfig(level=logging.INFO) + +# Configuration +OLLAMA_BASE_URL = "http://localhost:11434" +DATABASE_PATH = "./Video_descriptions_database_ollama" +COLLECTION_NAME = "Video_descriptions_ollama" +EMBEDDING_MODEL = "all-MiniLM-L6-v2" + +# Page config +st.set_page_config( + page_title="Video RAG with Ollama on Intel GPUs", + layout="wide", + initial_sidebar_state="expanded" +) + +# Custom CSS for better styling +st.markdown(""" + +""", unsafe_allow_html=True) + +st.markdown('
🎥 Video RAG: Semantic Video Search
', unsafe_allow_html=True) +st.markdown('
Powered by Ollama on Intel® Arc™ & Core™ Ultra Processors
', unsafe_allow_html=True) + +# Initialize session state +if "collection" not in st.session_state: + st.session_state.collection = None +if "embedding_model" not in st.session_state: + st.session_state.embedding_model = None +if "video_files" not in st.session_state: + st.session_state.video_files = [] +if "database_loaded" not in st.session_state: + st.session_state.database_loaded = False + +# Sidebar configuration +with st.sidebar: + st.markdown("### 🎥 Video RAG") + st.markdown("---") + + st.header("⚙️ Configuration") + + # Model selection - show all models + try: + response = ollama.list() + all_models = [model.model for model in response.models] + + if not all_models: + st.error("⚠️ No models found! Please pull a model:") + st.code("ollama pull llava", language="bash") + all_models = ["llava"] + + selected_model = st.selectbox( + "🤖 Model", + all_models, + key="vision_model" + ) + + model_lower = selected_model.lower() + vision_patterns = ['llava', 'llama3.2-vision', 'minicpm-v', 'qwen', 'cogvlm', 'bakllava'] + + if any(pattern in model_lower for pattern in vision_patterns): + if 'llava' in model_lower: + st.success("✅ Vision model") + elif 'llama3.2-vision' in model_lower: + st.info("💡 Vision model") + elif 'qwen' in model_lower: + st.success("✅ Vision model") + else: + st.info("💡 Vision model detected") + else: + st.warning("⚠️ This model may not support vision/images") + + except Exception as e: + st.warning(f"⚠️ Ollama not accessible: {e}") + st.info("Please ensure Ollama is running: `ollama serve`") + selected_model = "llava" + + max_tokens = 100 + temperature = 0.7 + + st.markdown("---") + + # Dataset configuration + st.subheader("📁 Dataset") + dataset_folder = st.text_input("Video Folder", ".") + max_videos = st.slider("Max Videos", 1, 128, 20) + + st.markdown("---") + + # Database info + st.subheader("🗄️ Database") + if os.path.exists(DATABASE_PATH): + st.success("✅ Database Ready") + if st.button("🔄 Reset Database", type="secondary"): + try: + shutil.rmtree(DATABASE_PATH) + st.success("Database reset!") + st.session_state.collection = None + st.session_state.database_loaded = False + st.rerun() + except Exception as e: + st.error(f"Error: {e}") + else: + st.info("💾 No database found") + + st.markdown("---") + st.markdown("### 🚀 Intel Optimizations") + st.markdown("✓ Intel Arc™ Graphics") + st.markdown("✓ Intel Core™ Ultra") + st.markdown("✓ Hardware Acceleration") + + +def encode_video_frame(video_path, frame_time=2.0): + """Extract and encode a frame from video.""" + try: + cap = cv2.VideoCapture(video_path) + if not cap.isOpened(): + return None + + fps = cap.get(cv2.CAP_PROP_FPS) + frame_number = int(frame_time * fps) + cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number) + + ret, frame = cap.read() + cap.release() + + if not ret: + return None + + _, buffer = cv2.imencode('.jpg', frame) + frame_base64 = base64.b64encode(buffer).decode('utf-8') + return frame_base64 + except Exception as e: + logging.error(f"Frame encoding error: {e}") + return None + + +def generate_video_description_ollama(video_path, model, max_tokens=100, temperature=0.7): + """Generate video description using Ollama vision model.""" + try: + frame_base64 = encode_video_frame(video_path, frame_time=2.0) + + if not frame_base64: + return "Unable to process video" + + response = ollama.chat( + model=model, + messages=[{ + 'role': 'user', + 'content': 'Describe this video frame concisely for search. Include main subjects, actions, setting, and key visual details in 2-3 sentences.', + 'images': [frame_base64] + }], + options={ + 'num_predict': max_tokens, + 'temperature': temperature, + 'num_ctx': 2048, + 'num_gpu': 99 + } + ) + + message = response['message'] + description = message.get('content', '') + + if not description or len(description.strip()) == 0: + description = message.get('thinking', '') + + if not description or len(description.strip()) == 0: + return "Empty response from model" + + return description + except Exception as e: + logging.error(f"Description generation error: {e}") + return "Error generating description" + + +def get_video_paths(folder, max_count): + """Get video file paths from folder.""" + try: + video_extensions = ['.mp4', '.avi', '.mov', '.mkv', '.flv', '.wmv'] + video_files = [] + + folder_path = os.path.abspath(folder) + + for root, dirs, files in os.walk(folder_path): + video_files.extend([ + os.path.join(root, f) for f in files + if any(f.lower().endswith(ext) for ext in video_extensions) + ]) + + if len(video_files) > max_count: + video_files = video_files[:max_count] + + return video_files + except Exception as e: + logging.error(f"Error finding videos: {e}") + return [] + + +def initialize_database(): + """Initialize ChromaDB and embedding model.""" + try: + client = chromadb.PersistentClient(path=DATABASE_PATH) + collection = client.get_or_create_collection( + name=COLLECTION_NAME, + metadata={"hnsw:space": "cosine"} + ) + + embedding_model = SentenceTransformer(EMBEDDING_MODEL) + + return collection, embedding_model + except Exception as e: + error_msg = str(e) + + # Handle tenant connection issues + if "Could not connect to tenant" in error_msg or "default_tenant" in error_msg: + st.error(f"Database initialization error: {e}") + st.warning("⚠️ ChromaDB database is corrupted or incompatible.") + + if st.button("🔄 Reset Database", type="primary"): + try: + # Remove corrupted database + if os.path.exists(DATABASE_PATH): + shutil.rmtree(DATABASE_PATH) + st.success("✅ Database removed. Click 'Start Processing Videos' again.") + st.rerun() + except Exception as reset_error: + st.error(f"Failed to reset database: {reset_error}") + + st.info("💡 **To fix manually:**\n\n1. Close this app\n2. Delete folder: `Video_descriptions_database_ollama`\n3. Restart the app") + return None, None + else: + st.error(f"Database initialization error: {e}") + return None, None + + +def get_existing_descriptions(collection): + """Get existing descriptions from database.""" + try: + all_items = collection.get(include=["metadatas"]) + existing = {meta['video_filename']: True for meta in all_items['metadatas']} + return existing + except: + return {} + + +# Main tabs +tab1, tab2, tab3, tab4 = st.tabs(["🎬 Process Videos", "🔍 Search Videos", "📊 Architecture", "ℹ️ About"]) + +with tab1: + st.header("Process Videos and Build Knowledge Base") + + col1, col2, col3 = st.columns(3) + with col1: + st.markdown('

🤖 AI Model

Qwen Vision-Language

', unsafe_allow_html=True) + with col2: + st.markdown('

💾 Storage

ChromaDB Vector Store

', unsafe_allow_html=True) + with col3: + st.markdown('

🔍 Search

Semantic Similarity

', unsafe_allow_html=True) + + st.markdown("---") + + if st.button("🚀 Start Processing Videos", type="primary", use_container_width=True): + with st.spinner("Initializing AI models on Intel GPU..."): + collection, embedding_model = initialize_database() + + if collection is None: + st.error("Failed to initialize database") + st.stop() + + st.session_state.collection = collection + st.session_state.embedding_model = embedding_model + + with st.spinner("Scanning for video files..."): + video_files = get_video_paths(dataset_folder, max_videos) + + if not video_files: + st.error(f"❌ No video files found in '{os.path.abspath(dataset_folder)}'") + st.info("💡 Supported: .mp4, .avi, .mov, .mkv, .flv, .wmv") + st.stop() + + st.session_state.video_files = video_files + st.success(f"✅ Found {len(video_files)} videos") + + existing = get_existing_descriptions(collection) + st.info(f"📚 Database contains {len(existing)} existing descriptions") + + progress_bar = st.progress(0) + status_text = st.empty() + + processed = 0 + skipped = 0 + + for idx, video_file in enumerate(video_files): + video_filename = os.path.basename(video_file) + progress = (idx + 1) / len(video_files) + progress_bar.progress(progress) + status_text.text(f"Processing {idx + 1}/{len(video_files)}: {video_filename}") + + if video_filename in existing: + skipped += 1 + continue + + try: + description = generate_video_description_ollama(video_file, selected_model, max_tokens, temperature) + embedding = embedding_model.encode(description).tolist() + + collection.add( + embeddings=[embedding], + documents=[description], + metadatas=[{"video_filename": video_filename}], + ids=[video_file] + ) + + processed += 1 + + except Exception as e: + st.warning(f"Error processing {video_filename}: {e}") + + progress_bar.progress(1.0) + status_text.empty() + + st.success(f"✅ Processing Complete!") + col1, col2, col3 = st.columns(3) + col1.metric("Processed", processed, delta=processed) + col2.metric("Skipped", skipped) + col3.metric("Total in Database", collection.count()) + + st.session_state.database_loaded = True + st.balloons() + +with tab2: + st.header("Semantic Video Search") + + col1, col2, col3 = st.columns(3) + with col1: + st.markdown('

🔍 AI-Powered

Semantic Understanding

', unsafe_allow_html=True) + with col2: + st.markdown('

⚡ Lightning Fast

Vector Search

', unsafe_allow_html=True) + with col3: + st.markdown('

🎯 Accurate Results

Ranked by Relevance

', unsafe_allow_html=True) + + st.markdown("---") + + if not st.session_state.database_loaded: + if os.path.exists(DATABASE_PATH): + with st.spinner("Loading knowledge base..."): + collection, embedding_model = initialize_database() + if collection: + st.session_state.collection = collection + st.session_state.embedding_model = embedding_model + st.session_state.database_loaded = True + st.success(f"✅ Loaded {collection.count()} video descriptions") + + if not st.session_state.database_loaded: + st.warning("⚠️ Please process videos first") + else: + with st.expander("📋 View All Video Descriptions", expanded=False): + try: + collection = st.session_state.collection + all_data = collection.get(include=["documents", "metadatas"]) + + st.info(f"📊 Total videos: {len(all_data['documents'])}") + + for i, (doc, metadata) in enumerate(zip(all_data['documents'], all_data['metadatas'])): + st.markdown(f"**{i+1}. {metadata['video_filename']}**") + st.text(doc) + if i < len(all_data['documents']) - 1: + st.markdown("---") + except Exception as e: + st.error(f"Error loading descriptions: {e}") + + st.markdown("### 🔍 Enter Your Search Query") + query = st.text_input("", placeholder="e.g., person playing basketball, sunset over ocean, dog running in park", label_visibility="collapsed") + + col1, col2 = st.columns([3, 1]) + with col1: + st.caption("💡 Try: 'person', 'animal', 'outdoor scene', 'sports activity', or keywords from descriptions above") + with col2: + search_button = st.button("🔍 Search", type="primary", use_container_width=True) + + if search_button and query: + with st.spinner("Searching with AI..."): + try: + collection = st.session_state.collection + embedding_model = st.session_state.embedding_model + + query_embedding = embedding_model.encode(query).tolist() + + results = collection.query( + query_embeddings=[query_embedding], + n_results=3, + include=["documents", "metadatas", "distances"] + ) + + st.success("✅ Search Complete!") + st.balloons() + st.markdown(f"**Query:** *{query}*") + st.markdown("---") + + for i, (doc, metadata, distance) in enumerate(zip( + results['documents'][0], + results['metadatas'][0], + results['distances'][0] + )): + similarity_score = 1 - distance + video_path = results['ids'][0][i] + + if similarity_score > 0.7: + score_emoji = "🟢" + score_label = "Excellent Match" + elif similarity_score > 0.5: + score_emoji = "🟡" + score_label = "Good Match" + else: + score_emoji = "🟠" + score_label = "Moderate Match" + + with st.expander(f"{score_emoji} Result {i+1}: {metadata['video_filename']} - {score_label} ({similarity_score:.2%})", expanded=(i==0)): + col1, col2 = st.columns([3, 2]) + + with col1: + st.markdown("#### 📝 Video Description") + st.info(doc) + st.markdown("#### 📊 Similarity Metrics") + st.progress(similarity_score) + st.caption(f"Similarity Score: {similarity_score:.2%} | Distance: {distance:.3f}") + + with col2: + if os.path.exists(video_path): + st.markdown("#### 🎬 Video Preview") + st.video(video_path) + else: + st.warning("Video file not found") + + except Exception as e: + st.error(f"Search error: {e}") + +with tab3: + st.header("System Architecture") + + st.markdown(""" + ### Video RAG Pipeline on Intel Hardware + + This system demonstrates the power of Intel GPUs for AI workloads, using Ollama for efficient model inference. + """) + + # Clean architecture diagram using Streamlit columns + st.markdown("### 🔄 System Architecture Flow") + + st.info("**Video Processing Pipeline** - How videos are converted to searchable descriptions") + + cols = st.columns(7) + with cols[0]: + st.markdown("### 📹\n**Video Files**\nInput videos") + with cols[1]: + st.markdown("### ➡️") + with cols[2]: + st.markdown("### 🎞️\n**Extract Frame**\nOpenCV") + with cols[3]: + st.markdown("### ➡️") + with cols[4]: + st.markdown("### 🤖\n**Ollama Vision**\n:blue[Intel GPU]") + with cols[5]: + st.markdown("### ➡️") + with cols[6]: + st.markdown("### 📝\n**Description**\nText output") + + st.markdown("") + + cols2 = st.columns(5) + with cols2[0]: + st.markdown("### 📝\n**Description**") + with cols2[1]: + st.markdown("### ➡️") + with cols2[2]: + st.markdown("### 🔢\n**Embeddings**\n:blue[Transformers]") + with cols2[3]: + st.markdown("### ➡️") + with cols2[4]: + st.markdown("### 💾\n**ChromaDB**\nVector store") + + st.markdown("---") + + st.info("**Search Pipeline** - How queries find relevant videos") + + cols3 = st.columns(7) + with cols3[0]: + st.markdown("### 👤\n**User Query**\nNatural text") + with cols3[1]: + st.markdown("### ➡️") + with cols3[2]: + st.markdown("### 🔢\n**Embedding**\nVector form") + with cols3[3]: + st.markdown("### ➡️") + with cols3[4]: + st.markdown("### 🔍\n**Similarity**\n:blue[Cosine dist]") + with cols3[5]: + st.markdown("### ➡️") + with cols3[6]: + st.markdown("### 🎬\n**Results**\nMatched videos") + + st.markdown("---") + + # Architecture diagram + col1, col2 = st.columns(2) + + with col1: + st.markdown("### 🎯 Key Components") + st.markdown(""" + **1. Video Processing** + - Frame extraction with OpenCV + - Vision model inference on Intel GPU + - Description generation + + **2. Embedding Generation** + - Sentence Transformers + - 384-dimensional vectors + - Semantic representation + + **3. Vector Storage** + - ChromaDB persistent storage + - Cosine similarity metric + - Efficient retrieval + """) + + with col2: + st.markdown("### 🚀 Intel Optimizations") + st.markdown(""" + **Hardware Acceleration** + - Intel Arc™ Graphics + - Intel Core™ Ultra Processors + - Intel Iris® Xe Graphics + + **Performance Benefits** + - Fast inference times + - Efficient memory usage + - Local processing + + **Ollama Integration** + - Optimized for Intel hardware + - Easy model management + - Production-ready + """) + + st.markdown("---") + + # Technical details + st.markdown("### 🔧 Technical Stack") + + col1, col2, col3 = st.columns(3) + + with col1: + st.markdown(""" + **AI Models** + - Qwen 2.5 VL + - Llama 3.2 Vision + - MiniLM Embeddings + """) + + with col2: + st.markdown(""" + **Infrastructure** + - Ollama Runtime + - ChromaDB + - Streamlit UI + """) + + with col3: + st.markdown(""" + **Intel Hardware** + - Arc™ GPUs + - Core™ Ultra CPUs + - Iris® Xe Graphics + """) + +with tab4: + st.header("About This Demo") + + st.markdown(""" + ### 🎯 Video RAG with Ollama on Intel GPUs + + This application demonstrates **semantic video search** powered by **Ollama on Intel GPUs**. + It combines vision-language models, vector embeddings, and similarity search to enable + natural language queries over video content and detailed image understanding. + + ### 💡 Use Cases + + - **Video Libraries**: Quickly find specific content in large video collections + - **Content Management**: Search videos by describing what you're looking for + - **Surveillance**: Locate specific events or activities in footage + - **Education**: Find relevant video segments for learning materials + - **Media Production**: Search stock footage by description + + ### 🏗️ How It Works + + 1. **Extract**: Take representative frames from videos + 2. **Describe**: Use AI vision models to generate detailed descriptions + 3. **Embed**: Convert descriptions to semantic vectors + 4. **Store**: Save vectors in a searchable database + 5. **Query**: Search using natural language + 6. **Retrieve**: Find most similar videos by semantic meaning + + ### 🚀 Why Intel GPUs? + + - **Performance**: Fast inference for vision models + - **Efficiency**: Optimized power consumption + - **Accessibility**: Available on mainstream devices + - **Local Processing**: No cloud dependency + - **Cost Effective**: Use existing hardware + """) + + st.markdown("---") + + st.markdown("### 🛠️ Technology Stack") + + st.code(""" + # Core Technologies + - Ollama: Local LLM runtime optimized for Intel GPUs + - Qwen 2.5 VL: State-of-the-art vision-language model + - ChromaDB: Efficient vector database + - Sentence Transformers: Text embedding generation + - Streamlit: Interactive web interface + + # Intel Optimizations + - Hardware acceleration on Arc and Iris Xe + - Optimized inference on Core Ultra processors + - Local processing without cloud dependency + """, language="python") + + st.markdown("---") + + # System status + st.markdown("### 📡 System Status") + + col1, col2, col3 = st.columns(3) + + with col1: + try: + ollama.list() + st.success("✅ Ollama Connected") + except: + st.error("❌ Ollama Offline") + + with col2: + if os.path.exists(DATABASE_PATH): + st.success(f"✅ Database Active") + else: + st.info("💾 Database Not Created") + + with col3: + if st.session_state.database_loaded: + st.success(f"✅ {st.session_state.collection.count()} Videos Indexed") + else: + st.info("⏳ Awaiting Processing") + +# Footer +st.markdown("---") +st.markdown( + '
' + '

🎥 Video RAG Demo | Powered by Ollama on ' + 'Intel Arc Graphics and Intel Core Ultra Processors

' + '
', + unsafe_allow_html=True +) diff --git a/Video-Description-Generation-Query-Retrieval/st_video_rag_demo_final.py b/Video-Description-Generation-Query-Retrieval/st_video_rag_demo_final.py new file mode 100644 index 0000000..293bb0e --- /dev/null +++ b/Video-Description-Generation-Query-Retrieval/st_video_rag_demo_final.py @@ -0,0 +1,701 @@ +import os +import base64 +import shutil +import logging +import chromadb +import warnings +import ollama +import cv2 +import streamlit as st +from sentence_transformers import SentenceTransformer + +warnings.filterwarnings("ignore") +logging.basicConfig(level=logging.INFO) + +# Configuration +OLLAMA_BASE_URL = "http://localhost:11434" +DATABASE_PATH = "./Video_descriptions_database_ollama" +COLLECTION_NAME = "Video_descriptions_ollama" +EMBEDDING_MODEL = "all-MiniLM-L6-v2" + +# Page config +st.set_page_config( + page_title="Video RAG with Ollama", + layout="wide", + initial_sidebar_state="expanded" +) + +# Custom CSS for better styling +st.markdown(""" + +""", unsafe_allow_html=True) + +st.markdown('
🎥 Video RAG: Semantic Video Search
', unsafe_allow_html=True) +st.markdown('
Powered by Ollama on Intel® Core™ Ultra Processors
', unsafe_allow_html=True) + +# Initialize session state +if "collection" not in st.session_state: + st.session_state.collection = None +if "embedding_model" not in st.session_state: + st.session_state.embedding_model = None +if "video_files" not in st.session_state: + st.session_state.video_files = [] +if "database_loaded" not in st.session_state: + st.session_state.database_loaded = False + +# Sidebar configuration +with st.sidebar: + st.markdown("### 🎥 Video RAG") + st.markdown("---") + + st.header("⚙️ Configuration") + + # Model selection + try: + response = ollama.list() + all_models = [model.model for model in response.models] + + if not all_models: + st.error("⚠️ No models found! Please pull a model:") + st.code("ollama pull llava", language="bash") + all_models = ["llava"] + + selected_model = st.selectbox( + "🤖 Model", + all_models, + key="vision_model" + ) + + model_lower = selected_model.lower() + vision_patterns = ['llava', 'llama3.2-vision', 'minicpm-v', 'qwen', 'cogvlm', 'bakllava'] + + if any(pattern in model_lower for pattern in vision_patterns): + if 'llava' in model_lower: + st.success("✅ Vision model") + elif 'llama3.2-vision' in model_lower: + st.info("💡 Vision model") + elif 'qwen' in model_lower: + st.success("✅ Vision model") + else: + st.info("💡 Vision model detected") + else: + st.warning("⚠️ This model may not support vision/images") + + except Exception as e: + st.warning(f"⚠️ Ollama not accessible: {e}") + st.info("Please ensure Ollama is running: `ollama serve`") + selected_model = "llava" + + max_tokens = 100 + temperature = 0.7 + + st.markdown("---") + + # Dataset configuration + st.subheader("📁 Dataset") + dataset_folder = st.text_input("Video Folder", "Step-Video-T2V-Eval") + max_videos = st.slider("Max Videos", 1, 128, 20) + + st.markdown("---") + + # Database info + st.subheader("🗄️ Database") + if os.path.exists(DATABASE_PATH): + st.success("✅ Database Ready") + if st.button("🔄 Reset Database", type="secondary"): + try: + shutil.rmtree(DATABASE_PATH) + st.success("Database reset!") + st.session_state.collection = None + st.session_state.database_loaded = False + st.rerun() + except Exception as e: + st.error(f"Error: {e}") + else: + st.info("💾 No database found") + + +def encode_video_frame(video_path, frame_time=2.0): + """Extract and encode a frame from video.""" + try: + cap = cv2.VideoCapture(video_path) + if not cap.isOpened(): + return None + + fps = cap.get(cv2.CAP_PROP_FPS) + frame_number = int(frame_time * fps) + cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number) + + ret, frame = cap.read() + cap.release() + + if not ret: + return None + + _, buffer = cv2.imencode('.jpg', frame) + frame_base64 = base64.b64encode(buffer).decode('utf-8') + return frame_base64 + except Exception as e: + logging.error(f"Frame encoding error: {e}") + return None + + +def generate_video_description_ollama(video_path, model, max_tokens=100, temperature=0.7): + """Generate video description using Ollama vision model.""" + try: + frame_base64 = encode_video_frame(video_path, frame_time=2.0) + + if not frame_base64: + st.warning(f"⚠️ Failed to extract frame from {os.path.basename(video_path)}") + return "Unable to process video" + + response = ollama.chat( + model=model, + messages=[{ + 'role': 'user', + 'content': 'Describe this video frame concisely for search. Include main subjects, actions, setting, and key visual details in 2-3 sentences.', + 'images': [frame_base64] + }], + options={ + 'num_predict': max_tokens, + 'temperature': temperature, + 'num_ctx': 2048, + 'num_gpu': 99 + } + ) + + message = response['message'] + description = message.get('content', '') + + if not description or len(description.strip()) == 0: + description = message.get('thinking', '') + + if not description or len(description.strip()) == 0: + st.error(f"❌ Empty response from model for {os.path.basename(video_path)}") + return "Empty response from model" + + st.success(f"✅ {os.path.basename(video_path)}: {description[:80]}...") + return description + except Exception as e: + logging.error(f"Description generation error: {e}") + st.error(f"❌ Error: {str(e)}") + return "Error generating description" + + +def get_video_paths(folder, max_count): + """Get video file paths from folder.""" + try: + video_extensions = ['.mp4', '.avi', '.mov', '.mkv', '.flv', '.wmv'] + video_files = [] + + folder_path = os.path.abspath(folder) + + for root, dirs, files in os.walk(folder_path): + video_files.extend([ + os.path.join(root, f) for f in files + if any(f.lower().endswith(ext) for ext in video_extensions) + ]) + + if len(video_files) > max_count: + video_files = video_files[:max_count] + + return video_files + except Exception as e: + logging.error(f"Error finding videos: {e}") + return [] + + +def initialize_database(): + """Initialize ChromaDB and embedding model.""" + try: + client = chromadb.PersistentClient(path=DATABASE_PATH) + collection = client.get_or_create_collection( + name=COLLECTION_NAME, + metadata={"hnsw:space": "cosine"} + ) + + embedding_model = SentenceTransformer(EMBEDDING_MODEL) + + return collection, embedding_model + except Exception as e: + error_msg = str(e) + + if "Could not connect to tenant" in error_msg or "default_tenant" in error_msg: + st.error(f"Database initialization error: {e}") + st.warning("⚠️ ChromaDB database is corrupted or incompatible.") + + if st.button("🔄 Reset Database", type="primary"): + try: + if os.path.exists(DATABASE_PATH): + shutil.rmtree(DATABASE_PATH) + st.success("✅ Database removed. Click 'Start Processing Videos' again.") + st.rerun() + except Exception as reset_error: + st.error(f"Failed to reset database: {reset_error}") + + st.info("💡 **To fix manually:**\n\n1. Close this app\n2. Delete folder: `Video_descriptions_database_ollama`\n3. Restart the app") + return None, None + else: + st.error(f"Database initialization error: {e}") + return None, None + + +def get_existing_descriptions(collection): + """Get existing descriptions from database.""" + try: + all_items = collection.get(include=["metadatas"]) + existing = {meta['video_filename']: True for meta in all_items['metadatas']} + return existing + except: + return {} + + +# Main tabs +tab1, tab2, tab3, tab4 = st.tabs(["🎬 Process Videos", "🔍 Search Videos", "📊 Architecture", "ℹ️ About"]) + +with tab1: + st.header("Process Videos and Build Knowledge Base") + + col1, col2, col3 = st.columns(3) + with col1: + st.markdown('

🤖 AI Model

Vision-Language Models

', unsafe_allow_html=True) + with col2: + st.markdown('

💾 Storage

ChromaDB Vector Store

', unsafe_allow_html=True) + with col3: + st.markdown('

🔍 Search

Semantic Similarity

', unsafe_allow_html=True) + + st.markdown("---") + + if st.button("🚀 Start Processing Videos", type="primary", use_container_width=True): + with st.spinner("Initializing AI models..."): + collection, embedding_model = initialize_database() + + if collection is None: + st.error("Failed to initialize database") + st.stop() + + st.session_state.collection = collection + st.session_state.embedding_model = embedding_model + + with st.spinner("Scanning for video files..."): + video_files = get_video_paths(dataset_folder, max_videos) + + if not video_files: + st.error(f"❌ No video files found in '{os.path.abspath(dataset_folder)}'") + st.info("💡 Supported: .mp4, .avi, .mov, .mkv, .flv, .wmv") + st.stop() + + st.session_state.video_files = video_files + st.success(f"✅ Found {len(video_files)} videos") + + existing = get_existing_descriptions(collection) + st.info(f"📚 Database contains {len(existing)} existing descriptions") + + progress_bar = st.progress(0) + status_text = st.empty() + + processed = 0 + skipped = 0 + + for idx, video_file in enumerate(video_files): + video_filename = os.path.basename(video_file) + progress = (idx + 1) / len(video_files) + progress_bar.progress(progress) + status_text.text(f"Processing {idx + 1}/{len(video_files)}: {video_filename}") + + if video_filename in existing: + skipped += 1 + continue + + try: + description = generate_video_description_ollama(video_file, selected_model, max_tokens, temperature) + embedding = embedding_model.encode(description).tolist() + + collection.add( + embeddings=[embedding], + documents=[description], + metadatas=[{"video_filename": video_filename}], + ids=[video_file] + ) + + processed += 1 + + except Exception as e: + st.warning(f"Error processing {video_filename}: {e}") + + progress_bar.progress(1.0) + status_text.empty() + + st.success(f"✅ Processing Complete!") + col1, col2, col3 = st.columns(3) + col1.metric("Processed", processed, delta=processed) + col2.metric("Skipped", skipped) + col3.metric("Total in Database", collection.count()) + + st.session_state.database_loaded = True + st.balloons() + +with tab2: + st.header("Semantic Video Search") + + col1, col2, col3 = st.columns(3) + with col1: + st.markdown('

🔍 AI-Powered

Semantic Understanding

', unsafe_allow_html=True) + with col2: + st.markdown('

⚡ Lightning Fast

Vector Search

', unsafe_allow_html=True) + with col3: + st.markdown('

🎯 Accurate Results

Ranked by Relevance

', unsafe_allow_html=True) + + st.markdown("---") + + if not st.session_state.database_loaded: + if os.path.exists(DATABASE_PATH): + with st.spinner("Loading knowledge base..."): + collection, embedding_model = initialize_database() + if collection: + st.session_state.collection = collection + st.session_state.embedding_model = embedding_model + st.session_state.database_loaded = True + st.success(f"✅ Loaded {collection.count()} video descriptions") + + if not st.session_state.database_loaded: + st.warning("⚠️ Please process videos first") + else: + with st.expander("📋 View All Video Descriptions", expanded=False): + try: + collection = st.session_state.collection + all_data = collection.get(include=["documents", "metadatas"]) + + st.info(f"📊 Total videos: {len(all_data['documents'])}") + + for i, (doc, metadata) in enumerate(zip(all_data['documents'], all_data['metadatas'])): + st.markdown(f"**{i+1}. {metadata['video_filename']}**") + st.text(doc) + if i < len(all_data['documents']) - 1: + st.markdown("---") + except Exception as e: + st.error(f"Error loading descriptions: {e}") + + st.markdown("### 🔍 Enter Your Search Query") + query = st.text_input("", placeholder="e.g., person playing basketball, sunset over ocean, dog running in park", label_visibility="collapsed") + + col1, col2 = st.columns([3, 1]) + with col1: + st.caption("💡 Try: 'person', 'animal', 'outdoor scene', 'sports activity', or keywords from descriptions above") + with col2: + search_button = st.button("🔍 Search", type="primary", use_container_width=True) + + if search_button and query: + with st.spinner("Searching with AI..."): + try: + collection = st.session_state.collection + embedding_model = st.session_state.embedding_model + + query_embedding = embedding_model.encode(query).tolist() + + results = collection.query( + query_embeddings=[query_embedding], + n_results=3, + include=["documents", "metadatas", "distances"] + ) + + st.success("✅ Search Complete!") + st.balloons() + st.markdown(f"**Query:** *{query}*") + st.markdown("---") + + for i, (doc, metadata, distance) in enumerate(zip( + results['documents'][0], + results['metadatas'][0], + results['distances'][0] + )): + similarity_score = 1 - distance + video_path = results['ids'][0][i] + + if similarity_score > 0.7: + score_emoji = "🟢" + score_label = "Excellent Match" + elif similarity_score > 0.5: + score_emoji = "🟡" + score_label = "Good Match" + else: + score_emoji = "🟠" + score_label = "Moderate Match" + + with st.expander(f"{score_emoji} Result {i+1}: {metadata['video_filename']} - {score_label} ({similarity_score:.2%})", expanded=(i==0)): + col1, col2 = st.columns([3, 2]) + + with col1: + st.markdown("#### 📝 Video Description") + st.info(doc) + st.markdown("#### 📊 Similarity Metrics") + st.progress(similarity_score) + st.caption(f"Similarity Score: {similarity_score:.2%} | Distance: {distance:.3f}") + + with col2: + if os.path.exists(video_path): + st.markdown("#### 🎬 Video Preview") + st.video(video_path) + else: + st.warning("Video file not found") + + except Exception as e: + st.error(f"Search error: {e}") + +with tab3: + st.header("System Architecture") + + st.markdown(""" + ### Video RAG Pipeline + + This system demonstrates semantic video search using vision-language models and vector embeddings. + """) + + st.markdown("### 🔄 System Architecture Flow") + + st.info("**Video Processing Pipeline** - How videos are converted to searchable descriptions") + + cols = st.columns(7) + with cols[0]: + st.markdown("### 📹\n**Video Files**\nInput videos") + with cols[1]: + st.markdown("### ➡️") + with cols[2]: + st.markdown("### 🎞️\n**Extract Frame**\nOpenCV") + with cols[3]: + st.markdown("### ➡️") + with cols[4]: + st.markdown("### 🤖\n**Ollama Vision**\nModel inference") + with cols[5]: + st.markdown("### ➡️") + with cols[6]: + st.markdown("### 📝\n**Description**\nText output") + + st.markdown("") + + cols2 = st.columns(5) + with cols2[0]: + st.markdown("### 📝\n**Description**") + with cols2[1]: + st.markdown("### ➡️") + with cols2[2]: + st.markdown("### 🔢\n**Embeddings**\nTransformers") + with cols2[3]: + st.markdown("### ➡️") + with cols2[4]: + st.markdown("### 💾\n**ChromaDB**\nVector store") + + st.markdown("---") + + st.info("**Search Pipeline** - How queries find relevant videos") + + cols3 = st.columns(7) + with cols3[0]: + st.markdown("### 👤\n**User Query**\nNatural text") + with cols3[1]: + st.markdown("### ➡️") + with cols3[2]: + st.markdown("### 🔢\n**Embedding**\nVector form") + with cols3[3]: + st.markdown("### ➡️") + with cols3[4]: + st.markdown("### 🔍\n**Similarity**\nCosine dist") + with cols3[5]: + st.markdown("### ➡️") + with cols3[6]: + st.markdown("### 🎬\n**Results**\nMatched videos") + + st.markdown("---") + + col1, col2 = st.columns(2) + + with col1: + st.markdown("### 🎯 Key Components") + st.markdown(""" + **1. Video Processing** + - Frame extraction with OpenCV + - Vision model inference + - Description generation + + **2. Embedding Generation** + - Sentence Transformers + - 384-dimensional vectors + - Semantic representation + + **3. Vector Storage** + - ChromaDB persistent storage + - Cosine similarity metric + - Efficient retrieval + """) + + with col2: + st.markdown("### 🚀 Features") + st.markdown(""" + **Performance** + - Fast inference times + - Efficient memory usage + - Local processing + + **Capabilities** + - Natural language search + - Semantic understanding + - Multi-modal analysis + + **Integration** + - Ollama runtime + - Easy model management + - Production-ready + """) + + st.markdown("---") + + st.markdown("### 🔧 Technical Stack") + + col1, col2, col3 = st.columns(3) + + with col1: + st.markdown(""" + **AI Models** + - Qwen Vision-Language + - Llama Vision + - MiniLM Embeddings + """) + + with col2: + st.markdown(""" + **Infrastructure** + - Ollama Runtime + - ChromaDB + - Streamlit UI + """) + + with col3: + st.markdown(""" + **Processing** + - OpenCV + - Sentence Transformers + - Python Backend + """) + +with tab4: + st.header("About This Demo") + + st.markdown(""" + ### 🎯 Video RAG with Ollama + + This application demonstrates **semantic video search** powered by **Ollama**. + It combines vision-language models, vector embeddings, and similarity search to enable + natural language queries over video content. + + ### 💡 Use Cases + + - **Video Libraries**: Quickly find specific content in large video collections + - **Content Management**: Search videos by describing what you're looking for + - **Surveillance**: Locate specific events or activities in footage + - **Education**: Find relevant video segments for learning materials + - **Media Production**: Search stock footage by description + + ### 🏗️ How It Works + + 1. **Extract**: Take representative frames from videos + 2. **Describe**: Use AI vision models to generate detailed descriptions + 3. **Embed**: Convert descriptions to semantic vectors + 4. **Store**: Save vectors in a searchable database + 5. **Query**: Search using natural language + 6. **Retrieve**: Find most similar videos by semantic meaning + """) + + st.markdown("---") + + st.markdown("### 🛠️ Technology Stack") + + st.code(""" + # Core Technologies + - Ollama: Local LLM runtime + - Vision-Language Models: Qwen, Llama, etc. + - ChromaDB: Efficient vector database + - Sentence Transformers: Text embedding generation + - Streamlit: Interactive web interface + - OpenCV: Video frame extraction + """, language="python") + + st.markdown("---") + + st.markdown("### 📡 System Status") + + col1, col2, col3 = st.columns(3) + + with col1: + try: + ollama.list() + st.success("✅ Ollama Connected") + except: + st.error("❌ Ollama Offline") + + with col2: + if os.path.exists(DATABASE_PATH): + st.success(f"✅ Database Active") + else: + st.info("💾 Database Not Created") + + with col3: + if st.session_state.database_loaded: + st.success(f"✅ {st.session_state.collection.count()} Videos Indexed") + else: + st.info("⏳ Awaiting Processing") + +# Footer +st.markdown("---") +st.markdown( + '
' + '

🎥 Video RAG Demo | Powered by Ollama

' + '
', + unsafe_allow_html=True +) diff --git a/Video-Description-Generation-Query-Retrieval/uv.lock b/Video-Description-Generation-Query-Retrieval/uv.lock index 822c84a..8dde654 100644 --- a/Video-Description-Generation-Query-Retrieval/uv.lock +++ b/Video-Description-Generation-Query-Retrieval/uv.lock @@ -1,13 +1,25 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.12.0" resolution-markers = [ - "python_full_version >= '3.13' and sys_platform == 'linux'", - "python_full_version < '3.13' and sys_platform == 'linux'", - "python_full_version >= '3.13' and sys_platform == 'win32'", - "python_full_version < '3.13' and sys_platform == 'win32'", - "python_full_version >= '3.13' and sys_platform != 'linux' and sys_platform != 'win32'", - "python_full_version < '3.13' and sys_platform != 'linux' and sys_platform != 'win32'", + "python_full_version >= '3.13'", + "python_full_version < '3.13'", +] + +[[package]] +name = "altair" +version = "5.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "narwhals" }, + { name = "packaging" }, + { name = "typing-extensions", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/b1/f2969c7bdb8ad8bbdda031687defdce2c19afba2aa2c8e1d2a17f78376d8/altair-5.5.0.tar.gz", hash = "sha256:d960ebe6178c56de3855a68c47b516be38640b73fb3b5111c2a9ca90546dd73d", size = 705305, upload-time = "2024-11-23T23:39:58.542Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/f3/0b6ced594e51cc95d8c1fc1640d3623770d01e4969d29c0bd09945fafefa/altair-5.5.0-py3-none-any.whl", hash = "sha256:91a310b926508d560fe0148d02a194f38b824122641ef528113d029fcd129f8c", size = 731200, upload-time = "2024-11-23T23:39:56.4Z" }, ] [[package]] @@ -115,32 +127,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, ] -[[package]] -name = "av" -version = "14.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/86/f6/0b473dab52dfdea05f28f3578b1c56b6c796ce85e76951bab7c4e38d5a74/av-14.4.0.tar.gz", hash = "sha256:3ecbf803a7fdf67229c0edada0830d6bfaea4d10bfb24f0c3f4e607cd1064b42", size = 3892203, upload-time = "2025-05-16T19:13:35.737Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/75/b8641653780336c90ba89e5352cac0afa6256a86a150c7703c0b38851c6d/av-14.4.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:a53e682b239dd23b4e3bc9568cfb1168fc629ab01925fdb2e7556eb426339e94", size = 19954125, upload-time = "2025-05-16T19:09:54.909Z" }, - { url = "https://files.pythonhosted.org/packages/99/e6/37fe6fa5853a48d54d749526365780a63a4bc530be6abf2115e3a21e292a/av-14.4.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:5aa0b901751a32703fa938d2155d56ce3faf3630e4a48d238b35d2f7e49e5395", size = 23751479, upload-time = "2025-05-16T19:09:57.113Z" }, - { url = "https://files.pythonhosted.org/packages/f7/75/9a5f0e6bda5f513b62bafd1cff2b495441a8b07ab7fb7b8e62f0c0d1683f/av-14.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3b316fed3597675fe2aacfed34e25fc9d5bb0196dc8c0b014ae5ed4adda48de", size = 33801401, upload-time = "2025-05-16T19:09:59.479Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c9/e4df32a2ad1cb7f3a112d0ed610c5e43c89da80b63c60d60e3dc23793ec0/av-14.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a587b5c5014c3c0e16143a0f8d99874e46b5d0c50db6111aa0b54206b5687c81", size = 32364330, upload-time = "2025-05-16T19:10:02.111Z" }, - { url = "https://files.pythonhosted.org/packages/ca/f0/64e7444a41817fde49a07d0239c033f7e9280bec4a4bb4784f5c79af95e6/av-14.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d53f75e8ac1ec8877a551c0db32a83c0aaeae719d05285281eaaba211bbc30", size = 35519508, upload-time = "2025-05-16T19:10:05.008Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a8/a370099daa9033a3b6f9b9bd815304b3d8396907a14d09845f27467ba138/av-14.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c8558cfde79dd8fc92d97c70e0f0fa8c94c7a66f68ae73afdf58598f0fe5e10d", size = 36448593, upload-time = "2025-05-16T19:10:07.887Z" }, - { url = "https://files.pythonhosted.org/packages/27/bb/edb6ceff8fa7259cb6330c51dbfbc98dd1912bd6eb5f7bc05a4bb14a9d6e/av-14.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:455b6410dea0ab2d30234ffb28df7d62ca3cdf10708528e247bec3a4cdcced09", size = 34701485, upload-time = "2025-05-16T19:10:10.886Z" }, - { url = "https://files.pythonhosted.org/packages/a7/8a/957da1f581aa1faa9a5dfa8b47ca955edb47f2b76b949950933b457bfa1d/av-14.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1661efbe9d975f927b8512d654704223d936f39016fad2ddab00aee7c40f412c", size = 37521981, upload-time = "2025-05-16T19:10:13.678Z" }, - { url = "https://files.pythonhosted.org/packages/28/76/3f1cf0568592f100fd68eb40ed8c491ce95ca3c1378cc2d4c1f6d1bd295d/av-14.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:fbbeef1f421a3461086853d6464ad5526b56ffe8ccb0ab3fd0a1f121dfbf26ad", size = 27925944, upload-time = "2025-05-16T19:10:16.485Z" }, - { url = "https://files.pythonhosted.org/packages/12/4c/b0205f77352312ff457ecdf31723dbf4403b7a03fc1659075d6d32f23ef7/av-14.4.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:3d2aea7c602b105363903e4017103bc4b60336e7aff80e1c22e8b4ec09fd125f", size = 19917341, upload-time = "2025-05-16T19:10:18.826Z" }, - { url = "https://files.pythonhosted.org/packages/e1/c4/9e783bd7d47828e9c67f9c773c99de45c5ae01b3e942f1abf6cbaf530267/av-14.4.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:38c18f036aeb6dc9abf5e867d998c867f9ec93a5f722b60721fdffc123bbb2ae", size = 23715363, upload-time = "2025-05-16T19:10:21.42Z" }, - { url = "https://files.pythonhosted.org/packages/b5/26/b2b406a676864d06b1c591205782d8527e7c99e5bc51a09862c3576e0087/av-14.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58c1e18c8be73b6eada2d9ec397852ec74ebe51938451bdf83644a807189d6c8", size = 33496968, upload-time = "2025-05-16T19:10:24.178Z" }, - { url = "https://files.pythonhosted.org/packages/89/09/0a032bbe30c7049fca243ec8cf01f4be49dd6e7f7b9c3c7f0cc13f83c9d3/av-14.4.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4c32ff03a357feb030634f093089a73cb474b04efe7fbfba31f229cb2fab115", size = 32075498, upload-time = "2025-05-16T19:10:27.384Z" }, - { url = "https://files.pythonhosted.org/packages/0b/1f/0fee20f74c1f48086366e59dbd37fa0684cd0f3c782a65cbb719d26c7acd/av-14.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af31d16ae25964a6a02e09cc132b9decd5ee493c5dcb21bcdf0d71b2d6adbd59", size = 35224910, upload-time = "2025-05-16T19:10:30.104Z" }, - { url = "https://files.pythonhosted.org/packages/9e/19/1c4a201c75a2a431a85a43fd15d1fad55a28c22d596461d861c8d70f9b92/av-14.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9fb297009e528f4851d25f3bb2781b2db18b59b10aed10240e947b77c582fb7", size = 36172918, upload-time = "2025-05-16T19:10:32.789Z" }, - { url = "https://files.pythonhosted.org/packages/00/48/26b7e5d911c807f5f017a285362470ba16f44e8ea46f8b09ab5e348dd15b/av-14.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:573314cb9eafec2827dc98c416c965330dc7508193adbccd281700d8673b9f0a", size = 34414492, upload-time = "2025-05-16T19:10:36.023Z" }, - { url = "https://files.pythonhosted.org/packages/6d/26/2f4badfa5b5b7b8f5f83d562b143a83ed940fa458eea4cad495ce95c9741/av-14.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f82ab27ee57c3b80eb50a5293222307dfdc02f810ea41119078cfc85ea3cf9a8", size = 37245826, upload-time = "2025-05-16T19:10:39.562Z" }, - { url = "https://files.pythonhosted.org/packages/f4/02/88dbb6f5a05998b730d2e695b05060297af127ac4250efbe0739daa446d5/av-14.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f682003bbcaac620b52f68ff0e85830fff165dea53949e217483a615993ca20", size = 27898395, upload-time = "2025-05-16T19:13:02.653Z" }, -] - [[package]] name = "babel" version = "2.17.0" @@ -239,12 +225,21 @@ css = [ { name = "tinycss2" }, ] +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, +] + [[package]] name = "build" version = "1.2.2.post1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "os_name == 'nt' and sys_platform != 'linux'" }, + { name = "colorama", marker = "os_name == 'nt'" }, { name = "packaging" }, { name = "pyproject-hooks" }, ] @@ -452,18 +447,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, ] -[[package]] -name = "decord" -version = "0.6.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/79/936af42edf90a7bd4e41a6cac89c913d4b47fa48a26b042d5129a9242ee3/decord-0.6.0-py3-none-manylinux2010_x86_64.whl", hash = "sha256:51997f20be8958e23b7c4061ba45d0efcd86bffd5fe81c695d0befee0d442976", size = 13602299, upload-time = "2021-06-14T21:30:55.486Z" }, - { url = "https://files.pythonhosted.org/packages/6c/be/e15b5b866da452e62635a7b27513f31cb581fa2ea9cc9b768b535d62a955/decord-0.6.0-py3-none-win_amd64.whl", hash = "sha256:02665d7c4f1193a330205a791bc128f7e108eb6ae5b67144437a02f700943bad", size = 24733380, upload-time = "2021-06-14T21:30:57.766Z" }, -] - [[package]] name = "defusedxml" version = "0.7.1" @@ -557,6 +540,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bb/61/78c7b3851add1481b048b5fdc29067397a1784e2910592bc81bb3f608635/fsspec-2025.5.1-py3-none-any.whl", hash = "sha256:24d3a2e663d5fc735ab256263c4075f374a174c3410c0b25e5bd1970bceaa462", size = 199052, upload-time = "2025-05-24T12:03:21.66Z" }, ] +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, +] + +[[package]] +name = "gitpython" +version = "3.1.45" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9a/c8/dd58967d119baab745caec2f9d853297cec1989ec1d63f677d3880632b88/gitpython-3.1.45.tar.gz", hash = "sha256:85b0ee964ceddf211c41b9f27a49086010a190fd8132a24e21f362a4b36a791c", size = 215076, upload-time = "2025-07-24T03:45:54.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77", size = 208168, upload-time = "2025-07-24T03:45:52.517Z" }, +] + [[package]] name = "google-auth" version = "2.40.2" @@ -704,11 +711,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/fb/5307bd3612eb0f0e62c3a916ae531d3a31e58fb5c82b58e3ebf7fd6f47a1/huggingface_hub-0.33.1-py3-none-any.whl", hash = "sha256:ec8d7444628210c0ba27e968e3c4c973032d44dcea59ca0d78ef3f612196f095", size = 515377, upload-time = "2025-06-25T12:02:55.611Z" }, ] -[package.optional-dependencies] -hf-xet = [ - { name = "hf-xet" }, -] - [[package]] name = "humanfriendly" version = "10.0" @@ -751,127 +753,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, ] -[[package]] -name = "intel-cmplr-lib-rt" -version = "2025.0.4" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13' and sys_platform == 'linux'", - "python_full_version < '3.13' and sys_platform == 'linux'", -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/80/2a10995012a5d80cca2d8cac5d3a763116f1d57c272ee4f07ca94ba63a1a/intel_cmplr_lib_rt-2025.0.4-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:440cf7cad2ed16f6a0a4589b03447b2d4fdae4f630ea26a4510e1cf5b40b1f1d", size = 45884211, upload-time = "2024-12-12T12:02:44.312Z" }, -] - -[[package]] -name = "intel-cmplr-lib-rt" -version = "2025.0.5" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13' and sys_platform == 'win32'", - "python_full_version < '3.13' and sys_platform == 'win32'", -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/16/85f1f3c1ab200d369b9774b1b6121d2790c8b8647a135882106bcd3aadf9/intel_cmplr_lib_rt-2025.0.5-py2.py3-none-win_amd64.whl", hash = "sha256:928395dc2dc6e423439ff72e52cd02fc5e79686d5b14ce20f58461d8ed7d950a", size = 17271117, upload-time = "2025-02-11T10:59:27.047Z" }, -] - -[[package]] -name = "intel-cmplr-lib-ur" -version = "2025.0.4" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13' and sys_platform == 'linux'", - "python_full_version < '3.13' and sys_platform == 'linux'", -] -dependencies = [ - { name = "umf", marker = "sys_platform == 'linux'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/d0/0d376a3eb3d5f8d0c0dd828a9f889e485d8ded1f7d501976c72916826cef/intel_cmplr_lib_ur-2025.0.4-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:d36d586721f0cb87b051aca1bf3f653ccee7960f15efb0132e5482d95c5b2c9b", size = 25158275, upload-time = "2024-12-12T12:03:36.653Z" }, -] - -[[package]] -name = "intel-cmplr-lib-ur" -version = "2025.0.5" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13' and sys_platform == 'win32'", - "python_full_version < '3.13' and sys_platform == 'win32'", -] -dependencies = [ - { name = "umf", marker = "sys_platform == 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/c1/23e258d315f4b39cd923236e6eba90d25f936968be7a0bdfc46b079cef25/intel_cmplr_lib_ur-2025.0.5-py2.py3-none-win_amd64.whl", hash = "sha256:d792c1cafada93aee1ab387e85ecd906a6bb0c0fc5d5fb9b2986c1dcaf301aae", size = 1163425, upload-time = "2025-02-11T10:59:31.57Z" }, -] - -[[package]] -name = "intel-cmplr-lic-rt" -version = "2025.0.4" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13' and sys_platform == 'linux'", - "python_full_version < '3.13' and sys_platform == 'linux'", -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/18/77cc45b2e48f35c126f478116b65cad1025cd89ab58038a93fc2ce03a6e9/intel_cmplr_lic_rt-2025.0.4-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:943d03ef2028cdb33140c84bad35b45d02186617ac0cd15849d179764132408a", size = 18913, upload-time = "2024-12-12T12:02:36.833Z" }, -] - -[[package]] -name = "intel-cmplr-lic-rt" -version = "2025.0.5" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13' and sys_platform == 'win32'", - "python_full_version < '3.13' and sys_platform == 'win32'", -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/83/b9/7ac88e49a14026446953c0d2cdf32198b043a10f0b43ad02e36c7b273501/intel_cmplr_lic_rt-2025.0.5-py2.py3-none-win_amd64.whl", hash = "sha256:2a37b5be844790664b7bf67aef33058103368face8ce1ff72643d6e2ffc3efbf", size = 49547, upload-time = "2025-02-11T10:59:46.543Z" }, -] - -[[package]] -name = "intel-pti" -version = "0.10.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/f2/313e46be14a27d4fb6eaab683a0c7589b0227549e2bf596fa6ccc0df8f8f/intel_pti-0.10.1-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:7e12361e8ac18996258c9d7f9ee831372c1c1c85028fa8502fb257e1dadf1de2", size = 678688, upload-time = "2025-02-20T11:29:43.507Z" }, - { url = "https://files.pythonhosted.org/packages/da/49/ac4c5413ae249cc79c0f04d926942a2802d3e5b6186d4bfe67db841f53f9/intel_pti-0.10.1-py2.py3-none-win_amd64.whl", hash = "sha256:bf9418aff05dd6807f3130eaae874ab2c5f79bcbfa1eaae7ba9b5c8ff8290038", size = 437518, upload-time = "2025-02-20T11:29:40.533Z" }, -] - -[[package]] -name = "intel-sycl-rt" -version = "2025.0.4" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13' and sys_platform == 'linux'", - "python_full_version < '3.13' and sys_platform == 'linux'", -] -dependencies = [ - { name = "intel-cmplr-lib-rt", version = "2025.0.4", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'linux'" }, - { name = "intel-cmplr-lib-ur", version = "2025.0.4", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'linux'" }, - { name = "intel-cmplr-lic-rt", version = "2025.0.4", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'linux'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/2b/3b99709cf8dd3304d05b853797a6fa962394ccf520536dc8e5eb9d3d9b1f/intel_sycl_rt-2025.0.4-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:85c5fd6029f62e8361af1d9ddb0f6c6f9cf4912bbe9ad684f89e9e5842128879", size = 12356130, upload-time = "2024-12-12T12:03:18.87Z" }, -] - -[[package]] -name = "intel-sycl-rt" -version = "2025.0.5" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13' and sys_platform == 'win32'", - "python_full_version < '3.13' and sys_platform == 'win32'", -] -dependencies = [ - { name = "intel-cmplr-lib-rt", version = "2025.0.5", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'win32'" }, - { name = "intel-cmplr-lib-ur", version = "2025.0.5", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'win32'" }, - { name = "intel-cmplr-lic-rt", version = "2025.0.5", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/11/46cb85d052bf13153f2cb3994dbfda6116a06bff55d209d04b3f424d94d6/intel_sycl_rt-2025.0.5-py2.py3-none-win_amd64.whl", hash = "sha256:10a322b5bb9a180cbeea45fe2c8603bff55f4dedf6302fff7f66dafc191ff580", size = 10786985, upload-time = "2025-02-11T10:59:35.055Z" }, -] - [[package]] name = "ipykernel" version = "6.29.5" @@ -1161,7 +1042,7 @@ dependencies = [ { name = "overrides" }, { name = "packaging" }, { name = "prometheus-client" }, - { name = "pywinpty", marker = "os_name == 'nt' and sys_platform != 'linux'" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, { name = "pyzmq" }, { name = "send2trash" }, { name = "terminado" }, @@ -1179,7 +1060,7 @@ name = "jupyter-server-terminals" version = "0.5.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pywinpty", marker = "os_name == 'nt' and sys_platform != 'linux'" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, { name = "terminado" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fc/d5/562469734f476159e99a55426d697cbf8e7eb5efe89fb0e0b4f83a3d3459/jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269", size = 31430, upload-time = "2024-03-12T14:37:03.049Z" } @@ -1398,6 +1279,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, ] +[[package]] +name = "narwhals" +version = "2.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/93/f8/e1c28f24b641871c14ccae7ba6381f3c7827789a06e947ce975ae8a9075a/narwhals-2.12.0.tar.gz", hash = "sha256:075b6d56f3a222613793e025744b129439ecdff9292ea6615dd983af7ba6ea44", size = 590404, upload-time = "2025-11-17T10:53:28.381Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/9a/c6f79de7ba3a0a8473129936b7b90aa461d3d46fec6f1627672b1dccf4e9/narwhals-2.12.0-py3-none-any.whl", hash = "sha256:baeba5d448a30b04c299a696bd9ee5ff73e4742143e06c49ca316b46539a7cbb", size = 425014, upload-time = "2025-11-17T10:53:26.65Z" }, +] + [[package]] name = "nbclient" version = "0.10.2" @@ -1537,6 +1427,139 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, ] +[[package]] +name = "nvidia-cublas-cu12" +version = "12.6.4.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/eb/ff4b8c503fa1f1796679dce648854d58751982426e4e4b37d6fce49d259c/nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08ed2686e9875d01b58e3cb379c6896df8e76c75e0d4a7f7dace3d7b6d9ef8eb", size = 393138322, upload-time = "2024-11-20T17:40:25.65Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.6.80" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/60/7b6497946d74bcf1de852a21824d63baad12cd417db4195fc1bfe59db953/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6768bad6cab4f19e8292125e5f1ac8aa7d1718704012a0e3272a6f61c4bce132", size = 8917980, upload-time = "2024-11-20T17:36:04.019Z" }, + { url = "https://files.pythonhosted.org/packages/a5/24/120ee57b218d9952c379d1e026c4479c9ece9997a4fb46303611ee48f038/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a3eff6cdfcc6a4c35db968a06fcadb061cbc7d6dde548609a941ff8701b98b73", size = 8917972, upload-time = "2024-10-01T16:58:06.036Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.6.77" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/2e/46030320b5a80661e88039f59060d1790298b4718944a65a7f2aeda3d9e9/nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:35b0cc6ee3a9636d5409133e79273ce1f3fd087abb0532d2d2e8fff1fe9efc53", size = 23650380, upload-time = "2024-10-01T17:00:14.643Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.6.77" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/23/e717c5ac26d26cf39a27fbc076240fad2e3b817e5889d671b67f4f9f49c5/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ba3b56a4f896141e25e19ab287cd71e52a6a0f4b29d0d31609f60e3b4d5219b7", size = 897690, upload-time = "2024-11-20T17:35:30.697Z" }, + { url = "https://files.pythonhosted.org/packages/f0/62/65c05e161eeddbafeca24dc461f47de550d9fa8a7e04eb213e32b55cfd99/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a84d15d5e1da416dd4774cb42edf5e954a3e60cc945698dc1d5be02321c44dc8", size = 897678, upload-time = "2024-10-01T16:57:33.821Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.5.1.17" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/78/4535c9c7f859a64781e43c969a3a7e84c54634e319a996d43ef32ce46f83/nvidia_cudnn_cu12-9.5.1.17-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:30ac3869f6db17d170e0e556dd6cc5eee02647abc31ca856634d5a40f82c15b2", size = 570988386, upload-time = "2024-10-25T19:54:26.39Z" }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/16/73727675941ab8e6ffd86ca3a4b7b47065edcca7a997920b831f8147c99d/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ccba62eb9cef5559abd5e0d54ceed2d9934030f51163df018532142a8ec533e5", size = 200221632, upload-time = "2024-11-20T17:41:32.357Z" }, + { url = "https://files.pythonhosted.org/packages/60/de/99ec247a07ea40c969d904fc14f3a356b3e2a704121675b75c366b694ee1/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.whl", hash = "sha256:768160ac89f6f7b459bee747e8d175dbf53619cfe74b2a5636264163138013ca", size = 200221622, upload-time = "2024-10-01T17:03:58.79Z" }, +] + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.11.1.6" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/66/cc9876340ac68ae71b15c743ddb13f8b30d5244af344ec8322b449e35426/nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc23469d1c7e52ce6c1d55253273d32c565dd22068647f3aa59b3c6b005bf159", size = 1142103, upload-time = "2024-11-20T17:42:11.83Z" }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.7.77" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/1b/44a01c4e70933637c93e6e1a8063d1e998b50213a6b65ac5a9169c47e98e/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a42cd1344297f70b9e39a1e4f467a4e1c10f1da54ff7a85c12197f6c652c8bdf", size = 56279010, upload-time = "2024-11-20T17:42:50.958Z" }, + { url = "https://files.pythonhosted.org/packages/4a/aa/2c7ff0b5ee02eaef890c0ce7d4f74bc30901871c5e45dee1ae6d0083cd80/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:99f1a32f1ac2bd134897fc7a203f779303261268a65762a623bf30cc9fe79117", size = 56279000, upload-time = "2024-10-01T17:04:45.274Z" }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cusparse-cu12" }, + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/6e/c2cf12c9ff8b872e92b4a5740701e51ff17689c4d726fca91875b07f655d/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9e49843a7707e42022babb9bcfa33c29857a93b88020c4e4434656a655b698c", size = 158229790, upload-time = "2024-11-20T17:43:43.211Z" }, + { url = "https://files.pythonhosted.org/packages/9f/81/baba53585da791d043c10084cf9553e074548408e04ae884cfe9193bd484/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6cf28f17f64107a0c4d7802be5ff5537b2130bfc112f25d5a30df227058ca0e6", size = 158229780, upload-time = "2024-10-01T17:05:39.875Z" }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/1e/b8b7c2f4099a37b96af5c9bb158632ea9e5d9d27d7391d7eb8fc45236674/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7556d9eca156e18184b94947ade0fba5bb47d69cec46bf8660fd2c71a4b48b73", size = 216561367, upload-time = "2024-11-20T17:44:54.824Z" }, + { url = "https://files.pythonhosted.org/packages/43/ac/64c4316ba163e8217a99680c7605f779accffc6a4bcd0c778c12948d3707/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:23749a6571191a215cb74d1cdbff4a86e7b19f1200c071b3fcf844a5bea23a2f", size = 216561357, upload-time = "2024-10-01T17:06:29.861Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/9a/72ef35b399b0e183bc2e8f6f558036922d453c4d8237dab26c666a04244b/nvidia_cusparselt_cu12-0.6.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e5c8a26c36445dd2e6812f1177978a24e2d37cacce7e090f297a688d1ec44f46", size = 156785796, upload-time = "2024-10-15T21:29:17.709Z" }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.26.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/ca/f42388aed0fddd64ade7493dbba36e1f534d4e6fdbdd355c6a90030ae028/nvidia_nccl_cu12-2.26.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:694cf3879a206553cc9d7dbda76b13efaf610fdb70a50cba303de1b0d1530ac6", size = 201319755, upload-time = "2025-03-13T00:29:55.296Z" }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.6.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/d7/c5383e47c7e9bf1c99d5bd2a8c935af2b6d705ad831a7ec5c97db4d82f4f/nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:eedc36df9e88b682efe4309aa16b5b4e78c2407eac59e8c10a6a47535164369a", size = 19744971, upload-time = "2024-11-20T17:46:53.366Z" }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.6.77" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/9a/fff8376f8e3d084cd1530e1ef7b879bb7d6d265620c95c1b322725c694f4/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b90bed3df379fa79afbd21be8e04a0314336b8ae16768b58f2d34cb1d04cd7d2", size = 89276, upload-time = "2024-11-20T17:38:27.621Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4e/0d0c945463719429b7bd21dece907ad0bde437a2ff12b9b12fee94722ab0/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6574241a3ec5fdc9334353ab8c479fe75841dbe8f4532a8fc97ce63503330ba1", size = 89265, upload-time = "2024-10-01T17:00:38.172Z" }, +] + [[package]] name = "oauthlib" version = "3.2.2" @@ -1546,6 +1569,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688, upload-time = "2022-10-17T20:04:24.037Z" }, ] +[[package]] +name = "ollama" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/5a/652dac4b7affc2b37b95386f8ae78f22808af09d720689e3d7a86b6ed98e/ollama-0.6.1.tar.gz", hash = "sha256:478c67546836430034b415ed64fa890fd3d1ff91781a9d548b3325274e69d7c6", size = 51620, upload-time = "2025-11-13T23:02:17.416Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/4f/4a617ee93d8208d2bcf26b2d8b9402ceaed03e3853c754940e2290fed063/ollama-0.6.1-py3-none-any.whl", hash = "sha256:fc4c984b345735c5486faeee67d8a265214a31cbb828167782dc642ce0a2bf8c", size = 14354, upload-time = "2025-11-13T23:02:16.292Z" }, +] + [[package]] name = "onnxruntime" version = "1.22.0" @@ -1571,6 +1607,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c3/16/873b955beda7bada5b0d798d3a601b2ff210e44ad5169f6d405b93892103/onnxruntime-1.22.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64845709f9e8a2809e8e009bc4c8f73b788cee9c6619b7d9930344eae4c9cd36", size = 16427482, upload-time = "2025-05-09T20:26:20.376Z" }, ] +[[package]] +name = "opencv-python" +version = "4.12.0.88" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/71/25c98e634b6bdeca4727c7f6d6927b056080668c5008ad3c8fc9e7f8f6ec/opencv-python-4.12.0.88.tar.gz", hash = "sha256:8b738389cede219405f6f3880b851efa3415ccd674752219377353f017d2994d", size = 95373294, upload-time = "2025-07-07T09:20:52.389Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/68/3da40142e7c21e9b1d4e7ddd6c58738feb013203e6e4b803d62cdd9eb96b/opencv_python-4.12.0.88-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:f9a1f08883257b95a5764bf517a32d75aec325319c8ed0f89739a57fae9e92a5", size = 37877727, upload-time = "2025-07-07T09:13:31.47Z" }, + { url = "https://files.pythonhosted.org/packages/33/7c/042abe49f58d6ee7e1028eefc3334d98ca69b030e3b567fe245a2b28ea6f/opencv_python-4.12.0.88-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:812eb116ad2b4de43ee116fcd8991c3a687f099ada0b04e68f64899c09448e81", size = 57326471, upload-time = "2025-07-07T09:13:41.26Z" }, + { url = "https://files.pythonhosted.org/packages/62/3a/440bd64736cf8116f01f3b7f9f2e111afb2e02beb2ccc08a6458114a6b5d/opencv_python-4.12.0.88-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:51fd981c7df6af3e8f70b1556696b05224c4e6b6777bdd2a46b3d4fb09de1a92", size = 45887139, upload-time = "2025-07-07T09:13:50.761Z" }, + { url = "https://files.pythonhosted.org/packages/68/1f/795e7f4aa2eacc59afa4fb61a2e35e510d06414dd5a802b51a012d691b37/opencv_python-4.12.0.88-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:092c16da4c5a163a818f120c22c5e4a2f96e0db4f24e659c701f1fe629a690f9", size = 67041680, upload-time = "2025-07-07T09:14:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/02/96/213fea371d3cb2f1d537612a105792aa0a6659fb2665b22cad709a75bd94/opencv_python-4.12.0.88-cp37-abi3-win32.whl", hash = "sha256:ff554d3f725b39878ac6a2e1fa232ec509c36130927afc18a1719ebf4fbf4357", size = 30284131, upload-time = "2025-07-07T09:14:08.819Z" }, + { url = "https://files.pythonhosted.org/packages/fa/80/eb88edc2e2b11cd2dd2e56f1c80b5784d11d6e6b7f04a1145df64df40065/opencv_python-4.12.0.88-cp37-abi3-win_amd64.whl", hash = "sha256:d98edb20aa932fd8ebd276a72627dad9dc097695b3d435a4257557bbb49a79d2", size = 39000307, upload-time = "2025-07-07T09:14:16.641Z" }, +] + [[package]] name = "opentelemetry-api" version = "1.33.1" @@ -1709,6 +1762,53 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] +[[package]] +name = "pandas" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, + { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, + { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, + { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, + { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, + { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, + { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, + { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, + { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, + { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, + { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, +] + [[package]] name = "pandocfilters" version = "1.5.1" @@ -1732,7 +1832,7 @@ name = "pexpect" version = "4.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ptyprocess", marker = "sys_platform != 'win32'" }, + { name = "ptyprocess" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } wheels = [ @@ -1873,6 +1973,35 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, ] +[[package]] +name = "pyarrow" +version = "21.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/c2/ea068b8f00905c06329a3dfcd40d0fcc2b7d0f2e355bdb25b65e0a0e4cd4/pyarrow-21.0.0.tar.gz", hash = "sha256:5051f2dccf0e283ff56335760cbc8622cf52264d67e359d5569541ac11b6d5bc", size = 1133487, upload-time = "2025-07-18T00:57:31.761Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/d4/d4f817b21aacc30195cf6a46ba041dd1be827efa4a623cc8bf39a1c2a0c0/pyarrow-21.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:3a302f0e0963db37e0a24a70c56cf91a4faa0bca51c23812279ca2e23481fccd", size = 31160305, upload-time = "2025-07-18T00:55:35.373Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9c/dcd38ce6e4b4d9a19e1d36914cb8e2b1da4e6003dd075474c4cfcdfe0601/pyarrow-21.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:b6b27cf01e243871390474a211a7922bfbe3bda21e39bc9160daf0da3fe48876", size = 32684264, upload-time = "2025-07-18T00:55:39.303Z" }, + { url = "https://files.pythonhosted.org/packages/4f/74/2a2d9f8d7a59b639523454bec12dba35ae3d0a07d8ab529dc0809f74b23c/pyarrow-21.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e72a8ec6b868e258a2cd2672d91f2860ad532d590ce94cdf7d5e7ec674ccf03d", size = 41108099, upload-time = "2025-07-18T00:55:42.889Z" }, + { url = "https://files.pythonhosted.org/packages/ad/90/2660332eeb31303c13b653ea566a9918484b6e4d6b9d2d46879a33ab0622/pyarrow-21.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b7ae0bbdc8c6674259b25bef5d2a1d6af5d39d7200c819cf99e07f7dfef1c51e", size = 42829529, upload-time = "2025-07-18T00:55:47.069Z" }, + { url = "https://files.pythonhosted.org/packages/33/27/1a93a25c92717f6aa0fca06eb4700860577d016cd3ae51aad0e0488ac899/pyarrow-21.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:58c30a1729f82d201627c173d91bd431db88ea74dcaa3885855bc6203e433b82", size = 43367883, upload-time = "2025-07-18T00:55:53.069Z" }, + { url = "https://files.pythonhosted.org/packages/05/d9/4d09d919f35d599bc05c6950095e358c3e15148ead26292dfca1fb659b0c/pyarrow-21.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:072116f65604b822a7f22945a7a6e581cfa28e3454fdcc6939d4ff6090126623", size = 45133802, upload-time = "2025-07-18T00:55:57.714Z" }, + { url = "https://files.pythonhosted.org/packages/71/30/f3795b6e192c3ab881325ffe172e526499eb3780e306a15103a2764916a2/pyarrow-21.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf56ec8b0a5c8c9d7021d6fd754e688104f9ebebf1bf4449613c9531f5346a18", size = 26203175, upload-time = "2025-07-18T00:56:01.364Z" }, + { url = "https://files.pythonhosted.org/packages/16/ca/c7eaa8e62db8fb37ce942b1ea0c6d7abfe3786ca193957afa25e71b81b66/pyarrow-21.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e99310a4ebd4479bcd1964dff9e14af33746300cb014aa4a3781738ac63baf4a", size = 31154306, upload-time = "2025-07-18T00:56:04.42Z" }, + { url = "https://files.pythonhosted.org/packages/ce/e8/e87d9e3b2489302b3a1aea709aaca4b781c5252fcb812a17ab6275a9a484/pyarrow-21.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d2fe8e7f3ce329a71b7ddd7498b3cfac0eeb200c2789bd840234f0dc271a8efe", size = 32680622, upload-time = "2025-07-18T00:56:07.505Z" }, + { url = "https://files.pythonhosted.org/packages/84/52/79095d73a742aa0aba370c7942b1b655f598069489ab387fe47261a849e1/pyarrow-21.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f522e5709379d72fb3da7785aa489ff0bb87448a9dc5a75f45763a795a089ebd", size = 41104094, upload-time = "2025-07-18T00:56:10.994Z" }, + { url = "https://files.pythonhosted.org/packages/89/4b/7782438b551dbb0468892a276b8c789b8bbdb25ea5c5eb27faadd753e037/pyarrow-21.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:69cbbdf0631396e9925e048cfa5bce4e8c3d3b41562bbd70c685a8eb53a91e61", size = 42825576, upload-time = "2025-07-18T00:56:15.569Z" }, + { url = "https://files.pythonhosted.org/packages/b3/62/0f29de6e0a1e33518dec92c65be0351d32d7ca351e51ec5f4f837a9aab91/pyarrow-21.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:731c7022587006b755d0bdb27626a1a3bb004bb56b11fb30d98b6c1b4718579d", size = 43368342, upload-time = "2025-07-18T00:56:19.531Z" }, + { url = "https://files.pythonhosted.org/packages/90/c7/0fa1f3f29cf75f339768cc698c8ad4ddd2481c1742e9741459911c9ac477/pyarrow-21.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc56bc708f2d8ac71bd1dcb927e458c93cec10b98eb4120206a4091db7b67b99", size = 45131218, upload-time = "2025-07-18T00:56:23.347Z" }, + { url = "https://files.pythonhosted.org/packages/01/63/581f2076465e67b23bc5a37d4a2abff8362d389d29d8105832e82c9c811c/pyarrow-21.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:186aa00bca62139f75b7de8420f745f2af12941595bbbfa7ed3870ff63e25636", size = 26087551, upload-time = "2025-07-18T00:56:26.758Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ab/357d0d9648bb8241ee7348e564f2479d206ebe6e1c47ac5027c2e31ecd39/pyarrow-21.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:a7a102574faa3f421141a64c10216e078df467ab9576684d5cd696952546e2da", size = 31290064, upload-time = "2025-07-18T00:56:30.214Z" }, + { url = "https://files.pythonhosted.org/packages/3f/8a/5685d62a990e4cac2043fc76b4661bf38d06efed55cf45a334b455bd2759/pyarrow-21.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:1e005378c4a2c6db3ada3ad4c217b381f6c886f0a80d6a316fe586b90f77efd7", size = 32727837, upload-time = "2025-07-18T00:56:33.935Z" }, + { url = "https://files.pythonhosted.org/packages/fc/de/c0828ee09525c2bafefd3e736a248ebe764d07d0fd762d4f0929dbc516c9/pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:65f8e85f79031449ec8706b74504a316805217b35b6099155dd7e227eef0d4b6", size = 41014158, upload-time = "2025-07-18T00:56:37.528Z" }, + { url = "https://files.pythonhosted.org/packages/6e/26/a2865c420c50b7a3748320b614f3484bfcde8347b2639b2b903b21ce6a72/pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3a81486adc665c7eb1a2bde0224cfca6ceaba344a82a971ef059678417880eb8", size = 42667885, upload-time = "2025-07-18T00:56:41.483Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f9/4ee798dc902533159250fb4321267730bc0a107d8c6889e07c3add4fe3a5/pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fc0d2f88b81dcf3ccf9a6ae17f89183762c8a94a5bdcfa09e05cfe413acf0503", size = 43276625, upload-time = "2025-07-18T00:56:48.002Z" }, + { url = "https://files.pythonhosted.org/packages/5a/da/e02544d6997037a4b0d22d8e5f66bc9315c3671371a8b18c79ade1cefe14/pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6299449adf89df38537837487a4f8d3bd91ec94354fdd2a7d30bc11c48ef6e79", size = 44951890, upload-time = "2025-07-18T00:56:52.568Z" }, + { url = "https://files.pythonhosted.org/packages/e5/4e/519c1bc1876625fe6b71e9a28287c43ec2f20f73c658b9ae1d485c0c206e/pyarrow-21.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:222c39e2c70113543982c6b34f3077962b44fca38c0bd9e68bb6781534425c10", size = 26371006, upload-time = "2025-07-18T00:56:56.379Z" }, +] + [[package]] name = "pyasn1" version = "0.6.1" @@ -2019,6 +2148,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, ] +[[package]] +name = "pydeck" +version = "0.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240, upload-time = "2024-05-10T15:36:21.153Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403, upload-time = "2024-05-10T15:36:17.36Z" }, +] + [[package]] name = "pygments" version = "2.19.1" @@ -2083,19 +2225,12 @@ wheels = [ ] [[package]] -name = "pytorch-triton-xpu" -version = "3.3.0" -source = { registry = "https://download.pytorch.org/whl/xpu" } -dependencies = [ - { name = "setuptools", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] +name = "pytz" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } wheels = [ - { url = "https://download.pytorch.org/whl/pytorch_triton_xpu-3.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:40f6fb65b345dc9a61813abe7ac9a585f2c9808f414d140cc2a5f11f53ee063c" }, - { url = "https://download.pytorch.org/whl/pytorch_triton_xpu-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:0dd07e6d5b872e42e48f5ee140e609d4554ca3cc509d5bf509ac232267cf358e" }, - { url = "https://download.pytorch.org/whl/pytorch_triton_xpu-3.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b22b4c02ec71b4bfc862ae3cdfd2871dc0b05d2b1802f5db2196e0f897d581e9" }, - { url = "https://download.pytorch.org/whl/pytorch_triton_xpu-3.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:a936a18182d8e065a9933afc9a3ebbffadd38604969f87c493831214539fc027" }, - { url = "https://download.pytorch.org/whl/pytorch_triton_xpu-3.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9821fe059de58e827ffc6aa10d69369b16c2f8c2a988b86bef9c2c6e396ab3aa" }, - { url = "https://download.pytorch.org/whl/pytorch_triton_xpu-3.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:07c18df9202c4dd30096ba22e36335cd5db827e83f110091b763fbd066498f3e" }, + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, ] [[package]] @@ -2189,26 +2324,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/05/4c/bf3cad0d64c3214ac881299c4562b815f05d503bccc513e3fd4fdc6f67e4/pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:26a2a7451606b87f67cdeca2c2789d86f605da08b4bd616b1a9981605ca3a364", size = 1395540, upload-time = "2025-04-04T12:04:30.562Z" }, ] -[[package]] -name = "qwen-vl-utils" -version = "0.0.11" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "av" }, - { name = "packaging" }, - { name = "pillow" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/9f/1229a40ebd49f689a0252144126f3865f31bb4151e942cf781a2936f0c4d/qwen_vl_utils-0.0.11.tar.gz", hash = "sha256:083ba1e5cfa5002165b1e3bddd4d6d26d1d6d34473884033ef12ae3fe8496cd5", size = 7924, upload-time = "2025-04-21T10:38:47.461Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/c2/ad7f93e1eea4ea0aefd1cc6fbe7a7095fd2f03a4d8fe2c3707e612b0866e/qwen_vl_utils-0.0.11-py3-none-any.whl", hash = "sha256:7fd5287ac04d6c1f01b93bf053b0be236a35149e414c9e864e3cc5bf2fe8cb7b", size = 7584, upload-time = "2025-04-21T10:38:45.595Z" }, -] - -[package.optional-dependencies] -decord = [ - { name = "decord" }, -] - [[package]] name = "referencing" version = "0.36.2" @@ -2490,8 +2605,7 @@ dependencies = [ { name = "pillow" }, { name = "scikit-learn" }, { name = "scipy" }, - { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "torch", version = "2.7.0+xpu", source = { registry = "https://download.pytorch.org/whl/xpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "torch" }, { name = "tqdm" }, { name = "transformers" }, { name = "typing-extensions" }, @@ -2528,6 +2642,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "smmap" +version = "5.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329, upload-time = "2025-01-02T07:14:40.909Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" }, +] + [[package]] name = "sniffio" version = "1.3.1" @@ -2561,24 +2684,44 @@ wheels = [ ] [[package]] -name = "sympy" -version = "1.14.0" +name = "streamlit" +version = "1.51.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "mpmath" }, + { name = "altair" }, + { name = "blinker" }, + { name = "cachetools" }, + { name = "click" }, + { name = "gitpython" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "protobuf" }, + { name = "pyarrow" }, + { name = "pydeck" }, + { name = "requests" }, + { name = "tenacity" }, + { name = "toml" }, + { name = "tornado" }, + { name = "typing-extensions" }, + { name = "watchdog", marker = "sys_platform != 'darwin'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +sdist = { url = "https://files.pythonhosted.org/packages/59/6d/327ddd5fc35fcf2aeecb4040668337f5565a1c6c95b1e892b8bfd4bb9031/streamlit-1.51.0.tar.gz", hash = "sha256:1e742a9c0b698f466c6f5bf58d333beda5a1fbe8de660743976791b5c1446ef6", size = 9742904, upload-time = "2025-10-29T17:07:39.082Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, + { url = "https://files.pythonhosted.org/packages/39/60/868371b6482ccd9ef423c6f62650066cf8271fdb2ee84f192695ad6b7a96/streamlit-1.51.0-py3-none-any.whl", hash = "sha256:4008b029f71401ce54946bb09a6a3e36f4f7652cbb48db701224557738cfda38", size = 10171702, upload-time = "2025-10-29T17:07:35.97Z" }, ] [[package]] -name = "tcmlib" -version = "1.2.0" +name = "sympy" +version = "1.14.0" source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/6e/e9544e6ddb3640e92d9937689e9e33018503ddbc069eeb2e6a7581870591/tcmlib-1.2.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:dee9a0665ffa0a74f0ec72844ef7d82e9178ebf6980a76ac65906303d5ed4d03", size = 4160997, upload-time = "2024-10-25T10:59:37.067Z" }, - { url = "https://files.pythonhosted.org/packages/5b/c8/c2a7bb169e21521a5bc82d955b30a064e266ab72485ac2dc33cd6ad153ea/tcmlib-1.2.0-py2.py3-none-win_amd64.whl", hash = "sha256:f751e005fd815ed0efeb87d4523f6a0938c5671e59ac935614f42d8ebb6489c9", size = 338379, upload-time = "2024-10-25T10:59:32.959Z" }, + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, ] [[package]] @@ -2596,7 +2739,7 @@ version = "0.18.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ptyprocess", marker = "os_name != 'nt'" }, - { name = "pywinpty", marker = "os_name == 'nt' and sys_platform != 'linux'" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, { name = "tornado" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701, upload-time = "2024-03-12T14:34:39.026Z" } @@ -2651,112 +2794,55 @@ wheels = [ ] [[package]] -name = "torch" -version = "2.7.0" +name = "toml" +version = "0.10.2" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13' and sys_platform != 'linux' and sys_platform != 'win32'", - "python_full_version < '3.13' and sys_platform != 'linux' and sys_platform != 'win32'", -] -dependencies = [ - { name = "filelock", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "fsspec", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "jinja2", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "networkx", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "setuptools", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "sympy", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "typing-extensions", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, -] +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/8d/b2939e5254be932db1a34b2bd099070c509e8887e0c5a90c498a917e4032/torch-2.7.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:30b7688a87239a7de83f269333651d8e582afffce6f591fff08c046f7787296e", size = 68574294, upload-time = "2025-04-23T14:34:47.098Z" }, - { url = "https://files.pythonhosted.org/packages/28/fd/74ba6fde80e2b9eef4237fe668ffae302c76f0e4221759949a632ca13afa/torch-2.7.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:edad98dddd82220465b106506bb91ee5ce32bd075cddbcf2b443dfaa2cbd83bf", size = 68856166, upload-time = "2025-04-23T14:34:04.012Z" }, - { url = "https://files.pythonhosted.org/packages/90/48/7e6477cf40d48cc0a61fa0d41ee9582b9a316b12772fcac17bc1a40178e7/torch-2.7.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:27f5007bdf45f7bb7af7f11d1828d5c2487e030690afb3d89a651fd7036a390e", size = 68575074, upload-time = "2025-04-23T14:32:38.136Z" }, + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, ] [[package]] name = "torch" -version = "2.7.0+xpu" -source = { registry = "https://download.pytorch.org/whl/xpu" } -resolution-markers = [ - "python_full_version >= '3.13' and sys_platform == 'linux'", - "python_full_version < '3.13' and sys_platform == 'linux'", - "python_full_version >= '3.13' and sys_platform == 'win32'", - "python_full_version < '3.13' and sys_platform == 'win32'", -] -dependencies = [ - { name = "filelock", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "fsspec", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "intel-cmplr-lib-rt", version = "2025.0.4", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'linux'" }, - { name = "intel-cmplr-lib-rt", version = "2025.0.5", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'win32'" }, - { name = "intel-cmplr-lib-ur", version = "2025.0.4", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'linux'" }, - { name = "intel-cmplr-lib-ur", version = "2025.0.5", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'win32'" }, - { name = "intel-cmplr-lic-rt", version = "2025.0.4", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'linux'" }, - { name = "intel-cmplr-lic-rt", version = "2025.0.5", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'win32'" }, - { name = "intel-pti", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "intel-sycl-rt", version = "2025.0.4", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'linux'" }, - { name = "intel-sycl-rt", version = "2025.0.5", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'win32'" }, - { name = "jinja2", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "networkx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pytorch-triton-xpu", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "setuptools", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "sympy", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tcmlib", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "umf", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -wheels = [ - { url = "https://download.pytorch.org/whl/xpu/torch-2.7.0%2Bxpu-cp312-cp312-linux_x86_64.whl", hash = "sha256:c806d44aa2ca5d225629f6fbc6c994d5deaac2d2cde449195bc8e3522ddd219a" }, - { url = "https://download.pytorch.org/whl/xpu/torch-2.7.0%2Bxpu-cp312-cp312-win_amd64.whl", hash = "sha256:94739e665d9b4d5cd7af5f517cb6103f6f9fb421c095184609653a24524040f5" }, - { url = "https://download.pytorch.org/whl/xpu/torch-2.7.0%2Bxpu-cp313-cp313-linux_x86_64.whl", hash = "sha256:25d8277b7f01d42e2e014ccbab57a2692b6ec4eff8dcf894eda1b297407cf97a" }, - { url = "https://download.pytorch.org/whl/xpu/torch-2.7.0%2Bxpu-cp313-cp313-win_amd64.whl", hash = "sha256:31df3cb674918e89bc8c532baa331dc84f4430e1f9c0ec379232db44cba78355" }, - { url = "https://download.pytorch.org/whl/xpu/torch-2.7.0%2Bxpu-cp313-cp313t-linux_x86_64.whl", hash = "sha256:f853aa4e926102a11a8522f415e53da39b7e431b7922835f62e8a71e33f7e7dd" }, - { url = "https://download.pytorch.org/whl/xpu/torch-2.7.0%2Bxpu-cp313-cp313t-win_amd64.whl", hash = "sha256:cc286e5042fb0f628b2fdf79f570861e9340e6cc19f3e6a2ca27ce44e6fffbaf" }, -] - -[[package]] -name = "torchvision" -version = "0.22.0" +version = "2.7.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13' and sys_platform != 'linux' and sys_platform != 'win32'", - "python_full_version < '3.13' and sys_platform != 'linux' and sys_platform != 'win32'", -] -dependencies = [ - { name = "numpy", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "pillow", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/ea/887d1d61cf4431a46280972de665f350af1898ce5006cd046326e5d0a2f2/torchvision-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:31c3165418fe21c3d81fe3459e51077c2f948801b8933ed18169f54652796a0f", size = 1947826, upload-time = "2025-04-23T14:41:59.188Z" }, - { url = "https://files.pythonhosted.org/packages/e1/2a/9b34685599dcb341d12fc2730055155623db7a619d2415a8d31f17050952/torchvision-0.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ece17995857dd328485c9c027c0b20ffc52db232e30c84ff6c95ab77201112c5", size = 1947823, upload-time = "2025-04-23T14:41:39.956Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a7/f43e9c8d13118b4ffbaebea664c9338ab20fa115a908125afd2238ff16e7/torchvision-0.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cdc96daa4658b47ce9384154c86ed1e70cba9d972a19f5de6e33f8f94a626790", size = 2137621, upload-time = "2025-04-23T14:41:51.427Z" }, -] - -[[package]] -name = "torchvision" -version = "0.22.0+xpu" -source = { registry = "https://download.pytorch.org/whl/xpu" } -resolution-markers = [ - "python_full_version >= '3.13' and sys_platform == 'linux'", - "python_full_version < '3.13' and sys_platform == 'linux'", - "python_full_version >= '3.13' and sys_platform == 'win32'", - "python_full_version < '3.13' and sys_platform == 'win32'", -] dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pillow", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "torch", version = "2.7.0+xpu", source = { registry = "https://download.pytorch.org/whl/xpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions" }, ] wheels = [ - { url = "https://download.pytorch.org/whl/xpu/torchvision-0.22.0%2Bxpu-cp312-cp312-linux_x86_64.whl", hash = "sha256:b7686d566dd17f5304f52e4a8371c2d8cb7b80edc75e64ea55da82eb33cdef26" }, - { url = "https://download.pytorch.org/whl/xpu/torchvision-0.22.0%2Bxpu-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c070dc5f1bb664c2d4a25175d21968ad01cabed26d82d1880a4642795ee958f0" }, - { url = "https://download.pytorch.org/whl/xpu/torchvision-0.22.0%2Bxpu-cp312-cp312-win_amd64.whl", hash = "sha256:544264f62530b8d7564399ec2b72841045bfd872638646e764303b4e62a93516" }, - { url = "https://download.pytorch.org/whl/xpu/torchvision-0.22.0%2Bxpu-cp313-cp313-linux_x86_64.whl", hash = "sha256:2b14385884e80c9d92bcdb3d91429157044548b38f1b4784f60065c3d93d0582" }, - { url = "https://download.pytorch.org/whl/xpu/torchvision-0.22.0%2Bxpu-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:73121c18fbd45a1dee9e05a2cec98c067602862433846472ff69b379bccd0ed5" }, - { url = "https://download.pytorch.org/whl/xpu/torchvision-0.22.0%2Bxpu-cp313-cp313-win_amd64.whl", hash = "sha256:ea3a5854452ab41961cf2e223ccc955b8f7018b6e92ef85ced9f923963c315b8" }, - { url = "https://download.pytorch.org/whl/xpu/torchvision-0.22.0%2Bxpu-cp313-cp313t-linux_x86_64.whl", hash = "sha256:5df2f7bfc497cc0138e8b6996bff99e8fcd60f9571e7022be30f16ca54bae33b" }, - { url = "https://download.pytorch.org/whl/xpu/torchvision-0.22.0%2Bxpu-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6d5aa0a6d19a860ab31d72874640ab5696d4792e82aa60b0bb19a43addf4fcdc" }, - { url = "https://download.pytorch.org/whl/xpu/torchvision-0.22.0%2Bxpu-cp313-cp313t-win_amd64.whl", hash = "sha256:31c075b44af2a1cb100b09c683a82521d9a057dc8874023800d8fcedc603dee1" }, + { url = "https://files.pythonhosted.org/packages/aa/5e/ac759f4c0ab7c01feffa777bd68b43d2ac61560a9770eeac074b450f81d4/torch-2.7.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:36a6368c7ace41ad1c0f69f18056020b6a5ca47bedaca9a2f3b578f5a104c26c", size = 99013250, upload-time = "2025-04-23T14:35:15.589Z" }, + { url = "https://files.pythonhosted.org/packages/9c/58/2d245b6f1ef61cf11dfc4aceeaacbb40fea706ccebac3f863890c720ab73/torch-2.7.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:15aab3e31c16feb12ae0a88dba3434a458874636f360c567caa6a91f6bfba481", size = 865042157, upload-time = "2025-04-23T14:32:56.011Z" }, + { url = "https://files.pythonhosted.org/packages/44/80/b353c024e6b624cd9ce1d66dcb9d24e0294680f95b369f19280e241a0159/torch-2.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:f56d4b2510934e072bab3ab8987e00e60e1262fb238176168f5e0c43a1320c6d", size = 212482262, upload-time = "2025-04-23T14:35:03.527Z" }, + { url = "https://files.pythonhosted.org/packages/ee/8d/b2939e5254be932db1a34b2bd099070c509e8887e0c5a90c498a917e4032/torch-2.7.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:30b7688a87239a7de83f269333651d8e582afffce6f591fff08c046f7787296e", size = 68574294, upload-time = "2025-04-23T14:34:47.098Z" }, + { url = "https://files.pythonhosted.org/packages/14/24/720ea9a66c29151b315ea6ba6f404650834af57a26b2a04af23ec246b2d5/torch-2.7.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:868ccdc11798535b5727509480cd1d86d74220cfdc42842c4617338c1109a205", size = 99015553, upload-time = "2025-04-23T14:34:41.075Z" }, + { url = "https://files.pythonhosted.org/packages/4b/27/285a8cf12bd7cd71f9f211a968516b07dcffed3ef0be585c6e823675ab91/torch-2.7.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9b52347118116cf3dff2ab5a3c3dd97c719eb924ac658ca2a7335652076df708", size = 865046389, upload-time = "2025-04-23T14:32:01.16Z" }, + { url = "https://files.pythonhosted.org/packages/74/c8/2ab2b6eadc45554af8768ae99668c5a8a8552e2012c7238ded7e9e4395e1/torch-2.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:434cf3b378340efc87c758f250e884f34460624c0523fe5c9b518d205c91dd1b", size = 212490304, upload-time = "2025-04-23T14:33:57.108Z" }, + { url = "https://files.pythonhosted.org/packages/28/fd/74ba6fde80e2b9eef4237fe668ffae302c76f0e4221759949a632ca13afa/torch-2.7.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:edad98dddd82220465b106506bb91ee5ce32bd075cddbcf2b443dfaa2cbd83bf", size = 68856166, upload-time = "2025-04-23T14:34:04.012Z" }, + { url = "https://files.pythonhosted.org/packages/cb/b4/8df3f9fe6bdf59e56a0e538592c308d18638eb5f5dc4b08d02abb173c9f0/torch-2.7.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2a885fc25afefb6e6eb18a7d1e8bfa01cc153e92271d980a49243b250d5ab6d9", size = 99091348, upload-time = "2025-04-23T14:33:48.975Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f5/0bd30e9da04c3036614aa1b935a9f7e505a9e4f1f731b15e165faf8a4c74/torch-2.7.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:176300ff5bc11a5f5b0784e40bde9e10a35c4ae9609beed96b4aeb46a27f5fae", size = 865104023, upload-time = "2025-04-23T14:30:40.537Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b7/2235d0c3012c596df1c8d39a3f4afc1ee1b6e318d469eda4c8bb68566448/torch-2.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d0ca446a93f474985d81dc866fcc8dccefb9460a29a456f79d99c29a78a66993", size = 212750916, upload-time = "2025-04-23T14:32:22.91Z" }, + { url = "https://files.pythonhosted.org/packages/90/48/7e6477cf40d48cc0a61fa0d41ee9582b9a316b12772fcac17bc1a40178e7/torch-2.7.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:27f5007bdf45f7bb7af7f11d1828d5c2487e030690afb3d89a651fd7036a390e", size = 68575074, upload-time = "2025-04-23T14:32:38.136Z" }, ] [[package]] @@ -2820,6 +2906,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/0c/68d03a38f6ab2ba2b2829eb11b334610dd236e7926787f7656001b68e1f2/transformers-4.53.0-py3-none-any.whl", hash = "sha256:7d8039ff032c01a2d7f8a8fe0066620367003275f023815a966e62203f9f5dd7", size = 10821970, upload-time = "2025-06-26T16:10:51.505Z" }, ] +[[package]] +name = "triton" +version = "3.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/53/ce18470914ab6cfbec9384ee565d23c4d1c55f0548160b1c7b33000b11fd/triton-3.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b68c778f6c4218403a6bd01be7484f6dc9e20fe2083d22dd8aef33e3b87a10a3", size = 156504509, upload-time = "2025-04-09T20:27:40.413Z" }, + { url = "https://files.pythonhosted.org/packages/7d/74/4bf2702b65e93accaa20397b74da46fb7a0356452c1bb94dbabaf0582930/triton-3.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47bc87ad66fa4ef17968299acacecaab71ce40a238890acc6ad197c3abe2b8f1", size = 156516468, upload-time = "2025-04-09T20:27:48.196Z" }, + { url = "https://files.pythonhosted.org/packages/0a/93/f28a696fa750b9b608baa236f8225dd3290e5aff27433b06143adc025961/triton-3.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce4700fc14032af1e049005ae94ba908e71cd6c2df682239aed08e49bc71b742", size = 156580729, upload-time = "2025-04-09T20:27:55.424Z" }, +] + [[package]] name = "typer" version = "0.16.0" @@ -2866,15 +2965,12 @@ wheels = [ ] [[package]] -name = "umf" -version = "0.9.1" +name = "tzdata" +version = "2025.2" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tcmlib", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/19/cef361ce4c5c1079f90d35ed853996f94cef5e68bc29b8e7d11a28b0c53b/umf-0.9.1-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:2f7b8eb45e182b24823be420063799ea89b4f1cad6b9510b1fc6e04b809c7ec2", size = 161551, upload-time = "2024-11-19T15:35:00.583Z" }, - { url = "https://files.pythonhosted.org/packages/56/2a/3ccea66d7390f3b6407b396ec289d5acad7f8f92a1bd8b66b58d376fc019/umf-0.9.1-py2.py3-none-win_amd64.whl", hash = "sha256:9d1336d63c7a6b36af1f76dab942a760168da519a1bfe6941fc6418198b10820", size = 72212, upload-time = "2024-11-19T15:36:28.366Z" }, + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, ] [[package]] @@ -2940,36 +3036,52 @@ wheels = [ ] [[package]] -name = "video-description-generation-query-retrieval" +name = "video-rag-ollama" version = "0.1.0" source = { virtual = "." } dependencies = [ { name = "chromadb" }, - { name = "huggingface-hub", extra = ["hf-xet"] }, + { name = "ipykernel" }, + { name = "ipywidgets" }, { name = "jupyter" }, - { name = "pytorch-triton-xpu", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "qwen-vl-utils", extra = ["decord"] }, + { name = "numpy" }, + { name = "ollama" }, + { name = "opencv-python" }, { name = "sentence-transformers" }, - { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "torch", version = "2.7.0+xpu", source = { registry = "https://download.pytorch.org/whl/xpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "torchvision", version = "0.22.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "torchvision", version = "0.22.0+xpu", source = { registry = "https://download.pytorch.org/whl/xpu" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "transformers" }, + { name = "streamlit" }, + { name = "tqdm" }, ] [package.metadata] requires-dist = [ { name = "chromadb", specifier = ">=1.0.12" }, - { name = "huggingface-hub", extras = ["hf-xet"], specifier = ">=0.32.3" }, + { name = "ipykernel", specifier = ">=6.25.0" }, + { name = "ipywidgets", specifier = ">=8.1.0" }, { name = "jupyter", specifier = ">=1.1.1" }, - { name = "pytorch-triton-xpu", marker = "sys_platform == 'linux' or sys_platform == 'win32'", specifier = ">=3.3.0", index = "https://download.pytorch.org/whl/xpu" }, - { name = "qwen-vl-utils", extras = ["decord"], specifier = ">=0.0.11" }, + { name = "numpy", specifier = ">=1.24.0" }, + { name = "ollama", specifier = ">=0.4.0" }, + { name = "opencv-python", specifier = ">=4.8.0" }, { name = "sentence-transformers", specifier = ">=4.1.0" }, - { name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32'", specifier = ">=2.7.0" }, - { name = "torch", marker = "sys_platform == 'linux' or sys_platform == 'win32'", specifier = ">=2.7.0", index = "https://download.pytorch.org/whl/xpu" }, - { name = "torchvision", marker = "sys_platform != 'linux' and sys_platform != 'win32'", specifier = ">=0.22.0" }, - { name = "torchvision", marker = "sys_platform == 'linux' or sys_platform == 'win32'", specifier = ">=0.22.0", index = "https://download.pytorch.org/whl/xpu" }, - { name = "transformers", specifier = ">=4.52.4" }, + { name = "streamlit", specifier = ">=1.30.0" }, + { name = "tqdm", specifier = ">=4.65.0" }, +] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, ] [[package]]