Files
wehub-resource-sync c889a57b6b
Test Suites / Build CI Environment (push) Has been cancelled
Test Suites / Basic Tests (push) Has been cancelled
Test Suites / End-to-End Tests (push) Has been cancelled
Test Suites / CLI Tests (push) Has been cancelled
Test Suites / Slow End-to-End Tests (push) Has been cancelled
Test Suites / Graph Database Tests (push) Has been cancelled
Test Suites / Vector DB Tests (push) Has been cancelled
Test Suites / Temporal Graph Test (push) Has been cancelled
Test Suites / Search Test on Different DBs (push) Has been cancelled
Test Suites / Example Tests (push) Has been cancelled
Test Suites / Notebook Tests (push) Has been cancelled
Test Suites / OS and Python Tests Ubuntu (push) Has been cancelled
Test Suites / OS and Python Tests Extended (push) Has been cancelled
Test Suites / LLM Test Suite (push) Has been cancelled
Test Suites / S3 File Storage Test (push) Has been cancelled
Test Suites / Run Integration Tests (push) Has been cancelled
Test Suites / MCP Tests (push) Has been cancelled
Test Suites / Docker Compose Test (push) Has been cancelled
Test Suites / Docker CI test (push) Has been cancelled
Test Suites / Relational DB Migration Tests (push) Has been cancelled
Test Suites / Distributed Cognee Test (push) Has been cancelled
Test Suites / DB Examples Tests (push) Has been cancelled
Test Suites / Test Completion Status (push) Has been cancelled
Test Suites / Claude Code Review (push) Has been cancelled
Test Suites / basic checks (push) Has been cancelled
build | Build and Push Cognee MCP Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
build | Build and Push Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.11) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.12) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (kuzu, kuzu) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (neo4j, neo4j) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Examples (push) Has been cancelled
Weighted Edges Tests / Code Quality for Weighted Edges (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:02:24 +08:00

224 lines
8.2 KiB
Python

"""BEAM benchmark adapter — loads synthetic long-context conversations.
Dataset: https://huggingface.co/datasets/Mohammadta/BEAM
Paper: "Beyond a Million Tokens: Benchmarking Long-Term Memory in LLMs"
Each conversation contains multi-session batches with 20 probing questions
across 10 skill categories (information_extraction, temporal_reasoning,
multi_session_reasoning, contradiction_resolution, event_ordering,
knowledge_update, summarization, abstention, preference_following,
instruction_following).
"""
import ast
import json
from typing import Any, Dict, List, Optional, Tuple, Union
from cognee.eval_framework.benchmark_adapters.base_benchmark_adapter import BaseBenchmarkAdapter
from cognee.shared.logging_utils import get_logger
logger = get_logger()
# Map BEAM question types to a normalized key for the answer field
# (the dataset uses different field names for the ground-truth answer)
_ANSWER_FIELD_NAMES = [
"answer",
"ideal_response",
"ideal_answer",
"ideal_summary",
]
def _extract_answer(question_dict: dict) -> str:
"""Extract the ground-truth answer from a BEAM probing question."""
for field in _ANSWER_FIELD_NAMES:
if field in question_dict and question_dict[field]:
val = question_dict[field]
return val if isinstance(val, str) else str(val)
return ""
def _flatten_chat(chat_batches: list) -> str:
"""Flatten the nested chat structure into a single text corpus.
The chat field is a list of batches, each batch is a list of messages.
We produce a readable conversation transcript.
"""
lines = []
for batch_idx, batch in enumerate(chat_batches):
lines.append(f"--- Session {batch_idx + 1} ---")
for msg in batch:
role = msg.get("role", "unknown").capitalize()
content = msg.get("content", "")
time_anchor = msg.get("time_anchor")
prefix = f"[{time_anchor}] " if time_anchor else ""
lines.append(f"{prefix}{role}: {content}")
lines.append("")
return "\n".join(lines)
class BEAMAdapter(BaseBenchmarkAdapter):
"""Adapter for the BEAM long-context conversation benchmark.
Loads conversations from the HuggingFace dataset and extracts
probing questions with rubric-based ground truth.
Args:
split: Dataset split to use ("100K", "500K", "1M"). Default "100K".
conversation_index: Which conversation to load (0-indexed). Default 0.
"""
def __init__(
self,
split: str = "100K",
conversation_index: int = 0,
max_batches: Optional[int] = None,
):
self.split = split
self.conversation_index = conversation_index
self.max_batches = max_batches
def load_corpus(
self,
limit: Optional[int] = None,
seed: int = 42,
load_golden_context: bool = False,
instance_filter: Optional[Union[str, List[str], List[int]]] = None,
) -> Tuple[List[str], List[Dict[str, Any]]]:
"""Load a single BEAM conversation as corpus + probing questions.
Returns:
corpus_list: List with one entry — the full conversation transcript.
question_answer_pairs: List of dicts with question, answer, rubric,
question_type, and optional source_chat_ids.
"""
try:
import datasets as _datasets_lib
load_dataset = _datasets_lib.load_dataset
except ImportError:
raise ImportError(
"The 'datasets' package is required for BEAM. Install it with: pip install datasets"
)
logger.info(
f"Loading BEAM dataset split={self.split}, conversation_index={self.conversation_index}"
)
ds = load_dataset("Mohammadta/BEAM", split=self.split)
if self.conversation_index >= len(ds):
raise IndexError(
f"conversation_index={self.conversation_index} out of range "
f"(split '{self.split}' has {len(ds)} conversations)"
)
row = ds[self.conversation_index]
# Build corpus — optionally truncate to first N batches for faster local runs
chat_batches = row["chat"]
if self.max_batches is not None and len(chat_batches) > self.max_batches:
logger.info(
f"Truncating conversation from {len(chat_batches)} batches "
f"to {self.max_batches} (max_batches)"
)
chat_batches = chat_batches[: self.max_batches]
corpus_text = _flatten_chat(chat_batches)
corpus_list = [corpus_text]
# Parse probing questions
probing_raw = row.get("probing_questions", "")
if isinstance(probing_raw, str):
try:
probing_data = ast.literal_eval(probing_raw)
except (ValueError, SyntaxError):
try:
probing_data = json.loads(probing_raw)
except json.JSONDecodeError:
logger.error("Failed to parse probing_questions field")
probing_data = {}
else:
probing_data = probing_raw if isinstance(probing_raw, dict) else {}
question_answer_pairs = []
for question_type, questions in probing_data.items():
if not isinstance(questions, list):
continue
for q in questions:
if not isinstance(q, dict) or "question" not in q:
continue
answer_text = _extract_answer(q)
rubric = q.get("rubric", [])
if isinstance(rubric, str):
rubric = [rubric]
qa_pair: Dict[str, Any] = {
"question": q["question"],
"answer": answer_text,
"question_type": question_type,
"rubric": rubric,
"difficulty": q.get("difficulty", "unknown"),
"conversation_id": row.get("conversation_id", str(self.conversation_index)),
}
source_ids = q.get("source_chat_ids")
if source_ids and load_golden_context:
golden = self._extract_golden_context(chat_batches, source_ids)
if golden:
qa_pair["golden_context"] = golden
question_answer_pairs.append(qa_pair)
# Apply instance filter if provided
if instance_filter is not None:
question_answer_pairs = self._filter_instances(
question_answer_pairs, instance_filter, id_key="question"
)
# Apply limit
if limit is not None and limit < len(question_answer_pairs):
question_answer_pairs = question_answer_pairs[:limit]
logger.info(
f"Loaded BEAM conversation {self.conversation_index}: "
f"{len(corpus_text)} chars corpus, "
f"{len(question_answer_pairs)} probing questions"
)
return corpus_list, question_answer_pairs
@staticmethod
def _extract_golden_context(chat_batches: list, source_ids: Any) -> str:
"""Extract messages referenced by source_chat_ids as golden context."""
# source_chat_ids can be a list of ints, or a dict with nested lists
ids_to_find: set = set()
if isinstance(source_ids, list):
for item in source_ids:
if isinstance(item, int):
ids_to_find.add(item)
elif isinstance(source_ids, dict):
for v in source_ids.values():
if isinstance(v, list):
for item in v:
if isinstance(item, int):
ids_to_find.add(item)
elif isinstance(v, int):
ids_to_find.add(v)
if not ids_to_find:
return ""
# Build id → message content map
context_parts = []
for batch in chat_batches:
for msg in batch:
msg_id = msg.get("id")
if msg_id in ids_to_find:
role = msg.get("role", "unknown").capitalize()
content = msg.get("content", "")
context_parts.append(f"{role}: {content}")
return "\n".join(context_parts)