diff --git a/README.md b/README.md index 90ac0e13..c9c47883 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,7 @@ GROQ_API_KEY= PINECONE_API_KEY = PORT = 8000 SEARCH_KEY = +HF_TOKEN = ``` *Run backend:* diff --git a/backend/app/llm_config.py b/backend/app/llm_config.py new file mode 100644 index 00000000..4cb96d2c --- /dev/null +++ b/backend/app/llm_config.py @@ -0,0 +1,4 @@ +import os + +# Default to a stable model +LLM_MODEL = os.getenv("LLM_MODEL", "llama-3.3-70b-versatile") \ No newline at end of file diff --git a/backend/app/modules/bias_detection/check_bias.py b/backend/app/modules/bias_detection/check_bias.py index a0644529..d814fdaa 100644 --- a/backend/app/modules/bias_detection/check_bias.py +++ b/backend/app/modules/bias_detection/check_bias.py @@ -27,6 +27,7 @@ from dotenv import load_dotenv import json from app.logging.logging_config import setup_logger +from app.llm_config import LLM_MODEL logger = setup_logger(__name__) @@ -61,7 +62,7 @@ def check_bias(text): "content": (f"Give bias score to the following article \n\n{text}"), }, ], - model="gemma2-9b-it", + model=LLM_MODEL, temperature=0.3, max_tokens=512, ) diff --git a/backend/app/modules/chat/get_rag_data.py b/backend/app/modules/chat/get_rag_data.py index 092e4587..23f9a37c 100644 --- a/backend/app/modules/chat/get_rag_data.py +++ b/backend/app/modules/chat/get_rag_data.py @@ -31,7 +31,7 @@ load_dotenv() -pc = Pinecone(os.getenv("PINECONE_API_KEY")) +pc = Pinecone(api_key=os.getenv("PINECONE_API_KEY")) index = pc.Index("perspective") diff --git a/backend/app/modules/chat/llm_processing.py b/backend/app/modules/chat/llm_processing.py index 2d5134fa..9fe0ace3 100644 --- a/backend/app/modules/chat/llm_processing.py +++ b/backend/app/modules/chat/llm_processing.py @@ -27,6 +27,7 @@ from groq import Groq from dotenv import load_dotenv from app.logging.logging_config import setup_logger +from app.llm_config import LLM_MODEL logger = setup_logger(__name__) @@ -55,7 +56,7 @@ def ask_llm(question, docs): """ response = client.chat.completions.create( - model="gemma2-9b-it", + model=LLM_MODEL, messages=[ {"role": "system", "content": "Use only the context to answer."}, {"role": "user", "content": prompt}, diff --git a/backend/app/modules/fact_check_tool.py b/backend/app/modules/fact_check_tool.py new file mode 100644 index 00000000..b19f6c52 --- /dev/null +++ b/backend/app/modules/fact_check_tool.py @@ -0,0 +1,166 @@ +""" +fact checking tool node implementation to replace the google search + +""" + +import os +import json +import asyncio +from groq import Groq +from langchain_community.tools import DuckDuckGoSearchRun +from app.logging.logging_config import setup_logger +from dotenv import load_dotenv +from app.llm_config import LLM_MODEL + +load_dotenv() + +client = Groq(api_key=os.getenv("GROQ_API_KEY")) +search_tool = DuckDuckGoSearchRun() + +logger = setup_logger(__name__) + +async def extract_claims_node(state): + logger.info("--- Fact Check Step 1: Extracting Claims ---") + try: + text = state.get("cleaned_text", "") + + response = await asyncio.to_thread( + client.chat.completions.create, + messages=[ + { + "role": "system", + "content": "Extract specific, verifiable factual claims from the text. Ignore opinions. Return a simple list of strings, one per line." + }, + {"role": "user", "content": text[:4000]} + ], + model=LLM_MODEL, + temperature=0.0 + ) + + raw_content = response.choices[0].message.content + + claims = [line.strip("- *") for line in raw_content.split("\n") if len(line.strip()) > 10] + + logger.info(f"Extracted {len(claims)} claims.") + return {"claims": claims} + + except Exception as e: + logger.error(f"Error extraction claims: {e}") + return {"claims": []} + +async def plan_searches_node(state): + logger.info("--- Fact Check Step 2: Planning Searches ---") + claims = state.get("claims", []) + + if not claims: + return {"search_queries": []} + + claims_text = "\n".join([f"{i}. {c}" for i, c in enumerate(claims)]) + + prompt = f""" + You are a search query generator. + For each claim, generate a search query to verify it. + + Output MUST be valid JSON in this format: + {{ + "searches": [ + {{"query": "search query 1", "claim_id": 0}}, + {{"query": "search query 2", "claim_id": 1}} + ] + }} + + Claims: + {claims_text} + """ + + try: + response = await asyncio.to_thread( + client.chat.completions.create, + messages=[{"role": "user", "content": prompt}], + model=LLM_MODEL, + temperature=0.0, + response_format={"type": "json_object"} + ) + + plan_json = json.loads(response.choices[0].message.content) + queries = plan_json.get("searches", []) + + return {"search_queries": queries} + + except Exception as e: + logger.error(f"Failed to plan searches: {e}") + return {"search_queries": []} + +async def execute_searches_node(state): + logger.info("--- Fact Check Step 3: Executing Parallel Searches ---") + queries = state.get("search_queries", []) + + if not queries: + return {"search_results": []} + + async def run_one_search(q): + try: + query_str = q.get("query") + c_id = q.get("claim_id") + + res = await asyncio.to_thread(search_tool.invoke, query_str) + logger.info(f"Search Result for Claim {c_id}: {res[:200]}...") + return {"claim_id": c_id, "result": res} + except Exception as e: + return {"claim_id": q.get("claim_id"), "result": "Search failed"} + + results = await asyncio.gather(*[run_one_search(q) for q in queries]) + + logger.info(f"Completed {len(results)} searches.") + return {"search_results": results} + +async def verify_facts_node(state): + logger.info("--- Fact Check Step 4: Verifying Facts ---") + claims = state.get("claims", []) + results = state.get("search_results", []) + + if not claims: + return {"facts": [], "fact_check_done": True} + + context = "Verify these claims based on the search results:\n" + for item in results: + c_id = item["claim_id"] + if c_id < len(claims): + context += f"\nClaim: {claims[c_id]}\nEvidence: {item['result']}\n" + + try: + response = await asyncio.to_thread( + client.chat.completions.create, + messages=[ + { + "role": "system", + "content": "You are a strict fact checker. Return a JSON list of objects with keys: 'claim', 'status' (True/False/Unverified), and 'reason'." + }, + {"role": "user", "content": context} + ], + model=LLM_MODEL, + temperature=0.0, + response_format={"type": "json_object"} + ) + + final_verdict_str = response.choices[0].message.content + + data = json.loads(final_verdict_str) + + facts_list = [] + if isinstance(data, dict): + # Look for common keys if wrapped + if "facts" in data: + facts_list = data["facts"] + elif "verified_claims" in data: + facts_list = data["verified_claims"] + else: + facts_list = [data] + elif isinstance(data, list): + facts_list = data + + return {"facts": facts_list, "fact_check_done": True} + + except Exception as e: + logger.error(f"Verification failed: {e}") + return {"facts": [], "fact_check_done": True} \ No newline at end of file diff --git a/backend/app/modules/facts_check/llm_processing.py b/backend/app/modules/facts_check/llm_processing.py index dc223a85..c3ead204 100644 --- a/backend/app/modules/facts_check/llm_processing.py +++ b/backend/app/modules/facts_check/llm_processing.py @@ -29,6 +29,7 @@ import json import re from app.logging.logging_config import setup_logger +from app.llm_config import LLM_MODEL logger = setup_logger(__name__) @@ -63,7 +64,7 @@ def run_claim_extractor_sdk(state): ), }, ], - model="gemma2-9b-it", + model=LLM_MODEL, temperature=0.3, max_tokens=512, ) @@ -128,7 +129,7 @@ def run_fact_verifier_sdk(search_results): ), }, ], - model="gemma2-9b-it", + model=LLM_MODEL, temperature=0.3, max_tokens=256, ) diff --git a/backend/app/modules/langgraph_builder.py b/backend/app/modules/langgraph_builder.py index 7729f945..b219e115 100644 --- a/backend/app/modules/langgraph_builder.py +++ b/backend/app/modules/langgraph_builder.py @@ -6,12 +6,12 @@ and retry logic. Workflow: - 1. Sentiment analysis on the cleaned text. - 2. Fact-checking detected claims. - 3. Generating a counter-perspective. - 4. Judging the quality of the generated perspective. - 5. Storing results and sending them downstream. - 6. Error handling at any step if failures occur. + 1. Parallel analysis: sentiment analysis and fact checking tool pipeline + (extract_claims -> plan_searches -> execute_searches -> verify_facts) + 2. Generating a counter-perspective. + 3. Judging the quality of the generated perspective. + 4. Storing results and sending. + 5. Error handling at any step if failures occur. Core Features: - Uses a TypedDict (`MyState`) to define the shape of the pipeline's @@ -31,18 +31,18 @@ """ +from typing import List, Any from langgraph.graph import StateGraph +from typing_extensions import TypedDict + from app.modules.langgraph_nodes import ( sentiment, - fact_check, generate_perspective, judge, store_and_send, error_handler, ) -from typing_extensions import TypedDict - class MyState(TypedDict): cleaned_text: str @@ -52,29 +52,26 @@ class MyState(TypedDict): score: int retries: int status: str + claims: List[str] + search_queries: List[Any] + search_results: List[Any] def build_langgraph(): graph = StateGraph(MyState) - graph.add_node("sentiment_analysis", sentiment.run_sentiment_sdk) - graph.add_node("fact_checking", fact_check.run_fact_check) + # parallel analysis runs sentiment and fact_check tool pipeline in parallel + graph.add_node("parallel_analysis", sentiment.run_parallel_analysis) + graph.add_node("generate_perspective", generate_perspective.generate_perspective) graph.add_node("judge_perspective", judge.judge_perspective) graph.add_node("store_and_send", store_and_send.store_and_send) graph.add_node("error_handler", error_handler.error_handler) - graph.set_entry_point( - "sentiment_analysis", - ) + graph.set_entry_point("parallel_analysis") graph.add_conditional_edges( - "sentiment_analysis", - lambda x: ("error_handler" if x.get("status") == "error" else "fact_checking"), - ) - - graph.add_conditional_edges( - "fact_checking", + "parallel_analysis", lambda x: ( "error_handler" if x.get("status") == "error" else "generate_perspective" ), @@ -101,6 +98,7 @@ def build_langgraph(): else "store_and_send" ), ) + graph.add_conditional_edges( "store_and_send", lambda x: ("error_handler" if x.get("status") == "error" else "__end__"), diff --git a/backend/app/modules/langgraph_nodes/generate_perspective.py b/backend/app/modules/langgraph_nodes/generate_perspective.py index be0c81f3..605b63dc 100644 --- a/backend/app/modules/langgraph_nodes/generate_perspective.py +++ b/backend/app/modules/langgraph_nodes/generate_perspective.py @@ -21,10 +21,11 @@ from app.utils.prompt_templates import generation_prompt +from app.llm_config import LLM_MODEL from langchain_groq import ChatGroq from pydantic import BaseModel, Field from app.logging.logging_config import setup_logger - +from typing import List logger = setup_logger(__name__) @@ -32,11 +33,11 @@ class PerspectiveOutput(BaseModel): - reasoning: str = Field(..., description="Chain-of-thought reasoning steps") + reasoning: List[str] = Field(description="Chain-of-thought reasoning steps", alias="reasoning_steps") perspective: str = Field(..., description="Generated opposite perspective") -my_llm = "llama-3.3-70b-versatile" +my_llm = LLM_MODEL llm = ChatGroq(model=my_llm, temperature=0.7) @@ -56,17 +57,18 @@ def generate_perspective(state): if not text: raise ValueError("Missing or empty 'cleaned_text' in state") - elif not facts: - raise ValueError("Missing or empty 'facts' in state") - - facts_str = "\n".join( - [ - f"Claim: {f['original_claim']}\n" - "Verdict: {f['verdict']}\nExplanation: " - "{f['explanation']}" - for f in state["facts"] - ] - ) + if not facts: + logger.warning("No facts found in state. Generating perspective based on text only.") + facts_str = "No specific claims verified." + else: + facts_str = "\n".join( + [ + f"Claim: {f.get('claim', f.get('original_claim', 'Unknown Claim'))}\n" + f"Verdict: {f.get('status', f.get('verdict', 'Unknown Verdict'))}\n" + f"Explanation: {f.get('reason', f.get('explanation', 'No explanation'))}" + for f in facts + ] + ) result = chain.invoke( { diff --git a/backend/app/modules/langgraph_nodes/judge.py b/backend/app/modules/langgraph_nodes/judge.py index 57100301..9613ad7f 100644 --- a/backend/app/modules/langgraph_nodes/judge.py +++ b/backend/app/modules/langgraph_nodes/judge.py @@ -19,12 +19,13 @@ from langchain_groq import ChatGroq from langchain.schema import HumanMessage from app.logging.logging_config import setup_logger +from app.llm_config import LLM_MODEL logger = setup_logger(__name__) # Init once groq_llm = ChatGroq( - model="gemma2-9b-it", + model=LLM_MODEL, temperature=0.0, max_tokens=10, ) diff --git a/backend/app/modules/langgraph_nodes/sentiment.py b/backend/app/modules/langgraph_nodes/sentiment.py index fef1d39d..0726e061 100644 --- a/backend/app/modules/langgraph_nodes/sentiment.py +++ b/backend/app/modules/langgraph_nodes/sentiment.py @@ -11,13 +11,19 @@ Functions: run_sentiment_sdk(state: dict) -> dict: Analyzes sentiment and updates the state with the result. + + run_parallel_analysis(state: dict) -> dict: + Runs sentiment analysis and fact-checking tool nodes in parallel. + Combines claims, search_queries, search_results, and facts into state. """ - +import asyncio import os from groq import Groq from dotenv import load_dotenv from app.logging.logging_config import setup_logger +from app.llm_config import LLM_MODEL +from app.modules import fact_check_tool logger = setup_logger(__name__) @@ -26,6 +32,67 @@ client = Groq(api_key=os.getenv("GROQ_API_KEY")) +async def run_parallel_analysis(state): + """ + The fact-checking pipeline runs sequentially: + extract_claims -> plan_searches -> execute_searches -> verify_facts + """ + async def run_fact_check_pipeline(state): + try: + claims_result = await fact_check_tool.extract_claims_node(state) + current_state = {**state, **claims_result} + searches_result = await fact_check_tool.plan_searches_node(current_state) + current_state = {**current_state, **searches_result} + + exec_result = await fact_check_tool.execute_searches_node(current_state) + current_state = {**current_state, **exec_result} + verify_result = await fact_check_tool.verify_facts_node(current_state) + current_state = {**current_state, **verify_result} + + return { + "claims": current_state.get("claims", []), + "search_queries": current_state.get("search_queries", []), + "search_results": current_state.get("search_results", []), + "facts": current_state.get("facts", []), + "status": "success" + } + except Exception as e: + logger.exception(f"Error in fact_check_pipeline: {e}") + return { + "status": "error", + "error_from": "fact_checking", + "message": str(e) + } + + sentiment_task = asyncio.to_thread(run_sentiment_sdk, state) + fact_check_task = run_fact_check_pipeline(state) + + sentiment_result, fact_check_result = await asyncio.gather( + sentiment_task, fact_check_task + ) + if sentiment_result.get("status") == "error": + return { + "status": "error", + "error_from": sentiment_result.get("error_from", "sentiment_analysis"), + "message": sentiment_result.get("message", "Unknown error") + } + if fact_check_result.get("status") == "error": + return { + "status": "error", + "error_from": fact_check_result.get("error_from", "fact_checking"), + "message": fact_check_result.get("message", "Unknown error") + } + + return { + **state, + "sentiment": sentiment_result.get("sentiment"), + "claims": fact_check_result.get("claims", []), + "search_queries": fact_check_result.get("search_queries", []), + "search_results": fact_check_result.get("search_results", []), + "facts": fact_check_result.get("facts", []), + "status": "success" + } + def run_sentiment_sdk(state): try: text = state.get("cleaned_text") @@ -49,7 +116,7 @@ def run_sentiment_sdk(state): ), }, ], - model="gemma2-9b-it", + model=LLM_MODEL, temperature=0.2, max_tokens=3, ) diff --git a/backend/app/modules/pipeline.py b/backend/app/modules/pipeline.py index 3e4a844e..1c81d57c 100644 --- a/backend/app/modules/pipeline.py +++ b/backend/app/modules/pipeline.py @@ -38,6 +38,7 @@ from app.modules.langgraph_builder import build_langgraph from app.logging.logging_config import setup_logger import json +import asyncio logger = setup_logger(__name__) @@ -64,8 +65,8 @@ def run_scraper_pipeline(url: str) -> dict: return result -def run_langgraph_workflow(state: dict): +async def run_langgraph_workflow(state: dict): """Execute the pre-compiled LangGraph workflow.""" - result = _LANGGRAPH_WORKFLOW.invoke(state) + result = await _LANGGRAPH_WORKFLOW.ainvoke(state) logger.info("LangGraph workflow executed successfully.") return result diff --git a/backend/app/modules/vector_store/chunk_rag_data.py b/backend/app/modules/vector_store/chunk_rag_data.py index 7e2a32eb..00b40d12 100644 --- a/backend/app/modules/vector_store/chunk_rag_data.py +++ b/backend/app/modules/vector_store/chunk_rag_data.py @@ -32,71 +32,79 @@ def chunk_rag_data(data): try: - # Validate required top-level fields required_fields = ["cleaned_text", "perspective", "facts"] for field in required_fields: if field not in data: - raise ValueError(f"Missing required field: {field}") + if field == "facts": + data["facts"] = [] + else: + raise ValueError(f"Missing required field: {field}") if not isinstance(data["facts"], list): - raise ValueError("Facts must be a list") - - # Validate perspective structure - perspective_data = data["perspective"] - if hasattr(perspective_data, "dict"): - perspective_data = perspective_data.dict() + logger.warning("Facts is not a list. Treating as empty.") + data["facts"] = [] article_id = generate_id(data["cleaned_text"]) chunks = [] - # Add counter-perspective chunk - perspective_obj = data["perspective"] - - # Optional safety check + perspective_data = data["perspective"] + + if hasattr(perspective_data, "dict"): + p_data = perspective_data.dict() + elif hasattr(perspective_data, "model_dump"): + p_data = perspective_data.model_dump() + elif isinstance(perspective_data, dict): + p_data = perspective_data + else: + p_data = { + "perspective": getattr(perspective_data, "perspective", ""), + "reasoning": getattr(perspective_data, "reasoning", "") + } - if not ( - hasattr(perspective_obj, "perspective") - and hasattr(perspective_obj, "reasoning") - ): - raise ValueError("Perspective object missing required fields") + p_text = p_data.get("perspective", "") + raw_reasoning = p_data.get("reasoning", "") + if isinstance(raw_reasoning, list): + p_reason = "\n".join(raw_reasoning) + else: + p_reason = str(raw_reasoning) - chunks.append( - { + if p_text: + chunks.append({ "id": f"{article_id}-perspective", - "text": perspective_obj.perspective, + "text": p_text, "metadata": { "type": "counter-perspective", - "reasoning": perspective_obj.reasoning, + "reasoning": p_reason, "article_id": article_id, }, - } - ) + }) - # Add each fact as a separate chunk for i, fact in enumerate(data["facts"]): - fact_fields = ["original_claim", "verdict", "explanation", "source_link"] - for field in fact_fields: - if field not in fact: - raise ValueError( - f"Missing required fact field: {field} in fact index {i}" - ) - - chunks.append( - { - "id": f"{article_id}-fact-{i}", - "text": fact["original_claim"], - "metadata": { - "type": "fact", - "verdict": fact["verdict"], - "explanation": fact["explanation"], - "source_link": fact["source_link"], - "article_id": article_id, - }, - } - ) + claim_text = fact.get("claim", fact.get("original_claim", "")) + verdict = fact.get("status", fact.get("verdict", "Unverified")) + explanation = fact.get("reason", fact.get("explanation", "No explanation provided")) + source = fact.get("source_link", "N/A") + + # SKIP invalid facts instead of Crashing + if not claim_text: + logger.warning(f"Skipping fact index {i}: Missing claim text.") + continue + + chunks.append({ + "id": f"{article_id}-fact-{i}", + "text": claim_text, + "metadata": { + "type": "fact", + "verdict": verdict, + "explanation": explanation, + "source_link": source, + "article_id": article_id, + }, + }) + logger.info(f"Generated {len(chunks)} chunks for storage.") return chunks except Exception as e: logger.exception(f"Failed to chunk the data: {e}") - raise + return [] \ No newline at end of file diff --git a/backend/app/routes/routes.py b/backend/app/routes/routes.py index 6988f5e8..f9f01c7b 100644 --- a/backend/app/routes/routes.py +++ b/backend/app/routes/routes.py @@ -71,7 +71,7 @@ async def bias_detection(request: URlRequest): async def run_pipelines(request: URlRequest): article_text = await asyncio.to_thread(run_scraper_pipeline, (request.url)) logger.debug(f"Scraper output: {json.dumps(article_text, indent=2, ensure_ascii=False)}") - data = await asyncio.to_thread(run_langgraph_workflow, (article_text)) + data = await run_langgraph_workflow(article_text) return data diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 70037f72..deb32a55 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -16,7 +16,7 @@ dependencies = [ "langchain-groq>=0.3.2", "langgraph>=0.4.8", "logging>=0.4.9.6", - "newspaper3k>=0.2.8", + "newspaper4k>=0.9.4.1", "nltk>=3.9.1", "pinecone>=7.3.0", "rake-nltk>=1.0.6", diff --git a/backend/uv.lock b/backend/uv.lock index fc1e19b5..138a1f1e 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -1,6 +1,10 @@ version = 1 revision = 2 requires-python = ">=3.13" +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version < '3.14'", +] [[package]] name = "aiohappyeyeballs" @@ -113,7 +117,7 @@ dependencies = [ { name = "langchain-groq" }, { name = "langgraph" }, { name = "logging" }, - { name = "newspaper3k" }, + { name = "newspaper4k" }, { name = "nltk" }, { name = "pinecone" }, { name = "rake-nltk" }, @@ -137,7 +141,7 @@ requires-dist = [ { name = "langchain-groq", specifier = ">=0.3.2" }, { name = "langgraph", specifier = ">=0.4.8" }, { name = "logging", specifier = ">=0.4.9.6" }, - { name = "newspaper3k", specifier = ">=0.2.8" }, + { name = "newspaper4k", specifier = ">=0.9.4.1" }, { name = "nltk", specifier = ">=3.9.1" }, { name = "pinecone", specifier = ">=7.3.0" }, { name = "rake-nltk", specifier = ">=1.0.6" }, @@ -161,6 +165,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285, upload-time = "2025-04-15T17:05:12.221Z" }, ] +[[package]] +name = "brotli" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/16/c92ca344d646e71a43b8bb353f0a6490d7f6e06210f8554c8f874e454285/brotli-1.2.0.tar.gz", hash = "sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a", size = 7388632, upload-time = "2025-11-05T18:39:42.86Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/d4/4ad5432ac98c73096159d9ce7ffeb82d151c2ac84adcc6168e476bb54674/brotli-1.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9e5825ba2c9998375530504578fd4d5d1059d09621a02065d1b6bfc41a8e05ab", size = 861523, upload-time = "2025-11-05T18:38:34.67Z" }, + { url = "https://files.pythonhosted.org/packages/91/9f/9cc5bd03ee68a85dc4bc89114f7067c056a3c14b3d95f171918c088bf88d/brotli-1.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0cf8c3b8ba93d496b2fae778039e2f5ecc7cff99df84df337ca31d8f2252896c", size = 444289, upload-time = "2025-11-05T18:38:35.6Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b6/fe84227c56a865d16a6614e2c4722864b380cb14b13f3e6bef441e73a85a/brotli-1.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8565e3cdc1808b1a34714b553b262c5de5fbda202285782173ec137fd13709f", size = 1528076, upload-time = "2025-11-05T18:38:36.639Z" }, + { url = "https://files.pythonhosted.org/packages/55/de/de4ae0aaca06c790371cf6e7ee93a024f6b4bb0568727da8c3de112e726c/brotli-1.2.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:26e8d3ecb0ee458a9804f47f21b74845cc823fd1bb19f02272be70774f56e2a6", size = 1626880, upload-time = "2025-11-05T18:38:37.623Z" }, + { url = "https://files.pythonhosted.org/packages/5f/16/a1b22cbea436642e071adcaf8d4b350a2ad02f5e0ad0da879a1be16188a0/brotli-1.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67a91c5187e1eec76a61625c77a6c8c785650f5b576ca732bd33ef58b0dff49c", size = 1419737, upload-time = "2025-11-05T18:38:38.729Z" }, + { url = "https://files.pythonhosted.org/packages/46/63/c968a97cbb3bdbf7f974ef5a6ab467a2879b82afbc5ffb65b8acbb744f95/brotli-1.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4ecdb3b6dc36e6d6e14d3a1bdc6c1057c8cbf80db04031d566eb6080ce283a48", size = 1484440, upload-time = "2025-11-05T18:38:39.916Z" }, + { url = "https://files.pythonhosted.org/packages/06/9d/102c67ea5c9fc171f423e8399e585dabea29b5bc79b05572891e70013cdd/brotli-1.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3e1b35d56856f3ed326b140d3c6d9db91740f22e14b06e840fe4bb1923439a18", size = 1593313, upload-time = "2025-11-05T18:38:41.24Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4a/9526d14fa6b87bc827ba1755a8440e214ff90de03095cacd78a64abe2b7d/brotli-1.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:54a50a9dad16b32136b2241ddea9e4df159b41247b2ce6aac0b3276a66a8f1e5", size = 1487945, upload-time = "2025-11-05T18:38:42.277Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e8/3fe1ffed70cbef83c5236166acaed7bb9c766509b157854c80e2f766b38c/brotli-1.2.0-cp313-cp313-win32.whl", hash = "sha256:1b1d6a4efedd53671c793be6dd760fcf2107da3a52331ad9ea429edf0902f27a", size = 334368, upload-time = "2025-11-05T18:38:43.345Z" }, + { url = "https://files.pythonhosted.org/packages/ff/91/e739587be970a113b37b821eae8097aac5a48e5f0eca438c22e4c7dd8648/brotli-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:b63daa43d82f0cdabf98dee215b375b4058cce72871fd07934f179885aad16e8", size = 369116, upload-time = "2025-11-05T18:38:44.609Z" }, + { url = "https://files.pythonhosted.org/packages/17/e1/298c2ddf786bb7347a1cd71d63a347a79e5712a7c0cba9e3c3458ebd976f/brotli-1.2.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:6c12dad5cd04530323e723787ff762bac749a7b256a5bece32b2243dd5c27b21", size = 863080, upload-time = "2025-11-05T18:38:45.503Z" }, + { url = "https://files.pythonhosted.org/packages/84/0c/aac98e286ba66868b2b3b50338ffbd85a35c7122e9531a73a37a29763d38/brotli-1.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3219bd9e69868e57183316ee19c84e03e8f8b5a1d1f2667e1aa8c2f91cb061ac", size = 445453, upload-time = "2025-11-05T18:38:46.433Z" }, + { url = "https://files.pythonhosted.org/packages/ec/f1/0ca1f3f99ae300372635ab3fe2f7a79fa335fee3d874fa7f9e68575e0e62/brotli-1.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:963a08f3bebd8b75ac57661045402da15991468a621f014be54e50f53a58d19e", size = 1528168, upload-time = "2025-11-05T18:38:47.371Z" }, + { url = "https://files.pythonhosted.org/packages/d6/a6/2ebfc8f766d46df8d3e65b880a2e220732395e6d7dc312c1e1244b0f074a/brotli-1.2.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9322b9f8656782414b37e6af884146869d46ab85158201d82bab9abbcb971dc7", size = 1627098, upload-time = "2025-11-05T18:38:48.385Z" }, + { url = "https://files.pythonhosted.org/packages/f3/2f/0976d5b097ff8a22163b10617f76b2557f15f0f39d6a0fe1f02b1a53e92b/brotli-1.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cf9cba6f5b78a2071ec6fb1e7bd39acf35071d90a81231d67e92d637776a6a63", size = 1419861, upload-time = "2025-11-05T18:38:49.372Z" }, + { url = "https://files.pythonhosted.org/packages/9c/97/d76df7176a2ce7616ff94c1fb72d307c9a30d2189fe877f3dd99af00ea5a/brotli-1.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7547369c4392b47d30a3467fe8c3330b4f2e0f7730e45e3103d7d636678a808b", size = 1484594, upload-time = "2025-11-05T18:38:50.655Z" }, + { url = "https://files.pythonhosted.org/packages/d3/93/14cf0b1216f43df5609f5b272050b0abd219e0b54ea80b47cef9867b45e7/brotli-1.2.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1530af5c3c275b8524f2e24841cbe2599d74462455e9bae5109e9ff42e9361", size = 1593455, upload-time = "2025-11-05T18:38:51.624Z" }, + { url = "https://files.pythonhosted.org/packages/b3/73/3183c9e41ca755713bdf2cc1d0810df742c09484e2e1ddd693bee53877c1/brotli-1.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d2d085ded05278d1c7f65560aae97b3160aeb2ea2c0b3e26204856beccb60888", size = 1488164, upload-time = "2025-11-05T18:38:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/64/6a/0c78d8f3a582859236482fd9fa86a65a60328a00983006bcf6d83b7b2253/brotli-1.2.0-cp314-cp314-win32.whl", hash = "sha256:832c115a020e463c2f67664560449a7bea26b0c1fdd690352addad6d0a08714d", size = 339280, upload-time = "2025-11-05T18:38:54.02Z" }, + { url = "https://files.pythonhosted.org/packages/f5/10/56978295c14794b2c12007b07f3e41ba26acda9257457d7085b0bb3bb90c/brotli-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3", size = 375639, upload-time = "2025-11-05T18:38:55.67Z" }, +] + [[package]] name = "bs4" version = "0.0.2" @@ -355,27 +387,16 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164, upload-time = "2025-03-23T22:55:42.101Z" }, ] -[[package]] -name = "feedfinder2" -version = "0.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beautifulsoup4" }, - { name = "requests" }, - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/35/82/1251fefec3bb4b03fd966c7e7f7a41c9fc2bb00d823a34c13f847fd61406/feedfinder2-0.0.4.tar.gz", hash = "sha256:3701ee01a6c85f8b865a049c30ba0b4608858c803fe8e30d1d289fdbe89d0efe", size = 3297, upload-time = "2016-01-25T15:09:17.492Z" } - [[package]] name = "feedparser" -version = "6.0.11" +version = "6.0.12" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sgmllib3k" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/aa/7af346ebeb42a76bf108027fe7f3328bb4e57a3a96e53e21fd9ef9dd6dd0/feedparser-6.0.11.tar.gz", hash = "sha256:c9d0407b64c6f2a065d0ebb292c2b35c01050cc0dc33757461aaabdc4c4184d5", size = 286197, upload-time = "2023-12-10T16:03:20.854Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/79/db7edb5e77d6dfbc54d7d9df72828be4318275b2e580549ff45a962f6461/feedparser-6.0.12.tar.gz", hash = "sha256:64f76ce90ae3e8ef5d1ede0f8d3b50ce26bcce71dd8ae5e82b1cd2d4a5f94228", size = 286579, upload-time = "2025-09-10T13:33:59.486Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/d4/8c31aad9cc18f451c49f7f9cfb5799dadffc88177f7917bc90a66459b1d7/feedparser-6.0.11-py3-none-any.whl", hash = "sha256:0be7ee7b395572b19ebeb1d6aafb0028dee11169f1c934e0ed67d54992f4ad45", size = 81343, upload-time = "2023-12-10T16:03:19.484Z" }, + { url = "https://files.pythonhosted.org/packages/4e/eb/c96d64137e29ae17d83ad2552470bafe3a7a915e85434d9942077d7fd011/feedparser-6.0.12-py3-none-any.whl", hash = "sha256:6bbff10f5a52662c00a2e3f86a38928c37c48f77b3c511aedcd51de933549324", size = 81480, upload-time = "2025-09-10T13:33:58.022Z" }, ] [[package]] @@ -594,12 +615,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] -[[package]] -name = "jieba3k" -version = "0.35.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/cb/2c8332bcdc14d33b0bedd18ae0a4981a069c3513e445120da3c3f23a8aaa/jieba3k-0.35.1.zip", hash = "sha256:980a4f2636b778d312518066be90c7697d410dd5a472385f5afced71a2db1c10", size = 7423646, upload-time = "2014-11-15T05:47:47.978Z" } - [[package]] name = "jinja2" version = "3.1.6" @@ -760,7 +775,7 @@ name = "langgraph-checkpoint" version = "2.0.26" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "langchain-core", marker = "python_full_version < '4.0'" }, + { name = "langchain-core", marker = "python_full_version < '4'" }, { name = "ormsgpack" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c5/61/e2518ac9216a4e9f4efda3ac61595e3c9e9ac00833141c9688e8d56bd7eb/langgraph_checkpoint-2.0.26.tar.gz", hash = "sha256:2b800195532d5efb079db9754f037281225ae175f7a395523f4bf41223cbc9d6", size = 37874, upload-time = "2025-05-15T17:31:22.466Z" } @@ -973,27 +988,26 @@ wheels = [ ] [[package]] -name = "newspaper3k" -version = "0.2.8" +name = "newspaper4k" +version = "0.9.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "beautifulsoup4" }, - { name = "cssselect" }, - { name = "feedfinder2" }, + { name = "brotli" }, { name = "feedparser" }, - { name = "jieba3k" }, - { name = "lxml" }, + { name = "lxml", extra = ["html-clean"] }, { name = "nltk" }, - { name = "pillow" }, + { name = "pillow", version = "11.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "pillow", version = "12.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, { name = "python-dateutil" }, { name = "pyyaml" }, { name = "requests" }, - { name = "tinysegmenter" }, { name = "tldextract" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ce/fb/8f8525be0cafa48926e85b0c06a7cb3e2a892d340b8036f8c8b1b572df1c/newspaper3k-0.2.8.tar.gz", hash = "sha256:9f1bd3e1fb48f400c715abf875cc7b0a67b7ddcd87f50c9aeeb8fcbbbd9004fb", size = 205685, upload-time = "2018-09-28T04:58:23.53Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/cc/cf743a3d06b10907cec76a675fe0857445907ded0e64ec4624483c1467ce/newspaper4k-0.9.4.1.tar.gz", hash = "sha256:5b1a92dfb04d6d379f9484fad4ad44741deb9ac3d55a6c178badf2a0d4bba903", size = 3971874, upload-time = "2025-11-18T06:08:56.543Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/b9/51afecb35bb61b188a4b44868001de348a0e8134b4dfa00ffc191567c4b9/newspaper3k-0.2.8-py3-none-any.whl", hash = "sha256:44a864222633d3081113d1030615991c3dbba87239f6bbf59d91240f71a22e3e", size = 211132, upload-time = "2018-09-28T04:58:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/63/e8/7e6c6a6e626fec2ec25f7b69fa0c446d1ca8a7fad121b61f2672e5779b76/newspaper4k-0.9.4.1-py3-none-any.whl", hash = "sha256:fab18fdb0637da0ea2452e18c5986c4af2263ba3016ff684f1c522f696bd39bd", size = 306153, upload-time = "2025-11-18T06:08:54.9Z" }, ] [[package]] @@ -1226,6 +1240,9 @@ wheels = [ name = "pillow" version = "11.2.1" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.14'", +] sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707, upload-time = "2025-04-12T17:50:03.289Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/36/9c/447528ee3776e7ab8897fe33697a7ff3f0475bb490c5ac1456a03dc57956/pillow-11.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28", size = 3190098, upload-time = "2025-04-12T17:48:23.915Z" }, @@ -1252,6 +1269,67 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/67/32/32dc030cfa91ca0fc52baebbba2e009bb001122a1daa8b6a79ad830b38d3/pillow-11.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681", size = 2417234, upload-time = "2025-04-12T17:49:08.399Z" }, ] +[[package]] +name = "pillow" +version = "12.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14'", +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", size = 47008828, upload-time = "2025-10-15T18:24:14.008Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/f2/de993bb2d21b33a98d031ecf6a978e4b61da207bef02f7b43093774c480d/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643", size = 4045493, upload-time = "2025-10-15T18:22:25.758Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b6/bc8d0c4c9f6f111a783d045310945deb769b806d7574764234ffd50bc5ea/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4", size = 4120461, upload-time = "2025-10-15T18:22:27.286Z" }, + { url = "https://files.pythonhosted.org/packages/5d/57/d60d343709366a353dc56adb4ee1e7d8a2cc34e3fbc22905f4167cfec119/pillow-12.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399", size = 3576912, upload-time = "2025-10-15T18:22:28.751Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a4/a0a31467e3f83b94d37568294b01d22b43ae3c5d85f2811769b9c66389dd/pillow-12.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5", size = 5249132, upload-time = "2025-10-15T18:22:30.641Z" }, + { url = "https://files.pythonhosted.org/packages/83/06/48eab21dd561de2914242711434c0c0eb992ed08ff3f6107a5f44527f5e9/pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b", size = 4650099, upload-time = "2025-10-15T18:22:32.73Z" }, + { url = "https://files.pythonhosted.org/packages/fc/bd/69ed99fd46a8dba7c1887156d3572fe4484e3f031405fcc5a92e31c04035/pillow-12.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3", size = 6230808, upload-time = "2025-10-15T18:22:34.337Z" }, + { url = "https://files.pythonhosted.org/packages/ea/94/8fad659bcdbf86ed70099cb60ae40be6acca434bbc8c4c0d4ef356d7e0de/pillow-12.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07", size = 8037804, upload-time = "2025-10-15T18:22:36.402Z" }, + { url = "https://files.pythonhosted.org/packages/20/39/c685d05c06deecfd4e2d1950e9a908aa2ca8bc4e6c3b12d93b9cafbd7837/pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e", size = 6345553, upload-time = "2025-10-15T18:22:38.066Z" }, + { url = "https://files.pythonhosted.org/packages/38/57/755dbd06530a27a5ed74f8cb0a7a44a21722ebf318edbe67ddbd7fb28f88/pillow-12.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344", size = 7037729, upload-time = "2025-10-15T18:22:39.769Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b6/7e94f4c41d238615674d06ed677c14883103dce1c52e4af16f000338cfd7/pillow-12.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27", size = 6459789, upload-time = "2025-10-15T18:22:41.437Z" }, + { url = "https://files.pythonhosted.org/packages/9c/14/4448bb0b5e0f22dd865290536d20ec8a23b64e2d04280b89139f09a36bb6/pillow-12.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79", size = 7130917, upload-time = "2025-10-15T18:22:43.152Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ca/16c6926cc1c015845745d5c16c9358e24282f1e588237a4c36d2b30f182f/pillow-12.0.0-cp313-cp313-win32.whl", hash = "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098", size = 6302391, upload-time = "2025-10-15T18:22:44.753Z" }, + { url = "https://files.pythonhosted.org/packages/6d/2a/dd43dcfd6dae9b6a49ee28a8eedb98c7d5ff2de94a5d834565164667b97b/pillow-12.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905", size = 7007477, upload-time = "2025-10-15T18:22:46.838Z" }, + { url = "https://files.pythonhosted.org/packages/77/f0/72ea067f4b5ae5ead653053212af05ce3705807906ba3f3e8f58ddf617e6/pillow-12.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a", size = 2435918, upload-time = "2025-10-15T18:22:48.399Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5e/9046b423735c21f0487ea6cb5b10f89ea8f8dfbe32576fe052b5ba9d4e5b/pillow-12.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3", size = 5251406, upload-time = "2025-10-15T18:22:49.905Z" }, + { url = "https://files.pythonhosted.org/packages/12/66/982ceebcdb13c97270ef7a56c3969635b4ee7cd45227fa707c94719229c5/pillow-12.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced", size = 4653218, upload-time = "2025-10-15T18:22:51.587Z" }, + { url = "https://files.pythonhosted.org/packages/16/b3/81e625524688c31859450119bf12674619429cab3119eec0e30a7a1029cb/pillow-12.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b", size = 6266564, upload-time = "2025-10-15T18:22:53.215Z" }, + { url = "https://files.pythonhosted.org/packages/98/59/dfb38f2a41240d2408096e1a76c671d0a105a4a8471b1871c6902719450c/pillow-12.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d", size = 8069260, upload-time = "2025-10-15T18:22:54.933Z" }, + { url = "https://files.pythonhosted.org/packages/dc/3d/378dbea5cd1874b94c312425ca77b0f47776c78e0df2df751b820c8c1d6c/pillow-12.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a", size = 6379248, upload-time = "2025-10-15T18:22:56.605Z" }, + { url = "https://files.pythonhosted.org/packages/84/b0/d525ef47d71590f1621510327acec75ae58c721dc071b17d8d652ca494d8/pillow-12.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe", size = 7066043, upload-time = "2025-10-15T18:22:58.53Z" }, + { url = "https://files.pythonhosted.org/packages/61/2c/aced60e9cf9d0cde341d54bf7932c9ffc33ddb4a1595798b3a5150c7ec4e/pillow-12.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee", size = 6490915, upload-time = "2025-10-15T18:23:00.582Z" }, + { url = "https://files.pythonhosted.org/packages/ef/26/69dcb9b91f4e59f8f34b2332a4a0a951b44f547c4ed39d3e4dcfcff48f89/pillow-12.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef", size = 7157998, upload-time = "2025-10-15T18:23:02.627Z" }, + { url = "https://files.pythonhosted.org/packages/61/2b/726235842220ca95fa441ddf55dd2382b52ab5b8d9c0596fe6b3f23dafe8/pillow-12.0.0-cp313-cp313t-win32.whl", hash = "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9", size = 6306201, upload-time = "2025-10-15T18:23:04.709Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3d/2afaf4e840b2df71344ababf2f8edd75a705ce500e5dc1e7227808312ae1/pillow-12.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b", size = 7013165, upload-time = "2025-10-15T18:23:06.46Z" }, + { url = "https://files.pythonhosted.org/packages/6f/75/3fa09aa5cf6ed04bee3fa575798ddf1ce0bace8edb47249c798077a81f7f/pillow-12.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47", size = 2437834, upload-time = "2025-10-15T18:23:08.194Z" }, + { url = "https://files.pythonhosted.org/packages/54/2a/9a8c6ba2c2c07b71bec92cf63e03370ca5e5f5c5b119b742bcc0cde3f9c5/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9", size = 4045531, upload-time = "2025-10-15T18:23:10.121Z" }, + { url = "https://files.pythonhosted.org/packages/84/54/836fdbf1bfb3d66a59f0189ff0b9f5f666cee09c6188309300df04ad71fa/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2", size = 4120554, upload-time = "2025-10-15T18:23:12.14Z" }, + { url = "https://files.pythonhosted.org/packages/0d/cd/16aec9f0da4793e98e6b54778a5fbce4f375c6646fe662e80600b8797379/pillow-12.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a", size = 3576812, upload-time = "2025-10-15T18:23:13.962Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b7/13957fda356dc46339298b351cae0d327704986337c3c69bb54628c88155/pillow-12.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b", size = 5252689, upload-time = "2025-10-15T18:23:15.562Z" }, + { url = "https://files.pythonhosted.org/packages/fc/f5/eae31a306341d8f331f43edb2e9122c7661b975433de5e447939ae61c5da/pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad", size = 4650186, upload-time = "2025-10-15T18:23:17.379Z" }, + { url = "https://files.pythonhosted.org/packages/86/62/2a88339aa40c4c77e79108facbd307d6091e2c0eb5b8d3cf4977cfca2fe6/pillow-12.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01", size = 6230308, upload-time = "2025-10-15T18:23:18.971Z" }, + { url = "https://files.pythonhosted.org/packages/c7/33/5425a8992bcb32d1cb9fa3dd39a89e613d09a22f2c8083b7bf43c455f760/pillow-12.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c", size = 8039222, upload-time = "2025-10-15T18:23:20.909Z" }, + { url = "https://files.pythonhosted.org/packages/d8/61/3f5d3b35c5728f37953d3eec5b5f3e77111949523bd2dd7f31a851e50690/pillow-12.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e", size = 6346657, upload-time = "2025-10-15T18:23:23.077Z" }, + { url = "https://files.pythonhosted.org/packages/3a/be/ee90a3d79271227e0f0a33c453531efd6ed14b2e708596ba5dd9be948da3/pillow-12.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e", size = 7038482, upload-time = "2025-10-15T18:23:25.005Z" }, + { url = "https://files.pythonhosted.org/packages/44/34/a16b6a4d1ad727de390e9bd9f19f5f669e079e5826ec0f329010ddea492f/pillow-12.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9", size = 6461416, upload-time = "2025-10-15T18:23:27.009Z" }, + { url = "https://files.pythonhosted.org/packages/b6/39/1aa5850d2ade7d7ba9f54e4e4c17077244ff7a2d9e25998c38a29749eb3f/pillow-12.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab", size = 7131584, upload-time = "2025-10-15T18:23:29.752Z" }, + { url = "https://files.pythonhosted.org/packages/bf/db/4fae862f8fad0167073a7733973bfa955f47e2cac3dc3e3e6257d10fab4a/pillow-12.0.0-cp314-cp314-win32.whl", hash = "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b", size = 6400621, upload-time = "2025-10-15T18:23:32.06Z" }, + { url = "https://files.pythonhosted.org/packages/2b/24/b350c31543fb0107ab2599464d7e28e6f856027aadda995022e695313d94/pillow-12.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b", size = 7142916, upload-time = "2025-10-15T18:23:34.71Z" }, + { url = "https://files.pythonhosted.org/packages/0f/9b/0ba5a6fd9351793996ef7487c4fdbde8d3f5f75dbedc093bb598648fddf0/pillow-12.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0", size = 2523836, upload-time = "2025-10-15T18:23:36.967Z" }, + { url = "https://files.pythonhosted.org/packages/f5/7a/ceee0840aebc579af529b523d530840338ecf63992395842e54edc805987/pillow-12.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6", size = 5255092, upload-time = "2025-10-15T18:23:38.573Z" }, + { url = "https://files.pythonhosted.org/packages/44/76/20776057b4bfd1aef4eeca992ebde0f53a4dce874f3ae693d0ec90a4f79b/pillow-12.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6", size = 4653158, upload-time = "2025-10-15T18:23:40.238Z" }, + { url = "https://files.pythonhosted.org/packages/82/3f/d9ff92ace07be8836b4e7e87e6a4c7a8318d47c2f1463ffcf121fc57d9cb/pillow-12.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1", size = 6267882, upload-time = "2025-10-15T18:23:42.434Z" }, + { url = "https://files.pythonhosted.org/packages/9f/7a/4f7ff87f00d3ad33ba21af78bfcd2f032107710baf8280e3722ceec28cda/pillow-12.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e", size = 8071001, upload-time = "2025-10-15T18:23:44.29Z" }, + { url = "https://files.pythonhosted.org/packages/75/87/fcea108944a52dad8cca0715ae6247e271eb80459364a98518f1e4f480c1/pillow-12.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca", size = 6380146, upload-time = "2025-10-15T18:23:46.065Z" }, + { url = "https://files.pythonhosted.org/packages/91/52/0d31b5e571ef5fd111d2978b84603fce26aba1b6092f28e941cb46570745/pillow-12.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925", size = 7067344, upload-time = "2025-10-15T18:23:47.898Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f4/2dd3d721f875f928d48e83bb30a434dee75a2531bca839bb996bb0aa5a91/pillow-12.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8", size = 6491864, upload-time = "2025-10-15T18:23:49.607Z" }, + { url = "https://files.pythonhosted.org/packages/30/4b/667dfcf3d61fc309ba5a15b141845cece5915e39b99c1ceab0f34bf1d124/pillow-12.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4", size = 7158911, upload-time = "2025-10-15T18:23:51.351Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2f/16cabcc6426c32218ace36bf0d55955e813f2958afddbf1d391849fee9d1/pillow-12.0.0-cp314-cp314t-win32.whl", hash = "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52", size = 6408045, upload-time = "2025-10-15T18:23:53.177Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/e29aa0c9c666cf787628d3f0dcf379f4791fba79f4936d02f8b37165bdf8/pillow-12.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a", size = 7148282, upload-time = "2025-10-15T18:23:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/c1/70/6b41bdcddf541b437bbb9f47f94d2db5d9ddef6c37ccab8c9107743748a4/pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", size = 2525630, upload-time = "2025-10-15T18:23:57.149Z" }, +] + [[package]] name = "pinecone" version = "7.3.0" @@ -1262,7 +1340,7 @@ dependencies = [ { name = "pinecone-plugin-interface" }, { name = "python-dateutil" }, { name = "typing-extensions" }, - { name = "urllib3", marker = "python_full_version < '4.0'" }, + { name = "urllib3", marker = "python_full_version < '4'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fa/38/12731d4af470851b4963eba616605868a8599ef4df51c7b6c928e5f3166d/pinecone-7.3.0.tar.gz", hash = "sha256:307edc155621d487c20dc71b76c3ad5d6f799569ba42064190d03917954f9a7b", size = 235256, upload-time = "2025-06-27T20:03:51.498Z" } wheels = [ @@ -1527,14 +1605,14 @@ wheels = [ [[package]] name = "requests-file" -version = "2.1.0" +version = "3.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/97/bf44e6c6bd8ddbb99943baf7ba8b1a8485bcd2fe0e55e5708d7fee4ff1ae/requests_file-2.1.0.tar.gz", hash = "sha256:0f549a3f3b0699415ac04d167e9cb39bccfb730cb832b4d20be3d9867356e658", size = 6891, upload-time = "2024-05-21T16:28:00.24Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3c/f8/5dc70102e4d337063452c82e1f0d95e39abfe67aa222ed8a5ddeb9df8de8/requests_file-3.0.1.tar.gz", hash = "sha256:f14243d7796c588f3521bd423c5dea2ee4cc730e54a3cac9574d78aca1272576", size = 6967, upload-time = "2025-10-20T18:56:42.279Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/25/dd878a121fcfdf38f52850f11c512e13ec87c2ea72385933818e5b6c15ce/requests_file-2.1.0-py2.py3-none-any.whl", hash = "sha256:cf270de5a4c5874e84599fc5778303d496c10ae5e870bfa378818f35d21bda5c", size = 4244, upload-time = "2024-05-21T16:27:57.733Z" }, + { url = "https://files.pythonhosted.org/packages/e1/d5/de8f089119205a09da657ed4784c584ede8381a0ce6821212a6d4ca47054/requests_file-3.0.1-py2.py3-none-any.whl", hash = "sha256:d0f5eb94353986d998f80ac63c7f146a307728be051d4d1cd390dbdb59c10fa2", size = 4514, upload-time = "2025-10-20T18:56:41.184Z" }, ] [[package]] @@ -1629,7 +1707,8 @@ version = "5.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, - { name = "pillow" }, + { name = "pillow", version = "11.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "pillow", version = "12.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, { name = "scikit-learn" }, { name = "scipy" }, { name = "torch" }, @@ -1747,12 +1826,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, ] -[[package]] -name = "tinysegmenter" -version = "0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/17/82/86982e4b6d16e4febc79c2a1d68ee3b707e8a020c5d2bc4af8052d0f136a/tinysegmenter-0.3.tar.gz", hash = "sha256:ed1f6d2e806a4758a73be589754384cbadadc7e1a414c81a166fc9adf2d40c6d", size = 16893, upload-time = "2017-07-23T11:18:29.85Z" } - [[package]] name = "tld" version = "0.13.1"