From 922164e25ca96479ae4df78c6c733fc4c847c8ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Boschi?= Date: Fri, 12 Dec 2025 14:38:37 +0100 Subject: [PATCH] fix recall trace visualization --- .../hindsight_api/engine/memory_engine.py | 112 ++-- .../hindsight_api/engine/search/__init__.py | 12 +- .../engine/search/graph_retrieval.py | 225 ++++++++ .../hindsight_api/engine/search/retrieval.py | 154 ++---- .../hindsight_api/engine/search/trace.py | 1 + .../hindsight_api/engine/search/tracer.py | 11 +- .../hindsight_api/engine/search/types.py | 5 +- hindsight-api/tests/test_combined_scoring.py | 323 ++++++++++++ hindsight-control-plane/src/app/layout.tsx | 2 +- .../src/components/documents-view.tsx | 10 +- .../src/components/entities-view.tsx | 123 +++-- .../src/components/memory-detail-panel.tsx | 75 +-- .../src/components/search-debug-view.tsx | 490 ++++++++++++++++-- uv.lock | 212 ++++---- 14 files changed, 1328 insertions(+), 427 deletions(-) create mode 100644 hindsight-api/hindsight_api/engine/search/graph_retrieval.py create mode 100644 hindsight-api/tests/test_combined_scoring.py diff --git a/hindsight-api/hindsight_api/engine/memory_engine.py b/hindsight-api/hindsight_api/engine/memory_engine.py index 464de44b..0b88a272 100644 --- a/hindsight-api/hindsight_api/engine/memory_engine.py +++ b/hindsight-api/hindsight_api/engine/memory_engine.py @@ -1203,49 +1203,57 @@ class MemoryEngine: temporal_info = f" | temporal_range={start_dt.strftime('%Y-%m-%d')} to {end_dt.strftime('%Y-%m-%d')}" log_buffer.append(f" [2] {total_retrievals}-way retrieval ({len(fact_type)} fact_types): {', '.join(timing_parts)} in {step_duration:.3f}s{temporal_info}") - # Record retrieval results for tracer (convert typed results to old format) + # Record retrieval results for tracer - per fact type if tracer: # Convert RetrievalResult to old tuple format for tracer def to_tuple_format(results): return [(r.id, r.__dict__) for r in results] - # Add semantic retrieval results - tracer.add_retrieval_results( - method_name="semantic", - results=to_tuple_format(semantic_results), - duration_seconds=aggregated_timings["semantic"], - score_field="similarity", - metadata={"limit": thinking_budget} - ) + # Add retrieval results per fact type (to show parallel execution in UI) + for idx, (ft_semantic, ft_bm25, ft_graph, ft_temporal, ft_timings, _) in enumerate(all_retrievals): + ft_name = fact_type[idx] if idx < len(fact_type) else "unknown" - # Add BM25 retrieval results - tracer.add_retrieval_results( - method_name="bm25", - results=to_tuple_format(bm25_results), - duration_seconds=aggregated_timings["bm25"], - score_field="bm25_score", - metadata={"limit": thinking_budget} - ) - - # Add graph retrieval results - tracer.add_retrieval_results( - method_name="graph", - results=to_tuple_format(graph_results), - duration_seconds=aggregated_timings["graph"], - score_field="similarity", # Graph uses similarity for activation - metadata={"budget": thinking_budget} - ) - - # Add temporal retrieval results if present - if temporal_results: + # Add semantic retrieval results for this fact type tracer.add_retrieval_results( - method_name="temporal", - results=to_tuple_format(temporal_results), - duration_seconds=aggregated_timings["temporal"], - score_field="temporal_score", - metadata={"budget": thinking_budget} + method_name="semantic", + results=to_tuple_format(ft_semantic), + duration_seconds=ft_timings.get("semantic", 0.0), + score_field="similarity", + metadata={"limit": thinking_budget}, + fact_type=ft_name ) + # Add BM25 retrieval results for this fact type + tracer.add_retrieval_results( + method_name="bm25", + results=to_tuple_format(ft_bm25), + duration_seconds=ft_timings.get("bm25", 0.0), + score_field="bm25_score", + metadata={"limit": thinking_budget}, + fact_type=ft_name + ) + + # Add graph retrieval results for this fact type + tracer.add_retrieval_results( + method_name="graph", + results=to_tuple_format(ft_graph), + duration_seconds=ft_timings.get("graph", 0.0), + score_field="activation", + metadata={"budget": thinking_budget}, + fact_type=ft_name + ) + + # Add temporal retrieval results for this fact type (even if empty, to show it ran) + if ft_temporal is not None: + tracer.add_retrieval_results( + method_name="temporal", + results=to_tuple_format(ft_temporal), + duration_seconds=ft_timings.get("temporal", 0.0), + score_field="temporal_score", + metadata={"budget": thinking_budget}, + fact_type=ft_name + ) + # Record entry points (from semantic results) for legacy graph view for rank, retrieval in enumerate(semantic_results[:10], start=1): # Top 10 as entry points tracer.add_entry_point(retrieval.id, retrieval.text, retrieval.similarity or 0.0, rank) @@ -1287,31 +1295,24 @@ class MemoryEngine: step_duration = time.time() - step_start log_buffer.append(f" [4] Reranking: {len(scored_results)} candidates scored in {step_duration:.3f}s") - if tracer: - # Convert to old format for tracer - results_dict = [sr.to_dict() for sr in scored_results] - tracer_merged = [(mc.id, mc.retrieval.__dict__, {"rrf_score": mc.rrf_score, **mc.source_ranks}) - for mc in merged_candidates] - tracer.add_reranked(results_dict, tracer_merged) - tracer.add_phase_metric("reranking", step_duration, { - "reranker_type": "cross-encoder", - "candidates_reranked": len(scored_results) - }) - # Step 4.5: Combine cross-encoder score with retrieval signals # This preserves retrieval work (RRF, temporal, recency) instead of pure cross-encoder ranking if scored_results: - # Normalize RRF scores to [0, 1] range + # Normalize RRF scores to [0, 1] range using min-max normalization rrf_scores = [sr.candidate.rrf_score for sr in scored_results] - max_rrf = max(rrf_scores) if rrf_scores else 1.0 + max_rrf = max(rrf_scores) if rrf_scores else 0.0 min_rrf = min(rrf_scores) if rrf_scores else 0.0 - rrf_range = max_rrf - min_rrf if max_rrf > min_rrf else 1.0 + rrf_range = max_rrf - min_rrf # Don't force to 1.0, let fallback handle it # Calculate recency based on occurred_start (more recent = higher score) now = utcnow() for sr in scored_results: - # Normalize RRF score - sr.rrf_normalized = (sr.candidate.rrf_score - min_rrf) / rrf_range if rrf_range > 0 else 0.5 + # Normalize RRF score (0-1 range, 0.5 if all same) + if rrf_range > 0: + sr.rrf_normalized = (sr.candidate.rrf_score - min_rrf) / rrf_range + else: + # All RRF scores are the same, use neutral value + sr.rrf_normalized = 0.5 # Calculate recency (decay over 365 days, minimum 0.1) sr.recency = 0.5 # default for missing dates @@ -1343,6 +1344,17 @@ class MemoryEngine: scored_results.sort(key=lambda x: x.weight, reverse=True) log_buffer.append(f" [4.6] Combined scoring: cross_encoder(0.6) + rrf(0.2) + temporal(0.1) + recency(0.1)") + # Add reranked results to tracer AFTER combined scoring (so normalized values are included) + if tracer: + results_dict = [sr.to_dict() for sr in scored_results] + tracer_merged = [(mc.id, mc.retrieval.__dict__, {"rrf_score": mc.rrf_score, **mc.source_ranks}) + for mc in merged_candidates] + tracer.add_reranked(results_dict, tracer_merged) + tracer.add_phase_metric("reranking", step_duration, { + "reranker_type": "cross-encoder", + "candidates_reranked": len(scored_results) + }) + # Step 5: Truncate to thinking_budget * 2 for token filtering rerank_limit = thinking_budget * 2 top_scored = scored_results[:rerank_limit] diff --git a/hindsight-api/hindsight_api/engine/search/__init__.py b/hindsight-api/hindsight_api/engine/search/__init__.py index aef63cd5..20d3c8a0 100644 --- a/hindsight-api/hindsight_api/engine/search/__init__.py +++ b/hindsight-api/hindsight_api/engine/search/__init__.py @@ -3,13 +3,23 @@ Search module for memory retrieval. Provides modular search architecture: - Retrieval: 4-way parallel (semantic + BM25 + graph + temporal) +- Graph retrieval: Pluggable strategies (BFS, PPR) - Reranking: Pluggable strategies (heuristic, cross-encoder) """ -from .retrieval import retrieve_parallel +from .retrieval import ( + retrieve_parallel, + get_default_graph_retriever, + set_default_graph_retriever, +) +from .graph_retrieval import GraphRetriever, BFSGraphRetriever from .reranking import CrossEncoderReranker __all__ = [ "retrieve_parallel", + "get_default_graph_retriever", + "set_default_graph_retriever", + "GraphRetriever", + "BFSGraphRetriever", "CrossEncoderReranker", ] diff --git a/hindsight-api/hindsight_api/engine/search/graph_retrieval.py b/hindsight-api/hindsight_api/engine/search/graph_retrieval.py new file mode 100644 index 00000000..2ebfa3cd --- /dev/null +++ b/hindsight-api/hindsight_api/engine/search/graph_retrieval.py @@ -0,0 +1,225 @@ +""" +Graph retrieval strategies for memory recall. + +This module provides an abstraction for graph-based memory retrieval, +allowing different algorithms (BFS spreading activation, PPR, etc.) to be +swapped without changing the rest of the recall pipeline. +""" + +from abc import ABC, abstractmethod +from typing import List, Optional +from datetime import datetime +import logging + +from .types import RetrievalResult +from ..db_utils import acquire_with_retry + +logger = logging.getLogger(__name__) + + +class GraphRetriever(ABC): + """ + Abstract base class for graph-based memory retrieval. + + Implementations traverse the memory graph (entity links, temporal links, + causal links) to find relevant facts that might not be found by + semantic or keyword search alone. + """ + + @property + @abstractmethod + def name(self) -> str: + """Return identifier for this retrieval strategy (e.g., 'bfs', 'ppr').""" + pass + + @abstractmethod + async def retrieve( + self, + pool, + query_embedding_str: str, + bank_id: str, + fact_type: str, + budget: int, + query_text: Optional[str] = None, + ) -> List[RetrievalResult]: + """ + Retrieve relevant facts via graph traversal. + + Args: + pool: Database connection pool + query_embedding_str: Query embedding as string (for finding entry points) + bank_id: Memory bank identifier + fact_type: Fact type to filter ('world', 'experience', 'opinion', 'observation') + budget: Maximum number of nodes to explore/return + query_text: Original query text (optional, for some strategies) + + Returns: + List of RetrievalResult objects with activation scores set + """ + pass + + +class BFSGraphRetriever(GraphRetriever): + """ + Graph retrieval using BFS-style spreading activation. + + Starting from semantic entry points, spreads activation through + the memory graph (entity, temporal, causal links) using breadth-first + traversal with decaying activation. + + This is the original Hindsight graph retrieval algorithm. + """ + + def __init__( + self, + entry_point_limit: int = 5, + entry_point_threshold: float = 0.5, + activation_decay: float = 0.8, + min_activation: float = 0.1, + batch_size: int = 20, + ): + """ + Initialize BFS graph retriever. + + Args: + entry_point_limit: Maximum number of entry points to start from + entry_point_threshold: Minimum semantic similarity for entry points + activation_decay: Decay factor per hop (activation *= decay) + min_activation: Minimum activation to continue spreading + batch_size: Number of nodes to process per batch (for neighbor fetching) + """ + self.entry_point_limit = entry_point_limit + self.entry_point_threshold = entry_point_threshold + self.activation_decay = activation_decay + self.min_activation = min_activation + self.batch_size = batch_size + + @property + def name(self) -> str: + return "bfs" + + async def retrieve( + self, + pool, + query_embedding_str: str, + bank_id: str, + fact_type: str, + budget: int, + query_text: Optional[str] = None, + ) -> List[RetrievalResult]: + """ + Retrieve facts using BFS spreading activation. + + Algorithm: + 1. Find entry points (top semantic matches above threshold) + 2. BFS traversal: visit neighbors, propagate decaying activation + 3. Boost causal links (causes, enables, prevents) + 4. Return visited nodes up to budget + """ + async with acquire_with_retry(pool) as conn: + return await self._retrieve_with_conn( + conn, query_embedding_str, bank_id, fact_type, budget + ) + + async def _retrieve_with_conn( + self, + conn, + query_embedding_str: str, + bank_id: str, + fact_type: str, + budget: int, + ) -> List[RetrievalResult]: + """Internal implementation with connection.""" + + # Step 1: Find entry points + entry_points = await conn.fetch( + """ + SELECT id, text, context, event_date, occurred_start, occurred_end, + mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, + 1 - (embedding <=> $1::vector) AS similarity + FROM memory_units + WHERE bank_id = $2 + AND embedding IS NOT NULL + AND fact_type = $3 + AND (1 - (embedding <=> $1::vector)) >= $4 + ORDER BY embedding <=> $1::vector + LIMIT $5 + """, + query_embedding_str, bank_id, fact_type, + self.entry_point_threshold, self.entry_point_limit + ) + + if not entry_points: + return [] + + # Step 2: BFS spreading activation + visited = set() + results = [] + queue = [ + (RetrievalResult.from_db_row(dict(r)), r["similarity"]) + for r in entry_points + ] + budget_remaining = budget + + while queue and budget_remaining > 0: + # Collect a batch of nodes to process + batch_nodes = [] + batch_activations = {} + + while queue and len(batch_nodes) < self.batch_size and budget_remaining > 0: + current, activation = queue.pop(0) + unit_id = current.id + + if unit_id not in visited: + visited.add(unit_id) + budget_remaining -= 1 + current.activation = activation + results.append(current) + batch_nodes.append(current.id) + batch_activations[unit_id] = activation + + # Batch fetch neighbors + if batch_nodes and budget_remaining > 0: + max_neighbors = len(batch_nodes) * 20 + neighbors = await conn.fetch( + """ + SELECT mu.id, mu.text, mu.context, mu.occurred_start, mu.occurred_end, + mu.mentioned_at, mu.access_count, mu.embedding, mu.fact_type, + mu.document_id, mu.chunk_id, + ml.weight, ml.link_type, ml.from_unit_id + FROM memory_links ml + JOIN memory_units mu ON ml.to_unit_id = mu.id + WHERE ml.from_unit_id = ANY($1::uuid[]) + AND ml.weight >= $2 + AND mu.fact_type = $3 + ORDER BY ml.weight DESC + LIMIT $4 + """, + batch_nodes, self.min_activation, fact_type, max_neighbors + ) + + for n in neighbors: + neighbor_id = str(n["id"]) + if neighbor_id not in visited: + parent_id = str(n["from_unit_id"]) + parent_activation = batch_activations.get(parent_id, 0.5) + + # Boost causal links + link_type = n["link_type"] + base_weight = n["weight"] + + if link_type in ("causes", "caused_by"): + causal_boost = 2.0 + elif link_type in ("enables", "prevents"): + causal_boost = 1.5 + else: + causal_boost = 1.0 + + effective_weight = base_weight * causal_boost + new_activation = parent_activation * effective_weight * self.activation_decay + + if new_activation > self.min_activation: + neighbor_result = RetrievalResult.from_db_row(dict(n)) + queue.append((neighbor_result, new_activation)) + + return results diff --git a/hindsight-api/hindsight_api/engine/search/retrieval.py b/hindsight-api/hindsight_api/engine/search/retrieval.py index f2784f35..3fa8e142 100644 --- a/hindsight-api/hindsight_api/engine/search/retrieval.py +++ b/hindsight-api/hindsight_api/engine/search/retrieval.py @@ -4,7 +4,7 @@ Retrieval module for 4-way parallel search. Implements: 1. Semantic retrieval (vector similarity) 2. BM25 retrieval (keyword/full-text search) -3. Graph retrieval (spreading activation) +3. Graph retrieval (via pluggable GraphRetriever interface) 4. Temporal retrieval (time-aware search with spreading) """ @@ -13,6 +13,24 @@ from datetime import datetime import asyncio from ..db_utils import acquire_with_retry from .types import RetrievalResult +from .graph_retrieval import GraphRetriever, BFSGraphRetriever + +# Default graph retriever instance (can be overridden) +_default_graph_retriever: Optional[GraphRetriever] = None + + +def get_default_graph_retriever() -> GraphRetriever: + """Get or create the default graph retriever.""" + global _default_graph_retriever + if _default_graph_retriever is None: + _default_graph_retriever = BFSGraphRetriever() + return _default_graph_retriever + + +def set_default_graph_retriever(retriever: GraphRetriever) -> None: + """Set the default graph retriever (for configuration/testing).""" + global _default_graph_retriever + _default_graph_retriever = retriever async def retrieve_semantic( @@ -105,121 +123,6 @@ async def retrieve_bm25( return [RetrievalResult.from_db_row(dict(r)) for r in results] -async def retrieve_graph( - conn, - query_emb_str: str, - bank_id: str, - fact_type: str, - budget: int -) -> List[RetrievalResult]: - """ - Graph retrieval via spreading activation. - - Args: - conn: Database connection - query_emb_str: Query embedding as string - agent_id: bank ID - fact_type: Fact type to filter - budget: Node budget for graph traversal - - Returns: - List of RetrievalResult objects - """ - # Find entry points - entry_points = await conn.fetch( - """ - SELECT id, text, context, event_date, occurred_start, occurred_end, mentioned_at, access_count, embedding, fact_type, document_id, chunk_id, - 1 - (embedding <=> $1::vector) AS similarity - FROM memory_units - WHERE bank_id = $2 - AND embedding IS NOT NULL - AND fact_type = $3 - AND (1 - (embedding <=> $1::vector)) >= 0.5 - ORDER BY embedding <=> $1::vector - LIMIT 5 - """, - query_emb_str, bank_id, fact_type - ) - - if not entry_points: - return [] - - # BFS-style spreading activation with batched neighbor fetching - visited = set() - results = [] - queue = [(RetrievalResult.from_db_row(dict(r)), r["similarity"]) for r in entry_points] - budget_remaining = budget - - # Process nodes in batches to reduce DB roundtrips - batch_size = 20 # Fetch neighbors for up to 20 nodes at once - - while queue and budget_remaining > 0: - # Collect a batch of nodes to process - batch_nodes = [] - batch_activations = {} - - while queue and len(batch_nodes) < batch_size and budget_remaining > 0: - current, activation = queue.pop(0) - unit_id = current.id - - if unit_id not in visited: - visited.add(unit_id) - budget_remaining -= 1 - results.append(current) - batch_nodes.append(current.id) - batch_activations[unit_id] = activation - - # Batch fetch neighbors for all nodes in this batch - # Fetch top weighted neighbors (batch_size * 20 = ~400 for good distribution) - if batch_nodes and budget_remaining > 0: - max_neighbors = len(batch_nodes) * 20 - neighbors = await conn.fetch( - """ - SELECT mu.id, mu.text, mu.context, mu.occurred_start, mu.occurred_end, mu.mentioned_at, - mu.access_count, mu.embedding, mu.fact_type, mu.document_id, mu.chunk_id, - ml.weight, ml.link_type, ml.from_unit_id - FROM memory_links ml - JOIN memory_units mu ON ml.to_unit_id = mu.id - WHERE ml.from_unit_id = ANY($1::uuid[]) - AND ml.weight >= 0.1 - AND mu.fact_type = $2 - ORDER BY ml.weight DESC - LIMIT $3 - """, - batch_nodes, fact_type, max_neighbors - ) - - for n in neighbors: - neighbor_id = str(n["id"]) - if neighbor_id not in visited: - # Get parent activation - parent_id = str(n["from_unit_id"]) - activation = batch_activations.get(parent_id, 0.5) - - # Boost activation for causal links (they're high-value relationships) - link_type = n["link_type"] - base_weight = n["weight"] - - # Causal links get 1.5-2.0x boost depending on type - if link_type in ("causes", "caused_by"): - # Direct causation - very strong relationship - causal_boost = 2.0 - elif link_type in ("enables", "prevents"): - # Conditional causation - strong but not as direct - causal_boost = 1.5 - else: - # Temporal, semantic, entity links - standard weight - causal_boost = 1.0 - - effective_weight = base_weight * causal_boost - new_activation = activation * effective_weight * 0.8 - if new_activation > 0.1: - neighbor_result = RetrievalResult.from_db_row(dict(n)) - queue.append((neighbor_result, new_activation)) - - return results - - async def retrieve_temporal( conn, query_emb_str: str, @@ -419,7 +322,8 @@ async def retrieve_parallel( fact_type: str, thinking_budget: int, question_date: Optional[datetime] = None, - query_analyzer: Optional["QueryAnalyzer"] = None + query_analyzer: Optional["QueryAnalyzer"] = None, + graph_retriever: Optional[GraphRetriever] = None, ) -> Tuple[List[RetrievalResult], List[RetrievalResult], List[RetrievalResult], Optional[List[RetrievalResult]], Dict[str, float], Optional[Tuple[datetime, datetime]]]: """ Run 3-way or 4-way parallel retrieval (adds temporal if detected). @@ -428,11 +332,12 @@ async def retrieve_parallel( pool: Database connection pool query_text: Query text query_embedding_str: Query embedding as string - agent_id: bank ID + bank_id: Bank ID fact_type: Fact type to filter thinking_budget: Budget for graph traversal and retrieval limits question_date: Optional date when question was asked (for temporal filtering) query_analyzer: Query analyzer to use (defaults to TransformerQueryAnalyzer) + graph_retriever: Graph retrieval strategy (defaults to BFSGraphRetriever) Returns: Tuple of (semantic_results, bm25_results, graph_results, temporal_results, timings, temporal_constraint) @@ -449,6 +354,9 @@ async def retrieve_parallel( query_text, reference_date=question_date, analyzer=query_analyzer ) + # Use provided graph retriever or default + retriever = graph_retriever or get_default_graph_retriever() + # Wrapper to track timing for each retrieval method async def timed_retrieval(name: str, coro): start = time.time() @@ -465,8 +373,14 @@ async def retrieve_parallel( return await retrieve_bm25(conn, query_text, bank_id, fact_type, limit=thinking_budget) async def run_graph(): - async with acquire_with_retry(pool) as conn: - return await retrieve_graph(conn, query_embedding_str, bank_id, fact_type, budget=thinking_budget) + return await retriever.retrieve( + pool=pool, + query_embedding_str=query_embedding_str, + bank_id=bank_id, + fact_type=fact_type, + budget=thinking_budget, + query_text=query_text, + ) async def run_temporal(start_date, end_date): async with acquire_with_retry(pool) as conn: diff --git a/hindsight-api/hindsight_api/engine/search/trace.py b/hindsight-api/hindsight_api/engine/search/trace.py index f3fcef85..959ec93b 100644 --- a/hindsight-api/hindsight_api/engine/search/trace.py +++ b/hindsight-api/hindsight_api/engine/search/trace.py @@ -108,6 +108,7 @@ class RetrievalResult(BaseModel): class RetrievalMethodResults(BaseModel): """Results from a single retrieval method.""" method_name: Literal["semantic", "bm25", "graph", "temporal"] = Field(description="Name of retrieval method") + fact_type: Optional[str] = Field(default=None, description="Fact type this retrieval was for (world, experience, opinion)") results: List[RetrievalResult] = Field(description="Retrieved results with ranks") duration_seconds: float = Field(description="Time taken for this retrieval") metadata: Dict[str, Any] = Field(default_factory=dict, description="Method-specific metadata") diff --git a/hindsight-api/hindsight_api/engine/search/tracer.py b/hindsight-api/hindsight_api/engine/search/tracer.py index cb2b8bf7..8d4312a6 100644 --- a/hindsight-api/hindsight_api/engine/search/tracer.py +++ b/hindsight-api/hindsight_api/engine/search/tracer.py @@ -289,7 +289,8 @@ class SearchTracer: results: List[tuple], # List of (doc_id, data) tuples duration_seconds: float, score_field: str, # e.g., "similarity", "bm25_score" - metadata: Optional[Dict[str, Any]] = None + metadata: Optional[Dict[str, Any]] = None, + fact_type: Optional[str] = None ): """ Record results from a single retrieval method. @@ -300,6 +301,7 @@ class SearchTracer: duration_seconds: Time taken for this retrieval score_field: Field name containing the score in data dict metadata: Optional metadata about this retrieval method + fact_type: Fact type this retrieval was for (world, experience, opinion) """ retrieval_results = [] for rank, (doc_id, data) in enumerate(results, start=1): @@ -313,7 +315,7 @@ class SearchTracer: text=data.get("text", ""), context=data.get("context", ""), event_date=data.get("event_date"), - fact_type=data.get("fact_type"), + fact_type=data.get("fact_type") or fact_type, score=score, score_name=score_field, ) @@ -322,6 +324,7 @@ class SearchTracer: self.retrieval_results.append( RetrievalMethodResults( method_name=method_name, + fact_type=fact_type, results=retrieval_results, duration_seconds=duration_seconds, metadata=metadata or {}, @@ -367,8 +370,10 @@ class SearchTracer: rank_change = rrf_rank - rank # Positive = moved up # Extract score components (only include non-None values) + # Keys from ScoredResult.to_dict(): cross_encoder_score, cross_encoder_score_normalized, + # rrf_normalized, temporal, recency, combined_score, weight score_components = {} - for key in ["semantic_similarity", "bm25_score", "rrf_score", "recency_normalized", "frequency_normalized", "cross_encoder_score", "cross_encoder_score_normalized"]: + for key in ["cross_encoder_score", "cross_encoder_score_normalized", "rrf_score", "rrf_normalized", "temporal", "recency", "combined_score"]: if key in result and result[key] is not None: score_components[key] = result[key] diff --git a/hindsight-api/hindsight_api/engine/search/types.py b/hindsight-api/hindsight_api/engine/search/types.py index 431ddecf..5e6c234d 100644 --- a/hindsight-api/hindsight_api/engine/search/types.py +++ b/hindsight-api/hindsight_api/engine/search/types.py @@ -31,8 +31,9 @@ class RetrievalResult: embedding: Optional[List[float]] = None # Retrieval-specific scores (only one will be set depending on retrieval method) - similarity: Optional[float] = None # Semantic/graph retrieval + similarity: Optional[float] = None # Semantic retrieval bm25_score: Optional[float] = None # BM25 retrieval + activation: Optional[float] = None # Graph retrieval (spreading activation) temporal_score: Optional[float] = None # Temporal retrieval temporal_proximity: Optional[float] = None # Temporal retrieval @@ -54,6 +55,7 @@ class RetrievalResult: embedding=row.get("embedding"), similarity=row.get("similarity"), bm25_score=row.get("bm25_score"), + activation=row.get("activation"), temporal_score=row.get("temporal_score"), temporal_proximity=row.get("temporal_proximity"), ) @@ -152,6 +154,7 @@ class ScoredResult: result["cross_encoder_score"] = self.cross_encoder_score result["cross_encoder_score_normalized"] = self.cross_encoder_score_normalized result["rrf_normalized"] = self.rrf_normalized + result["temporal"] = self.temporal result["recency"] = self.recency result["combined_score"] = self.combined_score result["weight"] = self.weight diff --git a/hindsight-api/tests/test_combined_scoring.py b/hindsight-api/tests/test_combined_scoring.py new file mode 100644 index 00000000..82f22c9e --- /dev/null +++ b/hindsight-api/tests/test_combined_scoring.py @@ -0,0 +1,323 @@ +""" +Tests for combined scoring functionality. + +Verifies that: +1. RRF scores are properly normalized to [0, 1] range +2. Combined scoring formula is applied correctly +3. Tracer captures normalized values (not raw values) +""" +import pytest +from datetime import datetime, timezone +from hindsight_api.engine.search.types import RetrievalResult, MergedCandidate, ScoredResult +from hindsight_api.engine.memory_engine import Budget + + +class TestRRFNormalization: + """Test that RRF scores are properly normalized.""" + + def test_rrf_normalized_range(self): + """RRF normalized values should be in [0, 1] range, not raw [0.04, 0.06].""" + # Simulate RRF scores like what we get from actual retrieval + raw_rrf_scores = [0.0607, 0.0550, 0.0480, 0.0390] + + max_rrf = max(raw_rrf_scores) + min_rrf = min(raw_rrf_scores) + rrf_range = max_rrf - min_rrf + + normalized = [] + for score in raw_rrf_scores: + if rrf_range > 0: + norm = (score - min_rrf) / rrf_range + else: + norm = 0.5 + normalized.append(norm) + + # Verify normalized values are in [0, 1] + for i, norm in enumerate(normalized): + assert 0.0 <= norm <= 1.0, f"Normalized RRF {norm} not in [0, 1] for raw {raw_rrf_scores[i]}" + + # Highest raw should be 1.0 + assert normalized[0] == 1.0, f"Highest RRF should normalize to 1.0, got {normalized[0]}" + + # Lowest raw should be 0.0 + assert normalized[-1] == 0.0, f"Lowest RRF should normalize to 0.0, got {normalized[-1]}" + + def test_rrf_all_same_scores(self): + """When all RRF scores are the same, normalized should be 0.5 (neutral).""" + raw_rrf_scores = [0.0500, 0.0500, 0.0500] + + max_rrf = max(raw_rrf_scores) + min_rrf = min(raw_rrf_scores) + rrf_range = max_rrf - min_rrf + + normalized = [] + for score in raw_rrf_scores: + if rrf_range > 0: + norm = (score - min_rrf) / rrf_range + else: + norm = 0.5 # Neutral value when all same + normalized.append(norm) + + # All should be 0.5 when scores are identical + for norm in normalized: + assert norm == 0.5, f"Expected 0.5 for identical scores, got {norm}" + + +class TestCombinedScoringFormula: + """Test that the combined scoring formula is applied correctly.""" + + def test_combined_score_calculation(self): + """Verify the weighted combination: 0.6*CE + 0.2*RRF + 0.1*temporal + 0.1*recency.""" + # Test case 1: All components at 1.0 + ce_norm = 1.0 + rrf_norm = 1.0 + temporal = 1.0 + recency = 1.0 + + expected = 0.6 * ce_norm + 0.2 * rrf_norm + 0.1 * temporal + 0.1 * recency + assert expected == 1.0, f"All 1.0 should give 1.0, got {expected}" + + # Test case 2: All components at 0.0 + ce_norm = 0.0 + rrf_norm = 0.0 + temporal = 0.0 + recency = 0.0 + + expected = 0.6 * ce_norm + 0.2 * rrf_norm + 0.1 * temporal + 0.1 * recency + assert expected == 0.0, f"All 0.0 should give 0.0, got {expected}" + + # Test case 3: High CE, low RRF (cross-encoder finds something retrieval missed) + ce_norm = 0.999 + rrf_norm = 0.0 # Lowest in set + temporal = 0.5 + recency = 0.5 + + expected = 0.6 * ce_norm + 0.2 * rrf_norm + 0.1 * temporal + 0.1 * recency + # 0.5994 + 0.0 + 0.05 + 0.05 = 0.6994 + assert abs(expected - 0.6994) < 0.001, f"Expected ~0.6994, got {expected}" + + # Test case 4: Medium CE, high RRF (retrieval consensus) + ce_norm = 0.8 + rrf_norm = 1.0 # Highest in set + temporal = 0.5 + recency = 0.5 + + expected = 0.6 * ce_norm + 0.2 * rrf_norm + 0.1 * temporal + 0.1 * recency + # 0.48 + 0.2 + 0.05 + 0.05 = 0.78 + assert abs(expected - 0.78) < 0.001, f"Expected ~0.78, got {expected}" + + def test_rrf_contribution_is_significant(self): + """Verify RRF actually contributes to the final score (not negligible).""" + # Same CE, different RRF + ce_norm = 0.8 + temporal = 0.5 + recency = 0.5 + + # Low RRF + score_low_rrf = 0.6 * ce_norm + 0.2 * 0.0 + 0.1 * temporal + 0.1 * recency + + # High RRF + score_high_rrf = 0.6 * ce_norm + 0.2 * 1.0 + 0.1 * temporal + 0.1 * recency + + # Difference should be 0.2 (20% contribution) + diff = score_high_rrf - score_low_rrf + assert abs(diff - 0.2) < 0.001, f"RRF should contribute 0.2 difference, got {diff}" + + +@pytest.mark.asyncio +async def test_trace_has_normalized_rrf(memory): + """Integration test: verify trace contains normalized RRF values, not raw.""" + bank_id = f"test_scoring_{datetime.now(timezone.utc).timestamp()}" + + try: + # Store multiple memories to ensure different RRF scores + await memory.retain_async( + bank_id=bank_id, + content="Python is a programming language created by Guido van Rossum", + context="tech facts", + ) + await memory.retain_async( + bank_id=bank_id, + content="JavaScript was created by Brendan Eich at Netscape", + context="tech facts", + ) + await memory.retain_async( + bank_id=bank_id, + content="The Eiffel Tower is located in Paris, France", + context="geography facts", + ) + await memory.retain_async( + bank_id=bank_id, + content="Mount Everest is the tallest mountain on Earth", + context="geography facts", + ) + + # Search with tracing + result = await memory.recall_async( + bank_id=bank_id, + query="programming languages", + fact_type=["world"], + budget=Budget.LOW, + max_tokens=1024, + enable_trace=True, + ) + + assert result.trace is not None, "Trace should be present" + trace = result.trace + + # Check reranked results have proper score_components + assert "reranked" in trace, "Trace should have reranked results" + assert len(trace["reranked"]) > 0, "Should have reranked results" + + has_valid_rrf = False + has_valid_temporal = False + has_valid_recency = False + + for r in trace["reranked"]: + sc = r.get("score_components", {}) + + # Check RRF normalized is present and in valid range + if "rrf_normalized" in sc: + rrf_norm = sc["rrf_normalized"] + assert 0.0 <= rrf_norm <= 1.0, f"rrf_normalized {rrf_norm} should be in [0, 1]" + # Should NOT be raw RRF score (which would be ~0.04-0.06) + # A normalized value of exactly 0.0 or 1.0 is valid (min/max of set) + # But raw scores like 0.0607 should never appear as normalized + if rrf_norm > 0.1: # Any value > 0.1 is likely properly normalized + has_valid_rrf = True + + # Check temporal is present and in valid range + if "temporal" in sc: + temporal = sc["temporal"] + assert 0.0 <= temporal <= 1.0, f"temporal {temporal} should be in [0, 1]" + has_valid_temporal = True + + # Check recency is present and in valid range + if "recency" in sc: + recency = sc["recency"] + assert 0.0 <= recency <= 1.0, f"recency {recency} should be in [0, 1]" + has_valid_recency = True + + # At least some results should have these components + # (might not have rrf > 0.1 if all scores are same, which is fine) + assert has_valid_temporal, "Should have temporal scores in trace" + assert has_valid_recency, "Should have recency scores in trace" + + print("\n✓ Combined scoring trace test passed!") + print(f" - Reranked results: {len(trace['reranked'])}") + if trace["reranked"]: + sc = trace["reranked"][0].get("score_components", {}) + print(f" - First result score components: {sc}") + + finally: + await memory.delete_bank(bank_id) + + +@pytest.mark.asyncio +async def test_rrf_normalized_not_raw_in_trace(memory): + """Verify that raw RRF scores (0.04-0.06 range) don't appear as normalized values.""" + bank_id = f"test_rrf_raw_{datetime.now(timezone.utc).timestamp()}" + + try: + # Store enough memories to get varied RRF scores + for i in range(5): + await memory.retain_async( + bank_id=bank_id, + content=f"Test fact number {i} about various topics", + context="test context", + ) + + result = await memory.recall_async( + bank_id=bank_id, + query="test fact", + fact_type=["world"], + budget=Budget.LOW, + max_tokens=512, + enable_trace=True, + ) + + trace = result.trace + assert trace is not None + + # Check that rrf_normalized values are NOT in the raw range + raw_rrf_range = (0.01, 0.08) # Raw RRF scores are typically in this range + + for r in trace.get("reranked", []): + sc = r.get("score_components", {}) + + if "rrf_normalized" in sc and "rrf_score" in sc: + rrf_norm = sc["rrf_normalized"] + rrf_raw = sc["rrf_score"] + + # Raw should be in the typical range + assert raw_rrf_range[0] <= rrf_raw <= raw_rrf_range[1], \ + f"Raw RRF {rrf_raw} should be in typical range {raw_rrf_range}" + + # Normalized should either be: + # - 0.0 (min in set) + # - 1.0 (max in set) + # - 0.5 (all same) + # - Something in between (0.0 to 1.0) + # But NOT the same as raw (which would indicate no normalization) + if len(trace["reranked"]) > 1: + # If we have multiple results, normalized should differ from raw + # (unless by coincidence, which is very unlikely) + assert rrf_norm != rrf_raw, \ + f"Normalized RRF ({rrf_norm}) should differ from raw ({rrf_raw})" + + print("\n✓ RRF raw vs normalized test passed!") + + finally: + await memory.delete_bank(bank_id) + + +@pytest.mark.asyncio +async def test_combined_score_matches_components(memory): + """Verify the final score actually equals the weighted sum of components.""" + bank_id = f"test_combined_{datetime.now(timezone.utc).timestamp()}" + + try: + await memory.retain_async( + bank_id=bank_id, + content="The quick brown fox jumps over the lazy dog", + context="test", + ) + await memory.retain_async( + bank_id=bank_id, + content="A quick test of the emergency broadcast system", + context="test", + ) + + result = await memory.recall_async( + bank_id=bank_id, + query="quick test", + fact_type=["world"], + budget=Budget.LOW, + max_tokens=512, + enable_trace=True, + ) + + trace = result.trace + assert trace is not None + + for r in trace.get("reranked", []): + sc = r.get("score_components", {}) + final_score = r.get("rerank_score", 0) + + # Get components (use defaults if missing) + ce = sc.get("cross_encoder_score_normalized", 0) + rrf = sc.get("rrf_normalized", 0.5) + tmp = sc.get("temporal", 0.5) + rec = sc.get("recency", 0.5) + + # Calculate expected score + expected = 0.6 * ce + 0.2 * rrf + 0.1 * tmp + 0.1 * rec + + # Allow small floating point difference + assert abs(final_score - expected) < 0.01, \ + f"Final score {final_score} doesn't match expected {expected} from components" + + print("\n✓ Combined score verification test passed!") + + finally: + await memory.delete_bank(bank_id) diff --git a/hindsight-control-plane/src/app/layout.tsx b/hindsight-control-plane/src/app/layout.tsx index 01ae1415..ddefc242 100644 --- a/hindsight-control-plane/src/app/layout.tsx +++ b/hindsight-control-plane/src/app/layout.tsx @@ -18,7 +18,7 @@ export default function RootLayout({ }>) { return ( - + {children} diff --git a/hindsight-control-plane/src/components/documents-view.tsx b/hindsight-control-plane/src/components/documents-view.tsx index 5ece2cf1..ad431ea1 100644 --- a/hindsight-control-plane/src/components/documents-view.tsx +++ b/hindsight-control-plane/src/components/documents-view.tsx @@ -193,7 +193,7 @@ export function DocumentsView() { {/* Document ID */}
Document ID
-
{selectedDocument.id}
+ {selectedDocument.id}
{/* Created & Memory Units */} @@ -201,11 +201,11 @@ export function DocumentsView() {
Created
-
{new Date(selectedDocument.created_at).toLocaleString()}
+
{new Date(selectedDocument.created_at).toLocaleString()}
Memory Units
-
{selectedDocument.memory_unit_count}
+
{selectedDocument.memory_unit_count}
)} @@ -214,7 +214,7 @@ export function DocumentsView() { {selectedDocument.original_text && (
Text Length
-
{selectedDocument.original_text.length.toLocaleString()} characters
+
{selectedDocument.original_text.length.toLocaleString()} characters
)} @@ -244,7 +244,7 @@ export function DocumentsView() {
Original Text
-
{selectedDocument.original_text}
+
{selectedDocument.original_text}
)} diff --git a/hindsight-control-plane/src/components/entities-view.tsx b/hindsight-control-plane/src/components/entities-view.tsx index 7752d94c..767cc976 100644 --- a/hindsight-control-plane/src/components/entities-view.tsx +++ b/hindsight-control-plane/src/components/entities-view.tsx @@ -92,9 +92,9 @@ export function EntitiesView() { }; return ( -
+
{/* Entity List */} -
+
{loading ? (
@@ -111,7 +111,6 @@ export function EntitiesView() { - ID Name Mentions First Seen @@ -123,11 +122,10 @@ export function EntitiesView() { loadEntityDetail(entity.id)} - className={`cursor-pointer ${ - selectedEntity?.id === entity.id ? 'bg-accent' : '' + className={`cursor-pointer hover:bg-muted/50 ${ + selectedEntity?.id === entity.id ? 'bg-primary/10' : '' }`} > - {entity.id.slice(0, 8)}... {entity.canonical_name} {entity.mention_count} {formatDate(entity.first_seen)} @@ -149,60 +147,81 @@ export function EntitiesView() { )} - {/* Entity Detail Panel */} + {/* Entity Detail Panel - Fixed overlay */} {selectedEntity && ( -
-
-

{selectedEntity.canonical_name}

- -
- -
-
ID: {selectedEntity.id}
-
Mentions: {selectedEntity.mention_count}
-
First seen: {formatDate(selectedEntity.first_seen)}
-
Last seen: {formatDate(selectedEntity.last_seen)}
-
- -
-
-

Observations

+
+
+ {/* Header */} +
+
+

{selectedEntity.canonical_name}

+

Entity details

+
- {loadingDetail ? ( -
Loading observations...
- ) : selectedEntity.observations && selectedEntity.observations.length > 0 ? ( -
    - {selectedEntity.observations.map((obs, idx) => ( -
  • -
    {obs.text}
    - {obs.mentioned_at && ( -
    - {formatDate(obs.mentioned_at)} -
    - )} -
  • - ))} -
- ) : ( -
- No observations yet. Click "Regenerate" to generate observations from facts. +
+ {/* Entity Info */} +
+
+
Mentions
+
{selectedEntity.mention_count}
+
+
+
First Seen
+
{formatDate(selectedEntity.first_seen)}
+
- )} + + {/* ID */} +
+
Entity ID
+ {selectedEntity.id} +
+ + {/* Observations */} +
+
+
Observations
+ +
+ + {loadingDetail ? ( +
Loading observations...
+ ) : selectedEntity.observations && selectedEntity.observations.length > 0 ? ( +
    + {selectedEntity.observations.map((obs, idx) => ( +
  • +
    {obs.text}
    + {obs.mentioned_at && ( +
    + {formatDate(obs.mentioned_at)} +
    + )} +
  • + ))} +
+ ) : ( +
+ No observations yet. Click "Regenerate" to generate observations from facts. +
+ )} +
+
)} diff --git a/hindsight-control-plane/src/components/memory-detail-panel.tsx b/hindsight-control-plane/src/components/memory-detail-panel.tsx index cc3c9791..87abb7df 100644 --- a/hindsight-control-plane/src/components/memory-detail-panel.tsx +++ b/hindsight-control-plane/src/components/memory-detail-panel.tsx @@ -49,6 +49,9 @@ export function MemoryDetailPanel({ if (!memory) return null; + // Handle both 'id' and 'node_id' (trace results use node_id) + const memoryId = memory.id || memory.node_id; + const labelSize = compact ? 'text-[10px]' : 'text-xs'; const textSize = compact ? 'text-xs' : 'text-sm'; @@ -129,24 +132,26 @@ export function MemoryDetailPanel({ )} {/* ID */} -
-
Memory ID
-
- {memory.id} - + {memoryId && ( +
+
Memory ID
+
+ {memoryId} + +
-
+ )} {/* Document/Chunk buttons */} {(memory.document_id || memory.chunk_id) && ( @@ -267,24 +272,26 @@ export function MemoryDetailPanel({ )} {/* ID */} -
-
Memory ID
-
- {memory.id} - + {memoryId && ( +
+
Memory ID
+
+ {memoryId} + +
-
+ )} {/* Document/Chunk buttons */} {(memory.document_id || memory.chunk_id) && ( diff --git a/hindsight-control-plane/src/components/search-debug-view.tsx b/hindsight-control-plane/src/components/search-debug-view.tsx index 7137e236..e4d85fb7 100644 --- a/hindsight-control-plane/src/components/search-debug-view.tsx +++ b/hindsight-control-plane/src/components/search-debug-view.tsx @@ -9,7 +9,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@ import { Checkbox } from '@/components/ui/checkbox'; import { Label } from '@/components/ui/label'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Search, Clock, Zap, ChevronRight, Database, FileText, Users } from 'lucide-react'; +import { Search, Clock, Zap, ChevronRight, ChevronDown, Database, FileText, Users, ArrowDown } from 'lucide-react'; import JsonView from 'react18-json-view'; import 'react18-json-view/src/style.css'; import { MemoryDetailPanel } from './memory-detail-panel'; @@ -38,6 +38,34 @@ export function SearchDebugView() { const [loading, setLoading] = useState(false); const [viewMode, setViewMode] = useState('results'); const [selectedMemory, setSelectedMemory] = useState(null); + const [expandedSteps, setExpandedSteps] = useState>(new Set()); + const [expandedResults, setExpandedResults] = useState>(new Set()); + + const toggleStep = (step: string) => { + setExpandedSteps(prev => { + const next = new Set(prev); + if (next.has(step)) { + next.delete(step); + } else { + next.add(step); + } + return next; + }); + }; + + const toggleExpandResults = (key: string) => { + setExpandedResults(prev => { + const next = new Set(prev); + if (next.has(key)) { + next.delete(key); + } else { + next.add(key); + } + return next; + }); + }; + + const INITIAL_RESULTS_COUNT = 5; const runSearch = async () => { if (!currentBank) { @@ -316,55 +344,431 @@ export function SearchDebugView() { {/* Trace View */} {viewMode === 'trace' && trace && ( - - - Recall Trace - - - {/* Retrieval Methods */} - {trace.retrieval_results && ( +
+ {/* Parallel Retrieval Methods - Grouped by Fact Type */} + {trace.retrieval_results && trace.retrieval_results.length > 0 && (() => { + // Group retrieval results by fact type + const factTypeGroups: Record = {}; + trace.retrieval_results.forEach((method: any) => { + const ft = method.fact_type || 'all'; + if (!factTypeGroups[ft]) factTypeGroups[ft] = []; + factTypeGroups[ft].push(method); + }); + const factTypes = Object.keys(factTypeGroups); + + return (
-

Retrieval Methods

-
- {trace.retrieval_results.map((method: any, idx: number) => ( -
-
- {method.method_name} - - {method.duration_seconds?.toFixed(3)}s - +
+
+ PARALLEL RETRIEVAL +
+
+ + {/* Fact type lanes */} +
+ {factTypes.map((factType, ftIdx) => { + const methods = factTypeGroups[factType]; + const laneKey = `lane-${factType}`; + const isLaneExpanded = expandedSteps.has(laneKey); + const totalResults = methods.reduce((sum: number, m: any) => sum + (m.results?.length || 0), 0); + const totalDuration = Math.max(...methods.map((m: any) => m.duration_seconds || 0)); + + // Color coding for fact types + const ftColors: Record = { + world: { bg: 'bg-blue-500/10', text: 'text-blue-500', border: 'border-blue-500/30' }, + experience: { bg: 'bg-green-500/10', text: 'text-green-500', border: 'border-green-500/30' }, + opinion: { bg: 'bg-purple-500/10', text: 'text-purple-500', border: 'border-purple-500/30' }, + all: { bg: 'bg-gray-500/10', text: 'text-gray-500', border: 'border-gray-500/30' }, + }; + const colors = ftColors[factType] || ftColors.all; + + return ( + + + {/* Lane Header */} +
toggleStep(laneKey)} + > +
+ + {factType.charAt(0).toUpperCase()} + +
+
+
+ {factType} + + {methods.length} methods + +
+ {/* Method summary pills */} +
+ {methods.map((m: any, mIdx: number) => ( + + {m.method_name}: {m.results?.length || 0} + + ))} +
+
+
+
{totalResults}
+
{totalDuration.toFixed(2)}s
+
+ {isLaneExpanded ? ( + + ) : ( + + )} +
+ + {/* Expanded: Show methods grid */} + {isLaneExpanded && ( +
+
+ {methods.map((method: any, mIdx: number) => { + const methodKey = `${laneKey}-method-${mIdx}`; + const isMethodExpanded = expandedSteps.has(methodKey); + const methodResults = method.results || []; + + return ( +
+
{ + e.stopPropagation(); + toggleStep(methodKey); + }} + > +
+ {method.method_name} + {isMethodExpanded ? ( + + ) : ( + + )} +
+
+
{methodResults.length}
+
{method.duration_seconds?.toFixed(2)}s
+
+
+ + {/* Method Results */} + {isMethodExpanded && methodResults.length > 0 && (() => { + const resultsKey = `results-${methodKey}`; + const showAll = expandedResults.has(resultsKey); + const displayResults = showAll ? methodResults : methodResults.slice(0, INITIAL_RESULTS_COUNT); + const hasMore = methodResults.length > INITIAL_RESULTS_COUNT; + + return ( +
+ {displayResults.map((r: any, rIdx: number) => ( +
{ + e.stopPropagation(); + setSelectedMemory(r); + }} + > +
+ {rIdx + 1} +
+

{r.text}

+
+ + {(r.score || r.similarity || 0).toFixed(4)} + +
+
+
+
+ ))} + {hasMore && ( + + )} +
+ ); + })()} +
+ ); + })} +
+
+ )} +
+
+ ); + })} +
+ + {/* Parallel indicator - vertical lines showing all run together */} +
+
+ {factTypes.map((ft, i) => { + const ftColors: Record = { + world: 'bg-blue-500', + experience: 'bg-green-500', + opinion: 'bg-purple-500', + all: 'bg-gray-500', + }; + return ( +
+
+
+ ); + })} +
+
+
+ +
+
+ ); + })()} + + {/* Step 2: RRF Merge */} + {trace.rrf_merged && (() => { + const stepKey = 'rrf-merge'; + const isExpanded = expandedSteps.has(stepKey); + + return ( +
+ toggleStep(stepKey)} + > + +
+
+
-
{method.results?.length || 0}
-
results
+
+
+ RRF Fusion + merge +
+
+ Reciprocal Rank Fusion of all retrieval results +
+
+
{trace.rrf_merged.length}
+ {isExpanded ? ( + + ) : ( + + )}
- ))} -
-
- )} + + - {/* RRF Merge */} - {trace.rrf_merged && ( -
-

RRF Merge

-
-
{trace.rrf_merged.length}
-
candidates after fusion
-
-
- )} + {/* Expanded Results */} + {isExpanded && trace.rrf_merged.length > 0 && (() => { + const resultsKey = 'results-rrf'; + const showAll = expandedResults.has(resultsKey); + const displayResults = showAll ? trace.rrf_merged : trace.rrf_merged.slice(0, INITIAL_RESULTS_COUNT); + const hasMore = trace.rrf_merged.length > INITIAL_RESULTS_COUNT; - {/* Reranking */} - {trace.reranked && ( -
-

Reranking

-
-
{trace.reranked.length}
-
results after cross-encoder
+ return ( +
+ {displayResults.map((r: any, rIdx: number) => ( +
{ + e.stopPropagation(); + setSelectedMemory(r); + }} + > +
+ {rIdx + 1} +
+

{r.text}

+
+ RRF Score: {(r.rrf_score || r.score || 0).toFixed(4)} +
+
+
+
+ ))} + {hasMore && ( + + )} +
+ ); + })()} + + {/* Arrow */} +
+
- )} - - + ); + })()} + + {/* Step 3: Combined Scoring */} + {trace.reranked && (() => { + const stepKey = 'reranking'; + const isExpanded = expandedSteps.has(stepKey); + + return ( +
+ toggleStep(stepKey)} + > + +
+
+ +
+
+
+ Combined Scoring + rerank +
+
+ 0.6×cross_encoder + 0.2×rrf + 0.1×temporal + 0.1×recency +
+
+
{trace.reranked.length}
+ {isExpanded ? ( + + ) : ( + + )} +
+
+
+ + {/* Expanded Results */} + {isExpanded && trace.reranked.length > 0 && (() => { + const resultsKey = 'results-rerank'; + const showAll = expandedResults.has(resultsKey); + const displayResults = showAll ? trace.reranked : trace.reranked.slice(0, INITIAL_RESULTS_COUNT); + const hasMore = trace.reranked.length > INITIAL_RESULTS_COUNT; + + return ( +
+ {displayResults.map((r: any, rIdx: number) => { + const sc = r.score_components || {}; + return ( +
{ + e.stopPropagation(); + setSelectedMemory(r); + }} + > +
+ {rIdx + 1} +
+

{r.text}

+
+ + = {(r.rerank_score || r.score || 0).toFixed(4)} + + {sc.cross_encoder_score_normalized !== undefined && ( + + CE: {sc.cross_encoder_score_normalized.toFixed(3)} + + )} + {sc.rrf_normalized !== undefined && ( + + RRF: {sc.rrf_normalized.toFixed(3)} + + )} + {sc.temporal !== undefined && ( + + Tmp: {sc.temporal.toFixed(3)} + + )} + {sc.recency !== undefined && ( + + Rec: {sc.recency.toFixed(3)} + + )} +
+
+
+
+ ); + })} + {hasMore && ( + + )} +
+ ); + })()} + + {/* Arrow */} +
+ +
+
+ ); + })()} + + {/* Final: Results */} + + +
+
+ +
+
+
+ Final Results + output +
+
+ Top results after all processing steps +
+
+
{results?.length || 0}
+
+
+
+
)} {/* JSON View */} diff --git a/uv.lock b/uv.lock index 4d7f5678..99b16639 100644 --- a/uv.lock +++ b/uv.lock @@ -1141,7 +1141,7 @@ wheels = [ [[package]] name = "hindsight-all" -version = "0.1.3" +version = "0.1.4" source = { editable = "hindsight" } dependencies = [ { name = "hindsight-api" }, @@ -1165,7 +1165,7 @@ provides-extras = ["test"] [[package]] name = "hindsight-api" -version = "0.1.3" +version = "0.1.4" source = { editable = "hindsight-api" } dependencies = [ { name = "alembic" }, @@ -1243,11 +1243,11 @@ requires-dist = [ { name = "python-dateutil", specifier = ">=2.8.0" }, { name = "python-dotenv", specifier = ">=1.0.0" }, { name = "rich", specifier = ">=13.0.0" }, - { name = "sentence-transformers", specifier = ">=3.0.0" }, + { name = "sentence-transformers", specifier = ">=3.0.0,<3.3.0" }, { name = "sqlalchemy", specifier = ">=2.0.44" }, { name = "tiktoken", specifier = ">=0.12.0" }, - { name = "torch", specifier = ">=2.0.0" }, - { name = "transformers", specifier = ">=4.30.0" }, + { name = "torch", specifier = ">=2.0.0,<2.6.0" }, + { name = "transformers", specifier = ">=4.30.0,<4.46.0" }, { name = "uvicorn", specifier = ">=0.38.0" }, { name = "wsproto", specifier = ">=1.0.0" }, ] @@ -1265,7 +1265,7 @@ dev = [ [[package]] name = "hindsight-client" -version = "0.1.3" +version = "0.1.4" source = { editable = "hindsight-clients/python" } dependencies = [ { name = "aiohttp" }, @@ -1297,7 +1297,7 @@ provides-extras = ["test"] [[package]] name = "hindsight-dev" -version = "0.1.3" +version = "0.1.4" source = { editable = "hindsight-dev" } dependencies = [ { name = "hindsight-api" }, @@ -2095,77 +2095,69 @@ wheels = [ [[package]] name = "nvidia-cublas-cu12" -version = "12.8.4.1" +version = "12.4.5.8" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921 }, + { url = "https://files.pythonhosted.org/packages/ae/71/1c91302526c45ab494c23f61c7a84aa568b8c1f9d196efa5993957faf906/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b", size = 363438805 }, ] [[package]] name = "nvidia-cuda-cupti-cu12" -version = "12.8.90" +version = "12.4.127" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621 }, + { url = "https://files.pythonhosted.org/packages/67/42/f4f60238e8194a3106d06a058d494b18e006c10bb2b915655bd9f6ea4cb1/nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb", size = 13813957 }, ] [[package]] name = "nvidia-cuda-nvrtc-cu12" -version = "12.8.93" +version = "12.4.127" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029 }, + { url = "https://files.pythonhosted.org/packages/2c/14/91ae57cd4db3f9ef7aa99f4019cfa8d54cb4caa7e00975df6467e9725a9f/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338", size = 24640306 }, ] [[package]] name = "nvidia-cuda-runtime-cu12" -version = "12.8.90" +version = "12.4.127" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765 }, + { url = "https://files.pythonhosted.org/packages/ea/27/1795d86fe88ef397885f2e580ac37628ed058a92ed2c39dc8eac3adf0619/nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5", size = 883737 }, ] [[package]] name = "nvidia-cudnn-cu12" -version = "9.10.2.21" +version = "9.1.0.70" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nvidia-cublas-cu12" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467 }, + { url = "https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f", size = 664752741 }, ] [[package]] name = "nvidia-cufft-cu12" -version = "11.3.3.83" +version = "11.2.1.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nvidia-nvjitlink-cu12" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695 }, -] - -[[package]] -name = "nvidia-cufile-cu12" -version = "1.13.1.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834 }, + { url = "https://files.pythonhosted.org/packages/27/94/3266821f65b92b3138631e9c8e7fe1fb513804ac934485a8d05776e1dd43/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9", size = 211459117 }, ] [[package]] name = "nvidia-curand-cu12" -version = "10.3.9.90" +version = "10.3.5.147" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976 }, + { url = "https://files.pythonhosted.org/packages/8a/6d/44ad094874c6f1b9c654f8ed939590bdc408349f137f9b98a3a23ccec411/nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b", size = 56305206 }, ] [[package]] name = "nvidia-cusolver-cu12" -version = "11.7.3.90" +version = "11.6.1.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nvidia-cublas-cu12" }, @@ -2173,58 +2165,42 @@ dependencies = [ { name = "nvidia-nvjitlink-cu12" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905 }, + { url = "https://files.pythonhosted.org/packages/3a/e1/5b9089a4b2a4790dfdea8b3a006052cfecff58139d5a4e34cb1a51df8d6f/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:19e33fa442bcfd085b3086c4ebf7e8debc07cfe01e11513cc6d332fd918ac260", size = 127936057 }, ] [[package]] name = "nvidia-cusparse-cu12" -version = "12.5.8.93" +version = "12.3.1.170" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nvidia-nvjitlink-cu12" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466 }, -] - -[[package]] -name = "nvidia-cusparselt-cu12" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691 }, + { url = "https://files.pythonhosted.org/packages/db/f7/97a9ea26ed4bbbfc2d470994b8b4f338ef663be97b8f677519ac195e113d/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ea4f11a2904e2a8dc4b1833cc1b5181cde564edd0d5cd33e3c168eff2d1863f1", size = 207454763 }, ] [[package]] name = "nvidia-nccl-cu12" -version = "2.27.5" +version = "2.21.5" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229 }, + { url = "https://files.pythonhosted.org/packages/df/99/12cd266d6233f47d00daf3a72739872bdc10267d0383508b0b9c84a18bb6/nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0", size = 188654414 }, ] [[package]] name = "nvidia-nvjitlink-cu12" -version = "12.8.93" +version = "12.4.127" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836 }, -] - -[[package]] -name = "nvidia-nvshmem-cu12" -version = "3.3.20" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145 }, + { url = "https://files.pythonhosted.org/packages/ff/ff/847841bacfbefc97a00036e0fce5a0f086b640756dc38caea5e1bb002655/nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57", size = 21066810 }, ] [[package]] name = "nvidia-nvtx-cu12" -version = "12.8.90" +version = "12.4.127" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954 }, + { url = "https://files.pythonhosted.org/packages/87/20/199b8713428322a2f22b722c62b8cc278cc53dffa9705d744484b5035ee9/nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a", size = 99144 }, ] [[package]] @@ -3830,7 +3806,7 @@ wheels = [ [[package]] name = "sentence-transformers" -version = "5.1.2" +version = "3.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, @@ -3840,11 +3816,10 @@ dependencies = [ { name = "torch" }, { name = "tqdm" }, { name = "transformers" }, - { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0f/96/f3f3409179d14dbfdbea8622e2e9eaa3c8836ddcaecd2cd5ff0a11731d20/sentence_transformers-5.1.2.tar.gz", hash = "sha256:0f6c8bd916a78dc65b366feb8d22fd885efdb37432e7630020d113233af2b856", size = 375185 } +sdist = { url = "https://files.pythonhosted.org/packages/de/61/708b20dedf26c460b416beb0acd5474c190dbca13e93b40858e99f17ac46/sentence_transformers-3.2.1.tar.gz", hash = "sha256:9fc38e620e5e1beba31d538a451778c9ccdbad77119d90f59f5bce49c4148e79", size = 202527 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/a6/a607a737dc1a00b7afe267b9bfde101b8cee2529e197e57471d23137d4e5/sentence_transformers-5.1.2-py3-none-any.whl", hash = "sha256:724ce0ea62200f413f1a5059712aff66495bc4e815a1493f7f9bca242414c333", size = 488009 }, + { url = "https://files.pythonhosted.org/packages/45/18/1ec591befcbdb2c97192a40fbe7c43a8b8a8b3c89b1fa101d3eeed4d79a4/sentence_transformers-3.2.1-py3-none-any.whl", hash = "sha256:c507e069eea33d15f1f2c72f74d7ea93abef298152cc235ab5af5e3a7584f738", size = 255758 }, ] [[package]] @@ -4007,14 +3982,14 @@ wheels = [ [[package]] name = "sympy" -version = "1.14.0" +version = "1.13.1" 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 } +sdist = { url = "https://files.pythonhosted.org/packages/ca/99/5a5b6f19ff9f083671ddf7b9632028436167cd3d33e11015754e41b249a4/sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f", size = 7533040 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, + { url = "https://files.pythonhosted.org/packages/b2/fe/81695a1aa331a842b582453b605175f419fe8540355886031328089d840a/sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8", size = 6189177 }, ] [[package]] @@ -4091,27 +4066,49 @@ wheels = [ [[package]] name = "tokenizers" -version = "0.22.1" +version = "0.20.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/46/fb6854cec3278fbfa4a75b50232c77622bc517ac886156e6afbfa4d8fc6e/tokenizers-0.22.1.tar.gz", hash = "sha256:61de6522785310a309b3407bac22d99c4db5dba349935e99e4d15ea2226af2d9", size = 363123 } +sdist = { url = "https://files.pythonhosted.org/packages/da/25/b1681c1c30ea3ea6e584ae3fffd552430b12faa599b558c4c4783f56d7ff/tokenizers-0.20.3.tar.gz", hash = "sha256:2278b34c5d0dd78e087e1ca7f9b1dcbf129d80211afa645f214bd6e051037539", size = 340513 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/33/f4b2d94ada7ab297328fc671fed209368ddb82f965ec2224eb1892674c3a/tokenizers-0.22.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:59fdb013df17455e5f950b4b834a7b3ee2e0271e6378ccb33aa74d178b513c73", size = 3069318 }, - { url = "https://files.pythonhosted.org/packages/1c/58/2aa8c874d02b974990e89ff95826a4852a8b2a273c7d1b4411cdd45a4565/tokenizers-0.22.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8d4e484f7b0827021ac5f9f71d4794aaef62b979ab7608593da22b1d2e3c4edc", size = 2926478 }, - { url = "https://files.pythonhosted.org/packages/1e/3b/55e64befa1e7bfea963cf4b787b2cea1011362c4193f5477047532ce127e/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d2962dd28bc67c1f205ab180578a78eef89ac60ca7ef7cbe9635a46a56422a", size = 3256994 }, - { url = "https://files.pythonhosted.org/packages/71/0b/fbfecf42f67d9b7b80fde4aabb2b3110a97fac6585c9470b5bff103a80cb/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38201f15cdb1f8a6843e6563e6e79f4abd053394992b9bbdf5213ea3469b4ae7", size = 3153141 }, - { url = "https://files.pythonhosted.org/packages/17/a9/b38f4e74e0817af8f8ef925507c63c6ae8171e3c4cb2d5d4624bf58fca69/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1cbe5454c9a15df1b3443c726063d930c16f047a3cc724b9e6e1a91140e5a21", size = 3508049 }, - { url = "https://files.pythonhosted.org/packages/d2/48/dd2b3dac46bb9134a88e35d72e1aa4869579eacc1a27238f1577270773ff/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7d094ae6312d69cc2a872b54b91b309f4f6fbce871ef28eb27b52a98e4d0214", size = 3710730 }, - { url = "https://files.pythonhosted.org/packages/93/0e/ccabc8d16ae4ba84a55d41345207c1e2ea88784651a5a487547d80851398/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afd7594a56656ace95cdd6df4cca2e4059d294c5cfb1679c57824b605556cb2f", size = 3412560 }, - { url = "https://files.pythonhosted.org/packages/d0/c6/dc3a0db5a6766416c32c034286d7c2d406da1f498e4de04ab1b8959edd00/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2ef6063d7a84994129732b47e7915e8710f27f99f3a3260b8a38fc7ccd083f4", size = 3250221 }, - { url = "https://files.pythonhosted.org/packages/d7/a6/2c8486eef79671601ff57b093889a345dd3d576713ef047776015dc66de7/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba0a64f450b9ef412c98f6bcd2a50c6df6e2443b560024a09fa6a03189726879", size = 9345569 }, - { url = "https://files.pythonhosted.org/packages/6b/16/32ce667f14c35537f5f605fe9bea3e415ea1b0a646389d2295ec348d5657/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:331d6d149fa9c7d632cde4490fb8bbb12337fa3a0232e77892be656464f4b446", size = 9271599 }, - { url = "https://files.pythonhosted.org/packages/51/7c/a5f7898a3f6baa3fc2685c705e04c98c1094c523051c805cdd9306b8f87e/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:607989f2ea68a46cb1dfbaf3e3aabdf3f21d8748312dbeb6263d1b3b66c5010a", size = 9533862 }, - { url = "https://files.pythonhosted.org/packages/36/65/7e75caea90bc73c1dd8d40438adf1a7bc26af3b8d0a6705ea190462506e1/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a0f307d490295717726598ef6fa4f24af9d484809223bbc253b201c740a06390", size = 9681250 }, - { url = "https://files.pythonhosted.org/packages/30/2c/959dddef581b46e6209da82df3b78471e96260e2bc463f89d23b1bf0e52a/tokenizers-0.22.1-cp39-abi3-win32.whl", hash = "sha256:b5120eed1442765cd90b903bb6cfef781fd8fe64e34ccaecbae4c619b7b12a82", size = 2472003 }, - { url = "https://files.pythonhosted.org/packages/b3/46/e33a8c93907b631a99377ef4c5f817ab453d0b34f93529421f42ff559671/tokenizers-0.22.1-cp39-abi3-win_amd64.whl", hash = "sha256:65fd6e3fb11ca1e78a6a93602490f134d1fdeb13bcef99389d5102ea318ed138", size = 2674684 }, + { url = "https://files.pythonhosted.org/packages/c6/93/6742ef9206409d5ce1fdf44d5ca1687cdc3847ba0485424e2c731e6bcf67/tokenizers-0.20.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:585b51e06ca1f4839ce7759941e66766d7b060dccfdc57c4ca1e5b9a33013a90", size = 2674224 }, + { url = "https://files.pythonhosted.org/packages/aa/14/e75ece72e99f6ef9ae07777ca9fdd78608f69466a5cecf636e9bd2f25d5c/tokenizers-0.20.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61cbf11954f3b481d08723ebd048ba4b11e582986f9be74d2c3bdd9293a4538d", size = 2558991 }, + { url = "https://files.pythonhosted.org/packages/46/54/033b5b2ba0c3ae01e026c6f7ced147d41a2fa1c573d00a66cb97f6d7f9b3/tokenizers-0.20.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef820880d5e4e8484e2fa54ff8d297bb32519eaa7815694dc835ace9130a3eea", size = 2892476 }, + { url = "https://files.pythonhosted.org/packages/e6/b0/cc369fb3297d61f3311cab523d16d48c869dc2f0ba32985dbf03ff811041/tokenizers-0.20.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:67ef4dcb8841a4988cd00dd288fb95dfc8e22ed021f01f37348fd51c2b055ba9", size = 2802775 }, + { url = "https://files.pythonhosted.org/packages/1a/74/62ad983e8ea6a63e04ed9c5be0b605056bf8aac2f0125f9b5e0b3e2b89fa/tokenizers-0.20.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff1ef8bd47a02b0dc191688ccb4da53600df5d4c9a05a4b68e1e3de4823e78eb", size = 3086138 }, + { url = "https://files.pythonhosted.org/packages/6b/ac/4637ba619db25094998523f9e6f5b456e1db1f8faa770a3d925d436db0c3/tokenizers-0.20.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:444d188186eab3148baf0615b522461b41b1f0cd58cd57b862ec94b6ac9780f1", size = 3098076 }, + { url = "https://files.pythonhosted.org/packages/58/ce/9793f2dc2ce529369807c9c74e42722b05034af411d60f5730b720388c7d/tokenizers-0.20.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37c04c032c1442740b2c2d925f1857885c07619224a533123ac7ea71ca5713da", size = 3379650 }, + { url = "https://files.pythonhosted.org/packages/50/f6/2841de926bc4118af996eaf0bdf0ea5b012245044766ffc0347e6c968e63/tokenizers-0.20.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:453c7769d22231960ee0e883d1005c93c68015025a5e4ae56275406d94a3c907", size = 2994005 }, + { url = "https://files.pythonhosted.org/packages/a3/b2/00915c4fed08e9505d37cf6eaab45b12b4bff8f6719d459abcb9ead86a4b/tokenizers-0.20.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4bb31f7b2847e439766aaa9cc7bccf7ac7088052deccdb2275c952d96f691c6a", size = 8977488 }, + { url = "https://files.pythonhosted.org/packages/e9/ac/1c069e7808181ff57bcf2d39e9b6fbee9133a55410e6ebdaa89f67c32e83/tokenizers-0.20.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:843729bf0f991b29655a069a2ff58a4c24375a553c70955e15e37a90dd4e045c", size = 9294935 }, + { url = "https://files.pythonhosted.org/packages/50/47/722feb70ee68d1c4412b12d0ea4acc2713179fd63f054913990f9e259492/tokenizers-0.20.3-cp311-none-win32.whl", hash = "sha256:efcce3a927b1e20ca694ba13f7a68c59b0bd859ef71e441db68ee42cf20c2442", size = 2197175 }, + { url = "https://files.pythonhosted.org/packages/75/68/1b4f928b15a36ed278332ac75d66d7eb65d865bf344d049c452c18447bf9/tokenizers-0.20.3-cp311-none-win_amd64.whl", hash = "sha256:88301aa0801f225725b6df5dea3d77c80365ff2362ca7e252583f2b4809c4cc0", size = 2381616 }, + { url = "https://files.pythonhosted.org/packages/07/00/92a08af2a6b0c88c50f1ab47d7189e695722ad9714b0ee78ea5e1e2e1def/tokenizers-0.20.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:49d12a32e190fad0e79e5bdb788d05da2f20d8e006b13a70859ac47fecf6ab2f", size = 2667951 }, + { url = "https://files.pythonhosted.org/packages/ec/9a/e17a352f0bffbf415cf7d73756f5c73a3219225fc5957bc2f39d52c61684/tokenizers-0.20.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:282848cacfb9c06d5e51489f38ec5aa0b3cd1e247a023061945f71f41d949d73", size = 2555167 }, + { url = "https://files.pythonhosted.org/packages/27/37/d108df55daf4f0fcf1f58554692ff71687c273d870a34693066f0847be96/tokenizers-0.20.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abe4e08c7d0cd6154c795deb5bf81d2122f36daf075e0c12a8b050d824ef0a64", size = 2898389 }, + { url = "https://files.pythonhosted.org/packages/b2/27/32f29da16d28f59472fa7fb38e7782069748c7e9ab9854522db20341624c/tokenizers-0.20.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ca94fc1b73b3883c98f0c88c77700b13d55b49f1071dfd57df2b06f3ff7afd64", size = 2795866 }, + { url = "https://files.pythonhosted.org/packages/29/4e/8a9a3c89e128c4a40f247b501c10279d2d7ade685953407c4d94c8c0f7a7/tokenizers-0.20.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef279c7e239f95c8bdd6ff319d9870f30f0d24915b04895f55b1adcf96d6c60d", size = 3085446 }, + { url = "https://files.pythonhosted.org/packages/b4/3b/a2a7962c496ebcd95860ca99e423254f760f382cd4bd376f8895783afaf5/tokenizers-0.20.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16384073973f6ccbde9852157a4fdfe632bb65208139c9d0c0bd0176a71fd67f", size = 3094378 }, + { url = "https://files.pythonhosted.org/packages/1f/f4/a8a33f0192a1629a3bd0afcad17d4d221bbf9276da4b95d226364208d5eb/tokenizers-0.20.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:312d522caeb8a1a42ebdec87118d99b22667782b67898a76c963c058a7e41d4f", size = 3385755 }, + { url = "https://files.pythonhosted.org/packages/9e/65/c83cb3545a65a9eaa2e13b22c93d5e00bd7624b354a44adbdc93d5d9bd91/tokenizers-0.20.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2b7cb962564785a83dafbba0144ecb7f579f1d57d8c406cdaa7f32fe32f18ad", size = 2997679 }, + { url = "https://files.pythonhosted.org/packages/55/e9/a80d4e592307688a67c7c59ab77e03687b6a8bd92eb5db763a2c80f93f57/tokenizers-0.20.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:124c5882ebb88dadae1fc788a582299fcd3a8bd84fc3e260b9918cf28b8751f5", size = 8989296 }, + { url = "https://files.pythonhosted.org/packages/90/af/60c957af8d2244321124e893828f1a4817cde1a2d08d09d423b73f19bd2f/tokenizers-0.20.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2b6e54e71f84c4202111a489879005cb14b92616a87417f6c102c833af961ea2", size = 9303621 }, + { url = "https://files.pythonhosted.org/packages/be/a9/96172310ee141009646d63a1ca267c099c462d747fe5ef7e33f74e27a683/tokenizers-0.20.3-cp312-none-win32.whl", hash = "sha256:83d9bfbe9af86f2d9df4833c22e94d94750f1d0cd9bfb22a7bb90a86f61cdb1c", size = 2188979 }, + { url = "https://files.pythonhosted.org/packages/bd/68/61d85ae7ae96dde7d0974ff3538db75d5cdc29be2e4329cd7fc51a283e22/tokenizers-0.20.3-cp312-none-win_amd64.whl", hash = "sha256:44def74cee574d609a36e17c8914311d1b5dbcfe37c55fd29369d42591b91cf2", size = 2380725 }, + { url = "https://files.pythonhosted.org/packages/07/19/36e9eaafb229616cb8502b42030fa7fe347550e76cb618de71b498fc3222/tokenizers-0.20.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0b630e0b536ef0e3c8b42c685c1bc93bd19e98c0f1543db52911f8ede42cf84", size = 2666813 }, + { url = "https://files.pythonhosted.org/packages/b9/c7/e2ce1d4f756c8a62ef93fdb4df877c2185339b6d63667b015bf70ea9d34b/tokenizers-0.20.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a02d160d2b19bcbfdf28bd9a4bf11be4cb97d0499c000d95d4c4b1a4312740b6", size = 2555354 }, + { url = "https://files.pythonhosted.org/packages/7c/cf/5309c2d173a6a67f9ec8697d8e710ea32418de6fd8541778032c202a1c3e/tokenizers-0.20.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e3d80d89b068bc30034034b5319218c7c0a91b00af19679833f55f3becb6945", size = 2897745 }, + { url = "https://files.pythonhosted.org/packages/2c/e5/af3078e32f225e680e69d61f78855880edb8d53f5850a1834d519b2b103f/tokenizers-0.20.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:174a54910bed1b089226512b4458ea60d6d6fd93060254734d3bc3540953c51c", size = 2794385 }, + { url = "https://files.pythonhosted.org/packages/0b/a7/bc421fe46650cc4eb4a913a236b88c243204f32c7480684d2f138925899e/tokenizers-0.20.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:098b8a632b8656aa5802c46689462c5c48f02510f24029d71c208ec2c822e771", size = 3084580 }, + { url = "https://files.pythonhosted.org/packages/c6/22/97e1e95ee81f75922c9f569c23cb2b1fdc7f5a7a29c4c9fae17e63f751a6/tokenizers-0.20.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:78c8c143e3ae41e718588281eb3e212c2b31623c9d6d40410ec464d7d6221fb5", size = 3093581 }, + { url = "https://files.pythonhosted.org/packages/d5/14/f0df0ee3b9e516121e23c0099bccd7b9f086ba9150021a750e99b16ce56f/tokenizers-0.20.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b26b0aadb18cd8701077362ba359a06683662d5cafe3e8e8aba10eb05c037f1", size = 3385934 }, + { url = "https://files.pythonhosted.org/packages/66/52/7a171bd4929e3ffe61a29b4340fe5b73484709f92a8162a18946e124c34c/tokenizers-0.20.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07d7851a72717321022f3774e84aa9d595a041d643fafa2e87fbc9b18711dac0", size = 2997311 }, + { url = "https://files.pythonhosted.org/packages/7c/64/f1993bb8ebf775d56875ca0d50a50f2648bfbbb143da92fe2e6ceeb4abd5/tokenizers-0.20.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:bd44e48a430ada902c6266a8245f5036c4fe744fcb51f699999fbe82aa438797", size = 8988601 }, + { url = "https://files.pythonhosted.org/packages/d6/3f/49fa63422159bbc2f2a4ac5bfc597d04d4ec0ad3d2ef46649b5e9a340e37/tokenizers-0.20.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a4c186bb006ccbe1f5cc4e0380d1ce7806f5955c244074fd96abc55e27b77f01", size = 9303950 }, + { url = "https://files.pythonhosted.org/packages/66/11/79d91aeb2817ad1993ef61c690afe73e6dbedbfb21918b302ef5a2ba9bfb/tokenizers-0.20.3-cp313-none-win32.whl", hash = "sha256:6e19e0f1d854d6ab7ea0c743d06e764d1d9a546932be0a67f33087645f00fe13", size = 2188941 }, + { url = "https://files.pythonhosted.org/packages/c2/ff/ac8410f868fb8b14b5e619efa304aa119cb8a40bd7df29fc81a898e64f99/tokenizers-0.20.3-cp313-none-win_amd64.whl", hash = "sha256:d50ede425c7e60966a9680d41b58b3a0950afa1bb570488e2972fa61662c4273", size = 2380269 }, ] [[package]] @@ -4125,7 +4122,7 @@ wheels = [ [[package]] name = "torch" -version = "2.9.0" +version = "2.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -4138,45 +4135,27 @@ dependencies = [ { 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-nvshmem-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", marker = "python_full_version >= '3.12'" }, { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "triton", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/58/fe/334225e6330e672b36aef23d77451fa906ea12881570c08638a91331a212/torch-2.9.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c596708b5105d0b199215acf0c9be7c1db5f1680d88eddadf4b75a299259a677", size = 104230578 }, - { url = "https://files.pythonhosted.org/packages/05/cc/49566caaa218872ec9a2912456f470ff92649894a4bc2e5274aa9ef87c4a/torch-2.9.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:51de31219c97c51cf4bf2be94d622e3deb5dcc526c6dc00e97c17eaec0fc1d67", size = 899815990 }, - { url = "https://files.pythonhosted.org/packages/74/25/e9ab21d5925b642d008f139d4a3c9664fc9ee1faafca22913c080cc4c0a5/torch-2.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd515c70059afd95f48b8192733764c08ca37a1d19803af6401b5ecad7c8676e", size = 109313698 }, - { url = "https://files.pythonhosted.org/packages/b3/b7/205ef3e94de636feffd64b28bb59a0dfac0771221201b9871acf9236f5ca/torch-2.9.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:614a185e4986326d526a91210c8fc1397e76e8cfafa78baf6296a790e53a9eec", size = 74463678 }, - { url = "https://files.pythonhosted.org/packages/d1/d3/3985739f3b8e88675127bf70f82b3a48ae083e39cda56305dbd90398fec0/torch-2.9.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e5f7af1dc4c0a7c4a260c2534f41ddaf209714f7c89145e644c44712fbd6b642", size = 104107898 }, - { url = "https://files.pythonhosted.org/packages/a5/4b/f4bb2e6c25d0272f798cd6d7a04ed315da76cec68c602d87040c7847287f/torch-2.9.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:01cff95ecd9a212ea2f141db28acccdceb6a4c54f64e6c51091146f5e2a772c6", size = 899738273 }, - { url = "https://files.pythonhosted.org/packages/66/11/c1c5ba6691cda6279087c35bd626536e4fd29521fe740abf5008377a9a02/torch-2.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:4582b162f541651f0cb184d3e291c05c2f556c7117c64a9873e2ee158d40062b", size = 109280887 }, - { url = "https://files.pythonhosted.org/packages/dd/5f/b85bd8c05312d71de9402bf5868d217c38827cfd09d8f8514e5be128a52b/torch-2.9.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:33f58e9a102a91259af289d50525c30323b5c9ae1d31322b6447c0814da68695", size = 74478983 }, - { url = "https://files.pythonhosted.org/packages/c2/1c/90eb13833cdf4969ea9707586d7b57095c3b6e2b223a7256bf111689bcb8/torch-2.9.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c30a17fc83eeab346913e237c64b15b5ba6407fff812f6c541e322e19bc9ea0e", size = 104111330 }, - { url = "https://files.pythonhosted.org/packages/0e/21/2254c54b8d523592c25ef4434769aa23e29b1e6bf5f4c0ad9e27bf442927/torch-2.9.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8f25033b8667b57857dfd01458fbf2a9e6a6df1f8def23aef0dc46292f6aa642", size = 899750243 }, - { url = "https://files.pythonhosted.org/packages/b7/a5/5cb94fa4fd1e78223455c23c200f30f6dc10c6d4a2bcc8f6e7f2a2588370/torch-2.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:d037f1b4ffd25013be4a7bf3651a0a910c68554956c7b2c92ebe87c76475dece", size = 109284513 }, - { url = "https://files.pythonhosted.org/packages/66/e8/fc414d8656250ee46120b44836ffbb3266343db424b3e18ca79ebbf69d4f/torch-2.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e4e5b5cba837a2a8d1a497ba9a58dae46fa392593eaa13b871c42f71847503a5", size = 74830362 }, - { url = "https://files.pythonhosted.org/packages/ed/5f/9474c98fc5ae0cd04b9466035428cd360e6611a86b8352a0fc2fa504acdc/torch-2.9.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:64693568f5dc4dbd5f880a478b1cea0201cc6b510d91d1bc54fea86ac5d1a637", size = 104144940 }, - { url = "https://files.pythonhosted.org/packages/2d/5a/8e0c1cf57830172c109d4bd6be2708cabeaf550983eee7029291322447a0/torch-2.9.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:f8ed31ddd7d10bfb3fbe0b9fe01b1243577f13d75e6f4a0839a283915ce3791e", size = 899744054 }, - { url = "https://files.pythonhosted.org/packages/6d/28/82c28b30fcb4b7c9cdd995763d18bbb830d6521356712faebbad92ffa61d/torch-2.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:eff527d4e4846e6f70d2afd8058b73825761203d66576a7e04ea2ecfebcb4ab8", size = 109517546 }, - { url = "https://files.pythonhosted.org/packages/ff/c3/a91f96ec74347fa5fd24453fa514bc61c61ecc79196fa760b012a1873d96/torch-2.9.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:f8877779cf56d1ce431a7636703bdb13307f5960bb1af49716d8b179225e0e6a", size = 74480732 }, - { url = "https://files.pythonhosted.org/packages/5c/73/9f70af34b334a7e0ef496ceec96b7ec767bd778ea35385ce6f77557534d1/torch-2.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7e614fae699838038d888729f82b687c03413c5989ce2a9481f9a7e7a396e0bb", size = 74433037 }, - { url = "https://files.pythonhosted.org/packages/b7/84/37cf88625901934c97109e583ecc21777d21c6f54cda97a7e5bbad1ee2f2/torch-2.9.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:dfb5b8cd310ba3436c7e14e8b7833ef658cf3045e50d2bdaed23c8fc517065eb", size = 104116482 }, - { url = "https://files.pythonhosted.org/packages/56/8e/ca8b17866943a8d4f4664d402ea84210aa274588b4c5d89918f5caa24eec/torch-2.9.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b3d29524993a478e46f5d598b249cd824b7ed98d7fba538bd9c4cde6c803948f", size = 899746916 }, - { url = "https://files.pythonhosted.org/packages/43/65/3b17c0fbbdab6501c5b320a52a648628d0d44e7379f64e27d9eef701b6bf/torch-2.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:71c7578984f5ec0eb645eb4816ac8435fcf3e3e2ae1901bcd2f519a9cafb5125", size = 109275151 }, - { url = "https://files.pythonhosted.org/packages/83/36/74f8c051f785500396e42f93542422422dfd874a174f21f8d955d36e5d64/torch-2.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:71d9309aee457bbe0b164bce2111cd911c4ed4e847e65d5077dbbcd3aba6befc", size = 74823353 }, - { url = "https://files.pythonhosted.org/packages/62/51/dc3b4e2f9ba98ae27238f0153ca098bf9340b2dafcc67fde645d496dfc2a/torch-2.9.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c08fb654d783899e204a32cca758a7ce8a45b2d78eeb89517cc937088316f78e", size = 104140340 }, - { url = "https://files.pythonhosted.org/packages/c0/8d/b00657f8141ac16af7bb6cda2e67de18499a3263b78d516b9a93fcbc98e3/torch-2.9.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ec8feb0099b2daa5728fbc7abb0b05730fd97e0f359ff8bda09865aaa7bd7d4b", size = 899731750 }, - { url = "https://files.pythonhosted.org/packages/fc/29/bd361e0cbb2c79ce6450f42643aaf6919956f89923a50571b0ebfe92d142/torch-2.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:695ba920f234ad4170c9c50e28d56c848432f8f530e6bc7f88fcb15ddf338e75", size = 109503850 }, + { url = "https://files.pythonhosted.org/packages/d1/35/e8b2daf02ce933e4518e6f5682c72fd0ed66c15910ea1fb4168f442b71c4/torch-2.5.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:de5b7d6740c4b636ef4db92be922f0edc425b65ed78c5076c43c42d362a45457", size = 906474467 }, + { url = "https://files.pythonhosted.org/packages/40/04/bd91593a4ca178ece93ca55f27e2783aa524aaccbfda66831d59a054c31e/torch-2.5.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:340ce0432cad0d37f5a31be666896e16788f1adf8ad7be481196b503dad675b9", size = 91919450 }, + { url = "https://files.pythonhosted.org/packages/0d/4a/e51420d46cfc90562e85af2fee912237c662ab31140ab179e49bd69401d6/torch-2.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:603c52d2fe06433c18b747d25f5c333f9c1d58615620578c326d66f258686f9a", size = 203098237 }, + { url = "https://files.pythonhosted.org/packages/d0/db/5d9cbfbc7968d79c5c09a0bc0bc3735da079f2fd07cc10498a62b320a480/torch-2.5.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:31f8c39660962f9ae4eeec995e3049b5492eb7360dd4f07377658ef4d728fa4c", size = 63884466 }, + { url = "https://files.pythonhosted.org/packages/8b/5c/36c114d120bfe10f9323ed35061bc5878cc74f3f594003854b0ea298942f/torch-2.5.1-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:ed231a4b3a5952177fafb661213d690a72caaad97d5824dd4fc17ab9e15cec03", size = 906389343 }, + { url = "https://files.pythonhosted.org/packages/6d/69/d8ada8b6e0a4257556d5b4ddeb4345ea8eeaaef3c98b60d1cca197c7ad8e/torch-2.5.1-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:3f4b7f10a247e0dcd7ea97dc2d3bfbfc90302ed36d7f3952b0008d0df264e697", size = 91811673 }, + { url = "https://files.pythonhosted.org/packages/5f/ba/607d013b55b9fd805db2a5c2662ec7551f1910b4eef39653eeaba182c5b2/torch-2.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:73e58e78f7d220917c5dbfad1a40e09df9929d3b95d25e57d9f8558f84c9a11c", size = 203046841 }, + { url = "https://files.pythonhosted.org/packages/57/6c/bf52ff061da33deb9f94f4121fde7ff3058812cb7d2036c97bc167793bd1/torch-2.5.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:8c712df61101964eb11910a846514011f0b6f5920c55dbf567bff8a34163d5b1", size = 63858109 }, + { url = "https://files.pythonhosted.org/packages/69/72/20cb30f3b39a9face296491a86adb6ff8f1a47a897e4d14667e6cf89d5c3/torch-2.5.1-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:9b61edf3b4f6e3b0e0adda8b3960266b9009d02b37555971f4d1c8f7a05afed7", size = 906393265 }, ] [[package]] @@ -4212,7 +4191,7 @@ wheels = [ [[package]] name = "transformers" -version = "4.57.1" +version = "4.45.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -4226,22 +4205,21 @@ dependencies = [ { name = "tokenizers" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/68/a39307bcc4116a30b2106f2e689130a48de8bd8a1e635b5e1030e46fcd9e/transformers-4.57.1.tar.gz", hash = "sha256:f06c837959196c75039809636cd964b959f6604b75b8eeec6fdfc0440b89cc55", size = 10142511 } +sdist = { url = "https://files.pythonhosted.org/packages/4b/4c/3862b2dd6cdf83b187897bd351da0f7fb74d0df642b03c6f5d06353a3ca0/transformers-4.45.2.tar.gz", hash = "sha256:72bc390f6b203892561f05f86bbfaa0e234aab8e927a83e62b9d92ea7e3ae101", size = 8478357 } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/d3/c16c3b3cf7655a67db1144da94b021c200ac1303f82428f2beef6c2e72bb/transformers-4.57.1-py3-none-any.whl", hash = "sha256:b10d05da8fa67dc41644dbbf9bc45a44cb86ae33da6f9295f5fbf5b7890bd267", size = 11990925 }, + { url = "https://files.pythonhosted.org/packages/f9/9d/030cc1b3e88172967e22ee1d012e0d5e0384eb70d2a098d1669d549aea29/transformers-4.45.2-py3-none-any.whl", hash = "sha256:c551b33660cfc815bae1f9f097ecfd1e65be623f13c6ee0dda372bd881460210", size = 9881312 }, ] [[package]] name = "triton" -version = "3.5.0" +version = "3.1.0" source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, +] wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/78/949a04391c21956c816523678f0e5fa308eb5b1e7622d88c4e4ef5fceca0/triton-3.5.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f34bfa21c5b3a203c0f0eab28dcc1e49bd1f67d22724e77fb6665a659200a4ec", size = 170433488 }, - { url = "https://files.pythonhosted.org/packages/f5/3a/e991574f3102147b642e49637e0281e9bb7c4ba254edb2bab78247c85e01/triton-3.5.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9e71db82261c4ffa3921cd050cd5faa18322d2d405c30eb56084afaff3b0833", size = 170476535 }, - { url = "https://files.pythonhosted.org/packages/6c/29/10728de8a6e932e517c10773486b8e99f85d1b1d9dd87d9a9616e1fef4a1/triton-3.5.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e6bb9aa5519c084a333acdba443789e50012a4b851cd486c54f0b8dc2a8d3a12", size = 170487289 }, - { url = "https://files.pythonhosted.org/packages/5c/38/db80e48b9220c9bce872b0f616ad0446cdf554a40b85c7865cbca99ab3c2/triton-3.5.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c83f2343e1a220a716c7b3ab9fccfcbe3ad4020d189549200e2d2e8d5868bed9", size = 170577179 }, - { url = "https://files.pythonhosted.org/packages/ff/60/1810655d1d856c9a4fcc90ee8966d85f552d98c53a6589f95ab2cbe27bb8/triton-3.5.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da0fa67ccd76c3dcfb0bffe1b1c57c685136a6bd33d141c24d9655d4185b1289", size = 170487949 }, - { url = "https://files.pythonhosted.org/packages/fb/b7/1dec8433ac604c061173d0589d99217fe7bf90a70bdc375e745d044b8aad/triton-3.5.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:317fe477ea8fd4524a6a8c499fb0a36984a56d0b75bf9c9cb6133a1c56d5a6e7", size = 170580176 }, + { url = "https://files.pythonhosted.org/packages/86/17/d9a5cf4fcf46291856d1e90762e36cbabd2a56c7265da0d1d9508c8e3943/triton-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f34f6e7885d1bf0eaaf7ba875a5f0ce6f3c13ba98f9503651c1e6dc6757ed5c", size = 209506424 }, + { url = "https://files.pythonhosted.org/packages/78/eb/65f5ba83c2a123f6498a3097746607e5b2f16add29e36765305e4ac7fdd8/triton-3.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8182f42fd8080a7d39d666814fa36c5e30cc00ea7eeeb1a2983dbb4c99a0fdc", size = 209551444 }, ] [[package]]