infinity-context 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- infinity_context-0.1.0.dist-info/METADATA +239 -0
- infinity_context-0.1.0.dist-info/RECORD +1053 -0
- infinity_context-0.1.0.dist-info/WHEEL +5 -0
- infinity_context-0.1.0.dist-info/entry_points.txt +9 -0
- infinity_context-0.1.0.dist-info/licenses/LICENSE +201 -0
- infinity_context-0.1.0.dist-info/top_level.txt +8 -0
- infinity_context_adapters/__init__.py +1 -0
- infinity_context_adapters/cognee/__init__.py +5 -0
- infinity_context_adapters/cognee/adapter.py +364 -0
- infinity_context_adapters/embeddings/__init__.py +6 -0
- infinity_context_adapters/embeddings/noop_adapter.py +5 -0
- infinity_context_adapters/embeddings/openai_adapter.py +104 -0
- infinity_context_adapters/extraction/__init__.py +41 -0
- infinity_context_adapters/extraction/content.py +828 -0
- infinity_context_adapters/extraction/docling_engine.py +255 -0
- infinity_context_adapters/extraction/document_normalization.py +434 -0
- infinity_context_adapters/extraction/factory.py +69 -0
- infinity_context_adapters/extraction/fallback_context.py +137 -0
- infinity_context_adapters/extraction/file_detection.py +378 -0
- infinity_context_adapters/extraction/image_evidence.py +319 -0
- infinity_context_adapters/extraction/media_tools.py +579 -0
- infinity_context_adapters/extraction/openai_json.py +356 -0
- infinity_context_adapters/extraction/openai_vision.py +722 -0
- infinity_context_adapters/extraction/prompts/__init__.py +1 -0
- infinity_context_adapters/extraction/prompts/semantic_memory_v1.md +20 -0
- infinity_context_adapters/extraction/schemas/__init__.py +1 -0
- infinity_context_adapters/extraction/schemas/semantic_memory_v1.json +62 -0
- infinity_context_adapters/extraction/transcription/__init__.py +11 -0
- infinity_context_adapters/extraction/transcription/openai_adapter.py +400 -0
- infinity_context_adapters/extraction/transcription_engine.py +544 -0
- infinity_context_adapters/extraction/video_evidence.py +129 -0
- infinity_context_adapters/extraction/whisper_engine.py +386 -0
- infinity_context_adapters/features/__init__.py +7 -0
- infinity_context_adapters/features/context_building/__init__.py +53 -0
- infinity_context_adapters/features/context_building/candidate_provider_chain.py +68 -0
- infinity_context_adapters/features/context_building/graphiti_candidate_provider.py +44 -0
- infinity_context_adapters/features/context_building/in_memory_candidate_provider.py +62 -0
- infinity_context_adapters/features/context_building/postgres_candidate_provider.py +44 -0
- infinity_context_adapters/features/context_building/qdrant_candidate_provider.py +44 -0
- infinity_context_adapters/features/context_building/query_request.py +99 -0
- infinity_context_adapters/features/context_building/records.py +113 -0
- infinity_context_adapters/features/document_ingestion/__init__.py +74 -0
- infinity_context_adapters/features/document_ingestion/composition.py +69 -0
- infinity_context_adapters/features/document_ingestion/extraction_adapter.py +114 -0
- infinity_context_adapters/features/document_ingestion/in_memory_chunk_index.py +67 -0
- infinity_context_adapters/features/document_ingestion/in_memory_document_store.py +176 -0
- infinity_context_adapters/features/document_ingestion/postgres_document_store.py +110 -0
- infinity_context_adapters/features/document_ingestion/qdrant_chunk_index.py +49 -0
- infinity_context_adapters/features/document_ingestion/records.py +88 -0
- infinity_context_adapters/features/memory_facts/__init__.py +53 -0
- infinity_context_adapters/features/memory_facts/graphiti_fact_projection.py +111 -0
- infinity_context_adapters/features/memory_facts/in_memory_fact_store.py +200 -0
- infinity_context_adapters/features/memory_facts/postgres_fact_store.py +126 -0
- infinity_context_adapters/features/memory_facts/qdrant_fact_projection.py +100 -0
- infinity_context_adapters/features/memory_scopes/__init__.py +44 -0
- infinity_context_adapters/features/memory_scopes/in_memory_scope_store.py +189 -0
- infinity_context_adapters/features/memory_scopes/postgres_scope_store.py +121 -0
- infinity_context_adapters/features/memory_scopes/records.py +86 -0
- infinity_context_adapters/graphiti/__init__.py +5 -0
- infinity_context_adapters/graphiti/adapter.py +613 -0
- infinity_context_adapters/graphiti/identity_evidence.py +711 -0
- infinity_context_adapters/graphiti/scope_identity.py +31 -0
- infinity_context_adapters/local_blob.py +101 -0
- infinity_context_adapters/noop/__init__.py +16 -0
- infinity_context_adapters/noop/adapters.py +156 -0
- infinity_context_adapters/noop/runtime.py +21 -0
- infinity_context_adapters/postgres/__init__.py +19 -0
- infinity_context_adapters/postgres/asset_mappers.py +348 -0
- infinity_context_adapters/postgres/asset_repositories.py +865 -0
- infinity_context_adapters/postgres/benchmark_projection_schema.py +241 -0
- infinity_context_adapters/postgres/benchmark_run_completion.py +645 -0
- infinity_context_adapters/postgres/benchmark_run_repositories.py +908 -0
- infinity_context_adapters/postgres/benchmark_writer_fence.py +139 -0
- infinity_context_adapters/postgres/canonical_keyword_trigram.py +31 -0
- infinity_context_adapters/postgres/canonical_retrieval_batching.py +388 -0
- infinity_context_adapters/postgres/fact_repositories.py +766 -0
- infinity_context_adapters/postgres/mappers.py +745 -0
- infinity_context_adapters/postgres/migrations/0001_core_facts.sql +95 -0
- infinity_context_adapters/postgres/migrations/0002_threads_documents_chunks.sql +97 -0
- infinity_context_adapters/postgres/migrations/0003_suggestions.sql +24 -0
- infinity_context_adapters/postgres/migrations/0004_service_tokens.sql +16 -0
- infinity_context_adapters/postgres/migrations/0005_data_classification_lite.sql +8 -0
- infinity_context_adapters/postgres/migrations/0006_service_token_usage_metadata.sql +5 -0
- infinity_context_adapters/postgres/migrations/0007_service_token_permissions.sql +2 -0
- infinity_context_adapters/postgres/migrations/0008_service_token_memory_scope.sql +2 -0
- infinity_context_adapters/postgres/migrations/0009_outbox_lifecycle_hardening.sql +15 -0
- infinity_context_adapters/postgres/migrations/0010_fact_taxonomy.sql +14 -0
- infinity_context_adapters/postgres/migrations/0011_asset_extractions.sql +66 -0
- infinity_context_adapters/postgres/migrations/0012_asset_extraction_execution_hardening.sql +20 -0
- infinity_context_adapters/postgres/migrations/0012_usage_governance.sql +33 -0
- infinity_context_adapters/postgres/migrations/0013_context_link_suggestions.sql +44 -0
- infinity_context_adapters/postgres/migrations/0014_memory_anchors.sql +26 -0
- infinity_context_adapters/postgres/migrations/0015_users_acl.sql +36 -0
- infinity_context_adapters/postgres/migrations/0016_managed_benchmark_runs.sql +27 -0
- infinity_context_adapters/postgres/migrations/0017_managed_benchmark_writer_fence.sql +104 -0
- infinity_context_adapters/postgres/migrations/0018_benchmark_projection_manifest.sql +51 -0
- infinity_context_adapters/postgres/migrations/0019_managed_benchmark_sealed_fence.sql +131 -0
- infinity_context_adapters/postgres/migrations/0020_benchmark_cleanup_completion.sql +219 -0
- infinity_context_adapters/postgres/migrations/0021_benchmark_unsealed_abort.sql +64 -0
- infinity_context_adapters/postgres/migrations/0022_canonical_keyword_trigram.sql +5 -0
- infinity_context_adapters/postgres/models.py +878 -0
- infinity_context_adapters/postgres/projection_fence.py +80 -0
- infinity_context_adapters/postgres/repositories.py +906 -0
- infinity_context_adapters/postgres/repository_helpers.py +115 -0
- infinity_context_adapters/postgres/scope_repositories.py +540 -0
- infinity_context_adapters/postgres/unit_of_work.py +785 -0
- infinity_context_adapters/postgres/usage_repositories.py +128 -0
- infinity_context_adapters/postgres/user_repositories.py +164 -0
- infinity_context_adapters/provider_errors.py +73 -0
- infinity_context_adapters/qdrant/__init__.py +5 -0
- infinity_context_adapters/qdrant/identity_evidence.py +504 -0
- infinity_context_adapters/qdrant/vector_adapter.py +718 -0
- infinity_context_adapters/s3_blob.py +242 -0
- infinity_context_cli/__init__.py +5 -0
- infinity_context_cli/__main__.py +6 -0
- infinity_context_cli/cli.py +964 -0
- infinity_context_cli/config.py +184 -0
- infinity_context_cli/doctor.py +397 -0
- infinity_context_cli/local_experience.py +250 -0
- infinity_context_cli/mcp_config.py +124 -0
- infinity_context_cli/onboarding.py +542 -0
- infinity_context_cli/runtime.py +179 -0
- infinity_context_contracts/__init__.py +16 -0
- infinity_context_contracts/_json.py +32 -0
- infinity_context_contracts/capabilities.py +67 -0
- infinity_context_contracts/common.py +54 -0
- infinity_context_contracts/features/__init__.py +12 -0
- infinity_context_contracts/features/context_building.py +248 -0
- infinity_context_contracts/features/document_ingestion.py +277 -0
- infinity_context_contracts/features/memory_facts.py +236 -0
- infinity_context_contracts/features/memory_scopes.py +329 -0
- infinity_context_contracts/health.py +23 -0
- infinity_context_core/__init__.py +76 -0
- infinity_context_core/agent_behavior_contract.py +72 -0
- infinity_context_core/application/__init__.py +580 -0
- infinity_context_core/application/anchor_creative_work_filters.py +148 -0
- infinity_context_core/application/anchor_event_extraction.py +1571 -0
- infinity_context_core/application/anchor_extraction.py +1144 -0
- infinity_context_core/application/anchor_identity_normalization.py +90 -0
- infinity_context_core/application/anchor_relation_projection.py +203 -0
- infinity_context_core/application/anchor_temporal_window.py +172 -0
- infinity_context_core/application/asset_extraction_mapping.py +236 -0
- infinity_context_core/application/asset_upload_policy.py +952 -0
- infinity_context_core/application/auto_apply.py +72 -0
- infinity_context_core/application/auto_memory.py +527 -0
- infinity_context_core/application/capture_policy.py +61 -0
- infinity_context_core/application/chunker.py +93 -0
- infinity_context_core/application/context_action_role_evidence.py +890 -0
- infinity_context_core/application/context_action_role_labels.py +176 -0
- infinity_context_core/application/context_action_roles.py +2365 -0
- infinity_context_core/application/context_action_subject_policy.py +71 -0
- infinity_context_core/application/context_activity_companion.py +120 -0
- infinity_context_core/application/context_activity_duration_rerank.py +118 -0
- infinity_context_core/application/context_activity_participant_grounding.py +87 -0
- infinity_context_core/application/context_aggregation_answer_slots.py +586 -0
- infinity_context_core/application/context_aggregation_evidence_text.py +409 -0
- infinity_context_core/application/context_aggregation_rerank.py +145 -0
- infinity_context_core/application/context_aggregation_subject.py +137 -0
- infinity_context_core/application/context_allergy_condition_inference.py +146 -0
- infinity_context_core/application/context_anchor_identity.py +157 -0
- infinity_context_core/application/context_anchor_intent_ranking.py +84 -0
- infinity_context_core/application/context_anchor_relations.py +67 -0
- infinity_context_core/application/context_anchors.py +393 -0
- infinity_context_core/application/context_answer_evidence_types.py +14 -0
- infinity_context_core/application/context_answer_unit_shapes.py +207 -0
- infinity_context_core/application/context_artifact_evidence.py +893 -0
- infinity_context_core/application/context_attribution_lexicon.py +254 -0
- infinity_context_core/application/context_awareness_cause_rerank.py +109 -0
- infinity_context_core/application/context_baby_birth_evidence.py +91 -0
- infinity_context_core/application/context_birdwatching_schedule_rerank.py +130 -0
- infinity_context_core/application/context_bm25_ranking.py +450 -0
- infinity_context_core/application/context_book_author_preference_exact_turns.py +110 -0
- infinity_context_core/application/context_building_legacy_candidate_adapter.py +530 -0
- infinity_context_core/application/context_canonical_fact_retrieval.py +409 -0
- infinity_context_core/application/context_causal_reason_rerank.py +145 -0
- infinity_context_core/application/context_choice_reason_rerank.py +109 -0
- infinity_context_core/application/context_collectors.py +931 -0
- infinity_context_core/application/context_commonality_rerank.py +428 -0
- infinity_context_core/application/context_community_membership_inference.py +139 -0
- infinity_context_core/application/context_competition_count_exact_turns.py +383 -0
- infinity_context_core/application/context_conflicting_evidence_rerank.py +282 -0
- infinity_context_core/application/context_conversation_counterparty.py +260 -0
- infinity_context_core/application/context_count_cardinality.py +164 -0
- infinity_context_core/application/context_creative_work_count_exact_turns.py +517 -0
- infinity_context_core/application/context_diagnostics.py +55 -0
- infinity_context_core/application/context_diagnostics_bundle_keys.py +433 -0
- infinity_context_core/application/context_diagnostics_bundle_projection.py +711 -0
- infinity_context_core/application/context_diagnostics_item_projection.py +410 -0
- infinity_context_core/application/context_diagnostics_primitives.py +131 -0
- infinity_context_core/application/context_diagnostics_ranking.py +143 -0
- infinity_context_core/application/context_diagnostics_signals.py +209 -0
- infinity_context_core/application/context_diagnostics_sources.py +82 -0
- infinity_context_core/application/context_dialogue_authority.py +382 -0
- infinity_context_core/application/context_distinct_set_evidence.py +994 -0
- infinity_context_core/application/context_distinct_set_selection.py +335 -0
- infinity_context_core/application/context_domain_rerank_apply.py +310 -0
- infinity_context_core/application/context_domain_rerank_signals.py +2391 -0
- infinity_context_core/application/context_domain_rerank_types.py +14 -0
- infinity_context_core/application/context_english_activity_displacement_answer_support.py +128 -0
- infinity_context_core/application/context_english_lifestyle_inference.py +122 -0
- infinity_context_core/application/context_english_lifestyle_inference_answer_support.py +291 -0
- infinity_context_core/application/context_english_temporal_dates.py +53 -0
- infinity_context_core/application/context_evidence_priority.py +262 -0
- infinity_context_core/application/context_evidence_reservation_safety.py +208 -0
- infinity_context_core/application/context_exercise_performance_rerank.py +71 -0
- infinity_context_core/application/context_food_inventory_exact_turns.py +583 -0
- infinity_context_core/application/context_frequency_rerank.py +109 -0
- infinity_context_core/application/context_future_plan_timing_rerank.py +110 -0
- infinity_context_core/application/context_generic_behavior_inference.py +348 -0
- infinity_context_core/application/context_geo_residence_aliases.py +149 -0
- infinity_context_core/application/context_hydration.py +329 -0
- infinity_context_core/application/context_identity_fact_rerank.py +362 -0
- infinity_context_core/application/context_identity_rerank.py +76 -0
- infinity_context_core/application/context_identity_terms.py +15 -0
- infinity_context_core/application/context_inference_evidence.py +484 -0
- infinity_context_core/application/context_inference_evidence_terms.py +744 -0
- infinity_context_core/application/context_internal_diagnostics.py +61 -0
- infinity_context_core/application/context_item_evidence.py +108 -0
- infinity_context_core/application/context_item_purchase_evidence.py +49 -0
- infinity_context_core/application/context_keyword_aggregation_prefilter.py +63 -0
- infinity_context_core/application/context_keyword_ranking.py +178 -0
- infinity_context_core/application/context_lexical.py +750 -0
- infinity_context_core/application/context_lifecycle_rerank.py +502 -0
- infinity_context_core/application/context_link_candidate_policy.py +864 -0
- infinity_context_core/application/context_link_expansion.py +727 -0
- infinity_context_core/application/context_link_expansion_items.py +441 -0
- infinity_context_core/application/context_link_policy.py +451 -0
- infinity_context_core/application/context_link_source_enrichment.py +120 -0
- infinity_context_core/application/context_media_time.py +368 -0
- infinity_context_core/application/context_museum_visit_policy.py +66 -0
- infinity_context_core/application/context_named_person_preference.py +313 -0
- infinity_context_core/application/context_national_park_inference.py +93 -0
- infinity_context_core/application/context_object_attribute_rerank.py +416 -0
- infinity_context_core/application/context_object_mismatch.py +63 -0
- infinity_context_core/application/context_occupational_role_identity.py +961 -0
- infinity_context_core/application/context_ordinal_sequence.py +134 -0
- infinity_context_core/application/context_owner_grounding.py +408 -0
- infinity_context_core/application/context_packer.py +858 -0
- infinity_context_core/application/context_packer_answer_slots.py +733 -0
- infinity_context_core/application/context_packer_answer_support.py +3101 -0
- infinity_context_core/application/context_packer_answer_support_patterns.py +1267 -0
- infinity_context_core/application/context_packer_answer_support_slots.py +1209 -0
- infinity_context_core/application/context_packer_answer_support_utils.py +473 -0
- infinity_context_core/application/context_packer_diagnostics.py +48 -0
- infinity_context_core/application/context_packer_exact_literal_turns.py +697 -0
- infinity_context_core/application/context_packer_exact_prepasses.py +758 -0
- infinity_context_core/application/context_packer_exact_turn_utils.py +133 -0
- infinity_context_core/application/context_packer_identity_policy.py +155 -0
- infinity_context_core/application/context_packer_inference_reservation.py +113 -0
- infinity_context_core/application/context_packer_inventory_prepasses.py +359 -0
- infinity_context_core/application/context_packer_inventory_slots.py +771 -0
- infinity_context_core/application/context_packer_marker_coverage.py +409 -0
- infinity_context_core/application/context_packer_rendering.py +338 -0
- infinity_context_core/application/context_packer_selection.py +487 -0
- infinity_context_core/application/context_packer_source_policy.py +123 -0
- infinity_context_core/application/context_paired_evidence_requirements.py +78 -0
- infinity_context_core/application/context_paired_evidence_roles.py +64 -0
- infinity_context_core/application/context_person_activity_exact_turns.py +375 -0
- infinity_context_core/application/context_person_aliases.py +49 -0
- infinity_context_core/application/context_person_kinship.py +296 -0
- infinity_context_core/application/context_person_occupation.py +240 -0
- infinity_context_core/application/context_person_relation_inventory.py +755 -0
- infinity_context_core/application/context_person_residence.py +398 -0
- infinity_context_core/application/context_person_team_membership.py +305 -0
- infinity_context_core/application/context_pet_ownership.py +223 -0
- infinity_context_core/application/context_polarity_rerank.py +338 -0
- infinity_context_core/application/context_policy.py +107 -0
- infinity_context_core/application/context_political_inference.py +128 -0
- infinity_context_core/application/context_possession_source.py +161 -0
- infinity_context_core/application/context_precise_temporal_evidence.py +156 -0
- infinity_context_core/application/context_preference_goal_rerank.py +428 -0
- infinity_context_core/application/context_preference_inference.py +335 -0
- infinity_context_core/application/context_property_interaction_evidence.py +659 -0
- infinity_context_core/application/context_provider_retrieval_terms.py +95 -0
- infinity_context_core/application/context_quality.py +488 -0
- infinity_context_core/application/context_quantity_evidence_patterns.py +254 -0
- infinity_context_core/application/context_quantity_evidence_slots.py +750 -0
- infinity_context_core/application/context_quantity_pending_clothing.py +200 -0
- infinity_context_core/application/context_query_artifact_inventory_expansions.py +76 -0
- infinity_context_core/application/context_query_decomposition.py +879 -0
- infinity_context_core/application/context_query_decomposition_answer_policy.py +707 -0
- infinity_context_core/application/context_query_decomposition_contracts.py +28 -0
- infinity_context_core/application/context_query_decomposition_event_policy.py +535 -0
- infinity_context_core/application/context_query_decomposition_inventory_policy.py +767 -0
- infinity_context_core/application/context_query_decomposition_shared.py +300 -0
- infinity_context_core/application/context_query_duration.py +171 -0
- infinity_context_core/application/context_query_entity_relation_expansions.py +212 -0
- infinity_context_core/application/context_query_event_summary_expansions.py +80 -0
- infinity_context_core/application/context_query_expansion.py +200 -0
- infinity_context_core/application/context_query_expansion_rule_catalog_part1.py +1279 -0
- infinity_context_core/application/context_query_expansion_rule_catalog_part2.py +770 -0
- infinity_context_core/application/context_query_expansion_rule_catalog_part3.py +982 -0
- infinity_context_core/application/context_query_expansion_rule_catalog_part4.py +1334 -0
- infinity_context_core/application/context_query_expansion_rule_catalog_part5.py +517 -0
- infinity_context_core/application/context_query_expansion_rule_terms.py +269 -0
- infinity_context_core/application/context_query_expansion_rules.py +66 -0
- infinity_context_core/application/context_query_expansion_selection.py +892 -0
- infinity_context_core/application/context_query_financial_resources_inference.py +92 -0
- infinity_context_core/application/context_query_food_expansions.py +89 -0
- infinity_context_core/application/context_query_frequency.py +194 -0
- infinity_context_core/application/context_query_game_expansions.py +141 -0
- infinity_context_core/application/context_query_health_lifestyle_expansions.py +178 -0
- infinity_context_core/application/context_query_identity_terms.py +81 -0
- infinity_context_core/application/context_query_intent.py +33 -0
- infinity_context_core/application/context_query_intent_common.py +29 -0
- infinity_context_core/application/context_query_intent_contracts.py +92 -0
- infinity_context_core/application/context_query_intent_extraction.py +614 -0
- infinity_context_core/application/context_query_intent_keys.py +499 -0
- infinity_context_core/application/context_query_intent_matching.py +407 -0
- infinity_context_core/application/context_query_organization_summary_expansions.py +75 -0
- infinity_context_core/application/context_query_personal_fact_expansions.py +314 -0
- infinity_context_core/application/context_query_pet_activity_expansions.py +329 -0
- infinity_context_core/application/context_query_preference_expansions.py +97 -0
- infinity_context_core/application/context_query_project_summary_expansions.py +60 -0
- infinity_context_core/application/context_query_relevance_ranking.py +76 -0
- infinity_context_core/application/context_query_sports_expansions.py +48 -0
- infinity_context_core/application/context_query_state_transition.py +53 -0
- infinity_context_core/application/context_query_support_role.py +165 -0
- infinity_context_core/application/context_query_temporal_expansions.py +18 -0
- infinity_context_core/application/context_query_travel_expansions.py +134 -0
- infinity_context_core/application/context_query_vehicle_expansions.py +79 -0
- infinity_context_core/application/context_query_workflow_expansions.py +233 -0
- infinity_context_core/application/context_query_workflow_intent.py +65 -0
- infinity_context_core/application/context_quote_scanner.py +342 -0
- infinity_context_core/application/context_rank_dedupe.py +171 -0
- infinity_context_core/application/context_rank_fusion.py +191 -0
- infinity_context_core/application/context_ranked_activity_reservation.py +586 -0
- infinity_context_core/application/context_ranked_evidence_coverage_reservation.py +427 -0
- infinity_context_core/application/context_ranked_evidence_priority_reservation.py +37 -0
- infinity_context_core/application/context_ranked_evidence_selection.py +260 -0
- infinity_context_core/application/context_ranking.py +3681 -0
- infinity_context_core/application/context_ranking_reason_policy.py +31 -0
- infinity_context_core/application/context_ranking_reason_priorities.py +859 -0
- infinity_context_core/application/context_ranking_reason_scores.py +519 -0
- infinity_context_core/application/context_recommendation_answer_support.py +358 -0
- infinity_context_core/application/context_recommendation_exact_turns.py +360 -0
- infinity_context_core/application/context_relation_requirement.py +498 -0
- infinity_context_core/application/context_relationship_status_evidence.py +83 -0
- infinity_context_core/application/context_relative_duration_evidence.py +61 -0
- infinity_context_core/application/context_relevance.py +430 -0
- infinity_context_core/application/context_reported_obligation_attribution.py +416 -0
- infinity_context_core/application/context_reporting_frames.py +338 -0
- infinity_context_core/application/context_requirement_coverage.py +767 -0
- infinity_context_core/application/context_requirement_guard.py +503 -0
- infinity_context_core/application/context_requirement_patterns.py +943 -0
- infinity_context_core/application/context_requirement_ranking.py +181 -0
- infinity_context_core/application/context_requirement_signals.py +101 -0
- infinity_context_core/application/context_rerank_domain_heuristics.py +433 -0
- infinity_context_core/application/context_rerank_evidence_signals.py +400 -0
- infinity_context_core/application/context_rerank_relevance.py +131 -0
- infinity_context_core/application/context_retrieval_performance.py +153 -0
- infinity_context_core/application/context_review_items.py +338 -0
- infinity_context_core/application/context_score_signal_rerank.py +21 -0
- infinity_context_core/application/context_short_story_progress_evidence.py +119 -0
- infinity_context_core/application/context_snippets.py +545 -0
- infinity_context_core/application/context_social_education_inference.py +265 -0
- infinity_context_core/application/context_social_inventory_rerank.py +329 -0
- infinity_context_core/application/context_source_family.py +100 -0
- infinity_context_core/application/context_source_grounding.py +277 -0
- infinity_context_core/application/context_source_sibling_answer_evidence_repair.py +1983 -0
- infinity_context_core/application/context_source_sibling_contracts.py +35 -0
- infinity_context_core/application/context_source_sibling_evidence.py +405 -0
- infinity_context_core/application/context_source_sibling_evidence_rules.py +798 -0
- infinity_context_core/application/context_source_sibling_evidence_shared.py +449 -0
- infinity_context_core/application/context_source_sibling_identity.py +114 -0
- infinity_context_core/application/context_source_sibling_inventory_evidence.py +28 -0
- infinity_context_core/application/context_source_sibling_patterns.py +946 -0
- infinity_context_core/application/context_source_sibling_place_evidence.py +561 -0
- infinity_context_core/application/context_source_sibling_policy.py +988 -0
- infinity_context_core/application/context_source_sibling_scoring.py +721 -0
- infinity_context_core/application/context_source_siblings.py +363 -0
- infinity_context_core/application/context_speaker_attribution.py +574 -0
- infinity_context_core/application/context_speaker_disambiguation.py +108 -0
- infinity_context_core/application/context_stage_diagnostics.py +111 -0
- infinity_context_core/application/context_state_evidence.py +196 -0
- infinity_context_core/application/context_state_residence_inference.py +163 -0
- infinity_context_core/application/context_state_transition_pairing.py +401 -0
- infinity_context_core/application/context_temporal_answer_grounding.py +187 -0
- infinity_context_core/application/context_temporal_hints.py +485 -0
- infinity_context_core/application/context_temporal_intent_policy.py +146 -0
- infinity_context_core/application/context_temporal_interval_requirements.py +107 -0
- infinity_context_core/application/context_temporal_metadata.py +29 -0
- infinity_context_core/application/context_temporal_query.py +1358 -0
- infinity_context_core/application/context_temporal_ranges.py +471 -0
- infinity_context_core/application/context_temporal_session_order.py +439 -0
- infinity_context_core/application/context_temporal_source_identity.py +426 -0
- infinity_context_core/application/context_temporal_source_turn.py +1290 -0
- infinity_context_core/application/context_temporal_source_turn_labels.py +265 -0
- infinity_context_core/application/context_temporal_source_turn_patterns.py +186 -0
- infinity_context_core/application/context_temporal_source_turn_types.py +62 -0
- infinity_context_core/application/context_travel_hobby_writing_evidence.py +176 -0
- infinity_context_core/application/context_travel_place_evidence.py +23 -0
- infinity_context_core/application/document_fragments.py +206 -0
- infinity_context_core/application/document_text.py +203 -0
- infinity_context_core/application/dto.py +311 -0
- infinity_context_core/application/dto_anchors.py +143 -0
- infinity_context_core/application/dto_assets.py +234 -0
- infinity_context_core/application/dto_benchmark_runs.py +114 -0
- infinity_context_core/application/dto_context.py +176 -0
- infinity_context_core/application/dto_context_links.py +170 -0
- infinity_context_core/application/dto_facts.py +275 -0
- infinity_context_core/application/dto_suggestions_capture.py +199 -0
- infinity_context_core/application/dto_workspace.py +101 -0
- infinity_context_core/application/extraction_coordinates.py +56 -0
- infinity_context_core/application/extraction_resource_policy.py +796 -0
- infinity_context_core/application/extractor.py +147 -0
- infinity_context_core/application/memory_digest_renderer.py +215 -0
- infinity_context_core/application/multimodal_manifest.py +418 -0
- infinity_context_core/application/normalize.py +29 -0
- infinity_context_core/application/observed_anchor_resolution.py +243 -0
- infinity_context_core/application/related_facts.py +144 -0
- infinity_context_core/application/review_payloads.py +104 -0
- infinity_context_core/application/safe_payload.py +86 -0
- infinity_context_core/application/semantic_dedupe.py +943 -0
- infinity_context_core/application/sensitive_text.py +39 -0
- infinity_context_core/application/source_refs.py +263 -0
- infinity_context_core/application/storage_key_safety.py +13 -0
- infinity_context_core/application/temporal_validity.py +31 -0
- infinity_context_core/application/use_cases/__init__.py +1 -0
- infinity_context_core/application/use_cases/anchors.py +1165 -0
- infinity_context_core/application/use_cases/asset_extraction_support.py +273 -0
- infinity_context_core/application/use_cases/asset_extractions.py +1454 -0
- infinity_context_core/application/use_cases/assets.py +463 -0
- infinity_context_core/application/use_cases/benchmark_runs.py +877 -0
- infinity_context_core/application/use_cases/benchmark_unsealed_abort.py +147 -0
- infinity_context_core/application/use_cases/blob_storage_cleanup.py +289 -0
- infinity_context_core/application/use_cases/blob_storage_integrity.py +351 -0
- infinity_context_core/application/use_cases/build_context.py +995 -0
- infinity_context_core/application/use_cases/build_context_final_packing.py +59 -0
- infinity_context_core/application/use_cases/build_context_item_projection.py +839 -0
- infinity_context_core/application/use_cases/build_context_keyword_aggregation.py +908 -0
- infinity_context_core/application/use_cases/build_context_keyword_aggregation_seeds.py +132 -0
- infinity_context_core/application/use_cases/build_context_keyword_aggregation_selection.py +280 -0
- infinity_context_core/application/use_cases/build_context_keyword_aggregation_support.py +161 -0
- infinity_context_core/application/use_cases/build_context_requirement_guard.py +156 -0
- infinity_context_core/application/use_cases/build_context_source_selection.py +935 -0
- infinity_context_core/application/use_cases/build_memory_digest.py +203 -0
- infinity_context_core/application/use_cases/build_memory_insights.py +979 -0
- infinity_context_core/application/use_cases/consolidate_capture.py +806 -0
- infinity_context_core/application/use_cases/context_link_crud.py +196 -0
- infinity_context_core/application/use_cases/context_link_reviews.py +441 -0
- infinity_context_core/application/use_cases/context_link_suggestions.py +1060 -0
- infinity_context_core/application/use_cases/context_link_visibility.py +106 -0
- infinity_context_core/application/use_cases/context_links.py +33 -0
- infinity_context_core/application/use_cases/delete_document.py +71 -0
- infinity_context_core/application/use_cases/delete_thread_memory.py +55 -0
- infinity_context_core/application/use_cases/duplicate_merge_resolution.py +307 -0
- infinity_context_core/application/use_cases/ensure_scope.py +29 -0
- infinity_context_core/application/use_cases/expire_suggestions.py +36 -0
- infinity_context_core/application/use_cases/export_graph.py +684 -0
- infinity_context_core/application/use_cases/fact_relations.py +204 -0
- infinity_context_core/application/use_cases/forget_fact.py +43 -0
- infinity_context_core/application/use_cases/get_capabilities.py +213 -0
- infinity_context_core/application/use_cases/get_capture.py +16 -0
- infinity_context_core/application/use_cases/get_session_status.py +23 -0
- infinity_context_core/application/use_cases/ingest_document.py +376 -0
- infinity_context_core/application/use_cases/ingest_episode.py +263 -0
- infinity_context_core/application/use_cases/list_captures.py +25 -0
- infinity_context_core/application/use_cases/memory_browser.py +414 -0
- infinity_context_core/application/use_cases/operations_console.py +293 -0
- infinity_context_core/application/use_cases/process_document.py +83 -0
- infinity_context_core/application/use_cases/purge_capture.py +34 -0
- infinity_context_core/application/use_cases/query_documents.py +42 -0
- infinity_context_core/application/use_cases/query_facts.py +57 -0
- infinity_context_core/application/use_cases/receive_capture.py +238 -0
- infinity_context_core/application/use_cases/related_facts.py +47 -0
- infinity_context_core/application/use_cases/remember_fact.py +145 -0
- infinity_context_core/application/use_cases/spaces_memory_scopes.py +134 -0
- infinity_context_core/application/use_cases/suggestions.py +804 -0
- infinity_context_core/application/use_cases/update_fact.py +43 -0
- infinity_context_core/application/use_cases/usage.py +49 -0
- infinity_context_core/application/use_cases/users.py +138 -0
- infinity_context_core/domain/__init__.py +1 -0
- infinity_context_core/domain/aggregation_admission.py +200 -0
- infinity_context_core/domain/anchor_entities.py +316 -0
- infinity_context_core/domain/assets.py +654 -0
- infinity_context_core/domain/capture.py +321 -0
- infinity_context_core/domain/distinct_set_membership.py +69 -0
- infinity_context_core/domain/entities.py +103 -0
- infinity_context_core/domain/entity_policies.py +119 -0
- infinity_context_core/domain/entity_types.py +150 -0
- infinity_context_core/domain/errors.py +53 -0
- infinity_context_core/domain/events.py +14 -0
- infinity_context_core/domain/extraction.py +529 -0
- infinity_context_core/domain/idempotency.py +12 -0
- infinity_context_core/domain/memory_item_entities.py +426 -0
- infinity_context_core/domain/source_refs.py +91 -0
- infinity_context_core/domain/suggestion_entities.py +236 -0
- infinity_context_core/domain/taxonomy.py +121 -0
- infinity_context_core/domain/usage.py +221 -0
- infinity_context_core/domain/workspace_entities.py +269 -0
- infinity_context_core/features/__init__.py +7 -0
- infinity_context_core/features/context_building/__init__.py +3 -0
- infinity_context_core/features/context_building/application/__init__.py +92 -0
- infinity_context_core/features/context_building/application/candidate_fusion.py +136 -0
- infinity_context_core/features/context_building/application/coverage_reservation_selector.py +212 -0
- infinity_context_core/features/context_building/application/handlers.py +155 -0
- infinity_context_core/features/context_building/application/inference_evidence_reservation.py +592 -0
- infinity_context_core/features/context_building/application/provider_pipeline.py +61 -0
- infinity_context_core/features/context_building/application/queries.py +107 -0
- infinity_context_core/features/context_building/application/use_cases.py +78 -0
- infinity_context_core/features/context_building/domain/__init__.py +95 -0
- infinity_context_core/features/context_building/domain/budget.py +97 -0
- infinity_context_core/features/context_building/domain/context.py +157 -0
- infinity_context_core/features/context_building/domain/evidence_obligations.py +60 -0
- infinity_context_core/features/context_building/domain/feature.py +15 -0
- infinity_context_core/features/context_building/domain/prompt_sections.py +150 -0
- infinity_context_core/features/context_building/domain/query_pipeline.py +276 -0
- infinity_context_core/features/context_building/domain/rendering.py +102 -0
- infinity_context_core/features/context_building/ports/__init__.py +8 -0
- infinity_context_core/features/context_building/ports/candidates.py +36 -0
- infinity_context_core/features/context_building/public.py +165 -0
- infinity_context_core/features/context_building/tests/test_application_ports_public_skeleton.py +575 -0
- infinity_context_core/features/context_building/tests/test_domain_context_building_skeleton.py +402 -0
- infinity_context_core/features/document_ingestion/__init__.py +3 -0
- infinity_context_core/features/document_ingestion/application/__init__.py +31 -0
- infinity_context_core/features/document_ingestion/application/commands.py +62 -0
- infinity_context_core/features/document_ingestion/application/use_cases.py +273 -0
- infinity_context_core/features/document_ingestion/domain/__init__.py +61 -0
- infinity_context_core/features/document_ingestion/domain/chunking.py +297 -0
- infinity_context_core/features/document_ingestion/domain/errors.py +22 -0
- infinity_context_core/features/document_ingestion/domain/feature.py +18 -0
- infinity_context_core/features/document_ingestion/domain/source_document.py +228 -0
- infinity_context_core/features/document_ingestion/ports/__init__.py +21 -0
- infinity_context_core/features/document_ingestion/ports/indexing.py +51 -0
- infinity_context_core/features/document_ingestion/ports/repositories.py +56 -0
- infinity_context_core/features/document_ingestion/public.py +95 -0
- infinity_context_core/features/document_ingestion/tests/test_application_handlers.py +226 -0
- infinity_context_core/features/document_ingestion/tests/test_application_ports_public_skeleton.py +334 -0
- infinity_context_core/features/document_ingestion/tests/test_domain_chunking_skeleton.py +79 -0
- infinity_context_core/features/document_ingestion/tests/test_domain_source_document_skeleton.py +101 -0
- infinity_context_core/features/memory_facts/__init__.py +3 -0
- infinity_context_core/features/memory_facts/application/__init__.py +37 -0
- infinity_context_core/features/memory_facts/application/commands.py +89 -0
- infinity_context_core/features/memory_facts/application/handlers.py +241 -0
- infinity_context_core/features/memory_facts/application/use_cases.py +47 -0
- infinity_context_core/features/memory_facts/domain/__init__.py +35 -0
- infinity_context_core/features/memory_facts/domain/fact.py +98 -0
- infinity_context_core/features/memory_facts/domain/feature.py +15 -0
- infinity_context_core/features/memory_facts/ports/__init__.py +31 -0
- infinity_context_core/features/memory_facts/ports/clock.py +14 -0
- infinity_context_core/features/memory_facts/ports/ids.py +19 -0
- infinity_context_core/features/memory_facts/ports/outbox.py +26 -0
- infinity_context_core/features/memory_facts/ports/repositories.py +30 -0
- infinity_context_core/features/memory_facts/ports/unit_of_work.py +43 -0
- infinity_context_core/features/memory_facts/public.py +79 -0
- infinity_context_core/features/memory_facts/tests/test_application_handlers.py +332 -0
- infinity_context_core/features/memory_facts/tests/test_application_ports_public_skeleton.py +318 -0
- infinity_context_core/features/memory_facts/tests/test_domain_fact_skeleton.py +222 -0
- infinity_context_core/features/memory_scopes/__init__.py +3 -0
- infinity_context_core/features/memory_scopes/application/__init__.py +55 -0
- infinity_context_core/features/memory_scopes/application/commands.py +102 -0
- infinity_context_core/features/memory_scopes/application/errors.py +27 -0
- infinity_context_core/features/memory_scopes/application/handlers.py +226 -0
- infinity_context_core/features/memory_scopes/application/use_cases.py +68 -0
- infinity_context_core/features/memory_scopes/domain/__init__.py +59 -0
- infinity_context_core/features/memory_scopes/domain/errors.py +22 -0
- infinity_context_core/features/memory_scopes/domain/feature.py +18 -0
- infinity_context_core/features/memory_scopes/domain/policies.py +197 -0
- infinity_context_core/features/memory_scopes/domain/scope.py +178 -0
- infinity_context_core/features/memory_scopes/ports/__init__.py +25 -0
- infinity_context_core/features/memory_scopes/ports/clock.py +14 -0
- infinity_context_core/features/memory_scopes/ports/ids.py +13 -0
- infinity_context_core/features/memory_scopes/ports/repositories.py +40 -0
- infinity_context_core/features/memory_scopes/ports/unit_of_work.py +39 -0
- infinity_context_core/features/memory_scopes/public.py +111 -0
- infinity_context_core/features/memory_scopes/tests/test_application_ports_public_skeleton.py +824 -0
- infinity_context_core/features/memory_scopes/tests/test_domain_scope_skeleton.py +428 -0
- infinity_context_core/memory_scope_snapshot_preview.py +863 -0
- infinity_context_core/memory_scope_snapshots.py +206 -0
- infinity_context_core/ports/__init__.py +164 -0
- infinity_context_core/ports/adapters.py +217 -0
- infinity_context_core/ports/assets.py +270 -0
- infinity_context_core/ports/auth.py +68 -0
- infinity_context_core/ports/auto_memory.py +71 -0
- infinity_context_core/ports/benchmark_runs.py +205 -0
- infinity_context_core/ports/capabilities.py +324 -0
- infinity_context_core/ports/captures.py +53 -0
- infinity_context_core/ports/clock.py +11 -0
- infinity_context_core/ports/derived_projection_policy.py +72 -0
- infinity_context_core/ports/extraction.py +201 -0
- infinity_context_core/ports/graph_evidence.py +170 -0
- infinity_context_core/ports/ids.py +11 -0
- infinity_context_core/ports/projection_fence.py +27 -0
- infinity_context_core/ports/repositories.py +563 -0
- infinity_context_core/ports/transcription.py +63 -0
- infinity_context_core/ports/unit_of_work.py +77 -0
- infinity_context_core/ports/usage.py +36 -0
- infinity_context_core/ports/vector_projection_evidence.py +230 -0
- infinity_context_core/ports/vision.py +36 -0
- infinity_context_core/processes/__init__.py +3 -0
- infinity_context_core/processes/document_memory_pipeline/__init__.py +3 -0
- infinity_context_core/processes/document_memory_pipeline/contracts.py +44 -0
- infinity_context_core/processes/document_memory_pipeline/process.py +72 -0
- infinity_context_core/processes/document_memory_pipeline/public.py +21 -0
- infinity_context_core/reporting.py +105 -0
- infinity_context_mcp/__init__.py +5 -0
- infinity_context_mcp/__main__.py +6 -0
- infinity_context_mcp/adapters/__init__.py +1 -0
- infinity_context_mcp/adapters/http_gateway.py +940 -0
- infinity_context_mcp/agent_behavior_bench.py +1731 -0
- infinity_context_mcp/agent_behavior_bench_metrics.py +419 -0
- infinity_context_mcp/agent_behavior_bench_redaction.py +149 -0
- infinity_context_mcp/agent_behavior_bench_types.py +127 -0
- infinity_context_mcp/agent_behavior_scenarios.py +35 -0
- infinity_context_mcp/agent_behavior_scenarios_core.py +484 -0
- infinity_context_mcp/agent_behavior_scenarios_realistic.py +473 -0
- infinity_context_mcp/agent_behavior_scenarios_transcript.py +196 -0
- infinity_context_mcp/agent_behavior_transcripts.py +231 -0
- infinity_context_mcp/agent_behavior_types.py +66 -0
- infinity_context_mcp/agent_behavior_utils.py +29 -0
- infinity_context_mcp/application/__init__.py +1 -0
- infinity_context_mcp/application/context_link_review.py +109 -0
- infinity_context_mcp/application/local_runtime.py +473 -0
- infinity_context_mcp/application/normalization.py +36 -0
- infinity_context_mcp/application/obsidian.py +568 -0
- infinity_context_mcp/application/policy.py +160 -0
- infinity_context_mcp/application/ports.py +324 -0
- infinity_context_mcp/application/prepare.py +254 -0
- infinity_context_mcp/application/readiness.py +71 -0
- infinity_context_mcp/application/review_batch.py +62 -0
- infinity_context_mcp/application/service.py +701 -0
- infinity_context_mcp/application/service_base.py +29 -0
- infinity_context_mcp/application/service_constants.py +89 -0
- infinity_context_mcp/application/service_context_links.py +224 -0
- infinity_context_mcp/application/service_duplicates.py +62 -0
- infinity_context_mcp/application/service_facts.py +461 -0
- infinity_context_mcp/application/service_helpers.py +179 -0
- infinity_context_mcp/application/service_lifecycle.py +348 -0
- infinity_context_mcp/application/service_policy.py +74 -0
- infinity_context_mcp/application/service_query.py +384 -0
- infinity_context_mcp/application/service_resources.py +192 -0
- infinity_context_mcp/application/service_response.py +95 -0
- infinity_context_mcp/application/service_scope.py +132 -0
- infinity_context_mcp/application/service_suggestions.py +820 -0
- infinity_context_mcp/application/suggest_batch.py +71 -0
- infinity_context_mcp/application/usage_guide.py +74 -0
- infinity_context_mcp/bench.py +279 -0
- infinity_context_mcp/config.py +213 -0
- infinity_context_mcp/domain/__init__.py +1 -0
- infinity_context_mcp/domain/context_links.py +117 -0
- infinity_context_mcp/domain/local_runtime_models.py +56 -0
- infinity_context_mcp/domain/memory_browser.py +35 -0
- infinity_context_mcp/domain/models.py +1036 -0
- infinity_context_mcp/domain/obsidian_models.py +67 -0
- infinity_context_mcp/domain/policy.py +112 -0
- infinity_context_mcp/domain/prepare_models.py +24 -0
- infinity_context_mcp/plugin_hook.py +873 -0
- infinity_context_mcp/plugin_hook_settings.py +253 -0
- infinity_context_mcp/server.py +133 -0
- infinity_context_mcp/server_context_tools.py +404 -0
- infinity_context_mcp/server_fact_tools.py +928 -0
- infinity_context_mcp/server_hardening.py +54 -0
- infinity_context_mcp/server_local_runtime_tools.py +164 -0
- infinity_context_mcp/server_obsidian_tools.py +380 -0
- infinity_context_mcp/server_query_tools.py +626 -0
- infinity_context_mcp/server_request_mapping.py +73 -0
- infinity_context_mcp/server_resources.py +187 -0
- infinity_context_mcp/server_response.py +26 -0
- infinity_context_obsidian/__init__.py +5 -0
- infinity_context_obsidian/cli.py +594 -0
- infinity_context_obsidian/conflicts.py +110 -0
- infinity_context_obsidian/doctor.py +319 -0
- infinity_context_obsidian/domain.py +92 -0
- infinity_context_obsidian/frontmatter.py +68 -0
- infinity_context_obsidian/gateway.py +66 -0
- infinity_context_obsidian/layout.py +179 -0
- infinity_context_obsidian/note_format.py +159 -0
- infinity_context_obsidian/plugin_bundle/__init__.py +1 -0
- infinity_context_obsidian/plugin_bundle/main.js +5 -0
- infinity_context_obsidian/plugin_bundle/manifest.json +10 -0
- infinity_context_obsidian/plugin_bundle/styles.css +123 -0
- infinity_context_obsidian/plugin_install.py +139 -0
- infinity_context_obsidian/ports.py +72 -0
- infinity_context_obsidian/setup.py +202 -0
- infinity_context_obsidian/state.py +85 -0
- infinity_context_obsidian/sync.py +741 -0
- infinity_context_obsidian/vault.py +61 -0
- infinity_context_sdk/__init__.py +1049 -0
- infinity_context_sdk/_payloads.py +401 -0
- infinity_context_sdk/_redaction.py +31 -0
- infinity_context_sdk/anchors.py +249 -0
- infinity_context_sdk/assets.py +554 -0
- infinity_context_sdk/capabilities.py +199 -0
- infinity_context_sdk/context.py +540 -0
- infinity_context_sdk/context_bundle_diagnostics.py +905 -0
- infinity_context_sdk/context_payload_utils.py +219 -0
- infinity_context_sdk/export.py +123 -0
- infinity_context_sdk/scopes.py +74 -0
- infinity_context_sdk/thread_memory.py +55 -0
- infinity_context_server/__init__.py +5 -0
- infinity_context_server/__main__.py +19 -0
- infinity_context_server/admin.py +832 -0
- infinity_context_server/admin_invariants.py +388 -0
- infinity_context_server/admin_outbox.py +130 -0
- infinity_context_server/admin_projection_repair.py +374 -0
- infinity_context_server/api/__init__.py +1 -0
- infinity_context_server/api/auth.py +432 -0
- infinity_context_server/api/dependencies.py +9 -0
- infinity_context_server/api/errors.py +94 -0
- infinity_context_server/api/legacy_client.py +356 -0
- infinity_context_server/api/policy.py +35 -0
- infinity_context_server/api/public_payload.py +27 -0
- infinity_context_server/api/v1/__init__.py +54 -0
- infinity_context_server/api/v1/anchors.py +469 -0
- infinity_context_server/api/v1/assets.py +409 -0
- infinity_context_server/api/v1/capabilities.py +144 -0
- infinity_context_server/api/v1/captures.py +329 -0
- infinity_context_server/api/v1/context.py +362 -0
- infinity_context_server/api/v1/context_links.py +611 -0
- infinity_context_server/api/v1/diagnostics.py +333 -0
- infinity_context_server/api/v1/digest.py +107 -0
- infinity_context_server/api/v1/documents.py +164 -0
- infinity_context_server/api/v1/episodes.py +146 -0
- infinity_context_server/api/v1/export.py +184 -0
- infinity_context_server/api/v1/facts.py +265 -0
- infinity_context_server/api/v1/health.py +24 -0
- infinity_context_server/api/v1/insights.py +100 -0
- infinity_context_server/api/v1/internal_memory_comparison_runs.py +406 -0
- infinity_context_server/api/v1/memory_browser.py +183 -0
- infinity_context_server/api/v1/operations.py +81 -0
- infinity_context_server/api/v1/scope_resolution.py +389 -0
- infinity_context_server/api/v1/source_refs.py +111 -0
- infinity_context_server/api/v1/spaces_memory_scopes.py +141 -0
- infinity_context_server/api/v1/suggestions.py +237 -0
- infinity_context_server/api/v1/thread_memory.py +74 -0
- infinity_context_server/api/v1/usage.py +75 -0
- infinity_context_server/api/v1/users.py +164 -0
- infinity_context_server/auth_scope.py +492 -0
- infinity_context_server/auth_tokens.py +283 -0
- infinity_context_server/backpressure.py +47 -0
- infinity_context_server/benchmark_projection_absence.py +155 -0
- infinity_context_server/benchmark_projection_delete_outbox.py +162 -0
- infinity_context_server/composition.py +897 -0
- infinity_context_server/config.py +223 -0
- infinity_context_server/db.py +39 -0
- infinity_context_server/deployment_readiness.py +205 -0
- infinity_context_server/derived_identity_evidence.py +987 -0
- infinity_context_server/derived_identity_target.py +39 -0
- infinity_context_server/derived_projection_policy.py +85 -0
- infinity_context_server/diagnostics.py +536 -0
- infinity_context_server/doctor.py +71 -0
- infinity_context_server/eval.py +3066 -0
- infinity_context_server/eval_auto_memory.py +1735 -0
- infinity_context_server/eval_auto_memory_metrics.py +224 -0
- infinity_context_server/eval_auto_memory_types.py +75 -0
- infinity_context_server/eval_case_catalog.py +1590 -0
- infinity_context_server/eval_case_runner.py +1100 -0
- infinity_context_server/eval_common.py +336 -0
- infinity_context_server/eval_constants.py +310 -0
- infinity_context_server/eval_fixtures.py +150 -0
- infinity_context_server/eval_graph.py +92 -0
- infinity_context_server/eval_hybrid.py +99 -0
- infinity_context_server/eval_multimodal_offline.py +697 -0
- infinity_context_server/eval_prompt_contract.py +402 -0
- infinity_context_server/eval_scorecard.py +2627 -0
- infinity_context_server/eval_semantic_linking.py +1526 -0
- infinity_context_server/eval_semantic_retrieval_api_canary.py +405 -0
- infinity_context_server/eval_semantic_retrieval_canary.py +359 -0
- infinity_context_server/eval_types.py +88 -0
- infinity_context_server/evidence_bundle.py +523 -0
- infinity_context_server/extraction_capabilities.py +1234 -0
- infinity_context_server/features/__init__.py +3 -0
- infinity_context_server/features/context_building/__init__.py +5 -0
- infinity_context_server/features/context_building/benchmark_budget.py +29 -0
- infinity_context_server/features/context_building/composition.py +49 -0
- infinity_context_server/features/context_building/context_requests.py +151 -0
- infinity_context_server/features/context_building/contracts.py +96 -0
- infinity_context_server/features/context_building/digest_api_responses.py +225 -0
- infinity_context_server/features/context_building/digest_requests.py +65 -0
- infinity_context_server/features/context_building/insights_api_responses.py +132 -0
- infinity_context_server/features/context_building/legacy_api_responses.py +875 -0
- infinity_context_server/features/context_building/mappers.py +205 -0
- infinity_context_server/features/context_building/public.py +66 -0
- infinity_context_server/features/context_building/routes.py +44 -0
- infinity_context_server/features/document_ingestion/__init__.py +5 -0
- infinity_context_server/features/document_ingestion/asset_requests.py +45 -0
- infinity_context_server/features/document_ingestion/asset_responses.py +365 -0
- infinity_context_server/features/document_ingestion/composition.py +49 -0
- infinity_context_server/features/document_ingestion/contracts.py +104 -0
- infinity_context_server/features/document_ingestion/mappers.py +386 -0
- infinity_context_server/features/document_ingestion/public.py +58 -0
- infinity_context_server/features/document_ingestion/routes.py +44 -0
- infinity_context_server/features/memory_facts/__init__.py +7 -0
- infinity_context_server/features/memory_facts/compatibility.py +204 -0
- infinity_context_server/features/memory_facts/composition.py +72 -0
- infinity_context_server/features/memory_facts/contracts.py +187 -0
- infinity_context_server/features/memory_facts/mappers.py +620 -0
- infinity_context_server/features/memory_facts/public.py +168 -0
- infinity_context_server/features/memory_facts/routes.py +118 -0
- infinity_context_server/features/memory_facts/suggestion_requests.py +283 -0
- infinity_context_server/features/memory_facts/suggestion_responses.py +382 -0
- infinity_context_server/features/memory_scopes/__init__.py +5 -0
- infinity_context_server/features/memory_scopes/compatibility.py +231 -0
- infinity_context_server/features/memory_scopes/composition.py +49 -0
- infinity_context_server/features/memory_scopes/contracts.py +103 -0
- infinity_context_server/features/memory_scopes/graph_export_responses.py +93 -0
- infinity_context_server/features/memory_scopes/mappers.py +290 -0
- infinity_context_server/features/memory_scopes/public.py +136 -0
- infinity_context_server/features/memory_scopes/routes.py +185 -0
- infinity_context_server/features/memory_scopes/snapshot_compatibility.py +146 -0
- infinity_context_server/features/memory_scopes/thread_memory_requests.py +127 -0
- infinity_context_server/llm_provider_readiness.py +267 -0
- infinity_context_server/longmemeval_session_identity.py +167 -0
- infinity_context_server/main.py +68 -0
- infinity_context_server/mem0_benchmark_auth_challenge.py +160 -0
- infinity_context_server/memora_comparison.py +658 -0
- infinity_context_server/memory_comparison_answer_context.py +2442 -0
- infinity_context_server/memory_comparison_answer_context_backfill.py +1027 -0
- infinity_context_server/memory_comparison_answer_context_diagnostics.py +694 -0
- infinity_context_server/memory_comparison_answer_context_inspection.py +103 -0
- infinity_context_server/memory_comparison_answer_context_retrieval_slice.py +175 -0
- infinity_context_server/memory_comparison_answer_context_risks.py +223 -0
- infinity_context_server/memory_comparison_backend_target.py +41 -0
- infinity_context_server/memory_comparison_benchmark.py +651 -0
- infinity_context_server/memory_comparison_benchmark_compact.py +387 -0
- infinity_context_server/memory_comparison_benchmark_compact_fast_gate.py +387 -0
- infinity_context_server/memory_comparison_benchmark_compact_samples.py +931 -0
- infinity_context_server/memory_comparison_benchmark_config.py +105 -0
- infinity_context_server/memory_comparison_benchmark_control.py +215 -0
- infinity_context_server/memory_comparison_benchmark_execution.py +238 -0
- infinity_context_server/memory_comparison_benchmark_identity.py +65 -0
- infinity_context_server/memory_comparison_benchmark_metrics.py +686 -0
- infinity_context_server/memory_comparison_benchmark_replay.py +400 -0
- infinity_context_server/memory_comparison_benchmark_shared.py +284 -0
- infinity_context_server/memory_comparison_bounded_provider.py +295 -0
- infinity_context_server/memory_comparison_bundle_partial_support.py +107 -0
- infinity_context_server/memory_comparison_bundle_planner.py +3521 -0
- infinity_context_server/memory_comparison_bundle_source_windows.py +247 -0
- infinity_context_server/memory_comparison_candidate_features.py +2014 -0
- infinity_context_server/memory_comparison_candidate_fusion.py +893 -0
- infinity_context_server/memory_comparison_candidate_risks.py +124 -0
- infinity_context_server/memory_comparison_canonical_source_hash.py +109 -0
- infinity_context_server/memory_comparison_case_identity.py +100 -0
- infinity_context_server/memory_comparison_case_loader.py +123 -0
- infinity_context_server/memory_comparison_chat_completions.py +402 -0
- infinity_context_server/memory_comparison_clean_state.py +607 -0
- infinity_context_server/memory_comparison_clean_state_http.py +120 -0
- infinity_context_server/memory_comparison_clean_state_runner.py +103 -0
- infinity_context_server/memory_comparison_cli.py +295 -0
- infinity_context_server/memory_comparison_codex_canary.py +315 -0
- infinity_context_server/memory_comparison_codex_llm.py +345 -0
- infinity_context_server/memory_comparison_compact_gap_report.py +289 -0
- infinity_context_server/memory_comparison_comparative_preference.py +71 -0
- infinity_context_server/memory_comparison_conversation_ingestion.py +198 -0
- infinity_context_server/memory_comparison_evidence.py +877 -0
- infinity_context_server/memory_comparison_evidence_commitment.py +20 -0
- infinity_context_server/memory_comparison_failure_diagnostics.py +809 -0
- infinity_context_server/memory_comparison_full_canonical_evidence.py +746 -0
- infinity_context_server/memory_comparison_full_canonical_source_evidence_trust.py +450 -0
- infinity_context_server/memory_comparison_full_delete_evidence.py +670 -0
- infinity_context_server/memory_comparison_full_delete_evidence_trust.py +441 -0
- infinity_context_server/memory_comparison_full_delete_evidence_witnesses.py +308 -0
- infinity_context_server/memory_comparison_full_execution_validation.py +697 -0
- infinity_context_server/memory_comparison_full_execution_validation_slots.py +655 -0
- infinity_context_server/memory_comparison_full_methodology.py +556 -0
- infinity_context_server/memory_comparison_full_policy_component_validation.py +899 -0
- infinity_context_server/memory_comparison_full_profiles.py +398 -0
- infinity_context_server/memory_comparison_full_run_component_sets.py +304 -0
- infinity_context_server/memory_comparison_full_run_components.py +787 -0
- infinity_context_server/memory_comparison_full_run_evidence.py +759 -0
- infinity_context_server/memory_comparison_full_scope.py +244 -0
- infinity_context_server/memory_comparison_full_source_evidence.py +866 -0
- infinity_context_server/memory_comparison_full_verdict.py +252 -0
- infinity_context_server/memory_comparison_gold_blind.py +654 -0
- infinity_context_server/memory_comparison_gold_blind_answer_contract.py +492 -0
- infinity_context_server/memory_comparison_gold_blind_contract.py +760 -0
- infinity_context_server/memory_comparison_gold_blind_dispatch_models.py +34 -0
- infinity_context_server/memory_comparison_gold_blind_execution_validation.py +170 -0
- infinity_context_server/memory_comparison_gold_blind_judge_binding.py +167 -0
- infinity_context_server/memory_comparison_gold_blind_judge_capability.py +387 -0
- infinity_context_server/memory_comparison_gold_blind_retrieval_contract.py +413 -0
- infinity_context_server/memory_comparison_gold_blind_run_proof.py +880 -0
- infinity_context_server/memory_comparison_gold_blind_run_validation.py +109 -0
- infinity_context_server/memory_comparison_gold_blind_validation.py +319 -0
- infinity_context_server/memory_comparison_http.py +921 -0
- infinity_context_server/memory_comparison_http_ingest_observation.py +710 -0
- infinity_context_server/memory_comparison_http_ingest_request.py +204 -0
- infinity_context_server/memory_comparison_intent.py +2925 -0
- infinity_context_server/memory_comparison_latency.py +455 -0
- infinity_context_server/memory_comparison_llm.py +473 -0
- infinity_context_server/memory_comparison_location_roles.py +169 -0
- infinity_context_server/memory_comparison_locomo_cases.py +328 -0
- infinity_context_server/memory_comparison_locomo_expected_turn.py +297 -0
- infinity_context_server/memory_comparison_locomo_transport.py +873 -0
- infinity_context_server/memory_comparison_longmemeval_cases.py +339 -0
- infinity_context_server/memory_comparison_managed_attestation.py +771 -0
- infinity_context_server/memory_comparison_managed_benchmark_registry_abort_http.py +120 -0
- infinity_context_server/memory_comparison_managed_benchmark_registry_contracts.py +715 -0
- infinity_context_server/memory_comparison_managed_benchmark_registry_http.py +855 -0
- infinity_context_server/memory_comparison_managed_benchmark_registry_recovery_http.py +164 -0
- infinity_context_server/memory_comparison_managed_benchmark_registry_wire.py +890 -0
- infinity_context_server/memory_comparison_managed_composite_assembler.py +524 -0
- infinity_context_server/memory_comparison_managed_corpus_projection.py +653 -0
- infinity_context_server/memory_comparison_managed_execution_receipts.py +991 -0
- infinity_context_server/memory_comparison_managed_http_derived_evidence.py +724 -0
- infinity_context_server/memory_comparison_managed_http_exact_cleanup.py +476 -0
- infinity_context_server/memory_comparison_managed_http_execution.py +673 -0
- infinity_context_server/memory_comparison_managed_http_lifecycle.py +905 -0
- infinity_context_server/memory_comparison_managed_http_lifecycle_credentials.py +83 -0
- infinity_context_server/memory_comparison_managed_http_lifecycle_evidence.py +366 -0
- infinity_context_server/memory_comparison_managed_http_lifecycle_registration.py +106 -0
- infinity_context_server/memory_comparison_managed_http_policy_lifecycle.py +894 -0
- infinity_context_server/memory_comparison_managed_http_policy_material_projection.py +315 -0
- infinity_context_server/memory_comparison_managed_http_policy_receipts.py +219 -0
- infinity_context_server/memory_comparison_managed_http_policy_registry_evidence.py +177 -0
- infinity_context_server/memory_comparison_managed_http_policy_requirements.py +824 -0
- infinity_context_server/memory_comparison_managed_http_policy_support.py +108 -0
- infinity_context_server/memory_comparison_managed_http_policy_validation.py +762 -0
- infinity_context_server/memory_comparison_managed_ingest_manifest.py +449 -0
- infinity_context_server/memory_comparison_managed_live_admission.py +886 -0
- infinity_context_server/memory_comparison_managed_live_cli.py +536 -0
- infinity_context_server/memory_comparison_managed_live_composition.py +636 -0
- infinity_context_server/memory_comparison_managed_llm_execution.py +850 -0
- infinity_context_server/memory_comparison_managed_llm_execution_dispatch.py +269 -0
- infinity_context_server/memory_comparison_managed_llm_execution_ports.py +225 -0
- infinity_context_server/memory_comparison_managed_mem0_auth.py +65 -0
- infinity_context_server/memory_comparison_managed_mem0_runtime_authority.py +241 -0
- infinity_context_server/memory_comparison_managed_mem0_runtime_http.py +541 -0
- infinity_context_server/memory_comparison_managed_plan_builder.py +685 -0
- infinity_context_server/memory_comparison_managed_preflight.py +907 -0
- infinity_context_server/memory_comparison_managed_preflight_cli.py +431 -0
- infinity_context_server/memory_comparison_managed_production_cli.py +325 -0
- infinity_context_server/memory_comparison_managed_production_composition.py +140 -0
- infinity_context_server/memory_comparison_managed_production_ports.py +194 -0
- infinity_context_server/memory_comparison_managed_production_runner.py +450 -0
- infinity_context_server/memory_comparison_managed_projection_manifest.py +438 -0
- infinity_context_server/memory_comparison_managed_provider_calls.py +450 -0
- infinity_context_server/memory_comparison_managed_quality_projection.py +347 -0
- infinity_context_server/memory_comparison_managed_registry_policy_lifecycle.py +677 -0
- infinity_context_server/memory_comparison_managed_run.py +892 -0
- infinity_context_server/memory_comparison_managed_run_contract.py +454 -0
- infinity_context_server/memory_comparison_managed_run_ports.py +79 -0
- infinity_context_server/memory_comparison_managed_runtime_credentials.py +916 -0
- infinity_context_server/memory_comparison_managed_runtime_credentials_capability.py +361 -0
- infinity_context_server/memory_comparison_managed_runtime_credentials_context.py +132 -0
- infinity_context_server/memory_comparison_managed_runtime_credentials_integrity.py +213 -0
- infinity_context_server/memory_comparison_managed_runtime_credentials_models.py +81 -0
- infinity_context_server/memory_comparison_managed_runtime_credentials_targets.py +28 -0
- infinity_context_server/memory_comparison_mem0_contract.py +491 -0
- infinity_context_server/memory_comparison_mem0_http_observation.py +296 -0
- infinity_context_server/memory_comparison_mem0_locomo_prompts.py +328 -0
- infinity_context_server/memory_comparison_mem0_longmemeval_prompts.py +365 -0
- infinity_context_server/memory_comparison_mem0_official_chat.py +504 -0
- infinity_context_server/memory_comparison_mem0_official_prompt_renderer.py +307 -0
- infinity_context_server/memory_comparison_mem0_oss_contract.py +456 -0
- infinity_context_server/memory_comparison_mem0_oss_manifest.py +563 -0
- infinity_context_server/memory_comparison_mem0_platform_contract.py +467 -0
- infinity_context_server/memory_comparison_mem0_runtime_attestation.py +861 -0
- infinity_context_server/memory_comparison_models.py +398 -0
- infinity_context_server/memory_comparison_openai_official_transport.py +525 -0
- infinity_context_server/memory_comparison_preflight.py +1147 -0
- infinity_context_server/memory_comparison_probe_transport.py +154 -0
- infinity_context_server/memory_comparison_prompt_policy.py +603 -0
- infinity_context_server/memory_comparison_provider_provenance.py +392 -0
- infinity_context_server/memory_comparison_quality_accessors.py +339 -0
- infinity_context_server/memory_comparison_quality_actionable_gaps.py +2038 -0
- infinity_context_server/memory_comparison_quality_answer_context_gaps.py +450 -0
- infinity_context_server/memory_comparison_quality_answer_context_risks.py +75 -0
- infinity_context_server/memory_comparison_quality_bundle_gaps.py +860 -0
- infinity_context_server/memory_comparison_quality_bundle_table.py +805 -0
- infinity_context_server/memory_comparison_quality_diagnostics.py +2798 -0
- infinity_context_server/memory_comparison_quality_fusion.py +241 -0
- infinity_context_server/memory_comparison_quality_query_plan_gaps.py +533 -0
- infinity_context_server/memory_comparison_quality_query_role_gaps.py +845 -0
- infinity_context_server/memory_comparison_quality_query_roles.py +815 -0
- infinity_context_server/memory_comparison_quality_rerank_gaps.py +492 -0
- infinity_context_server/memory_comparison_quality_selected_weakness.py +544 -0
- infinity_context_server/memory_comparison_quality_support.py +921 -0
- infinity_context_server/memory_comparison_query_integrity.py +301 -0
- infinity_context_server/memory_comparison_query_plan.py +524 -0
- infinity_context_server/memory_comparison_query_terms.py +2055 -0
- infinity_context_server/memory_comparison_relation_support.py +2100 -0
- infinity_context_server/memory_comparison_rerank.py +3209 -0
- infinity_context_server/memory_comparison_rerank_intents.py +405 -0
- infinity_context_server/memory_comparison_rerank_policies.py +1726 -0
- infinity_context_server/memory_comparison_rerank_policy.py +675 -0
- infinity_context_server/memory_comparison_rerank_shapes.py +152 -0
- infinity_context_server/memory_comparison_rerank_terms.py +973 -0
- infinity_context_server/memory_comparison_rerank_text.py +722 -0
- infinity_context_server/memory_comparison_retrieval_policy.py +108 -0
- infinity_context_server/memory_comparison_retrieval_width.py +383 -0
- infinity_context_server/memory_comparison_service_probe.py +278 -0
- infinity_context_server/memory_comparison_service_probes.py +547 -0
- infinity_context_server/memory_comparison_session_identity_contract.py +328 -0
- infinity_context_server/memory_comparison_source_identity.py +1655 -0
- infinity_context_server/memory_comparison_subscription_chat.py +717 -0
- infinity_context_server/memory_comparison_subscription_live_probe.py +323 -0
- infinity_context_server/memory_comparison_subscription_probe.py +368 -0
- infinity_context_server/memory_comparison_target_identity.py +42 -0
- infinity_context_server/memory_comparison_temporal_grounding.py +771 -0
- infinity_context_server/memory_comparison_token_budget_contract.py +665 -0
- infinity_context_server/memory_comparison_token_latency.py +136 -0
- infinity_context_server/memory_scope_transfer.py +933 -0
- infinity_context_server/memory_scope_transfer_assets.py +181 -0
- infinity_context_server/memory_scope_transfer_conflicts.py +565 -0
- infinity_context_server/memory_scope_transfer_context.py +295 -0
- infinity_context_server/memory_scope_transfer_extractions.py +390 -0
- infinity_context_server/memory_scope_transfer_facts.py +65 -0
- infinity_context_server/memory_scope_transfer_records.py +630 -0
- infinity_context_server/memory_scope_transfer_relations.py +84 -0
- infinity_context_server/memory_scope_transfer_remap.py +710 -0
- infinity_context_server/memory_scope_transfer_scope.py +267 -0
- infinity_context_server/memory_scope_transfer_support.py +108 -0
- infinity_context_server/memory_scope_transfer_temporal.py +24 -0
- infinity_context_server/metrics.py +264 -0
- infinity_context_server/official_public_benchmark.py +979 -0
- infinity_context_server/pagination.py +60 -0
- infinity_context_server/processes/__init__.py +38 -0
- infinity_context_server/processes/extractions.py +40 -0
- infinity_context_server/processes/outbox.py +47 -0
- infinity_context_server/processes/projections.py +372 -0
- infinity_context_server/provider_budget.py +59 -0
- infinity_context_server/provider_circuit.py +333 -0
- infinity_context_server/public_benchmark.py +2532 -0
- infinity_context_server/public_benchmark_artifacts.py +91 -0
- infinity_context_server/public_benchmark_case_diagnostics.py +231 -0
- infinity_context_server/public_benchmark_checkpoint.py +751 -0
- infinity_context_server/public_benchmark_environment.py +102 -0
- infinity_context_server/public_benchmark_execution.py +445 -0
- infinity_context_server/public_benchmark_http.py +39 -0
- infinity_context_server/public_benchmark_manifest.py +129 -0
- infinity_context_server/public_benchmark_metrics.py +645 -0
- infinity_context_server/public_benchmark_metrics_policy.py +117 -0
- infinity_context_server/public_benchmark_models.py +112 -0
- infinity_context_server/public_benchmark_progress.py +224 -0
- infinity_context_server/public_benchmark_results.py +76 -0
- infinity_context_server/public_benchmark_run_diagnostics.py +163 -0
- infinity_context_server/public_benchmark_selection.py +379 -0
- infinity_context_server/public_benchmark_transport.py +62 -0
- infinity_context_server/public_benchmark_unsupported.py +228 -0
- infinity_context_server/ranked_evidence_answer_support.py +665 -0
- infinity_context_server/ranked_evidence_document_seed_contract.py +30 -0
- infinity_context_server/ranked_evidence_evaluator_helpers.py +135 -0
- infinity_context_server/ranked_evidence_retrieval_request.py +36 -0
- infinity_context_server/ranked_evidence_seed_case.py +109 -0
- infinity_context_server/ranked_evidence_semantic_gate.py +980 -0
- infinity_context_server/ranked_evidence_semantic_gate_cli.py +137 -0
- infinity_context_server/ranked_evidence_semantic_gate_decision.py +163 -0
- infinity_context_server/ranked_evidence_semantic_metrics.py +375 -0
- infinity_context_server/ranked_evidence_session_identity.py +100 -0
- infinity_context_server/storage_maintenance.py +187 -0
- infinity_context_server/top_evidence_dataset_profile.py +442 -0
- infinity_context_server/top_evidence_policy.py +274 -0
- infinity_context_server/top_evidence_preflight.py +693 -0
- infinity_context_server/web/assets/memory-browser-capture.js +252 -0
- infinity_context_server/web/assets/memory-browser-operations.js +143 -0
- infinity_context_server/web/assets/memory-browser-review.js +687 -0
- infinity_context_server/web/assets/memory-browser.css +854 -0
- infinity_context_server/web/assets/memory-browser.js +3299 -0
- infinity_context_server/web/index.html +261 -0
- infinity_context_server/web_ui.py +29 -0
- infinity_context_server/worker.py +380 -0
|
@@ -0,0 +1,2532 @@
|
|
|
1
|
+
"""Normalized public memory benchmark runner.
|
|
2
|
+
|
|
3
|
+
The runner intentionally talks to Infinity Context through the HTTP API surface, even
|
|
4
|
+
when it uses an in-process FastAPI TestClient. This keeps the benchmark close to
|
|
5
|
+
how external users exercise the platform while avoiding a hard dependency on a
|
|
6
|
+
specific provider adapter.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import concurrent.futures
|
|
12
|
+
import hashlib
|
|
13
|
+
import json
|
|
14
|
+
import tempfile
|
|
15
|
+
import time
|
|
16
|
+
from collections import defaultdict
|
|
17
|
+
from collections.abc import Iterable, Mapping, Sequence
|
|
18
|
+
from contextlib import nullcontext
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from functools import lru_cache
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
from infinity_context_core.application.context_lexical import text_variant_profile
|
|
24
|
+
from infinity_context_core.application.context_relevance import (
|
|
25
|
+
QueryRelevance,
|
|
26
|
+
score_query_relevance_against_profile,
|
|
27
|
+
)
|
|
28
|
+
from infinity_context_core.reporting import with_report_provenance
|
|
29
|
+
|
|
30
|
+
from infinity_context_server.public_benchmark_artifacts import (
|
|
31
|
+
validate_artifact_paths_do_not_overwrite_dataset as _validate_artifacts_not_dataset,
|
|
32
|
+
)
|
|
33
|
+
from infinity_context_server.public_benchmark_artifacts import (
|
|
34
|
+
validate_distinct_artifact_paths as _validate_distinct_artifact_paths,
|
|
35
|
+
)
|
|
36
|
+
from infinity_context_server.public_benchmark_artifacts import (
|
|
37
|
+
write_json_atomic as _write_json_atomic,
|
|
38
|
+
)
|
|
39
|
+
from infinity_context_server.public_benchmark_case_diagnostics import (
|
|
40
|
+
case_answer_preview as _case_answer_preview,
|
|
41
|
+
)
|
|
42
|
+
from infinity_context_server.public_benchmark_case_diagnostics import (
|
|
43
|
+
case_evidence_ref_previews as _case_evidence_ref_previews,
|
|
44
|
+
)
|
|
45
|
+
from infinity_context_server.public_benchmark_case_diagnostics import (
|
|
46
|
+
case_evidence_refs as _case_evidence_refs,
|
|
47
|
+
)
|
|
48
|
+
from infinity_context_server.public_benchmark_case_diagnostics import (
|
|
49
|
+
case_expected_terms_preview as _case_expected_terms_preview,
|
|
50
|
+
)
|
|
51
|
+
from infinity_context_server.public_benchmark_case_diagnostics import (
|
|
52
|
+
preview_value as _preview_value,
|
|
53
|
+
)
|
|
54
|
+
from infinity_context_server.public_benchmark_case_diagnostics import (
|
|
55
|
+
response_evidence_text as _evidence_text,
|
|
56
|
+
)
|
|
57
|
+
from infinity_context_server.public_benchmark_checkpoint import (
|
|
58
|
+
BenchmarkSeedStats as _BenchmarkSeedStats,
|
|
59
|
+
)
|
|
60
|
+
from infinity_context_server.public_benchmark_checkpoint import (
|
|
61
|
+
CaseRunResult,
|
|
62
|
+
case_result_key,
|
|
63
|
+
load_checkpoint_resume_state_with_diagnostics,
|
|
64
|
+
safe_identifier,
|
|
65
|
+
seed_corpus_identity,
|
|
66
|
+
seed_corpus_metadata,
|
|
67
|
+
selected_case_fingerprint,
|
|
68
|
+
)
|
|
69
|
+
from infinity_context_server.public_benchmark_checkpoint import (
|
|
70
|
+
SeedCorpusMetadata as _SeedCorpusMetadata,
|
|
71
|
+
)
|
|
72
|
+
from infinity_context_server.public_benchmark_environment import (
|
|
73
|
+
check_local_benchmark_environment,
|
|
74
|
+
local_environment_failure_result,
|
|
75
|
+
)
|
|
76
|
+
from infinity_context_server.public_benchmark_execution import (
|
|
77
|
+
CaseExecutionEntry as _CaseExecutionEntry,
|
|
78
|
+
)
|
|
79
|
+
from infinity_context_server.public_benchmark_execution import (
|
|
80
|
+
CaseExecutionGroupOutcome as _CaseExecutionGroupOutcome,
|
|
81
|
+
)
|
|
82
|
+
from infinity_context_server.public_benchmark_execution import (
|
|
83
|
+
case_exception_outcome as _case_exception_outcome,
|
|
84
|
+
)
|
|
85
|
+
from infinity_context_server.public_benchmark_execution import (
|
|
86
|
+
case_execution_groups as _case_execution_groups,
|
|
87
|
+
)
|
|
88
|
+
from infinity_context_server.public_benchmark_execution import (
|
|
89
|
+
case_execution_parallelism as _case_execution_parallelism,
|
|
90
|
+
)
|
|
91
|
+
from infinity_context_server.public_benchmark_execution import (
|
|
92
|
+
emit_case_completed as _emit_case_completed,
|
|
93
|
+
)
|
|
94
|
+
from infinity_context_server.public_benchmark_execution import (
|
|
95
|
+
emit_case_failed as _emit_case_failed,
|
|
96
|
+
)
|
|
97
|
+
from infinity_context_server.public_benchmark_execution import (
|
|
98
|
+
emit_case_started as _emit_case_started,
|
|
99
|
+
)
|
|
100
|
+
from infinity_context_server.public_benchmark_execution import (
|
|
101
|
+
execute_case_group_with_isolated_state as _execute_case_group_with_isolated_state,
|
|
102
|
+
)
|
|
103
|
+
from infinity_context_server.public_benchmark_execution import (
|
|
104
|
+
execute_case_sequentially as _execute_case_sequentially,
|
|
105
|
+
)
|
|
106
|
+
from infinity_context_server.public_benchmark_execution import (
|
|
107
|
+
merge_case_execution_group_outcome as _merge_case_execution_group_outcome,
|
|
108
|
+
)
|
|
109
|
+
from infinity_context_server.public_benchmark_execution import (
|
|
110
|
+
normalize_parallelism as _normalize_parallelism,
|
|
111
|
+
)
|
|
112
|
+
from infinity_context_server.public_benchmark_execution import (
|
|
113
|
+
ordered_run_results as _ordered_run_results,
|
|
114
|
+
)
|
|
115
|
+
from infinity_context_server.public_benchmark_http import (
|
|
116
|
+
auth_headers as _auth_headers,
|
|
117
|
+
)
|
|
118
|
+
from infinity_context_server.public_benchmark_http import (
|
|
119
|
+
post_required as _post_required,
|
|
120
|
+
)
|
|
121
|
+
from infinity_context_server.public_benchmark_http import (
|
|
122
|
+
response_data as _response_data,
|
|
123
|
+
)
|
|
124
|
+
from infinity_context_server.public_benchmark_manifest import (
|
|
125
|
+
build_execution_manifest as _build_execution_manifest,
|
|
126
|
+
)
|
|
127
|
+
from infinity_context_server.public_benchmark_metrics import (
|
|
128
|
+
accuracy as _accuracy,
|
|
129
|
+
)
|
|
130
|
+
from infinity_context_server.public_benchmark_metrics import (
|
|
131
|
+
benchmark_summaries as _benchmark_summaries,
|
|
132
|
+
)
|
|
133
|
+
from infinity_context_server.public_benchmark_metrics import (
|
|
134
|
+
benchmark_summary_case_count as _benchmark_summary_case_count,
|
|
135
|
+
)
|
|
136
|
+
from infinity_context_server.public_benchmark_metrics import (
|
|
137
|
+
case_failures as _case_failures,
|
|
138
|
+
)
|
|
139
|
+
from infinity_context_server.public_benchmark_metrics import (
|
|
140
|
+
case_payload as _case_payload,
|
|
141
|
+
)
|
|
142
|
+
from infinity_context_server.public_benchmark_metrics import (
|
|
143
|
+
dataset_source_metadata as _dataset_source_metadata,
|
|
144
|
+
)
|
|
145
|
+
from infinity_context_server.public_benchmark_metrics import (
|
|
146
|
+
flat_capability_accuracy as _flat_capability_accuracy,
|
|
147
|
+
)
|
|
148
|
+
from infinity_context_server.public_benchmark_metrics import (
|
|
149
|
+
flat_capability_case_count as _flat_capability_case_count,
|
|
150
|
+
)
|
|
151
|
+
from infinity_context_server.public_benchmark_metrics import (
|
|
152
|
+
flat_capability_failure_count as _flat_capability_failure_count,
|
|
153
|
+
)
|
|
154
|
+
from infinity_context_server.public_benchmark_metrics import (
|
|
155
|
+
progress_case_outcome_fields as _progress_case_outcome_fields,
|
|
156
|
+
)
|
|
157
|
+
from infinity_context_server.public_benchmark_metrics import (
|
|
158
|
+
progress_timing_fields as _progress_timing_fields,
|
|
159
|
+
)
|
|
160
|
+
from infinity_context_server.public_benchmark_metrics import (
|
|
161
|
+
run_metric_summary as _run_metric_summary,
|
|
162
|
+
)
|
|
163
|
+
from infinity_context_server.public_benchmark_models import (
|
|
164
|
+
BenchmarkDocumentInput,
|
|
165
|
+
BenchmarkHttpClientPort,
|
|
166
|
+
BenchmarkHttpResponsePort,
|
|
167
|
+
BenchmarkMemoryInput,
|
|
168
|
+
BenchmarkValidationError,
|
|
169
|
+
PublicBenchmarkCase,
|
|
170
|
+
)
|
|
171
|
+
from infinity_context_server.public_benchmark_models import (
|
|
172
|
+
TestClientBenchmarkAdapter as _TestClientBenchmarkAdapter,
|
|
173
|
+
)
|
|
174
|
+
from infinity_context_server.public_benchmark_progress import (
|
|
175
|
+
DEFAULT_CHECKPOINT_MIN_INTERVAL_SECONDS as _DEFAULT_CHECKPOINT_MIN_INTERVAL_SECONDS,
|
|
176
|
+
)
|
|
177
|
+
from infinity_context_server.public_benchmark_progress import (
|
|
178
|
+
_BenchmarkProgress,
|
|
179
|
+
)
|
|
180
|
+
from infinity_context_server.public_benchmark_progress import (
|
|
181
|
+
emit_setup_failure_progress as _emit_setup_failure_progress,
|
|
182
|
+
)
|
|
183
|
+
from infinity_context_server.public_benchmark_results import (
|
|
184
|
+
duplicate_case_failure_result as _duplicate_case_failure_result,
|
|
185
|
+
)
|
|
186
|
+
from infinity_context_server.public_benchmark_results import (
|
|
187
|
+
setup_failure_result as _setup_failure_result,
|
|
188
|
+
)
|
|
189
|
+
from infinity_context_server.public_benchmark_results import (
|
|
190
|
+
write_report as _write_report,
|
|
191
|
+
)
|
|
192
|
+
from infinity_context_server.public_benchmark_run_diagnostics import (
|
|
193
|
+
bounded_case_id_details as _bounded_case_id_details,
|
|
194
|
+
)
|
|
195
|
+
from infinity_context_server.public_benchmark_run_diagnostics import (
|
|
196
|
+
bounded_checkpoint_failure_case_id_details as _bounded_checkpoint_failure_case_id_details,
|
|
197
|
+
)
|
|
198
|
+
from infinity_context_server.public_benchmark_run_diagnostics import (
|
|
199
|
+
emit_case_progress_snapshot as _emit_case_progress_snapshot,
|
|
200
|
+
)
|
|
201
|
+
from infinity_context_server.public_benchmark_run_diagnostics import (
|
|
202
|
+
public_artifact_reports as _public_artifact_reports,
|
|
203
|
+
)
|
|
204
|
+
from infinity_context_server.public_benchmark_run_diagnostics import (
|
|
205
|
+
public_request_artifact_fields as _public_request_artifact_fields,
|
|
206
|
+
)
|
|
207
|
+
from infinity_context_server.public_benchmark_selection import (
|
|
208
|
+
CASE_SELECTION_FIRST,
|
|
209
|
+
CASE_SELECTION_STRATIFIED,
|
|
210
|
+
SUPPORTED_CASE_SELECTION_STRATEGIES,
|
|
211
|
+
)
|
|
212
|
+
from infinity_context_server.public_benchmark_selection import (
|
|
213
|
+
case_selection_missing_capabilities as _case_selection_missing_capabilities,
|
|
214
|
+
)
|
|
215
|
+
from infinity_context_server.public_benchmark_selection import (
|
|
216
|
+
case_selection_missing_case_ids as _case_selection_missing_case_ids,
|
|
217
|
+
)
|
|
218
|
+
from infinity_context_server.public_benchmark_selection import (
|
|
219
|
+
missing_capability_failures as _missing_capability_failures,
|
|
220
|
+
)
|
|
221
|
+
from infinity_context_server.public_benchmark_selection import (
|
|
222
|
+
normalize_requested_capabilities as _normalize_requested_capabilities,
|
|
223
|
+
)
|
|
224
|
+
from infinity_context_server.public_benchmark_selection import (
|
|
225
|
+
normalize_requested_case_ids as _normalize_requested_case_ids,
|
|
226
|
+
)
|
|
227
|
+
from infinity_context_server.public_benchmark_selection import (
|
|
228
|
+
select_cases as _select_cases,
|
|
229
|
+
)
|
|
230
|
+
from infinity_context_server.public_benchmark_unsupported import (
|
|
231
|
+
augment_case_selection_with_unsupported_requested_cases as _augment_unsupported_cases,
|
|
232
|
+
)
|
|
233
|
+
from infinity_context_server.public_benchmark_unsupported import (
|
|
234
|
+
missing_case_id_failures_for_selection as _missing_case_id_failures_for_selection,
|
|
235
|
+
)
|
|
236
|
+
from infinity_context_server.public_benchmark_unsupported import (
|
|
237
|
+
unsupported_case_reports_for_selection as _unsupported_case_reports_for_selection,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class _LazyHttpxModule:
|
|
242
|
+
def __getattr__(self, name: str) -> object:
|
|
243
|
+
import httpx as httpx_module
|
|
244
|
+
|
|
245
|
+
return getattr(httpx_module, name)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
httpx = _LazyHttpxModule()
|
|
249
|
+
|
|
250
|
+
__all__ = (
|
|
251
|
+
"BenchmarkDocumentInput",
|
|
252
|
+
"BenchmarkHttpClientPort",
|
|
253
|
+
"BenchmarkHttpResponsePort",
|
|
254
|
+
"BenchmarkMemoryInput",
|
|
255
|
+
"BenchmarkValidationError",
|
|
256
|
+
"CASE_SELECTION_FIRST",
|
|
257
|
+
"CASE_SELECTION_STRATIFIED",
|
|
258
|
+
"PublicBenchmarkCase",
|
|
259
|
+
"SUPPORTED_CASE_SELECTION_STRATEGIES",
|
|
260
|
+
"_write_json_atomic",
|
|
261
|
+
"load_public_benchmark_case_count",
|
|
262
|
+
"load_public_benchmark_dataset_profile",
|
|
263
|
+
"run_public_memory_benchmark",
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
PROJECT_ROOT = Path(__file__).resolve().parents[3]
|
|
267
|
+
PUBLIC_MEMORY_BENCHMARK_SUITE = "public-memory-benchmark"
|
|
268
|
+
LOCOMO_BENCHMARK_SUITE = "locomo"
|
|
269
|
+
LONGMEMEVAL_BENCHMARK_SUITE = "longmemeval"
|
|
270
|
+
SUPPORTED_BENCHMARKS = frozenset({LOCOMO_BENCHMARK_SUITE, LONGMEMEVAL_BENCHMARK_SUITE})
|
|
271
|
+
_DEFAULT_MIN_ACCURACY = 0.85
|
|
272
|
+
_DEFAULT_REQUEST_TIMEOUT_SECONDS = 30.0
|
|
273
|
+
_PUBLIC_BENCHMARK_CONTEXT_TOKEN_BUDGET = 4000
|
|
274
|
+
_PUBLIC_BENCHMARK_MAX_FACTS = 20
|
|
275
|
+
_PUBLIC_BENCHMARK_MAX_CHUNKS = 50
|
|
276
|
+
_PUBLIC_BENCHMARK_MAX_REUSE_DETAIL_EVENTS_PER_CASE = 3
|
|
277
|
+
_MAX_LOCOMO_OBSERVATION_EVIDENCE_IDS = 8
|
|
278
|
+
_RESUME_REUSE_POLICY = "successful_cases_only"
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@dataclass(frozen=True)
|
|
282
|
+
class _OfficialLocomoTurn:
|
|
283
|
+
session_key: str
|
|
284
|
+
dia_id: str
|
|
285
|
+
speaker: str
|
|
286
|
+
text: str
|
|
287
|
+
turn_index: int
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def run_public_memory_benchmark(
|
|
291
|
+
*,
|
|
292
|
+
dataset_path: Path,
|
|
293
|
+
api_url: str | None = None,
|
|
294
|
+
auth_token: str | None = None,
|
|
295
|
+
report_out: Path | None = None,
|
|
296
|
+
progress_out: Path | None = None,
|
|
297
|
+
checkpoint_out: Path | None = None,
|
|
298
|
+
checkpoint_every_cases: int = 25,
|
|
299
|
+
checkpoint_min_interval_seconds: float = _DEFAULT_CHECKPOINT_MIN_INTERVAL_SECONDS,
|
|
300
|
+
local_state_dir: Path | None = None,
|
|
301
|
+
benchmark: str | None = None,
|
|
302
|
+
min_accuracy: float = _DEFAULT_MIN_ACCURACY,
|
|
303
|
+
max_cases: int | None = None,
|
|
304
|
+
case_selection_strategy: str = CASE_SELECTION_FIRST,
|
|
305
|
+
case_ids: Sequence[str] | None = None,
|
|
306
|
+
capabilities: Sequence[str] | None = None,
|
|
307
|
+
resume_from_checkpoint: bool = False,
|
|
308
|
+
parallelism: int = 1,
|
|
309
|
+
request_timeout_seconds: float = _DEFAULT_REQUEST_TIMEOUT_SECONDS,
|
|
310
|
+
) -> dict[str, object]:
|
|
311
|
+
"""Run normalized public memory cases and optionally write a JSON report."""
|
|
312
|
+
|
|
313
|
+
_validate_distinct_artifact_paths(
|
|
314
|
+
error_factory=BenchmarkValidationError,
|
|
315
|
+
report_out=report_out,
|
|
316
|
+
progress_out=progress_out,
|
|
317
|
+
checkpoint_out=checkpoint_out,
|
|
318
|
+
)
|
|
319
|
+
_validate_artifacts_not_dataset(
|
|
320
|
+
dataset_path=dataset_path,
|
|
321
|
+
error_factory=BenchmarkValidationError,
|
|
322
|
+
report_out=report_out,
|
|
323
|
+
progress_out=progress_out,
|
|
324
|
+
checkpoint_out=checkpoint_out,
|
|
325
|
+
)
|
|
326
|
+
started = time.perf_counter()
|
|
327
|
+
request_timeout_seconds = _normalize_request_timeout_seconds(request_timeout_seconds)
|
|
328
|
+
checkpoint_min_interval_seconds = _normalize_checkpoint_min_interval_seconds(
|
|
329
|
+
checkpoint_min_interval_seconds
|
|
330
|
+
)
|
|
331
|
+
requested_case_ids = _normalize_requested_case_ids(case_ids)
|
|
332
|
+
requested_capabilities = _normalize_requested_capabilities(capabilities)
|
|
333
|
+
cases = _load_cases(dataset_path)
|
|
334
|
+
canonical_benchmark = _normalize_benchmark_name(benchmark) if benchmark else None
|
|
335
|
+
if canonical_benchmark:
|
|
336
|
+
cases = tuple(case for case in cases if case.benchmark == canonical_benchmark)
|
|
337
|
+
cases, case_selection = _select_cases(
|
|
338
|
+
cases,
|
|
339
|
+
max_cases=max_cases,
|
|
340
|
+
strategy=case_selection_strategy,
|
|
341
|
+
case_ids=requested_case_ids,
|
|
342
|
+
capabilities=requested_capabilities,
|
|
343
|
+
capability_resolver=_case_capability,
|
|
344
|
+
error_factory=BenchmarkValidationError,
|
|
345
|
+
)
|
|
346
|
+
case_selection = _augment_unsupported_cases(
|
|
347
|
+
dataset_path=dataset_path,
|
|
348
|
+
benchmark=canonical_benchmark,
|
|
349
|
+
requested_case_ids=requested_case_ids,
|
|
350
|
+
case_selection=case_selection,
|
|
351
|
+
locomo_benchmark=LOCOMO_BENCHMARK_SUITE,
|
|
352
|
+
load_dataset_payload=_load_dataset_payload,
|
|
353
|
+
is_official_locomo_sample=_is_official_locomo_sample,
|
|
354
|
+
official_locomo_case_ids=_official_locomo_case_ids,
|
|
355
|
+
first_str=_first_str,
|
|
356
|
+
case_hash=_case_hash,
|
|
357
|
+
)
|
|
358
|
+
request_artifact_fields = _public_request_artifact_fields(
|
|
359
|
+
case_selection=case_selection,
|
|
360
|
+
requested_case_ids=requested_case_ids,
|
|
361
|
+
requested_capabilities=requested_capabilities,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
duplicate_case_keys = _duplicate_case_keys(cases)
|
|
365
|
+
if duplicate_case_keys:
|
|
366
|
+
result = _duplicate_case_failure_result(
|
|
367
|
+
suite=PUBLIC_MEMORY_BENCHMARK_SUITE,
|
|
368
|
+
cases=cases,
|
|
369
|
+
duplicate_case_keys=duplicate_case_keys,
|
|
370
|
+
)
|
|
371
|
+
result = _with_public_benchmark_provenance(result, dataset_path=dataset_path)
|
|
372
|
+
result.update(request_artifact_fields)
|
|
373
|
+
result["failures"] = _public_artifact_reports(result.get("failures"))
|
|
374
|
+
_write_report(result, report_out)
|
|
375
|
+
return result
|
|
376
|
+
|
|
377
|
+
if not cases:
|
|
378
|
+
missing_case_ids = _case_selection_missing_case_ids(case_selection)
|
|
379
|
+
missing_capabilities = _case_selection_missing_capabilities(case_selection)
|
|
380
|
+
case_selection_failures = _missing_case_id_failures_for_selection(case_selection)
|
|
381
|
+
unsupported_cases = _unsupported_case_reports_for_selection(case_selection)
|
|
382
|
+
capability_selection_failures = _missing_capability_failures(missing_capabilities)
|
|
383
|
+
unsupported_case_id_count = len(unsupported_cases)
|
|
384
|
+
selection_failures = case_selection_failures + capability_selection_failures
|
|
385
|
+
failures = selection_failures or (
|
|
386
|
+
[]
|
|
387
|
+
if unsupported_cases
|
|
388
|
+
else [
|
|
389
|
+
{
|
|
390
|
+
"case_id": "dataset",
|
|
391
|
+
"category": "setup",
|
|
392
|
+
"reason": "no_supported_cases",
|
|
393
|
+
}
|
|
394
|
+
]
|
|
395
|
+
)
|
|
396
|
+
result = {
|
|
397
|
+
"suite": PUBLIC_MEMORY_BENCHMARK_SUITE,
|
|
398
|
+
"status": "failed",
|
|
399
|
+
"ok": False,
|
|
400
|
+
"benchmark_scope": "normalized_public_memory_retrieval",
|
|
401
|
+
"evaluation_mode": "retrieved_expected_terms",
|
|
402
|
+
"dataset_path_label": dataset_path.name,
|
|
403
|
+
**request_artifact_fields,
|
|
404
|
+
"checks": {
|
|
405
|
+
"dataset_loaded": False,
|
|
406
|
+
"case_count": False,
|
|
407
|
+
"requested_case_ids_found": not case_selection_failures,
|
|
408
|
+
"requested_case_ids_supported": unsupported_case_id_count == 0,
|
|
409
|
+
"requested_capabilities_found": not missing_capabilities,
|
|
410
|
+
},
|
|
411
|
+
"metrics": {
|
|
412
|
+
"case_count": 0,
|
|
413
|
+
"benchmark_count": 0,
|
|
414
|
+
"missing_case_id_count": len(missing_case_ids),
|
|
415
|
+
"unsupported_case_id_count": unsupported_case_id_count,
|
|
416
|
+
"missing_capability_count": len(missing_capabilities),
|
|
417
|
+
"accuracy": 0.0,
|
|
418
|
+
},
|
|
419
|
+
"benchmarks": [],
|
|
420
|
+
"cases": [],
|
|
421
|
+
"failures": _public_artifact_reports(failures),
|
|
422
|
+
"unsupported_cases": _public_artifact_reports(unsupported_cases),
|
|
423
|
+
}
|
|
424
|
+
result = _with_public_benchmark_provenance(result, dataset_path=dataset_path)
|
|
425
|
+
_write_report(result, report_out)
|
|
426
|
+
return result
|
|
427
|
+
|
|
428
|
+
token = auth_token or (_default_service_token() if api_url else "test-token")
|
|
429
|
+
if not token:
|
|
430
|
+
result = _setup_failure_result(
|
|
431
|
+
suite=PUBLIC_MEMORY_BENCHMARK_SUITE,
|
|
432
|
+
reason="auth_token_required",
|
|
433
|
+
case_count=len(cases),
|
|
434
|
+
)
|
|
435
|
+
result = _with_public_benchmark_provenance(result, dataset_path=dataset_path)
|
|
436
|
+
result.update(request_artifact_fields)
|
|
437
|
+
result["failures"] = _public_artifact_reports(result.get("failures"))
|
|
438
|
+
_write_report(result, report_out)
|
|
439
|
+
return result
|
|
440
|
+
|
|
441
|
+
if api_url:
|
|
442
|
+
from infinity_context_server.public_benchmark_transport import (
|
|
443
|
+
HttpBenchmarkAdapter,
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
with httpx.Client(
|
|
447
|
+
base_url=api_url.rstrip("/"),
|
|
448
|
+
timeout=request_timeout_seconds,
|
|
449
|
+
) as http_client:
|
|
450
|
+
adapter: BenchmarkHttpClientPort = HttpBenchmarkAdapter(http_client)
|
|
451
|
+
result = _execute_cases(
|
|
452
|
+
adapter=adapter,
|
|
453
|
+
headers=_auth_headers(token),
|
|
454
|
+
cases=cases,
|
|
455
|
+
dataset_path=dataset_path,
|
|
456
|
+
min_accuracy=min_accuracy,
|
|
457
|
+
started=started,
|
|
458
|
+
case_selection=case_selection,
|
|
459
|
+
requested_case_ids=requested_case_ids,
|
|
460
|
+
requested_capabilities=requested_capabilities,
|
|
461
|
+
progress_out=progress_out,
|
|
462
|
+
checkpoint_out=checkpoint_out,
|
|
463
|
+
checkpoint_every_cases=checkpoint_every_cases,
|
|
464
|
+
checkpoint_min_interval_seconds=checkpoint_min_interval_seconds,
|
|
465
|
+
resume_from_checkpoint=resume_from_checkpoint,
|
|
466
|
+
parallelism=parallelism,
|
|
467
|
+
transport_mode="external_http",
|
|
468
|
+
request_timeout_seconds=request_timeout_seconds,
|
|
469
|
+
)
|
|
470
|
+
else:
|
|
471
|
+
environment = check_local_benchmark_environment()
|
|
472
|
+
if not environment.ok:
|
|
473
|
+
reason = environment.reason or "local_environment_unavailable"
|
|
474
|
+
environment_diagnostics = dict(environment.diagnostics)
|
|
475
|
+
_emit_setup_failure_progress(
|
|
476
|
+
progress_out=progress_out,
|
|
477
|
+
dataset_path=dataset_path,
|
|
478
|
+
dataset_hash=_dataset_hash(dataset_path),
|
|
479
|
+
started=started,
|
|
480
|
+
reason=reason,
|
|
481
|
+
case_count=len(cases),
|
|
482
|
+
diagnostics={"local_environment": environment_diagnostics},
|
|
483
|
+
)
|
|
484
|
+
result = local_environment_failure_result(
|
|
485
|
+
environment,
|
|
486
|
+
suite=PUBLIC_MEMORY_BENCHMARK_SUITE,
|
|
487
|
+
case_count=len(cases),
|
|
488
|
+
)
|
|
489
|
+
result = _with_public_benchmark_provenance(result, dataset_path=dataset_path)
|
|
490
|
+
result.update(request_artifact_fields)
|
|
491
|
+
result["failures"] = _public_artifact_reports(result.get("failures"))
|
|
492
|
+
_write_report(result, report_out)
|
|
493
|
+
return result
|
|
494
|
+
from fastapi.testclient import TestClient
|
|
495
|
+
|
|
496
|
+
from infinity_context_server.config import DeployProfile, Settings
|
|
497
|
+
from infinity_context_server.main import create_app
|
|
498
|
+
|
|
499
|
+
state_context = (
|
|
500
|
+
nullcontext(_prepare_local_state_dir(local_state_dir))
|
|
501
|
+
if local_state_dir is not None
|
|
502
|
+
else tempfile.TemporaryDirectory(prefix="memo-public-benchmark-")
|
|
503
|
+
)
|
|
504
|
+
with state_context as tmp_dir:
|
|
505
|
+
tmp_path = Path(tmp_dir)
|
|
506
|
+
app = create_app(
|
|
507
|
+
Settings(
|
|
508
|
+
deploy_profile=DeployProfile.TEST,
|
|
509
|
+
database_url=f"sqlite+aiosqlite:///{tmp_path / 'memory.db'}",
|
|
510
|
+
auto_create_schema=True,
|
|
511
|
+
service_token=token,
|
|
512
|
+
qdrant_enabled=False,
|
|
513
|
+
graphiti_enabled=False,
|
|
514
|
+
embeddings_enabled=False,
|
|
515
|
+
)
|
|
516
|
+
)
|
|
517
|
+
with TestClient(app) as test_client:
|
|
518
|
+
adapter = _TestClientBenchmarkAdapter(test_client)
|
|
519
|
+
result = _execute_cases(
|
|
520
|
+
adapter=adapter,
|
|
521
|
+
headers=_auth_headers(token),
|
|
522
|
+
cases=cases,
|
|
523
|
+
dataset_path=dataset_path,
|
|
524
|
+
min_accuracy=min_accuracy,
|
|
525
|
+
started=started,
|
|
526
|
+
case_selection=case_selection,
|
|
527
|
+
requested_case_ids=requested_case_ids,
|
|
528
|
+
requested_capabilities=requested_capabilities,
|
|
529
|
+
progress_out=progress_out,
|
|
530
|
+
checkpoint_out=checkpoint_out,
|
|
531
|
+
checkpoint_every_cases=checkpoint_every_cases,
|
|
532
|
+
checkpoint_min_interval_seconds=checkpoint_min_interval_seconds,
|
|
533
|
+
resume_from_checkpoint=(resume_from_checkpoint and local_state_dir is not None),
|
|
534
|
+
parallelism=parallelism,
|
|
535
|
+
force_sequential_reason="local_in_process_transport",
|
|
536
|
+
transport_mode="local_in_process",
|
|
537
|
+
request_timeout_seconds=request_timeout_seconds,
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
result = _with_public_benchmark_provenance(result, dataset_path=dataset_path)
|
|
541
|
+
if local_state_dir is not None and not api_url:
|
|
542
|
+
result["local_state"] = {
|
|
543
|
+
"enabled": True,
|
|
544
|
+
"state_dir_label": local_state_dir.expanduser().name,
|
|
545
|
+
"database_label": "memory.db",
|
|
546
|
+
}
|
|
547
|
+
_write_report(result, report_out)
|
|
548
|
+
return result
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def _default_service_token() -> str:
|
|
552
|
+
from infinity_context_server.config import Settings
|
|
553
|
+
|
|
554
|
+
return Settings().service_token
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
def _prepare_local_state_dir(local_state_dir: Path) -> Path:
|
|
558
|
+
state_dir = local_state_dir.expanduser()
|
|
559
|
+
state_dir.mkdir(parents=True, exist_ok=True)
|
|
560
|
+
return state_dir
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
def load_public_benchmark_case_count(
|
|
564
|
+
*,
|
|
565
|
+
dataset_path: Path,
|
|
566
|
+
benchmark: str | None = None,
|
|
567
|
+
) -> int:
|
|
568
|
+
"""Return the normalized case count without running retrieval or writing data."""
|
|
569
|
+
|
|
570
|
+
return int(
|
|
571
|
+
load_public_benchmark_dataset_profile(
|
|
572
|
+
dataset_path=dataset_path,
|
|
573
|
+
benchmark=benchmark,
|
|
574
|
+
)["case_count"]
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def load_public_benchmark_dataset_profile(
|
|
579
|
+
*,
|
|
580
|
+
dataset_path: Path,
|
|
581
|
+
benchmark: str | None = None,
|
|
582
|
+
) -> dict[str, object]:
|
|
583
|
+
"""Return safe normalized dataset profile without running retrieval."""
|
|
584
|
+
|
|
585
|
+
cases = _load_cases(dataset_path)
|
|
586
|
+
if benchmark:
|
|
587
|
+
canonical_benchmark = _normalize_benchmark_name(benchmark)
|
|
588
|
+
cases = tuple(case for case in cases if case.benchmark == canonical_benchmark)
|
|
589
|
+
case_keys = tuple(f"{case.benchmark}:{case.case_id}" for case in cases)
|
|
590
|
+
unique_case_keys = set(case_keys)
|
|
591
|
+
benchmark_counts: dict[str, int] = defaultdict(int)
|
|
592
|
+
capability_counts: dict[str, int] = defaultdict(int)
|
|
593
|
+
for case in cases:
|
|
594
|
+
benchmark_counts[case.benchmark] += 1
|
|
595
|
+
capability = _case_capability(case)
|
|
596
|
+
if capability:
|
|
597
|
+
capability_counts[f"{case.benchmark}:{capability}"] += 1
|
|
598
|
+
return {
|
|
599
|
+
"case_count": len(cases),
|
|
600
|
+
"unique_case_id_count": len(unique_case_keys),
|
|
601
|
+
"duplicate_case_id_count": len(case_keys) - len(unique_case_keys),
|
|
602
|
+
"benchmark_counts": dict(sorted(benchmark_counts.items())),
|
|
603
|
+
"capability_counts": dict(sorted(capability_counts.items())),
|
|
604
|
+
"dataset_hash": _dataset_hash(dataset_path),
|
|
605
|
+
"dataset_path_label": dataset_path.name,
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
def _with_public_benchmark_provenance(
|
|
610
|
+
result: dict[str, object],
|
|
611
|
+
*,
|
|
612
|
+
dataset_path: Path,
|
|
613
|
+
) -> dict[str, object]:
|
|
614
|
+
return with_report_provenance(
|
|
615
|
+
result,
|
|
616
|
+
generated_by="infinity_context_server.public_benchmark",
|
|
617
|
+
run_id=_dataset_hash(dataset_path)[:16],
|
|
618
|
+
cwd=PROJECT_ROOT,
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
def _execute_cases(
|
|
623
|
+
*,
|
|
624
|
+
adapter: BenchmarkHttpClientPort,
|
|
625
|
+
headers: Mapping[str, str],
|
|
626
|
+
cases: Sequence[PublicBenchmarkCase],
|
|
627
|
+
dataset_path: Path,
|
|
628
|
+
min_accuracy: float,
|
|
629
|
+
started: float,
|
|
630
|
+
case_selection: Mapping[str, object] | None = None,
|
|
631
|
+
requested_case_ids: Sequence[str] = (),
|
|
632
|
+
requested_capabilities: Sequence[str] = (),
|
|
633
|
+
progress_out: Path | None = None,
|
|
634
|
+
checkpoint_out: Path | None = None,
|
|
635
|
+
checkpoint_every_cases: int = 25,
|
|
636
|
+
checkpoint_min_interval_seconds: float = _DEFAULT_CHECKPOINT_MIN_INTERVAL_SECONDS,
|
|
637
|
+
resume_from_checkpoint: bool = False,
|
|
638
|
+
parallelism: int = 1,
|
|
639
|
+
force_sequential_reason: str | None = None,
|
|
640
|
+
transport_mode: str = "custom_adapter",
|
|
641
|
+
request_timeout_seconds: float = _DEFAULT_REQUEST_TIMEOUT_SECONDS,
|
|
642
|
+
) -> dict[str, object]:
|
|
643
|
+
_validate_distinct_artifact_paths(
|
|
644
|
+
error_factory=BenchmarkValidationError,
|
|
645
|
+
progress_out=progress_out,
|
|
646
|
+
checkpoint_out=checkpoint_out,
|
|
647
|
+
)
|
|
648
|
+
_validate_artifacts_not_dataset(
|
|
649
|
+
dataset_path=dataset_path,
|
|
650
|
+
error_factory=BenchmarkValidationError,
|
|
651
|
+
progress_out=progress_out,
|
|
652
|
+
checkpoint_out=checkpoint_out,
|
|
653
|
+
)
|
|
654
|
+
requested_parallelism = _normalize_parallelism(
|
|
655
|
+
parallelism,
|
|
656
|
+
error_factory=BenchmarkValidationError,
|
|
657
|
+
)
|
|
658
|
+
request_timeout_seconds = _normalize_request_timeout_seconds(request_timeout_seconds)
|
|
659
|
+
checkpoint_min_interval_seconds = _normalize_checkpoint_min_interval_seconds(
|
|
660
|
+
checkpoint_min_interval_seconds
|
|
661
|
+
)
|
|
662
|
+
dataset_hash = _dataset_hash(dataset_path)
|
|
663
|
+
scope_slug = f"public-benchmark-{dataset_hash[:16]}"
|
|
664
|
+
run_results: list[CaseRunResult] = []
|
|
665
|
+
failures: list[dict[str, object]] = []
|
|
666
|
+
seeded_source_keys: set[tuple[str, str, str, str]] = set()
|
|
667
|
+
seeded_corpus_identities: set[tuple[str, str, str]] = set()
|
|
668
|
+
seed_corpus_metadata_cache: dict[tuple[int, int], _SeedCorpusMetadata] = {}
|
|
669
|
+
seed_stats = _BenchmarkSeedStats()
|
|
670
|
+
progress = _BenchmarkProgress(
|
|
671
|
+
dataset_path=dataset_path,
|
|
672
|
+
dataset_hash=dataset_hash,
|
|
673
|
+
total_case_count=len(cases),
|
|
674
|
+
case_selection=case_selection,
|
|
675
|
+
started=started,
|
|
676
|
+
progress_out=progress_out,
|
|
677
|
+
checkpoint_out=checkpoint_out,
|
|
678
|
+
checkpoint_every_cases=checkpoint_every_cases,
|
|
679
|
+
checkpoint_min_interval_seconds=checkpoint_min_interval_seconds,
|
|
680
|
+
selected_case_fingerprint=selected_case_fingerprint(cases),
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
progress.event(
|
|
684
|
+
"run_started",
|
|
685
|
+
total_case_count=len(cases),
|
|
686
|
+
benchmark_count=len({case.benchmark for case in cases}),
|
|
687
|
+
requested_parallelism=requested_parallelism,
|
|
688
|
+
request_timeout_seconds=request_timeout_seconds,
|
|
689
|
+
checkpoint_min_interval_seconds=checkpoint_min_interval_seconds,
|
|
690
|
+
transport_mode=transport_mode,
|
|
691
|
+
)
|
|
692
|
+
execution_manifest_preview = _build_execution_manifest(
|
|
693
|
+
suite=PUBLIC_MEMORY_BENCHMARK_SUITE,
|
|
694
|
+
evaluation_mode="retrieved_expected_terms",
|
|
695
|
+
dataset_path=dataset_path,
|
|
696
|
+
dataset_hash=dataset_hash,
|
|
697
|
+
selected_case_count=len(cases),
|
|
698
|
+
selected_case_fingerprint=progress.selected_case_fingerprint or "",
|
|
699
|
+
case_selection=case_selection,
|
|
700
|
+
requested_case_ids=requested_case_ids,
|
|
701
|
+
requested_capabilities=requested_capabilities,
|
|
702
|
+
transport_mode=transport_mode,
|
|
703
|
+
requested_parallelism=requested_parallelism,
|
|
704
|
+
effective_parallelism=requested_parallelism,
|
|
705
|
+
parallelism_degraded_reason=None,
|
|
706
|
+
request_timeout_seconds=request_timeout_seconds,
|
|
707
|
+
checkpoint_every_cases=checkpoint_every_cases,
|
|
708
|
+
checkpoint_min_interval_seconds=checkpoint_min_interval_seconds,
|
|
709
|
+
resume_from_checkpoint=resume_from_checkpoint,
|
|
710
|
+
resume_reuse_policy=_RESUME_REUSE_POLICY,
|
|
711
|
+
retrieval_contract=_retrieval_contract_manifest(),
|
|
712
|
+
)
|
|
713
|
+
resumed_case_keys: set[tuple[str, str]] = set()
|
|
714
|
+
resume_report: dict[str, object] = {
|
|
715
|
+
"requested": resume_from_checkpoint,
|
|
716
|
+
"status": "disabled",
|
|
717
|
+
"reason": "not_requested",
|
|
718
|
+
"resume_reuse_policy": _RESUME_REUSE_POLICY,
|
|
719
|
+
"resumed_case_count": 0,
|
|
720
|
+
"selected_case_count": len(cases),
|
|
721
|
+
"checkpoint_case_count": 0,
|
|
722
|
+
"checkpoint_success_case_count": 0,
|
|
723
|
+
"checkpoint_failed_case_count": 0,
|
|
724
|
+
"checkpoint_invalid_case_count": 0,
|
|
725
|
+
"checkpoint_failure_diagnostic_count": 0,
|
|
726
|
+
"checkpoint_failed_case_requeued_count": 0,
|
|
727
|
+
"checkpoint_failed_case_ids": [],
|
|
728
|
+
"checkpoint_failed_case_id_truncated_count": 0,
|
|
729
|
+
"checkpoint_failures": [],
|
|
730
|
+
}
|
|
731
|
+
if resume_from_checkpoint:
|
|
732
|
+
resume_load = load_checkpoint_resume_state_with_diagnostics(
|
|
733
|
+
checkpoint_out=checkpoint_out,
|
|
734
|
+
dataset_hash=dataset_hash,
|
|
735
|
+
case_selection=case_selection,
|
|
736
|
+
cases=cases,
|
|
737
|
+
execution_fingerprint=str(execution_manifest_preview["execution_fingerprint"]),
|
|
738
|
+
)
|
|
739
|
+
checkpoint_failures = [dict(item) for item in resume_load.checkpoint_failures]
|
|
740
|
+
checkpoint_failed_case_ids, checkpoint_failed_case_id_truncated_count = (
|
|
741
|
+
_bounded_checkpoint_failure_case_id_details(checkpoint_failures)
|
|
742
|
+
)
|
|
743
|
+
resume_report = {
|
|
744
|
+
"requested": True,
|
|
745
|
+
"status": resume_load.status,
|
|
746
|
+
"reason": resume_load.reason,
|
|
747
|
+
"resume_reuse_policy": _RESUME_REUSE_POLICY,
|
|
748
|
+
"resumed_case_count": 0,
|
|
749
|
+
"selected_case_count": resume_load.selected_case_count,
|
|
750
|
+
"checkpoint_case_count": resume_load.checkpoint_case_count,
|
|
751
|
+
"checkpoint_success_case_count": resume_load.checkpoint_success_case_count,
|
|
752
|
+
"checkpoint_failed_case_count": resume_load.checkpoint_failed_case_count,
|
|
753
|
+
"checkpoint_invalid_case_count": resume_load.checkpoint_invalid_case_count,
|
|
754
|
+
"checkpoint_failure_diagnostic_count": len(checkpoint_failures),
|
|
755
|
+
"checkpoint_failed_case_requeued_count": resume_load.checkpoint_failed_case_count,
|
|
756
|
+
"checkpoint_failed_case_ids": checkpoint_failed_case_ids,
|
|
757
|
+
"checkpoint_failed_case_id_truncated_count": (
|
|
758
|
+
checkpoint_failed_case_id_truncated_count
|
|
759
|
+
),
|
|
760
|
+
"checkpoint_failures": checkpoint_failures,
|
|
761
|
+
}
|
|
762
|
+
resume_state = resume_load.state
|
|
763
|
+
if resume_state is not None:
|
|
764
|
+
run_results.extend(resume_state.run_results)
|
|
765
|
+
failures.extend(dict(item) for item in resume_state.failures)
|
|
766
|
+
seeded_source_keys.update(resume_state.seeded_source_keys)
|
|
767
|
+
seeded_corpus_identities.update(resume_state.seeded_corpus_identities)
|
|
768
|
+
seed_stats = resume_state.seed_stats
|
|
769
|
+
resumed_case_keys = {
|
|
770
|
+
case_result_key(result.benchmark, result.case_id)
|
|
771
|
+
for result in resume_state.run_results
|
|
772
|
+
}
|
|
773
|
+
resume_report["resumed_case_count"] = len(resumed_case_keys)
|
|
774
|
+
progress.event(
|
|
775
|
+
"run_resumed",
|
|
776
|
+
reason=resume_load.reason,
|
|
777
|
+
resume_reuse_policy=_RESUME_REUSE_POLICY,
|
|
778
|
+
resumed_case_count=len(resumed_case_keys),
|
|
779
|
+
checkpoint_case_count=resume_load.checkpoint_case_count,
|
|
780
|
+
checkpoint_success_case_count=resume_load.checkpoint_success_case_count,
|
|
781
|
+
checkpoint_failed_case_count=resume_load.checkpoint_failed_case_count,
|
|
782
|
+
checkpoint_invalid_case_count=resume_load.checkpoint_invalid_case_count,
|
|
783
|
+
checkpoint_failure_diagnostic_count=len(checkpoint_failures),
|
|
784
|
+
checkpoint_failed_case_requeued_count=(resume_load.checkpoint_failed_case_count),
|
|
785
|
+
checkpoint_failed_case_ids=checkpoint_failed_case_ids,
|
|
786
|
+
checkpoint_failed_case_id_truncated_count=(
|
|
787
|
+
checkpoint_failed_case_id_truncated_count
|
|
788
|
+
),
|
|
789
|
+
seeded_source_count=len(seeded_source_keys),
|
|
790
|
+
seed_source_attempt_count=seed_stats.source_attempt_count,
|
|
791
|
+
seed_cache_hit_count=seed_stats.seed_cache_hit_count,
|
|
792
|
+
)
|
|
793
|
+
else:
|
|
794
|
+
progress.event(
|
|
795
|
+
"run_resume_skipped",
|
|
796
|
+
reason=resume_load.reason,
|
|
797
|
+
resume_reuse_policy=_RESUME_REUSE_POLICY,
|
|
798
|
+
selected_case_count=resume_load.selected_case_count,
|
|
799
|
+
checkpoint_case_count=resume_load.checkpoint_case_count,
|
|
800
|
+
checkpoint_success_case_count=resume_load.checkpoint_success_case_count,
|
|
801
|
+
checkpoint_failed_case_count=resume_load.checkpoint_failed_case_count,
|
|
802
|
+
checkpoint_invalid_case_count=resume_load.checkpoint_invalid_case_count,
|
|
803
|
+
checkpoint_failure_diagnostic_count=len(checkpoint_failures),
|
|
804
|
+
checkpoint_failed_case_requeued_count=(resume_load.checkpoint_failed_case_count),
|
|
805
|
+
checkpoint_failed_case_ids=checkpoint_failed_case_ids,
|
|
806
|
+
checkpoint_failed_case_id_truncated_count=(
|
|
807
|
+
checkpoint_failed_case_id_truncated_count
|
|
808
|
+
),
|
|
809
|
+
)
|
|
810
|
+
pending_entries = tuple(
|
|
811
|
+
_CaseExecutionEntry(case_index=case_index, case=case)
|
|
812
|
+
for case_index, case in enumerate(cases, start=1)
|
|
813
|
+
if case_result_key(case.benchmark, case.case_id) not in resumed_case_keys
|
|
814
|
+
)
|
|
815
|
+
resumed_case_ids, resumed_case_id_truncated_count = _bounded_case_id_details(
|
|
816
|
+
case for case in cases if case_result_key(case.benchmark, case.case_id) in resumed_case_keys
|
|
817
|
+
)
|
|
818
|
+
pending_case_ids, pending_case_id_truncated_count = _bounded_case_id_details(
|
|
819
|
+
entry.case for entry in pending_entries
|
|
820
|
+
)
|
|
821
|
+
resume_report["resumed_case_ids"] = resumed_case_ids
|
|
822
|
+
resume_report["pending_case_ids"] = pending_case_ids
|
|
823
|
+
resume_report["resumed_case_id_truncated_count"] = resumed_case_id_truncated_count
|
|
824
|
+
resume_report["pending_case_id_truncated_count"] = pending_case_id_truncated_count
|
|
825
|
+
if (
|
|
826
|
+
force_sequential_reason is not None
|
|
827
|
+
and requested_parallelism > 1
|
|
828
|
+
and len(pending_entries) > 1
|
|
829
|
+
):
|
|
830
|
+
effective_parallelism = 1
|
|
831
|
+
parallelism_degraded_reason = force_sequential_reason
|
|
832
|
+
else:
|
|
833
|
+
effective_parallelism, parallelism_degraded_reason = _case_execution_parallelism(
|
|
834
|
+
pending_entries,
|
|
835
|
+
requested_parallelism=requested_parallelism,
|
|
836
|
+
)
|
|
837
|
+
progress.event(
|
|
838
|
+
"run_execution_configured",
|
|
839
|
+
pending_case_count=len(pending_entries),
|
|
840
|
+
pending_case_ids=pending_case_ids,
|
|
841
|
+
pending_case_id_truncated_count=pending_case_id_truncated_count,
|
|
842
|
+
resumed_case_count=len(resumed_case_keys),
|
|
843
|
+
resumed_case_ids=resumed_case_ids,
|
|
844
|
+
resumed_case_id_truncated_count=resumed_case_id_truncated_count,
|
|
845
|
+
requested_parallelism=requested_parallelism,
|
|
846
|
+
effective_parallelism=effective_parallelism,
|
|
847
|
+
parallelism_degraded_reason=parallelism_degraded_reason,
|
|
848
|
+
request_timeout_seconds=request_timeout_seconds,
|
|
849
|
+
checkpoint_min_interval_seconds=checkpoint_min_interval_seconds,
|
|
850
|
+
resume_reuse_policy=_RESUME_REUSE_POLICY,
|
|
851
|
+
checkpoint_failed_case_requeued_count=resume_report[
|
|
852
|
+
"checkpoint_failed_case_requeued_count"
|
|
853
|
+
],
|
|
854
|
+
checkpoint_failed_case_ids=resume_report["checkpoint_failed_case_ids"],
|
|
855
|
+
checkpoint_failed_case_id_truncated_count=resume_report[
|
|
856
|
+
"checkpoint_failed_case_id_truncated_count"
|
|
857
|
+
],
|
|
858
|
+
transport_mode=transport_mode,
|
|
859
|
+
)
|
|
860
|
+
execution_manifest = _build_execution_manifest(
|
|
861
|
+
suite=PUBLIC_MEMORY_BENCHMARK_SUITE,
|
|
862
|
+
evaluation_mode="retrieved_expected_terms",
|
|
863
|
+
dataset_path=dataset_path,
|
|
864
|
+
dataset_hash=dataset_hash,
|
|
865
|
+
selected_case_count=len(cases),
|
|
866
|
+
selected_case_fingerprint=progress.selected_case_fingerprint or "",
|
|
867
|
+
case_selection=case_selection,
|
|
868
|
+
requested_case_ids=requested_case_ids,
|
|
869
|
+
requested_capabilities=requested_capabilities,
|
|
870
|
+
transport_mode=transport_mode,
|
|
871
|
+
requested_parallelism=requested_parallelism,
|
|
872
|
+
effective_parallelism=effective_parallelism,
|
|
873
|
+
parallelism_degraded_reason=parallelism_degraded_reason,
|
|
874
|
+
request_timeout_seconds=request_timeout_seconds,
|
|
875
|
+
checkpoint_every_cases=checkpoint_every_cases,
|
|
876
|
+
checkpoint_min_interval_seconds=checkpoint_min_interval_seconds,
|
|
877
|
+
resume_from_checkpoint=resume_from_checkpoint,
|
|
878
|
+
resume_reuse_policy=_RESUME_REUSE_POLICY,
|
|
879
|
+
retrieval_contract=_retrieval_contract_manifest(),
|
|
880
|
+
)
|
|
881
|
+
progress.execution_manifest = execution_manifest
|
|
882
|
+
progress.event(
|
|
883
|
+
"run_execution_manifest",
|
|
884
|
+
execution_fingerprint=execution_manifest["execution_fingerprint"],
|
|
885
|
+
manifest_fingerprint=execution_manifest["manifest_fingerprint"],
|
|
886
|
+
transport_mode=transport_mode,
|
|
887
|
+
requested_parallelism=requested_parallelism,
|
|
888
|
+
effective_parallelism=effective_parallelism,
|
|
889
|
+
parallelism_degraded_reason=parallelism_degraded_reason,
|
|
890
|
+
resume_reuse_policy=_RESUME_REUSE_POLICY,
|
|
891
|
+
)
|
|
892
|
+
|
|
893
|
+
def run_case_adapter(
|
|
894
|
+
case: PublicBenchmarkCase,
|
|
895
|
+
source_keys: set[tuple[str, str, str, str]],
|
|
896
|
+
corpus_identities: set[tuple[str, str, str]],
|
|
897
|
+
metadata_cache: dict[tuple[int, int], _SeedCorpusMetadata],
|
|
898
|
+
stats: _BenchmarkSeedStats,
|
|
899
|
+
case_progress: _BenchmarkProgress | None,
|
|
900
|
+
case_index: int | None,
|
|
901
|
+
total_case_count: int | None,
|
|
902
|
+
) -> CaseRunResult:
|
|
903
|
+
return _run_case(
|
|
904
|
+
adapter=adapter,
|
|
905
|
+
headers=headers,
|
|
906
|
+
scope_slug=scope_slug,
|
|
907
|
+
dataset_hash=dataset_hash,
|
|
908
|
+
case=case,
|
|
909
|
+
seeded_source_keys=source_keys,
|
|
910
|
+
seeded_corpus_identities=corpus_identities,
|
|
911
|
+
seed_corpus_metadata_cache=metadata_cache,
|
|
912
|
+
seed_stats=stats,
|
|
913
|
+
progress=case_progress,
|
|
914
|
+
case_index=case_index,
|
|
915
|
+
total_case_count=total_case_count,
|
|
916
|
+
)
|
|
917
|
+
|
|
918
|
+
if effective_parallelism <= 1:
|
|
919
|
+
for entry in pending_entries:
|
|
920
|
+
_execute_case_sequentially(
|
|
921
|
+
entry=entry,
|
|
922
|
+
run_case=run_case_adapter,
|
|
923
|
+
capability_resolver=_case_capability,
|
|
924
|
+
seeded_source_keys=seeded_source_keys,
|
|
925
|
+
seeded_corpus_identities=seeded_corpus_identities,
|
|
926
|
+
seed_corpus_metadata_cache=seed_corpus_metadata_cache,
|
|
927
|
+
seed_stats=seed_stats,
|
|
928
|
+
progress=progress,
|
|
929
|
+
total_case_count=len(cases),
|
|
930
|
+
run_results=run_results,
|
|
931
|
+
failures=failures,
|
|
932
|
+
effective_parallelism=effective_parallelism,
|
|
933
|
+
)
|
|
934
|
+
_emit_case_progress_snapshot(
|
|
935
|
+
progress=progress,
|
|
936
|
+
run_results=run_results,
|
|
937
|
+
failures=failures,
|
|
938
|
+
seeded_source_count=len(seeded_source_keys),
|
|
939
|
+
seed_stats=seed_stats,
|
|
940
|
+
effective_parallelism=effective_parallelism,
|
|
941
|
+
)
|
|
942
|
+
progress.checkpoint(
|
|
943
|
+
processed_case_count=len(run_results),
|
|
944
|
+
run_results=run_results,
|
|
945
|
+
failures=failures,
|
|
946
|
+
seeded_source_count=len(seeded_source_keys),
|
|
947
|
+
seed_stats=seed_stats,
|
|
948
|
+
)
|
|
949
|
+
else:
|
|
950
|
+
result_by_key = {
|
|
951
|
+
case_result_key(result.benchmark, result.case_id): result for result in run_results
|
|
952
|
+
}
|
|
953
|
+
execution_groups = _case_execution_groups(pending_entries)
|
|
954
|
+
with concurrent.futures.ThreadPoolExecutor(
|
|
955
|
+
max_workers=effective_parallelism,
|
|
956
|
+
thread_name_prefix="public-benchmark-context",
|
|
957
|
+
) as executor:
|
|
958
|
+
future_to_group = {}
|
|
959
|
+
for group in execution_groups:
|
|
960
|
+
for entry in group.entries:
|
|
961
|
+
_emit_case_started(
|
|
962
|
+
progress=progress,
|
|
963
|
+
entry=entry,
|
|
964
|
+
capability_resolver=_case_capability,
|
|
965
|
+
total_case_count=len(cases),
|
|
966
|
+
effective_parallelism=effective_parallelism,
|
|
967
|
+
)
|
|
968
|
+
future = executor.submit(
|
|
969
|
+
_execute_case_group_with_isolated_state,
|
|
970
|
+
group=group,
|
|
971
|
+
run_case=run_case_adapter,
|
|
972
|
+
capability_resolver=_case_capability,
|
|
973
|
+
)
|
|
974
|
+
future_to_group[future] = group
|
|
975
|
+
for future in concurrent.futures.as_completed(future_to_group):
|
|
976
|
+
group = future_to_group[future]
|
|
977
|
+
try:
|
|
978
|
+
group_outcome = future.result()
|
|
979
|
+
except Exception as exc:
|
|
980
|
+
group_outcome = _CaseExecutionGroupOutcome(
|
|
981
|
+
group=group,
|
|
982
|
+
case_outcomes=tuple(
|
|
983
|
+
_case_exception_outcome(
|
|
984
|
+
entry,
|
|
985
|
+
exc,
|
|
986
|
+
capability_resolver=_case_capability,
|
|
987
|
+
)
|
|
988
|
+
for entry in group.entries
|
|
989
|
+
),
|
|
990
|
+
seeded_source_keys=frozenset(),
|
|
991
|
+
seeded_corpus_identities=frozenset(),
|
|
992
|
+
seed_stats=_BenchmarkSeedStats(),
|
|
993
|
+
)
|
|
994
|
+
_merge_case_execution_group_outcome(
|
|
995
|
+
outcome=group_outcome,
|
|
996
|
+
seeded_source_keys=seeded_source_keys,
|
|
997
|
+
seeded_corpus_identities=seeded_corpus_identities,
|
|
998
|
+
seed_stats=seed_stats,
|
|
999
|
+
)
|
|
1000
|
+
for outcome in group_outcome.case_outcomes:
|
|
1001
|
+
entry = outcome.entry
|
|
1002
|
+
if outcome.failure is not None:
|
|
1003
|
+
failures.append(outcome.failure)
|
|
1004
|
+
_emit_case_failed(
|
|
1005
|
+
progress=progress,
|
|
1006
|
+
entry=entry,
|
|
1007
|
+
reason=str(outcome.failure["reason"]),
|
|
1008
|
+
seeded_source_count=len(seeded_source_keys),
|
|
1009
|
+
seed_cache_hit_count=seed_stats.seed_cache_hit_count,
|
|
1010
|
+
effective_parallelism=effective_parallelism,
|
|
1011
|
+
)
|
|
1012
|
+
else:
|
|
1013
|
+
_emit_case_completed(
|
|
1014
|
+
progress=progress,
|
|
1015
|
+
entry=entry,
|
|
1016
|
+
result=outcome.result,
|
|
1017
|
+
seeded_source_count=len(seeded_source_keys),
|
|
1018
|
+
seed_cache_hit_count=seed_stats.seed_cache_hit_count,
|
|
1019
|
+
effective_parallelism=effective_parallelism,
|
|
1020
|
+
)
|
|
1021
|
+
result_by_key[
|
|
1022
|
+
case_result_key(outcome.result.benchmark, outcome.result.case_id)
|
|
1023
|
+
] = outcome.result
|
|
1024
|
+
run_results[:] = _ordered_run_results(cases, result_by_key)
|
|
1025
|
+
_emit_case_progress_snapshot(
|
|
1026
|
+
progress=progress,
|
|
1027
|
+
run_results=run_results,
|
|
1028
|
+
failures=failures,
|
|
1029
|
+
seeded_source_count=len(seeded_source_keys),
|
|
1030
|
+
seed_stats=seed_stats,
|
|
1031
|
+
effective_parallelism=effective_parallelism,
|
|
1032
|
+
)
|
|
1033
|
+
progress.checkpoint(
|
|
1034
|
+
processed_case_count=len(run_results),
|
|
1035
|
+
run_results=run_results,
|
|
1036
|
+
failures=failures,
|
|
1037
|
+
seeded_source_count=len(seeded_source_keys),
|
|
1038
|
+
seed_stats=seed_stats,
|
|
1039
|
+
)
|
|
1040
|
+
|
|
1041
|
+
progress.event(
|
|
1042
|
+
"run_completed",
|
|
1043
|
+
total_case_count=len(cases),
|
|
1044
|
+
processed_case_count=len(run_results),
|
|
1045
|
+
processed_case_ratio=_ratio(len(run_results), len(cases)),
|
|
1046
|
+
**_progress_timing_fields(
|
|
1047
|
+
processed_case_count=len(run_results),
|
|
1048
|
+
total_case_count=len(cases),
|
|
1049
|
+
started=started,
|
|
1050
|
+
),
|
|
1051
|
+
accuracy_so_far=_accuracy(run_results),
|
|
1052
|
+
capability_accuracy_so_far=_flat_capability_accuracy(run_results),
|
|
1053
|
+
capability_case_count_so_far=_flat_capability_case_count(run_results),
|
|
1054
|
+
capability_failure_count_so_far=_flat_capability_failure_count(run_results),
|
|
1055
|
+
**_progress_case_outcome_fields(
|
|
1056
|
+
processed_case_count=len(run_results),
|
|
1057
|
+
run_results=run_results,
|
|
1058
|
+
failures=failures,
|
|
1059
|
+
total_case_count=len(cases),
|
|
1060
|
+
),
|
|
1061
|
+
seeded_source_count=len(seeded_source_keys),
|
|
1062
|
+
seed_source_attempt_count=seed_stats.source_attempt_count,
|
|
1063
|
+
seed_cache_hit_count=seed_stats.seed_cache_hit_count,
|
|
1064
|
+
effective_parallelism=effective_parallelism,
|
|
1065
|
+
)
|
|
1066
|
+
progress.checkpoint(
|
|
1067
|
+
processed_case_count=len(cases),
|
|
1068
|
+
run_results=run_results,
|
|
1069
|
+
failures=failures,
|
|
1070
|
+
seeded_source_count=len(seeded_source_keys),
|
|
1071
|
+
seed_stats=seed_stats,
|
|
1072
|
+
force=True,
|
|
1073
|
+
)
|
|
1074
|
+
|
|
1075
|
+
benchmarks = _benchmark_summaries(run_results, min_accuracy=min_accuracy)
|
|
1076
|
+
missing_case_ids = _case_selection_missing_case_ids(case_selection)
|
|
1077
|
+
missing_capabilities = _case_selection_missing_capabilities(case_selection)
|
|
1078
|
+
case_selection_failures = _missing_case_id_failures_for_selection(case_selection)
|
|
1079
|
+
unsupported_cases = _unsupported_case_reports_for_selection(case_selection)
|
|
1080
|
+
capability_selection_failures = _missing_capability_failures(missing_capabilities)
|
|
1081
|
+
benchmark_accuracy_ok = bool(run_results) and all(item["ok"] is True for item in benchmarks)
|
|
1082
|
+
unsupported_case_id_count = len(unsupported_cases)
|
|
1083
|
+
ok = (
|
|
1084
|
+
benchmark_accuracy_ok
|
|
1085
|
+
and not unsupported_cases
|
|
1086
|
+
and not case_selection_failures
|
|
1087
|
+
and not capability_selection_failures
|
|
1088
|
+
)
|
|
1089
|
+
case_keys = tuple(f"{case.benchmark}:{case.case_id}" for case in cases)
|
|
1090
|
+
request_artifact_fields = _public_request_artifact_fields(
|
|
1091
|
+
case_selection=case_selection,
|
|
1092
|
+
requested_case_ids=requested_case_ids,
|
|
1093
|
+
requested_capabilities=requested_capabilities,
|
|
1094
|
+
)
|
|
1095
|
+
result: dict[str, object] = {
|
|
1096
|
+
"suite": PUBLIC_MEMORY_BENCHMARK_SUITE,
|
|
1097
|
+
"status": "ok" if ok else "failed",
|
|
1098
|
+
"ok": ok,
|
|
1099
|
+
"benchmark_scope": "normalized_public_memory_retrieval",
|
|
1100
|
+
"evaluation_mode": "retrieved_expected_terms",
|
|
1101
|
+
"dataset_path_label": dataset_path.name,
|
|
1102
|
+
"dataset_hash": dataset_hash,
|
|
1103
|
+
**request_artifact_fields,
|
|
1104
|
+
"dataset_sources": {
|
|
1105
|
+
summary["name"]: _dataset_source_metadata(
|
|
1106
|
+
dataset_path=dataset_path,
|
|
1107
|
+
dataset_hash=dataset_hash,
|
|
1108
|
+
source_kind="local_dataset",
|
|
1109
|
+
case_count=_benchmark_summary_case_count(summary),
|
|
1110
|
+
)
|
|
1111
|
+
for summary in benchmarks
|
|
1112
|
+
if isinstance(summary.get("name"), str)
|
|
1113
|
+
},
|
|
1114
|
+
"checks": {
|
|
1115
|
+
"dataset_loaded": True,
|
|
1116
|
+
"case_count": len(run_results) > 0,
|
|
1117
|
+
"unique_case_ids": True,
|
|
1118
|
+
"minimum_accuracy_met": benchmark_accuracy_ok,
|
|
1119
|
+
"requested_case_ids_found": not case_selection_failures,
|
|
1120
|
+
"requested_case_ids_supported": unsupported_case_id_count == 0,
|
|
1121
|
+
"requested_capabilities_found": not missing_capabilities,
|
|
1122
|
+
"no_request_failures": (
|
|
1123
|
+
not failures and not case_selection_failures and not capability_selection_failures
|
|
1124
|
+
),
|
|
1125
|
+
},
|
|
1126
|
+
"metrics": {
|
|
1127
|
+
"benchmark_count": len(benchmarks),
|
|
1128
|
+
**_run_metric_summary(run_results),
|
|
1129
|
+
"unique_case_id_count": len(set(case_keys)),
|
|
1130
|
+
"duplicate_case_id_count": 0,
|
|
1131
|
+
"missing_case_id_count": len(missing_case_ids),
|
|
1132
|
+
"unsupported_case_id_count": unsupported_case_id_count,
|
|
1133
|
+
"missing_capability_count": len(missing_capabilities),
|
|
1134
|
+
"seed_source_attempt_count": seed_stats.source_attempt_count,
|
|
1135
|
+
"seeded_source_count": len(seeded_source_keys),
|
|
1136
|
+
"seed_cache_hit_count": seed_stats.seed_cache_hit_count,
|
|
1137
|
+
"requested_parallelism": requested_parallelism,
|
|
1138
|
+
"effective_parallelism": effective_parallelism,
|
|
1139
|
+
"parallelism_degraded": parallelism_degraded_reason is not None,
|
|
1140
|
+
"parallelism_degraded_reason": parallelism_degraded_reason,
|
|
1141
|
+
"request_timeout_seconds": request_timeout_seconds,
|
|
1142
|
+
"checkpoint_min_interval_seconds": checkpoint_min_interval_seconds,
|
|
1143
|
+
"resumed_case_count": len(resumed_case_keys),
|
|
1144
|
+
"pending_case_count": len(pending_entries),
|
|
1145
|
+
"checkpoint_failed_case_requeued_count": resume_report[
|
|
1146
|
+
"checkpoint_failed_case_requeued_count"
|
|
1147
|
+
],
|
|
1148
|
+
**_progress_timing_fields(
|
|
1149
|
+
processed_case_count=len(run_results),
|
|
1150
|
+
total_case_count=len(cases),
|
|
1151
|
+
started=started,
|
|
1152
|
+
),
|
|
1153
|
+
},
|
|
1154
|
+
"resume": resume_report,
|
|
1155
|
+
"execution_manifest": execution_manifest,
|
|
1156
|
+
"benchmarks": benchmarks,
|
|
1157
|
+
"cases": [_case_payload(item) for item in run_results],
|
|
1158
|
+
"failures": _public_artifact_reports(
|
|
1159
|
+
failures
|
|
1160
|
+
+ case_selection_failures
|
|
1161
|
+
+ capability_selection_failures
|
|
1162
|
+
+ _case_failures(run_results)
|
|
1163
|
+
),
|
|
1164
|
+
"unsupported_cases": _public_artifact_reports(unsupported_cases),
|
|
1165
|
+
"elapsed_ms": round((time.perf_counter() - started) * 1000, 2),
|
|
1166
|
+
}
|
|
1167
|
+
for summary in benchmarks:
|
|
1168
|
+
name = summary["name"]
|
|
1169
|
+
metrics = summary.get("metrics", {})
|
|
1170
|
+
if isinstance(name, str) and isinstance(metrics, dict):
|
|
1171
|
+
result["metrics"][f"{name}_accuracy"] = metrics.get("accuracy")
|
|
1172
|
+
result["metrics"][f"{name}_case_count"] = metrics.get("case_count")
|
|
1173
|
+
return result
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
def _normalize_request_timeout_seconds(value: float) -> float:
|
|
1177
|
+
if isinstance(value, bool) or value <= 0:
|
|
1178
|
+
raise BenchmarkValidationError("request_timeout_seconds must be greater than zero")
|
|
1179
|
+
return float(value)
|
|
1180
|
+
|
|
1181
|
+
|
|
1182
|
+
def _normalize_checkpoint_min_interval_seconds(value: float) -> float:
|
|
1183
|
+
if isinstance(value, bool) or value < 0:
|
|
1184
|
+
raise BenchmarkValidationError(
|
|
1185
|
+
"checkpoint_min_interval_seconds must be greater than or equal to zero"
|
|
1186
|
+
)
|
|
1187
|
+
return float(value)
|
|
1188
|
+
|
|
1189
|
+
|
|
1190
|
+
def _retrieval_contract_manifest() -> dict[str, object]:
|
|
1191
|
+
return {
|
|
1192
|
+
"context_token_budget": _PUBLIC_BENCHMARK_CONTEXT_TOKEN_BUDGET,
|
|
1193
|
+
"max_facts": _PUBLIC_BENCHMARK_MAX_FACTS,
|
|
1194
|
+
"max_chunks": _PUBLIC_BENCHMARK_MAX_CHUNKS,
|
|
1195
|
+
"max_reuse_detail_events_per_case": _PUBLIC_BENCHMARK_MAX_REUSE_DETAIL_EVENTS_PER_CASE,
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
def _run_case(
|
|
1200
|
+
*,
|
|
1201
|
+
adapter: BenchmarkHttpClientPort,
|
|
1202
|
+
headers: Mapping[str, str],
|
|
1203
|
+
scope_slug: str,
|
|
1204
|
+
dataset_hash: str,
|
|
1205
|
+
case: PublicBenchmarkCase,
|
|
1206
|
+
seeded_source_keys: set[tuple[str, str, str, str]],
|
|
1207
|
+
seeded_corpus_identities: set[tuple[str, str, str]],
|
|
1208
|
+
seed_corpus_metadata_cache: dict[tuple[int, int], _SeedCorpusMetadata],
|
|
1209
|
+
seed_stats: _BenchmarkSeedStats,
|
|
1210
|
+
progress: _BenchmarkProgress | None = None,
|
|
1211
|
+
case_index: int | None = None,
|
|
1212
|
+
total_case_count: int | None = None,
|
|
1213
|
+
) -> CaseRunResult:
|
|
1214
|
+
memory_scope_ref = case.memory_scope_external_ref or f"{case.benchmark}-{case.case_id}"
|
|
1215
|
+
thread_ref = case.thread_external_ref or f"{case.benchmark}-{case.case_id}"
|
|
1216
|
+
corpus_identity = seed_corpus_identity(
|
|
1217
|
+
case,
|
|
1218
|
+
memory_scope_ref=memory_scope_ref,
|
|
1219
|
+
thread_ref=thread_ref,
|
|
1220
|
+
)
|
|
1221
|
+
corpus_metadata = seed_corpus_metadata(
|
|
1222
|
+
case,
|
|
1223
|
+
cache=seed_corpus_metadata_cache,
|
|
1224
|
+
)
|
|
1225
|
+
reused_source_count = 0
|
|
1226
|
+
reused_source_detail_event_count = 0
|
|
1227
|
+
reused_source_kind_counts: dict[str, int] = defaultdict(int)
|
|
1228
|
+
|
|
1229
|
+
def record_reused_source(
|
|
1230
|
+
*,
|
|
1231
|
+
source_kind: str,
|
|
1232
|
+
source_index: int,
|
|
1233
|
+
source_id: str,
|
|
1234
|
+
source_type: str | None = None,
|
|
1235
|
+
) -> None:
|
|
1236
|
+
nonlocal reused_source_count, reused_source_detail_event_count
|
|
1237
|
+
seed_stats.seed_cache_hit_count += 1
|
|
1238
|
+
reused_source_count += 1
|
|
1239
|
+
reused_source_kind_counts[source_kind] += 1
|
|
1240
|
+
if (
|
|
1241
|
+
progress is None
|
|
1242
|
+
or reused_source_detail_event_count
|
|
1243
|
+
>= _PUBLIC_BENCHMARK_MAX_REUSE_DETAIL_EVENTS_PER_CASE
|
|
1244
|
+
):
|
|
1245
|
+
return
|
|
1246
|
+
event_fields: dict[str, object] = {
|
|
1247
|
+
"case_index": case_index,
|
|
1248
|
+
"total_case_count": total_case_count,
|
|
1249
|
+
"case_id": case.case_id,
|
|
1250
|
+
"benchmark": case.benchmark,
|
|
1251
|
+
"source_kind": source_kind,
|
|
1252
|
+
"source_index": source_index,
|
|
1253
|
+
"source_id_hash": _short_hash(source_id),
|
|
1254
|
+
"seeded_source_count": len(seeded_source_keys),
|
|
1255
|
+
"seed_cache_hit_count": seed_stats.seed_cache_hit_count,
|
|
1256
|
+
"reuse_detail_event_index": reused_source_detail_event_count + 1,
|
|
1257
|
+
"reuse_detail_event_limit": _PUBLIC_BENCHMARK_MAX_REUSE_DETAIL_EVENTS_PER_CASE,
|
|
1258
|
+
}
|
|
1259
|
+
if source_type:
|
|
1260
|
+
event_fields["source_type"] = source_type
|
|
1261
|
+
progress.event("source_seed_reused", **event_fields)
|
|
1262
|
+
reused_source_detail_event_count += 1
|
|
1263
|
+
|
|
1264
|
+
if corpus_metadata.reusable_by_identity and corpus_identity in seeded_corpus_identities:
|
|
1265
|
+
seed_stats.source_attempt_count += corpus_metadata.source_count
|
|
1266
|
+
seed_stats.seed_cache_hit_count += corpus_metadata.source_count
|
|
1267
|
+
if progress is not None:
|
|
1268
|
+
progress.event(
|
|
1269
|
+
"source_seed_corpus_reused",
|
|
1270
|
+
case_index=case_index,
|
|
1271
|
+
total_case_count=total_case_count,
|
|
1272
|
+
case_id=case.case_id,
|
|
1273
|
+
benchmark=case.benchmark,
|
|
1274
|
+
reused_source_count=corpus_metadata.source_count,
|
|
1275
|
+
reused_source_kind_counts=dict(corpus_metadata.source_kind_counts),
|
|
1276
|
+
seeded_source_count=len(seeded_source_keys),
|
|
1277
|
+
seed_cache_hit_count=seed_stats.seed_cache_hit_count,
|
|
1278
|
+
)
|
|
1279
|
+
else:
|
|
1280
|
+
for index, memory in enumerate(case.memories):
|
|
1281
|
+
source_id = safe_identifier(
|
|
1282
|
+
memory.source_external_id or f"{dataset_hash}:{case.case_id}:memory:{index}",
|
|
1283
|
+
max_chars=160,
|
|
1284
|
+
)
|
|
1285
|
+
seed_key = (memory_scope_ref, thread_ref, "fact", source_id)
|
|
1286
|
+
seed_stats.source_attempt_count += 1
|
|
1287
|
+
if seed_key not in seeded_source_keys:
|
|
1288
|
+
if progress is not None:
|
|
1289
|
+
progress.event(
|
|
1290
|
+
"source_seed_started",
|
|
1291
|
+
case_index=case_index,
|
|
1292
|
+
total_case_count=total_case_count,
|
|
1293
|
+
case_id=case.case_id,
|
|
1294
|
+
benchmark=case.benchmark,
|
|
1295
|
+
source_kind="fact",
|
|
1296
|
+
source_index=index + 1,
|
|
1297
|
+
source_id_hash=_short_hash(source_id),
|
|
1298
|
+
payload_chars=len(memory.text),
|
|
1299
|
+
seeded_source_count=len(seeded_source_keys),
|
|
1300
|
+
)
|
|
1301
|
+
_post_required(
|
|
1302
|
+
adapter,
|
|
1303
|
+
"/v1/facts",
|
|
1304
|
+
headers=headers,
|
|
1305
|
+
payload={
|
|
1306
|
+
"space_slug": scope_slug,
|
|
1307
|
+
"memory_scope_external_ref": memory_scope_ref,
|
|
1308
|
+
"thread_external_ref": thread_ref,
|
|
1309
|
+
"text": memory.text,
|
|
1310
|
+
"kind": memory.kind,
|
|
1311
|
+
"source_refs": [
|
|
1312
|
+
{
|
|
1313
|
+
"source_type": "public_benchmark",
|
|
1314
|
+
"source_id": source_id,
|
|
1315
|
+
"quote_preview": memory.text[:240],
|
|
1316
|
+
}
|
|
1317
|
+
],
|
|
1318
|
+
"classification": "internal",
|
|
1319
|
+
},
|
|
1320
|
+
idempotency_key=source_id,
|
|
1321
|
+
)
|
|
1322
|
+
seeded_source_keys.add(seed_key)
|
|
1323
|
+
seed_stats.seeded_source_count = len(seeded_source_keys)
|
|
1324
|
+
if progress is not None:
|
|
1325
|
+
progress.event(
|
|
1326
|
+
"source_seed_completed",
|
|
1327
|
+
case_index=case_index,
|
|
1328
|
+
total_case_count=total_case_count,
|
|
1329
|
+
case_id=case.case_id,
|
|
1330
|
+
benchmark=case.benchmark,
|
|
1331
|
+
source_kind="fact",
|
|
1332
|
+
source_index=index + 1,
|
|
1333
|
+
source_id_hash=_short_hash(source_id),
|
|
1334
|
+
seeded_source_count=len(seeded_source_keys),
|
|
1335
|
+
)
|
|
1336
|
+
else:
|
|
1337
|
+
record_reused_source(
|
|
1338
|
+
source_kind="fact",
|
|
1339
|
+
source_index=index + 1,
|
|
1340
|
+
source_id=source_id,
|
|
1341
|
+
)
|
|
1342
|
+
|
|
1343
|
+
for index, document in enumerate(case.documents):
|
|
1344
|
+
source_id = safe_identifier(
|
|
1345
|
+
document.source_external_id or f"{dataset_hash}:{case.case_id}:doc:{index}",
|
|
1346
|
+
max_chars=240,
|
|
1347
|
+
)
|
|
1348
|
+
seed_key = (memory_scope_ref, thread_ref, "document", source_id)
|
|
1349
|
+
seed_stats.source_attempt_count += 1
|
|
1350
|
+
if seed_key not in seeded_source_keys:
|
|
1351
|
+
if progress is not None:
|
|
1352
|
+
progress.event(
|
|
1353
|
+
"source_seed_started",
|
|
1354
|
+
case_index=case_index,
|
|
1355
|
+
total_case_count=total_case_count,
|
|
1356
|
+
case_id=case.case_id,
|
|
1357
|
+
benchmark=case.benchmark,
|
|
1358
|
+
source_kind="document",
|
|
1359
|
+
source_index=index + 1,
|
|
1360
|
+
source_id_hash=_short_hash(source_id),
|
|
1361
|
+
source_type=document.source_type,
|
|
1362
|
+
payload_chars=len(document.text),
|
|
1363
|
+
seeded_source_count=len(seeded_source_keys),
|
|
1364
|
+
)
|
|
1365
|
+
_post_required(
|
|
1366
|
+
adapter,
|
|
1367
|
+
"/v1/documents",
|
|
1368
|
+
headers=headers,
|
|
1369
|
+
payload={
|
|
1370
|
+
"space_slug": scope_slug,
|
|
1371
|
+
"memory_scope_external_ref": memory_scope_ref,
|
|
1372
|
+
"thread_external_ref": thread_ref,
|
|
1373
|
+
"title": document.title,
|
|
1374
|
+
"text": document.text,
|
|
1375
|
+
"source_type": document.source_type,
|
|
1376
|
+
"source_external_id": source_id,
|
|
1377
|
+
"classification": document.classification,
|
|
1378
|
+
},
|
|
1379
|
+
idempotency_key=source_id,
|
|
1380
|
+
)
|
|
1381
|
+
seeded_source_keys.add(seed_key)
|
|
1382
|
+
seed_stats.seeded_source_count = len(seeded_source_keys)
|
|
1383
|
+
if progress is not None:
|
|
1384
|
+
progress.event(
|
|
1385
|
+
"source_seed_completed",
|
|
1386
|
+
case_index=case_index,
|
|
1387
|
+
total_case_count=total_case_count,
|
|
1388
|
+
case_id=case.case_id,
|
|
1389
|
+
benchmark=case.benchmark,
|
|
1390
|
+
source_kind="document",
|
|
1391
|
+
source_index=index + 1,
|
|
1392
|
+
source_id_hash=_short_hash(source_id),
|
|
1393
|
+
source_type=document.source_type,
|
|
1394
|
+
seeded_source_count=len(seeded_source_keys),
|
|
1395
|
+
)
|
|
1396
|
+
else:
|
|
1397
|
+
record_reused_source(
|
|
1398
|
+
source_kind="document",
|
|
1399
|
+
source_index=index + 1,
|
|
1400
|
+
source_id=source_id,
|
|
1401
|
+
source_type=document.source_type,
|
|
1402
|
+
)
|
|
1403
|
+
|
|
1404
|
+
if corpus_metadata.reusable_by_identity:
|
|
1405
|
+
seeded_corpus_identities.add(corpus_identity)
|
|
1406
|
+
|
|
1407
|
+
if progress is not None and reused_source_count:
|
|
1408
|
+
progress.event(
|
|
1409
|
+
"source_seed_reuse_summary",
|
|
1410
|
+
case_index=case_index,
|
|
1411
|
+
total_case_count=total_case_count,
|
|
1412
|
+
case_id=case.case_id,
|
|
1413
|
+
benchmark=case.benchmark,
|
|
1414
|
+
reused_source_count=reused_source_count,
|
|
1415
|
+
reused_source_kind_counts=dict(sorted(reused_source_kind_counts.items())),
|
|
1416
|
+
reuse_detail_event_count=reused_source_detail_event_count,
|
|
1417
|
+
reuse_detail_event_limit=_PUBLIC_BENCHMARK_MAX_REUSE_DETAIL_EVENTS_PER_CASE,
|
|
1418
|
+
seeded_source_count=len(seeded_source_keys),
|
|
1419
|
+
seed_cache_hit_count=seed_stats.seed_cache_hit_count,
|
|
1420
|
+
)
|
|
1421
|
+
|
|
1422
|
+
if progress is not None:
|
|
1423
|
+
progress.event(
|
|
1424
|
+
"context_request_started",
|
|
1425
|
+
case_index=case_index,
|
|
1426
|
+
total_case_count=total_case_count,
|
|
1427
|
+
case_id=case.case_id,
|
|
1428
|
+
benchmark=case.benchmark,
|
|
1429
|
+
seeded_source_count=len(seeded_source_keys),
|
|
1430
|
+
)
|
|
1431
|
+
started = time.perf_counter()
|
|
1432
|
+
response = _post_required(
|
|
1433
|
+
adapter,
|
|
1434
|
+
"/v1/context",
|
|
1435
|
+
headers=headers,
|
|
1436
|
+
payload={
|
|
1437
|
+
"space_slug": scope_slug,
|
|
1438
|
+
"memory_scope_external_ref": memory_scope_ref,
|
|
1439
|
+
"thread_external_ref": thread_ref,
|
|
1440
|
+
"query": case.question,
|
|
1441
|
+
"token_budget": _PUBLIC_BENCHMARK_CONTEXT_TOKEN_BUDGET,
|
|
1442
|
+
"max_facts": _PUBLIC_BENCHMARK_MAX_FACTS,
|
|
1443
|
+
"max_chunks": _PUBLIC_BENCHMARK_MAX_CHUNKS,
|
|
1444
|
+
},
|
|
1445
|
+
)
|
|
1446
|
+
latency_ms = round((time.perf_counter() - started) * 1000, 2)
|
|
1447
|
+
if progress is not None:
|
|
1448
|
+
progress.event(
|
|
1449
|
+
"context_request_completed",
|
|
1450
|
+
case_index=case_index,
|
|
1451
|
+
total_case_count=total_case_count,
|
|
1452
|
+
case_id=case.case_id,
|
|
1453
|
+
benchmark=case.benchmark,
|
|
1454
|
+
latency_ms=latency_ms,
|
|
1455
|
+
)
|
|
1456
|
+
data = _response_data(response)
|
|
1457
|
+
evidence_text = _evidence_text(data)
|
|
1458
|
+
normalized_evidence = _normalize_text(evidence_text)
|
|
1459
|
+
covered_terms = tuple(
|
|
1460
|
+
term for term in case.expected_terms if _normalize_text(term) in normalized_evidence
|
|
1461
|
+
)
|
|
1462
|
+
missing = tuple(
|
|
1463
|
+
term for term in case.expected_terms if _normalize_text(term) not in normalized_evidence
|
|
1464
|
+
)
|
|
1465
|
+
leaked = tuple(
|
|
1466
|
+
term for term in case.forbidden_terms if _normalize_text(term) in normalized_evidence
|
|
1467
|
+
)
|
|
1468
|
+
items = data.get("items", [])
|
|
1469
|
+
item_ids = tuple(
|
|
1470
|
+
str(item.get("item_id")) for item in items if isinstance(item, dict) and item.get("item_id")
|
|
1471
|
+
)
|
|
1472
|
+
evidence_refs = _case_evidence_refs(case)
|
|
1473
|
+
covered_evidence_refs = tuple(
|
|
1474
|
+
ref for ref in evidence_refs if _normalize_text(ref) in normalized_evidence
|
|
1475
|
+
)
|
|
1476
|
+
missing_evidence_refs = tuple(
|
|
1477
|
+
ref for ref in evidence_refs if _normalize_text(ref) not in normalized_evidence
|
|
1478
|
+
)
|
|
1479
|
+
evidence_ref_previews = _case_evidence_ref_previews(case, refs=evidence_refs)
|
|
1480
|
+
missing_evidence_ref_previews = _case_evidence_ref_previews(
|
|
1481
|
+
case,
|
|
1482
|
+
refs=missing_evidence_refs,
|
|
1483
|
+
)
|
|
1484
|
+
return CaseRunResult(
|
|
1485
|
+
benchmark=case.benchmark,
|
|
1486
|
+
case_id=case.case_id,
|
|
1487
|
+
capability=_case_capability(case),
|
|
1488
|
+
ok=not missing and not leaked,
|
|
1489
|
+
expected_ok=not missing,
|
|
1490
|
+
forbidden_ok=not leaked,
|
|
1491
|
+
missing_terms=missing,
|
|
1492
|
+
leaked_terms=leaked,
|
|
1493
|
+
item_ids=item_ids,
|
|
1494
|
+
latency_ms=latency_ms,
|
|
1495
|
+
question_preview=_preview_value(case.question),
|
|
1496
|
+
answer_preview=_case_answer_preview(case),
|
|
1497
|
+
expected_terms_preview=_case_expected_terms_preview(case),
|
|
1498
|
+
evidence_refs=evidence_refs,
|
|
1499
|
+
evidence_ref_previews=evidence_ref_previews,
|
|
1500
|
+
covered_terms=covered_terms,
|
|
1501
|
+
covered_evidence_refs=covered_evidence_refs,
|
|
1502
|
+
missing_evidence_refs=missing_evidence_refs,
|
|
1503
|
+
missing_evidence_ref_previews=missing_evidence_ref_previews,
|
|
1504
|
+
)
|
|
1505
|
+
|
|
1506
|
+
|
|
1507
|
+
def _load_cases(dataset_path: Path) -> tuple[PublicBenchmarkCase, ...]:
|
|
1508
|
+
if not dataset_path.exists():
|
|
1509
|
+
raise BenchmarkValidationError(f"Dataset does not exist: {dataset_path}")
|
|
1510
|
+
cases = _cases_from_payload(_load_dataset_payload(dataset_path))
|
|
1511
|
+
if not cases:
|
|
1512
|
+
raise BenchmarkValidationError("Dataset does not contain benchmark cases")
|
|
1513
|
+
return cases
|
|
1514
|
+
|
|
1515
|
+
|
|
1516
|
+
def _load_dataset_payload(dataset_path: Path) -> object:
|
|
1517
|
+
text = dataset_path.read_text(encoding="utf-8")
|
|
1518
|
+
stripped = text.strip()
|
|
1519
|
+
if not stripped:
|
|
1520
|
+
return ()
|
|
1521
|
+
if stripped.startswith("[") or (stripped.startswith("{") and "\n" not in stripped):
|
|
1522
|
+
return json.loads(stripped)
|
|
1523
|
+
return [json.loads(line) for line in stripped.splitlines() if line.strip()]
|
|
1524
|
+
|
|
1525
|
+
|
|
1526
|
+
def _cases_from_payload(payload: object) -> tuple[PublicBenchmarkCase, ...]:
|
|
1527
|
+
if isinstance(payload, Mapping):
|
|
1528
|
+
if _is_official_locomo_sample(payload):
|
|
1529
|
+
return _official_locomo_cases(payload)
|
|
1530
|
+
if _is_official_longmemeval_row(payload):
|
|
1531
|
+
return (_official_longmemeval_case(payload),)
|
|
1532
|
+
raw_cases = payload.get("cases") or payload.get("data") or payload.get("items")
|
|
1533
|
+
if raw_cases is not None:
|
|
1534
|
+
return _cases_from_payload(raw_cases)
|
|
1535
|
+
return (_normalize_case(payload),)
|
|
1536
|
+
|
|
1537
|
+
if not isinstance(payload, Sequence) or isinstance(payload, str | bytes):
|
|
1538
|
+
raise BenchmarkValidationError("Dataset root must be a case list, object or JSONL")
|
|
1539
|
+
|
|
1540
|
+
cases: list[PublicBenchmarkCase] = []
|
|
1541
|
+
for item in payload:
|
|
1542
|
+
if not isinstance(item, Mapping):
|
|
1543
|
+
continue
|
|
1544
|
+
if _is_official_locomo_sample(item):
|
|
1545
|
+
cases.extend(_official_locomo_cases(item))
|
|
1546
|
+
elif _is_official_longmemeval_row(item):
|
|
1547
|
+
cases.append(_official_longmemeval_case(item))
|
|
1548
|
+
else:
|
|
1549
|
+
cases.append(_normalize_case(item))
|
|
1550
|
+
return tuple(cases)
|
|
1551
|
+
|
|
1552
|
+
|
|
1553
|
+
def _duplicate_case_keys(cases: Sequence[PublicBenchmarkCase]) -> tuple[str, ...]:
|
|
1554
|
+
seen: set[str] = set()
|
|
1555
|
+
duplicates: list[str] = []
|
|
1556
|
+
for case in cases:
|
|
1557
|
+
key = f"{case.benchmark}:{case.case_id}"
|
|
1558
|
+
if key in seen and key not in duplicates:
|
|
1559
|
+
duplicates.append(key)
|
|
1560
|
+
seen.add(key)
|
|
1561
|
+
return tuple(duplicates)
|
|
1562
|
+
|
|
1563
|
+
|
|
1564
|
+
def _is_official_locomo_sample(raw: Mapping[str, object]) -> bool:
|
|
1565
|
+
return isinstance(raw.get("conversation"), Mapping) and isinstance(raw.get("qa"), list)
|
|
1566
|
+
|
|
1567
|
+
|
|
1568
|
+
def _official_locomo_cases(raw: Mapping[str, object]) -> tuple[PublicBenchmarkCase, ...]:
|
|
1569
|
+
sample_id = _first_str(raw, "sample_id", "id") or _case_hash(raw)
|
|
1570
|
+
documents = _official_locomo_documents(raw, sample_id=sample_id)
|
|
1571
|
+
evidence_lookup = _official_locomo_evidence_lookup(raw)
|
|
1572
|
+
raw_qas = raw.get("qa")
|
|
1573
|
+
cases: list[PublicBenchmarkCase] = []
|
|
1574
|
+
if not isinstance(raw_qas, Sequence) or isinstance(raw_qas, str | bytes):
|
|
1575
|
+
return ()
|
|
1576
|
+
for index, qa in enumerate(raw_qas):
|
|
1577
|
+
if not isinstance(qa, Mapping):
|
|
1578
|
+
continue
|
|
1579
|
+
question = _first_str(qa, "question", "query")
|
|
1580
|
+
evidence_terms = _official_locomo_evidence_terms(qa, evidence_lookup)
|
|
1581
|
+
expected_terms = evidence_terms or _official_locomo_supported_answer_terms(
|
|
1582
|
+
qa,
|
|
1583
|
+
documents=documents,
|
|
1584
|
+
)
|
|
1585
|
+
if not question or not expected_terms:
|
|
1586
|
+
continue
|
|
1587
|
+
category = qa.get("category")
|
|
1588
|
+
case_id = f"{sample_id}:qa:{index + 1}"
|
|
1589
|
+
cases.append(
|
|
1590
|
+
PublicBenchmarkCase(
|
|
1591
|
+
benchmark=LOCOMO_BENCHMARK_SUITE,
|
|
1592
|
+
case_id=case_id,
|
|
1593
|
+
question=question,
|
|
1594
|
+
expected_terms=expected_terms,
|
|
1595
|
+
documents=documents,
|
|
1596
|
+
memory_scope_external_ref=f"locomo-{sample_id}",
|
|
1597
|
+
thread_external_ref=f"locomo-{sample_id}",
|
|
1598
|
+
metadata={
|
|
1599
|
+
"source_format": "official_locomo",
|
|
1600
|
+
"sample_id": sample_id,
|
|
1601
|
+
"qa_index": index,
|
|
1602
|
+
"category": category,
|
|
1603
|
+
"answer_preview": _preview_value(qa.get("answer")),
|
|
1604
|
+
"evidence": qa.get("evidence") if isinstance(qa.get("evidence"), list) else [],
|
|
1605
|
+
"evidence_previews": _official_locomo_evidence_previews(
|
|
1606
|
+
qa,
|
|
1607
|
+
evidence_lookup=evidence_lookup,
|
|
1608
|
+
),
|
|
1609
|
+
},
|
|
1610
|
+
)
|
|
1611
|
+
)
|
|
1612
|
+
return tuple(cases)
|
|
1613
|
+
|
|
1614
|
+
|
|
1615
|
+
def _official_locomo_case_ids(raw: Mapping[str, object]) -> tuple[str, ...]:
|
|
1616
|
+
return tuple(case.case_id for case in _official_locomo_cases(raw))
|
|
1617
|
+
|
|
1618
|
+
|
|
1619
|
+
def _official_locomo_evidence_lookup(raw: Mapping[str, object]) -> dict[str, str]:
|
|
1620
|
+
conversation = raw.get("conversation")
|
|
1621
|
+
if not isinstance(conversation, Mapping):
|
|
1622
|
+
return {}
|
|
1623
|
+
lookup: dict[str, str] = {}
|
|
1624
|
+
for key in sorted(conversation, key=_session_sort_key):
|
|
1625
|
+
if not _is_session_key(key):
|
|
1626
|
+
continue
|
|
1627
|
+
turns = conversation.get(key)
|
|
1628
|
+
if not isinstance(turns, Sequence) or isinstance(turns, str | bytes):
|
|
1629
|
+
continue
|
|
1630
|
+
for turn in turns:
|
|
1631
|
+
if not isinstance(turn, Mapping):
|
|
1632
|
+
continue
|
|
1633
|
+
dia_id = _first_str(turn, "dia_id", "id")
|
|
1634
|
+
evidence_text = "\n".join(_official_locomo_turn_evidence_parts(turn))
|
|
1635
|
+
if dia_id and evidence_text:
|
|
1636
|
+
lookup[dia_id] = evidence_text
|
|
1637
|
+
return lookup
|
|
1638
|
+
|
|
1639
|
+
|
|
1640
|
+
def _official_locomo_turn_evidence_parts(turn: Mapping[str, object]) -> tuple[str, ...]:
|
|
1641
|
+
text = _first_str(turn, "text", "content", "utterance")
|
|
1642
|
+
caption = _first_str(turn, "blip_caption", "caption")
|
|
1643
|
+
visual_query = _first_str(turn, "query", "image_query", "visual_query")
|
|
1644
|
+
parts: list[str] = []
|
|
1645
|
+
if text:
|
|
1646
|
+
parts.append(text)
|
|
1647
|
+
if caption:
|
|
1648
|
+
parts.append(f"image caption: {caption}")
|
|
1649
|
+
if visual_query:
|
|
1650
|
+
parts.append(f"visual query: {visual_query}")
|
|
1651
|
+
return tuple(_unique(parts))
|
|
1652
|
+
|
|
1653
|
+
|
|
1654
|
+
def _official_locomo_session_turns(
|
|
1655
|
+
raw: Mapping[str, object],
|
|
1656
|
+
) -> dict[str, tuple[_OfficialLocomoTurn, ...]]:
|
|
1657
|
+
conversation = raw.get("conversation")
|
|
1658
|
+
if not isinstance(conversation, Mapping):
|
|
1659
|
+
return {}
|
|
1660
|
+
turns_by_session: dict[str, tuple[_OfficialLocomoTurn, ...]] = {}
|
|
1661
|
+
for key in sorted(conversation, key=_session_sort_key):
|
|
1662
|
+
if not _is_session_key(key):
|
|
1663
|
+
continue
|
|
1664
|
+
turns = conversation.get(key)
|
|
1665
|
+
if not isinstance(turns, Sequence) or isinstance(turns, str | bytes):
|
|
1666
|
+
continue
|
|
1667
|
+
session_turns: list[_OfficialLocomoTurn] = []
|
|
1668
|
+
for index, turn in enumerate(turns):
|
|
1669
|
+
if not isinstance(turn, Mapping):
|
|
1670
|
+
continue
|
|
1671
|
+
dia_id = _first_str(turn, "dia_id", "id")
|
|
1672
|
+
evidence_text = "\n".join(_official_locomo_turn_evidence_parts(turn))
|
|
1673
|
+
if not dia_id or not evidence_text:
|
|
1674
|
+
continue
|
|
1675
|
+
session_turns.append(
|
|
1676
|
+
_OfficialLocomoTurn(
|
|
1677
|
+
session_key=key,
|
|
1678
|
+
dia_id=dia_id,
|
|
1679
|
+
speaker=_first_str(turn, "speaker", "role", "author") or "speaker",
|
|
1680
|
+
text=evidence_text,
|
|
1681
|
+
turn_index=index,
|
|
1682
|
+
)
|
|
1683
|
+
)
|
|
1684
|
+
if session_turns:
|
|
1685
|
+
turns_by_session[key] = tuple(session_turns)
|
|
1686
|
+
return turns_by_session
|
|
1687
|
+
|
|
1688
|
+
|
|
1689
|
+
def _official_locomo_evidence_terms(
|
|
1690
|
+
qa: Mapping[str, object],
|
|
1691
|
+
evidence_lookup: Mapping[str, str],
|
|
1692
|
+
) -> tuple[str, ...]:
|
|
1693
|
+
terms: list[str] = []
|
|
1694
|
+
for evidence_id in _official_locomo_qa_evidence_ids(qa.get("evidence")):
|
|
1695
|
+
if evidence_id and evidence_id in evidence_lookup:
|
|
1696
|
+
terms.append(evidence_id)
|
|
1697
|
+
return tuple(_unique(terms))
|
|
1698
|
+
|
|
1699
|
+
|
|
1700
|
+
def _official_locomo_evidence_previews(
|
|
1701
|
+
qa: Mapping[str, object],
|
|
1702
|
+
*,
|
|
1703
|
+
evidence_lookup: Mapping[str, str],
|
|
1704
|
+
) -> dict[str, str]:
|
|
1705
|
+
previews: dict[str, str] = {}
|
|
1706
|
+
for evidence_id in _official_locomo_qa_evidence_ids(qa.get("evidence")):
|
|
1707
|
+
evidence_text = evidence_lookup.get(evidence_id)
|
|
1708
|
+
if evidence_id and evidence_text:
|
|
1709
|
+
previews[evidence_id] = _preview_value(evidence_text, max_chars=240)
|
|
1710
|
+
return previews
|
|
1711
|
+
|
|
1712
|
+
|
|
1713
|
+
def _official_locomo_qa_evidence_ids(evidence: object) -> tuple[str, ...]:
|
|
1714
|
+
values: list[str] = []
|
|
1715
|
+
for item in _official_locomo_qa_evidence_values(evidence):
|
|
1716
|
+
evidence_id = str(item).strip()
|
|
1717
|
+
if evidence_id:
|
|
1718
|
+
values.append(evidence_id)
|
|
1719
|
+
return tuple(_unique(values))
|
|
1720
|
+
|
|
1721
|
+
|
|
1722
|
+
def _official_locomo_qa_evidence_values(evidence: object) -> tuple[object, ...]:
|
|
1723
|
+
if isinstance(evidence, Mapping):
|
|
1724
|
+
values: list[object] = []
|
|
1725
|
+
for key in (
|
|
1726
|
+
"dia_id",
|
|
1727
|
+
"evidence",
|
|
1728
|
+
"evidence_id",
|
|
1729
|
+
"evidence_ids",
|
|
1730
|
+
"id",
|
|
1731
|
+
"turn_id",
|
|
1732
|
+
"turn_ids",
|
|
1733
|
+
):
|
|
1734
|
+
if key in evidence:
|
|
1735
|
+
values.extend(_official_locomo_qa_evidence_values(evidence.get(key)))
|
|
1736
|
+
return tuple(values)
|
|
1737
|
+
if isinstance(evidence, Sequence) and not isinstance(evidence, str | bytes):
|
|
1738
|
+
flattened: list[object] = []
|
|
1739
|
+
for item in evidence:
|
|
1740
|
+
flattened.extend(_official_locomo_qa_evidence_values(item))
|
|
1741
|
+
return tuple(flattened)
|
|
1742
|
+
return (evidence,) if evidence is not None else ()
|
|
1743
|
+
|
|
1744
|
+
|
|
1745
|
+
def _official_locomo_supported_answer_terms(
|
|
1746
|
+
qa: Mapping[str, object],
|
|
1747
|
+
*,
|
|
1748
|
+
documents: Sequence[BenchmarkDocumentInput],
|
|
1749
|
+
) -> tuple[str, ...]:
|
|
1750
|
+
answer_terms = _terms(qa, "answer", "expected_answer", "answers")
|
|
1751
|
+
if not answer_terms:
|
|
1752
|
+
return ()
|
|
1753
|
+
searchable_text = _normalize_text("\n".join(document.text for document in documents))
|
|
1754
|
+
return tuple(term for term in answer_terms if _normalize_text(term) in searchable_text)
|
|
1755
|
+
|
|
1756
|
+
|
|
1757
|
+
def _official_locomo_documents(
|
|
1758
|
+
raw: Mapping[str, object],
|
|
1759
|
+
*,
|
|
1760
|
+
sample_id: str,
|
|
1761
|
+
) -> tuple[BenchmarkDocumentInput, ...]:
|
|
1762
|
+
conversation = raw.get("conversation")
|
|
1763
|
+
if not isinstance(conversation, Mapping):
|
|
1764
|
+
return ()
|
|
1765
|
+
documents: list[BenchmarkDocumentInput] = []
|
|
1766
|
+
for key in sorted(conversation, key=_session_sort_key):
|
|
1767
|
+
if not _is_session_key(key):
|
|
1768
|
+
continue
|
|
1769
|
+
turns = conversation.get(key)
|
|
1770
|
+
if not isinstance(turns, Sequence) or isinstance(turns, str | bytes):
|
|
1771
|
+
continue
|
|
1772
|
+
date_value = conversation.get(f"{key}_date_time")
|
|
1773
|
+
lines = [f"{key} date: {date_value}"] if isinstance(date_value, str) else [key]
|
|
1774
|
+
for turn in turns:
|
|
1775
|
+
if not isinstance(turn, Mapping):
|
|
1776
|
+
continue
|
|
1777
|
+
dia_id = _first_str(turn, "dia_id", "id")
|
|
1778
|
+
speaker = _first_str(turn, "speaker", "role", "author") or "speaker"
|
|
1779
|
+
text = _first_str(turn, "text", "content", "utterance")
|
|
1780
|
+
prefix = f"{dia_id} " if dia_id else ""
|
|
1781
|
+
if text:
|
|
1782
|
+
lines.append(f"{prefix}{speaker}: {text}")
|
|
1783
|
+
caption = _first_str(turn, "blip_caption", "caption")
|
|
1784
|
+
if caption:
|
|
1785
|
+
lines.append(f"{prefix}{speaker} image caption: {caption}")
|
|
1786
|
+
visual_query = _first_str(turn, "query", "image_query", "visual_query")
|
|
1787
|
+
if visual_query:
|
|
1788
|
+
lines.append(f"{prefix}{speaker} visual query: {visual_query}")
|
|
1789
|
+
if len(lines) > 1:
|
|
1790
|
+
documents.append(
|
|
1791
|
+
BenchmarkDocumentInput(
|
|
1792
|
+
title=f"LoCoMo {sample_id} {key}",
|
|
1793
|
+
text="\n".join(lines),
|
|
1794
|
+
source_type="locomo_session",
|
|
1795
|
+
source_external_id=f"locomo:{sample_id}:{key}",
|
|
1796
|
+
)
|
|
1797
|
+
)
|
|
1798
|
+
documents.extend(_official_locomo_observation_documents(raw, sample_id=sample_id))
|
|
1799
|
+
documents.extend(_official_locomo_turn_documents(raw, sample_id=sample_id))
|
|
1800
|
+
documents.extend(_official_locomo_session_summary_documents(raw, sample_id=sample_id))
|
|
1801
|
+
documents.extend(_official_locomo_event_summary_documents(raw, sample_id=sample_id))
|
|
1802
|
+
return tuple(documents)
|
|
1803
|
+
|
|
1804
|
+
|
|
1805
|
+
def _official_locomo_turn_documents(
|
|
1806
|
+
raw: Mapping[str, object],
|
|
1807
|
+
*,
|
|
1808
|
+
sample_id: str,
|
|
1809
|
+
) -> tuple[BenchmarkDocumentInput, ...]:
|
|
1810
|
+
turns_by_session = _official_locomo_session_turns(raw)
|
|
1811
|
+
documents: list[BenchmarkDocumentInput] = []
|
|
1812
|
+
conversation = raw.get("conversation")
|
|
1813
|
+
conversation_map = conversation if isinstance(conversation, Mapping) else {}
|
|
1814
|
+
for session_key in sorted(turns_by_session, key=_session_sort_key):
|
|
1815
|
+
date_value = conversation_map.get(f"{session_key}_date_time")
|
|
1816
|
+
for turn in turns_by_session[session_key]:
|
|
1817
|
+
lines = [f"{session_key} turn {turn.dia_id}"]
|
|
1818
|
+
if isinstance(date_value, str) and date_value.strip():
|
|
1819
|
+
lines.append(f"{session_key} date: {date_value.strip()}")
|
|
1820
|
+
lines.append(f"{turn.dia_id} {turn.speaker}: {turn.text}")
|
|
1821
|
+
documents.append(
|
|
1822
|
+
BenchmarkDocumentInput(
|
|
1823
|
+
title=f"LoCoMo {sample_id} {session_key} turn {turn.dia_id}",
|
|
1824
|
+
text="\n".join(lines),
|
|
1825
|
+
source_type="locomo_turn",
|
|
1826
|
+
source_external_id=(f"locomo:{sample_id}:{session_key}:{turn.dia_id}:turn"),
|
|
1827
|
+
)
|
|
1828
|
+
)
|
|
1829
|
+
return tuple(documents)
|
|
1830
|
+
|
|
1831
|
+
|
|
1832
|
+
def _official_locomo_observation_documents(
|
|
1833
|
+
raw: Mapping[str, object],
|
|
1834
|
+
*,
|
|
1835
|
+
sample_id: str,
|
|
1836
|
+
) -> tuple[BenchmarkDocumentInput, ...]:
|
|
1837
|
+
observation = raw.get("observation")
|
|
1838
|
+
if not isinstance(observation, Mapping):
|
|
1839
|
+
return ()
|
|
1840
|
+
turns_by_session = _official_locomo_session_turns(raw)
|
|
1841
|
+
conversation = raw.get("conversation")
|
|
1842
|
+
conversation_map = conversation if isinstance(conversation, Mapping) else {}
|
|
1843
|
+
documents: list[BenchmarkDocumentInput] = []
|
|
1844
|
+
for key in sorted(observation, key=_session_sort_key):
|
|
1845
|
+
value = observation.get(key)
|
|
1846
|
+
if not isinstance(value, Mapping):
|
|
1847
|
+
continue
|
|
1848
|
+
session_key = key.removesuffix("_observation")
|
|
1849
|
+
session_turns = turns_by_session.get(session_key, ())
|
|
1850
|
+
lines = [f"{session_key} observations"]
|
|
1851
|
+
date_value = conversation_map.get(f"{session_key}_date_time")
|
|
1852
|
+
if isinstance(date_value, str) and date_value.strip():
|
|
1853
|
+
lines.append(f"{session_key} date: {date_value.strip()}")
|
|
1854
|
+
for actor, raw_items in value.items():
|
|
1855
|
+
actor_name = str(actor).strip() or "speaker"
|
|
1856
|
+
for item in _as_list(raw_items):
|
|
1857
|
+
text, evidence_ids = _official_locomo_observation_item(item)
|
|
1858
|
+
if not text or not evidence_ids:
|
|
1859
|
+
continue
|
|
1860
|
+
evidence_id = evidence_ids[0]
|
|
1861
|
+
evidence_ids = _official_locomo_related_observation_evidence_ids(
|
|
1862
|
+
text=text,
|
|
1863
|
+
evidence_id=evidence_id,
|
|
1864
|
+
explicit_evidence_ids=evidence_ids,
|
|
1865
|
+
actor_name=actor_name,
|
|
1866
|
+
session_turns=session_turns,
|
|
1867
|
+
)
|
|
1868
|
+
related_ids = tuple(item for item in evidence_ids if item != evidence_id)
|
|
1869
|
+
related_label = f" Related turns: {' '.join(related_ids)}." if related_ids else ""
|
|
1870
|
+
lines.append(f"{evidence_id} {actor_name}: {text}{related_label}")
|
|
1871
|
+
if len(lines) <= 1:
|
|
1872
|
+
continue
|
|
1873
|
+
documents.append(
|
|
1874
|
+
BenchmarkDocumentInput(
|
|
1875
|
+
title=f"LoCoMo {sample_id} {session_key} observations",
|
|
1876
|
+
text="\n".join(lines),
|
|
1877
|
+
source_type="locomo_observation",
|
|
1878
|
+
source_external_id=f"locomo:{sample_id}:{session_key}:observation",
|
|
1879
|
+
)
|
|
1880
|
+
)
|
|
1881
|
+
return tuple(documents)
|
|
1882
|
+
|
|
1883
|
+
|
|
1884
|
+
def _official_locomo_related_observation_evidence_ids(
|
|
1885
|
+
*,
|
|
1886
|
+
text: str,
|
|
1887
|
+
evidence_id: str,
|
|
1888
|
+
explicit_evidence_ids: tuple[str, ...] = (),
|
|
1889
|
+
actor_name: str,
|
|
1890
|
+
session_turns: tuple[_OfficialLocomoTurn, ...],
|
|
1891
|
+
) -> tuple[str, ...]:
|
|
1892
|
+
evidence_id = evidence_id.strip()
|
|
1893
|
+
if not evidence_id:
|
|
1894
|
+
return ()
|
|
1895
|
+
explicit_turn = next(
|
|
1896
|
+
(turn for turn in session_turns if turn.dia_id == evidence_id),
|
|
1897
|
+
None,
|
|
1898
|
+
)
|
|
1899
|
+
actor_key = actor_name.casefold().strip()
|
|
1900
|
+
ranked: list[tuple[tuple[int, int, int, int, int], _OfficialLocomoTurn]] = []
|
|
1901
|
+
for turn in session_turns:
|
|
1902
|
+
if turn.dia_id == evidence_id:
|
|
1903
|
+
continue
|
|
1904
|
+
if actor_key and turn.speaker.casefold().strip() != actor_key:
|
|
1905
|
+
continue
|
|
1906
|
+
relevance = _score_official_locomo_turn_relevance(query=text, turn_text=turn.text)
|
|
1907
|
+
if not _is_related_locomo_observation_turn(relevance):
|
|
1908
|
+
continue
|
|
1909
|
+
distance = (
|
|
1910
|
+
abs(turn.turn_index - explicit_turn.turn_index)
|
|
1911
|
+
if explicit_turn is not None
|
|
1912
|
+
else turn.turn_index
|
|
1913
|
+
)
|
|
1914
|
+
ranked.append(
|
|
1915
|
+
(
|
|
1916
|
+
(
|
|
1917
|
+
-relevance.distinctive_term_hits,
|
|
1918
|
+
-relevance.unique_term_hits,
|
|
1919
|
+
-relevance.capped_frequency_hits,
|
|
1920
|
+
distance,
|
|
1921
|
+
turn.turn_index,
|
|
1922
|
+
),
|
|
1923
|
+
turn,
|
|
1924
|
+
)
|
|
1925
|
+
)
|
|
1926
|
+
ranked.sort(key=lambda item: item[0])
|
|
1927
|
+
adjacent_ids = _official_locomo_adjacent_evidence_ids(
|
|
1928
|
+
explicit_turn=explicit_turn,
|
|
1929
|
+
session_turns=session_turns,
|
|
1930
|
+
)
|
|
1931
|
+
related_ids = tuple(turn.dia_id for _, turn in ranked[:3])
|
|
1932
|
+
session_order = {turn.dia_id: turn.turn_index for turn in session_turns}
|
|
1933
|
+
return tuple(
|
|
1934
|
+
sorted(
|
|
1935
|
+
_unique((evidence_id, *explicit_evidence_ids, *adjacent_ids, *related_ids)),
|
|
1936
|
+
key=lambda item: session_order.get(item, 10_000),
|
|
1937
|
+
)
|
|
1938
|
+
)
|
|
1939
|
+
|
|
1940
|
+
|
|
1941
|
+
def _official_locomo_adjacent_evidence_ids(
|
|
1942
|
+
*,
|
|
1943
|
+
explicit_turn: _OfficialLocomoTurn | None,
|
|
1944
|
+
session_turns: tuple[_OfficialLocomoTurn, ...],
|
|
1945
|
+
) -> tuple[str, ...]:
|
|
1946
|
+
if explicit_turn is None:
|
|
1947
|
+
return ()
|
|
1948
|
+
return tuple(
|
|
1949
|
+
turn.dia_id
|
|
1950
|
+
for turn in session_turns
|
|
1951
|
+
if turn.dia_id != explicit_turn.dia_id
|
|
1952
|
+
and turn.speaker.casefold().strip() == explicit_turn.speaker.casefold().strip()
|
|
1953
|
+
and explicit_turn.turn_index - 1 <= turn.turn_index <= explicit_turn.turn_index + 2
|
|
1954
|
+
)
|
|
1955
|
+
|
|
1956
|
+
|
|
1957
|
+
def _is_related_locomo_observation_turn(relevance: QueryRelevance) -> bool:
|
|
1958
|
+
if relevance.unique_term_hits < 2:
|
|
1959
|
+
return False
|
|
1960
|
+
return relevance.distinctive_term_hits >= 2 or relevance.hit_ratio >= 0.2
|
|
1961
|
+
|
|
1962
|
+
|
|
1963
|
+
def _official_locomo_observation_item(item: object) -> tuple[str, tuple[str, ...]]:
|
|
1964
|
+
if isinstance(item, Mapping):
|
|
1965
|
+
text = _first_str(item, "text", "content", "observation", "summary")
|
|
1966
|
+
evidence_ids = _official_locomo_observation_evidence_ids(item)
|
|
1967
|
+
return text or "", evidence_ids
|
|
1968
|
+
if isinstance(item, Sequence) and not isinstance(item, str | bytes):
|
|
1969
|
+
values = tuple(
|
|
1970
|
+
str(value).strip()
|
|
1971
|
+
for value in _flatten_benchmark_scalar_values(item)
|
|
1972
|
+
if str(value).strip()
|
|
1973
|
+
)
|
|
1974
|
+
if len(values) >= 2:
|
|
1975
|
+
return values[0], tuple(_unique(values[1:])[:_MAX_LOCOMO_OBSERVATION_EVIDENCE_IDS])
|
|
1976
|
+
return "", ()
|
|
1977
|
+
|
|
1978
|
+
|
|
1979
|
+
def _official_locomo_observation_evidence_ids(item: Mapping[str, object]) -> tuple[str, ...]:
|
|
1980
|
+
values: list[str] = []
|
|
1981
|
+
for key in (
|
|
1982
|
+
"dia_id",
|
|
1983
|
+
"evidence",
|
|
1984
|
+
"evidence_id",
|
|
1985
|
+
"evidence_ids",
|
|
1986
|
+
"id",
|
|
1987
|
+
"turn_id",
|
|
1988
|
+
"turn_ids",
|
|
1989
|
+
):
|
|
1990
|
+
if key not in item:
|
|
1991
|
+
continue
|
|
1992
|
+
values.extend(
|
|
1993
|
+
str(value).strip()
|
|
1994
|
+
for value in _flatten_benchmark_scalar_values(item.get(key))
|
|
1995
|
+
if str(value).strip()
|
|
1996
|
+
)
|
|
1997
|
+
if values:
|
|
1998
|
+
break
|
|
1999
|
+
return tuple(_unique(values)[:_MAX_LOCOMO_OBSERVATION_EVIDENCE_IDS])
|
|
2000
|
+
|
|
2001
|
+
|
|
2002
|
+
def _flatten_benchmark_scalar_values(value: object) -> tuple[object, ...]:
|
|
2003
|
+
if isinstance(value, Mapping):
|
|
2004
|
+
return ()
|
|
2005
|
+
if isinstance(value, Sequence) and not isinstance(value, str | bytes):
|
|
2006
|
+
flattened: list[object] = []
|
|
2007
|
+
for item in value:
|
|
2008
|
+
flattened.extend(_flatten_benchmark_scalar_values(item))
|
|
2009
|
+
return tuple(flattened)
|
|
2010
|
+
return (value,) if value is not None else ()
|
|
2011
|
+
|
|
2012
|
+
|
|
2013
|
+
def _official_locomo_session_summary_documents(
|
|
2014
|
+
raw: Mapping[str, object],
|
|
2015
|
+
*,
|
|
2016
|
+
sample_id: str,
|
|
2017
|
+
) -> tuple[BenchmarkDocumentInput, ...]:
|
|
2018
|
+
summaries = raw.get("session_summary")
|
|
2019
|
+
if not isinstance(summaries, Mapping):
|
|
2020
|
+
return ()
|
|
2021
|
+
conversation = raw.get("conversation")
|
|
2022
|
+
conversation_map = conversation if isinstance(conversation, Mapping) else {}
|
|
2023
|
+
documents: list[BenchmarkDocumentInput] = []
|
|
2024
|
+
for key in sorted(summaries, key=_session_sort_key):
|
|
2025
|
+
summary = summaries.get(key)
|
|
2026
|
+
if not isinstance(summary, str) or not summary.strip():
|
|
2027
|
+
continue
|
|
2028
|
+
session_key = key.removesuffix("_summary")
|
|
2029
|
+
lines = [f"{session_key} summary"]
|
|
2030
|
+
date_value = conversation_map.get(f"{session_key}_date_time")
|
|
2031
|
+
if isinstance(date_value, str) and date_value.strip():
|
|
2032
|
+
lines.append(f"{session_key} date: {date_value.strip()}")
|
|
2033
|
+
lines.append(summary.strip())
|
|
2034
|
+
documents.append(
|
|
2035
|
+
BenchmarkDocumentInput(
|
|
2036
|
+
title=f"LoCoMo {sample_id} {session_key} summary",
|
|
2037
|
+
text="\n".join(lines),
|
|
2038
|
+
source_type="locomo_session_summary",
|
|
2039
|
+
source_external_id=f"locomo:{sample_id}:{session_key}:summary",
|
|
2040
|
+
)
|
|
2041
|
+
)
|
|
2042
|
+
return tuple(documents)
|
|
2043
|
+
|
|
2044
|
+
|
|
2045
|
+
def _official_locomo_event_summary_documents(
|
|
2046
|
+
raw: Mapping[str, object],
|
|
2047
|
+
*,
|
|
2048
|
+
sample_id: str,
|
|
2049
|
+
) -> tuple[BenchmarkDocumentInput, ...]:
|
|
2050
|
+
summaries = raw.get("event_summary")
|
|
2051
|
+
if not isinstance(summaries, Mapping):
|
|
2052
|
+
return ()
|
|
2053
|
+
turns_by_session = _official_locomo_session_turns(raw)
|
|
2054
|
+
documents: list[BenchmarkDocumentInput] = []
|
|
2055
|
+
for key in sorted(summaries, key=_session_sort_key):
|
|
2056
|
+
value = summaries.get(key)
|
|
2057
|
+
session_key = key.removeprefix("events_")
|
|
2058
|
+
session_turns = turns_by_session.get(session_key, ())
|
|
2059
|
+
lines = [f"{session_key} events"]
|
|
2060
|
+
if isinstance(value, Mapping):
|
|
2061
|
+
date = _first_str(value, "date", "session_date")
|
|
2062
|
+
if date:
|
|
2063
|
+
lines.append(f"date: {date}")
|
|
2064
|
+
for actor, raw_items in value.items():
|
|
2065
|
+
if str(actor).strip().lower() in {"date", "session_date"}:
|
|
2066
|
+
continue
|
|
2067
|
+
actor_name = str(actor).strip() or "speaker"
|
|
2068
|
+
for item in _as_list(raw_items):
|
|
2069
|
+
text = _official_locomo_event_summary_text(item)
|
|
2070
|
+
if text:
|
|
2071
|
+
evidence_ids = _official_locomo_related_event_summary_evidence_ids(
|
|
2072
|
+
text=text,
|
|
2073
|
+
actor_name=actor_name,
|
|
2074
|
+
session_turns=session_turns,
|
|
2075
|
+
)
|
|
2076
|
+
evidence_label = " ".join(evidence_ids)
|
|
2077
|
+
prefix = f"{evidence_label} " if evidence_label else ""
|
|
2078
|
+
lines.append(f"{prefix}{actor_name}: {text}")
|
|
2079
|
+
else:
|
|
2080
|
+
text = _official_locomo_event_summary_text(value)
|
|
2081
|
+
if text:
|
|
2082
|
+
lines.append(text)
|
|
2083
|
+
if len(lines) <= 1:
|
|
2084
|
+
continue
|
|
2085
|
+
documents.append(
|
|
2086
|
+
BenchmarkDocumentInput(
|
|
2087
|
+
title=f"LoCoMo {sample_id} {session_key} events",
|
|
2088
|
+
text="\n".join(lines),
|
|
2089
|
+
source_type="locomo_event_summary",
|
|
2090
|
+
source_external_id=f"locomo:{sample_id}:{session_key}:events",
|
|
2091
|
+
)
|
|
2092
|
+
)
|
|
2093
|
+
return tuple(documents)
|
|
2094
|
+
|
|
2095
|
+
|
|
2096
|
+
def _official_locomo_related_event_summary_evidence_ids(
|
|
2097
|
+
*,
|
|
2098
|
+
text: str,
|
|
2099
|
+
actor_name: str,
|
|
2100
|
+
session_turns: tuple[_OfficialLocomoTurn, ...],
|
|
2101
|
+
) -> tuple[str, ...]:
|
|
2102
|
+
actor_key = actor_name.casefold().strip()
|
|
2103
|
+
ranked: list[tuple[tuple[int, int, int, int], _OfficialLocomoTurn]] = []
|
|
2104
|
+
for turn in session_turns:
|
|
2105
|
+
if actor_key and turn.speaker.casefold().strip() != actor_key:
|
|
2106
|
+
continue
|
|
2107
|
+
relevance = _score_official_locomo_turn_relevance(query=text, turn_text=turn.text)
|
|
2108
|
+
if not _is_related_locomo_observation_turn(relevance):
|
|
2109
|
+
continue
|
|
2110
|
+
ranked.append(
|
|
2111
|
+
(
|
|
2112
|
+
(
|
|
2113
|
+
-relevance.distinctive_term_hits,
|
|
2114
|
+
-relevance.unique_term_hits,
|
|
2115
|
+
-relevance.capped_frequency_hits,
|
|
2116
|
+
turn.turn_index,
|
|
2117
|
+
),
|
|
2118
|
+
turn,
|
|
2119
|
+
)
|
|
2120
|
+
)
|
|
2121
|
+
ranked.sort(key=lambda item: item[0])
|
|
2122
|
+
session_order = {turn.dia_id: turn.turn_index for turn in session_turns}
|
|
2123
|
+
return tuple(
|
|
2124
|
+
sorted(
|
|
2125
|
+
_unique(turn.dia_id for _, turn in ranked[:4]),
|
|
2126
|
+
key=lambda item: session_order.get(item, 10_000),
|
|
2127
|
+
)
|
|
2128
|
+
)
|
|
2129
|
+
|
|
2130
|
+
|
|
2131
|
+
def _score_official_locomo_turn_relevance(
|
|
2132
|
+
*,
|
|
2133
|
+
query: str,
|
|
2134
|
+
turn_text: str,
|
|
2135
|
+
) -> QueryRelevance:
|
|
2136
|
+
text_counts, text_variants = _official_locomo_turn_text_variant_profile(turn_text)
|
|
2137
|
+
return score_query_relevance_against_profile(
|
|
2138
|
+
query=query,
|
|
2139
|
+
text_counts=text_counts,
|
|
2140
|
+
text_variants=text_variants,
|
|
2141
|
+
)
|
|
2142
|
+
|
|
2143
|
+
|
|
2144
|
+
@lru_cache(maxsize=8192)
|
|
2145
|
+
def _official_locomo_turn_text_variant_profile(
|
|
2146
|
+
turn_text: str,
|
|
2147
|
+
) -> tuple[Mapping[str, int], tuple[tuple[str, ...], ...]]:
|
|
2148
|
+
return text_variant_profile(turn_text)
|
|
2149
|
+
|
|
2150
|
+
|
|
2151
|
+
def _official_locomo_event_summary_text(item: object) -> str:
|
|
2152
|
+
if isinstance(item, str) and item.strip():
|
|
2153
|
+
return item.strip()
|
|
2154
|
+
if isinstance(item, Mapping):
|
|
2155
|
+
return _first_str(item, "text", "content", "event", "summary") or ""
|
|
2156
|
+
if isinstance(item, Sequence) and not isinstance(item, str | bytes):
|
|
2157
|
+
return " ".join(str(value).strip() for value in item if str(value).strip())
|
|
2158
|
+
return ""
|
|
2159
|
+
|
|
2160
|
+
|
|
2161
|
+
def _is_session_key(value: object) -> bool:
|
|
2162
|
+
return (
|
|
2163
|
+
isinstance(value, str) and value.startswith("session_") and not value.endswith("_date_time")
|
|
2164
|
+
)
|
|
2165
|
+
|
|
2166
|
+
|
|
2167
|
+
def _session_sort_key(value: object) -> tuple[int, str]:
|
|
2168
|
+
if not isinstance(value, str):
|
|
2169
|
+
return (10**9, "")
|
|
2170
|
+
if _is_session_key(value):
|
|
2171
|
+
try:
|
|
2172
|
+
return (int(value.rsplit("_", 1)[1]), value)
|
|
2173
|
+
except ValueError:
|
|
2174
|
+
return (10**9, value)
|
|
2175
|
+
return (10**9, value)
|
|
2176
|
+
|
|
2177
|
+
|
|
2178
|
+
def _is_official_longmemeval_row(raw: Mapping[str, object]) -> bool:
|
|
2179
|
+
return (
|
|
2180
|
+
isinstance(raw.get("haystack_sessions"), list)
|
|
2181
|
+
and isinstance(raw.get("question"), str)
|
|
2182
|
+
and raw.get("answer") is not None
|
|
2183
|
+
)
|
|
2184
|
+
|
|
2185
|
+
|
|
2186
|
+
def _official_longmemeval_case(raw: Mapping[str, object]) -> PublicBenchmarkCase:
|
|
2187
|
+
question_id = _first_str(raw, "question_id", "id") or _case_hash(raw)
|
|
2188
|
+
question = _first_str(raw, "question", "query")
|
|
2189
|
+
expected_terms = _official_longmemeval_evidence_terms(raw) or _terms(
|
|
2190
|
+
raw,
|
|
2191
|
+
"answer",
|
|
2192
|
+
"expected_answer",
|
|
2193
|
+
"answers",
|
|
2194
|
+
)
|
|
2195
|
+
if not question or not expected_terms:
|
|
2196
|
+
raise BenchmarkValidationError(f"LongMemEval row {question_id} is missing question/answer")
|
|
2197
|
+
return PublicBenchmarkCase(
|
|
2198
|
+
benchmark=LONGMEMEVAL_BENCHMARK_SUITE,
|
|
2199
|
+
case_id=question_id,
|
|
2200
|
+
question=question,
|
|
2201
|
+
expected_terms=expected_terms,
|
|
2202
|
+
documents=_official_longmemeval_documents(raw, question_id=question_id),
|
|
2203
|
+
memory_scope_external_ref=f"longmemeval-{question_id}",
|
|
2204
|
+
thread_external_ref=f"longmemeval-{question_id}",
|
|
2205
|
+
metadata={
|
|
2206
|
+
"source_format": "official_longmemeval",
|
|
2207
|
+
"question_type": raw.get("question_type"),
|
|
2208
|
+
"question_date": raw.get("question_date"),
|
|
2209
|
+
"answer_preview": _preview_value(raw.get("answer")),
|
|
2210
|
+
"answer_session_ids": raw.get("answer_session_ids")
|
|
2211
|
+
if isinstance(raw.get("answer_session_ids"), list)
|
|
2212
|
+
else [],
|
|
2213
|
+
},
|
|
2214
|
+
)
|
|
2215
|
+
|
|
2216
|
+
|
|
2217
|
+
def _official_longmemeval_evidence_terms(raw: Mapping[str, object]) -> tuple[str, ...]:
|
|
2218
|
+
answer_session_ids = tuple(
|
|
2219
|
+
item.strip()
|
|
2220
|
+
for item in _as_list(raw.get("answer_session_ids"))
|
|
2221
|
+
if isinstance(item, str) and item.strip()
|
|
2222
|
+
)
|
|
2223
|
+
if not answer_session_ids:
|
|
2224
|
+
return ()
|
|
2225
|
+
haystack_session_ids = {
|
|
2226
|
+
item.strip()
|
|
2227
|
+
for item in _as_list(raw.get("haystack_session_ids"))
|
|
2228
|
+
if isinstance(item, str) and item.strip()
|
|
2229
|
+
}
|
|
2230
|
+
if not haystack_session_ids:
|
|
2231
|
+
return tuple(_unique(answer_session_ids))
|
|
2232
|
+
return tuple(_unique(item for item in answer_session_ids if item in haystack_session_ids))
|
|
2233
|
+
|
|
2234
|
+
|
|
2235
|
+
def _official_longmemeval_documents(
|
|
2236
|
+
raw: Mapping[str, object],
|
|
2237
|
+
*,
|
|
2238
|
+
question_id: str,
|
|
2239
|
+
) -> tuple[BenchmarkDocumentInput, ...]:
|
|
2240
|
+
sessions = raw.get("haystack_sessions")
|
|
2241
|
+
if not isinstance(sessions, Sequence) or isinstance(sessions, str | bytes):
|
|
2242
|
+
return ()
|
|
2243
|
+
dates = raw.get("haystack_dates")
|
|
2244
|
+
session_ids = raw.get("haystack_session_ids")
|
|
2245
|
+
documents: list[BenchmarkDocumentInput] = []
|
|
2246
|
+
for index, session in enumerate(sessions):
|
|
2247
|
+
session_id = _sequence_str(session_ids, index) or f"session-{index + 1}"
|
|
2248
|
+
date_value = _sequence_str(dates, index)
|
|
2249
|
+
lines = [f"{session_id} date: {date_value}"] if date_value else [session_id]
|
|
2250
|
+
for message in _as_list(session):
|
|
2251
|
+
if isinstance(message, Mapping):
|
|
2252
|
+
role = _first_str(message, "role", "speaker", "author") or "speaker"
|
|
2253
|
+
content = _first_str(message, "content", "text", "message")
|
|
2254
|
+
if content:
|
|
2255
|
+
lines.append(f"{role}: {content}")
|
|
2256
|
+
elif isinstance(message, str) and message.strip():
|
|
2257
|
+
lines.append(message.strip())
|
|
2258
|
+
if len(lines) > 1:
|
|
2259
|
+
documents.append(
|
|
2260
|
+
BenchmarkDocumentInput(
|
|
2261
|
+
title=f"LongMemEval {question_id} {session_id}",
|
|
2262
|
+
text="\n".join(lines),
|
|
2263
|
+
source_type="longmemeval_session",
|
|
2264
|
+
source_external_id=f"longmemeval:{question_id}:{session_id}",
|
|
2265
|
+
)
|
|
2266
|
+
)
|
|
2267
|
+
return tuple(documents)
|
|
2268
|
+
|
|
2269
|
+
|
|
2270
|
+
def _sequence_str(value: object, index: int) -> str | None:
|
|
2271
|
+
if not isinstance(value, Sequence) or isinstance(value, str | bytes):
|
|
2272
|
+
return None
|
|
2273
|
+
if index >= len(value):
|
|
2274
|
+
return None
|
|
2275
|
+
item = value[index]
|
|
2276
|
+
return item.strip() if isinstance(item, str) and item.strip() else None
|
|
2277
|
+
|
|
2278
|
+
|
|
2279
|
+
def _normalize_case(raw: Mapping[str, object]) -> PublicBenchmarkCase:
|
|
2280
|
+
benchmark = _normalize_benchmark_name(
|
|
2281
|
+
_first_str(raw, "benchmark", "source_benchmark", "dataset", "source")
|
|
2282
|
+
)
|
|
2283
|
+
case_id = _first_str(raw, "case_id", "qa_id", "uid", "id", "sample_id") or _case_hash(raw)
|
|
2284
|
+
question = _question(raw)
|
|
2285
|
+
expected_terms = _terms(raw, "expected_terms", "expected", "answer_terms")
|
|
2286
|
+
if not expected_terms:
|
|
2287
|
+
expected_terms = _terms(raw, "answer", "expected_answer", "ground_truth", "gold_answer")
|
|
2288
|
+
forbidden_terms = _terms(raw, "forbidden_terms", "forbidden", "must_not_retrieve")
|
|
2289
|
+
is_abstention_case = _is_normalized_abstention_case(raw)
|
|
2290
|
+
if is_abstention_case:
|
|
2291
|
+
expected_terms = ()
|
|
2292
|
+
memories = _memory_inputs(raw)
|
|
2293
|
+
documents = _document_inputs(raw)
|
|
2294
|
+
if not question:
|
|
2295
|
+
raise BenchmarkValidationError(f"Case {case_id} is missing question")
|
|
2296
|
+
if not expected_terms and not is_abstention_case:
|
|
2297
|
+
raise BenchmarkValidationError(f"Case {case_id} is missing expected_terms or answer")
|
|
2298
|
+
if is_abstention_case and not forbidden_terms:
|
|
2299
|
+
raise BenchmarkValidationError(
|
|
2300
|
+
f"Case {case_id} is missing forbidden_terms for abstention evaluation"
|
|
2301
|
+
)
|
|
2302
|
+
if not memories and not documents:
|
|
2303
|
+
raise BenchmarkValidationError(f"Case {case_id} is missing memories/documents")
|
|
2304
|
+
metadata = dict(_metadata(raw))
|
|
2305
|
+
metadata.setdefault(
|
|
2306
|
+
"answer_preview",
|
|
2307
|
+
_preview_value(
|
|
2308
|
+
_first_present(raw, "answer", "expected_answer", "ground_truth", "gold_answer")
|
|
2309
|
+
),
|
|
2310
|
+
)
|
|
2311
|
+
return PublicBenchmarkCase(
|
|
2312
|
+
benchmark=benchmark,
|
|
2313
|
+
case_id=case_id,
|
|
2314
|
+
question=question,
|
|
2315
|
+
expected_terms=expected_terms,
|
|
2316
|
+
forbidden_terms=forbidden_terms,
|
|
2317
|
+
memories=memories,
|
|
2318
|
+
documents=documents,
|
|
2319
|
+
memory_scope_external_ref=_first_str(
|
|
2320
|
+
raw, "memory_scope_external_ref", "user_id", "memory_scope_id"
|
|
2321
|
+
),
|
|
2322
|
+
thread_external_ref=_first_str(raw, "thread_external_ref", "thread_id", "session_id"),
|
|
2323
|
+
metadata=metadata,
|
|
2324
|
+
)
|
|
2325
|
+
|
|
2326
|
+
|
|
2327
|
+
def _is_normalized_abstention_case(raw: Mapping[str, object]) -> bool:
|
|
2328
|
+
qa = raw.get("qa")
|
|
2329
|
+
return _mapping_requests_abstention(raw) or (
|
|
2330
|
+
isinstance(qa, Mapping) and _mapping_requests_abstention(qa)
|
|
2331
|
+
)
|
|
2332
|
+
|
|
2333
|
+
|
|
2334
|
+
def _mapping_requests_abstention(raw: Mapping[str, object]) -> bool:
|
|
2335
|
+
for key in ("answerable", "is_answerable", "has_answer"):
|
|
2336
|
+
if raw.get(key) is False:
|
|
2337
|
+
return True
|
|
2338
|
+
for key in ("abstention", "no_answer", "unanswerable", "hard_negative"):
|
|
2339
|
+
if raw.get(key) is True:
|
|
2340
|
+
return True
|
|
2341
|
+
return False
|
|
2342
|
+
|
|
2343
|
+
|
|
2344
|
+
def _normalize_benchmark_name(value: object) -> str:
|
|
2345
|
+
if not isinstance(value, str) or not value.strip():
|
|
2346
|
+
raise BenchmarkValidationError("Benchmark name is required")
|
|
2347
|
+
normalized = value.strip().lower().replace("_", "-").replace(" ", "-")
|
|
2348
|
+
if normalized in {"locomo", "lo-co-mo", "long-context-memory"}:
|
|
2349
|
+
return LOCOMO_BENCHMARK_SUITE
|
|
2350
|
+
if normalized in {"longmemeval", "longmem-eval", "long-memory-eval"}:
|
|
2351
|
+
return LONGMEMEVAL_BENCHMARK_SUITE
|
|
2352
|
+
raise BenchmarkValidationError(f"Unsupported benchmark: {value}")
|
|
2353
|
+
|
|
2354
|
+
|
|
2355
|
+
def _question(raw: Mapping[str, object]) -> str:
|
|
2356
|
+
direct = _first_str(raw, "question", "query", "input")
|
|
2357
|
+
if direct:
|
|
2358
|
+
return direct
|
|
2359
|
+
qa = raw.get("qa")
|
|
2360
|
+
if isinstance(qa, Mapping):
|
|
2361
|
+
return _first_str(qa, "question", "query", "input") or ""
|
|
2362
|
+
return ""
|
|
2363
|
+
|
|
2364
|
+
|
|
2365
|
+
def _terms(raw: Mapping[str, object], *keys: str) -> tuple[str, ...]:
|
|
2366
|
+
terms: list[str] = []
|
|
2367
|
+
for key in keys:
|
|
2368
|
+
value = raw.get(key)
|
|
2369
|
+
terms.extend(_term_values(value))
|
|
2370
|
+
if terms:
|
|
2371
|
+
break
|
|
2372
|
+
qa = raw.get("qa")
|
|
2373
|
+
if not terms and isinstance(qa, Mapping):
|
|
2374
|
+
for key in keys:
|
|
2375
|
+
terms.extend(_term_values(qa.get(key)))
|
|
2376
|
+
if terms:
|
|
2377
|
+
break
|
|
2378
|
+
return tuple(_unique(terms))
|
|
2379
|
+
|
|
2380
|
+
|
|
2381
|
+
def _term_values(value: object) -> list[str]:
|
|
2382
|
+
if isinstance(value, str) and value.strip():
|
|
2383
|
+
return [value.strip()]
|
|
2384
|
+
if isinstance(value, int | float | bool):
|
|
2385
|
+
return [str(value)]
|
|
2386
|
+
if isinstance(value, Sequence) and not isinstance(value, str | bytes):
|
|
2387
|
+
return [
|
|
2388
|
+
str(item).strip()
|
|
2389
|
+
for item in _flatten_benchmark_scalar_values(value)
|
|
2390
|
+
if str(item).strip()
|
|
2391
|
+
]
|
|
2392
|
+
return []
|
|
2393
|
+
|
|
2394
|
+
|
|
2395
|
+
def _memory_inputs(raw: Mapping[str, object]) -> tuple[BenchmarkMemoryInput, ...]:
|
|
2396
|
+
values = _first_present(raw, "memories", "facts", "conversation", "messages", "history")
|
|
2397
|
+
entries: list[BenchmarkMemoryInput] = []
|
|
2398
|
+
for index, value in enumerate(_as_list(values)):
|
|
2399
|
+
if isinstance(value, str) and value.strip():
|
|
2400
|
+
entries.append(BenchmarkMemoryInput(text=value.strip()))
|
|
2401
|
+
elif isinstance(value, Mapping):
|
|
2402
|
+
text = _first_str(value, "text", "content", "message", "utterance")
|
|
2403
|
+
speaker = _first_str(value, "speaker", "role", "author")
|
|
2404
|
+
kind = _first_str(value, "kind", "memory_kind") or "note"
|
|
2405
|
+
if text:
|
|
2406
|
+
prefix = f"{speaker}: " if speaker else ""
|
|
2407
|
+
entries.append(BenchmarkMemoryInput(text=f"{prefix}{text}", kind=kind))
|
|
2408
|
+
elif isinstance(value, Sequence) and not isinstance(value, str | bytes):
|
|
2409
|
+
nested = " ".join(str(item).strip() for item in value if str(item).strip())
|
|
2410
|
+
if nested:
|
|
2411
|
+
entries.append(BenchmarkMemoryInput(text=f"session {index}: {nested}"))
|
|
2412
|
+
return tuple(entries)
|
|
2413
|
+
|
|
2414
|
+
|
|
2415
|
+
def _document_inputs(raw: Mapping[str, object]) -> tuple[BenchmarkDocumentInput, ...]:
|
|
2416
|
+
values = _first_present(raw, "documents", "chunks", "passages", "haystack", "context")
|
|
2417
|
+
docs: list[BenchmarkDocumentInput] = []
|
|
2418
|
+
for index, value in enumerate(_as_list(values)):
|
|
2419
|
+
if isinstance(value, str) and value.strip():
|
|
2420
|
+
docs.append(BenchmarkDocumentInput(title=f"benchmark document {index + 1}", text=value))
|
|
2421
|
+
elif isinstance(value, Mapping):
|
|
2422
|
+
text = _first_str(value, "text", "content", "body", "passage")
|
|
2423
|
+
if not text:
|
|
2424
|
+
continue
|
|
2425
|
+
title = (
|
|
2426
|
+
_first_str(value, "title", "name", "session_id")
|
|
2427
|
+
or f"benchmark document {index + 1}"
|
|
2428
|
+
)
|
|
2429
|
+
docs.append(BenchmarkDocumentInput(title=title, text=text))
|
|
2430
|
+
return tuple(docs)
|
|
2431
|
+
|
|
2432
|
+
|
|
2433
|
+
def _first_present(raw: Mapping[str, object], *keys: str) -> object | None:
|
|
2434
|
+
for key in keys:
|
|
2435
|
+
if key in raw:
|
|
2436
|
+
return raw[key]
|
|
2437
|
+
return None
|
|
2438
|
+
|
|
2439
|
+
|
|
2440
|
+
def _first_str(raw: Mapping[str, object], *keys: str) -> str | None:
|
|
2441
|
+
for key in keys:
|
|
2442
|
+
value = raw.get(key)
|
|
2443
|
+
if isinstance(value, str) and value.strip():
|
|
2444
|
+
return value.strip()
|
|
2445
|
+
return None
|
|
2446
|
+
|
|
2447
|
+
|
|
2448
|
+
def _as_list(value: object) -> list[object]:
|
|
2449
|
+
if value is None:
|
|
2450
|
+
return []
|
|
2451
|
+
if isinstance(value, list):
|
|
2452
|
+
return value
|
|
2453
|
+
return [value]
|
|
2454
|
+
|
|
2455
|
+
|
|
2456
|
+
def _metadata(raw: Mapping[str, object]) -> Mapping[str, object]:
|
|
2457
|
+
value = raw.get("metadata")
|
|
2458
|
+
return value if isinstance(value, Mapping) else {}
|
|
2459
|
+
|
|
2460
|
+
|
|
2461
|
+
def _case_capability(case: PublicBenchmarkCase) -> str:
|
|
2462
|
+
if case.benchmark == LONGMEMEVAL_BENCHMARK_SUITE:
|
|
2463
|
+
question_type = case.metadata.get("question_type")
|
|
2464
|
+
return _longmemeval_capability(question_type)
|
|
2465
|
+
if case.benchmark == LOCOMO_BENCHMARK_SUITE:
|
|
2466
|
+
return _locomo_capability(case.metadata.get("category"))
|
|
2467
|
+
value = case.metadata.get("capability")
|
|
2468
|
+
return _safe_metric_key(value) if isinstance(value, str) else ""
|
|
2469
|
+
|
|
2470
|
+
|
|
2471
|
+
def _longmemeval_capability(value: object) -> str:
|
|
2472
|
+
normalized = _safe_metric_key(value)
|
|
2473
|
+
if not normalized:
|
|
2474
|
+
return ""
|
|
2475
|
+
return {
|
|
2476
|
+
"single_session_user": "information_extraction",
|
|
2477
|
+
"single_session_assistant": "information_extraction",
|
|
2478
|
+
"single_session_preference": "information_extraction",
|
|
2479
|
+
"multi_session": "multi_session_reasoning",
|
|
2480
|
+
"temporal_reasoning": "temporal_reasoning",
|
|
2481
|
+
"knowledge_update": "knowledge_update",
|
|
2482
|
+
}.get(normalized, normalized)
|
|
2483
|
+
|
|
2484
|
+
|
|
2485
|
+
def _locomo_capability(value: object) -> str:
|
|
2486
|
+
if isinstance(value, int):
|
|
2487
|
+
return f"locomo_category_{value}"
|
|
2488
|
+
if isinstance(value, float) and value.is_integer():
|
|
2489
|
+
return f"locomo_category_{int(value)}"
|
|
2490
|
+
normalized = _safe_metric_key(value)
|
|
2491
|
+
return f"locomo_{normalized}" if normalized else ""
|
|
2492
|
+
|
|
2493
|
+
|
|
2494
|
+
def _safe_metric_key(value: object) -> str:
|
|
2495
|
+
if not isinstance(value, str) or not value.strip():
|
|
2496
|
+
return ""
|
|
2497
|
+
normalized = value.strip().casefold().replace("-", "_").replace(" ", "_")
|
|
2498
|
+
return "".join(char for char in normalized if char.isalnum() or char == "_").strip("_")
|
|
2499
|
+
|
|
2500
|
+
|
|
2501
|
+
def _ratio(numerator: int, denominator: int) -> float:
|
|
2502
|
+
if denominator <= 0:
|
|
2503
|
+
return 0.0
|
|
2504
|
+
return round(numerator / denominator, 4)
|
|
2505
|
+
|
|
2506
|
+
|
|
2507
|
+
def _dataset_hash(dataset_path: Path) -> str:
|
|
2508
|
+
return hashlib.sha256(dataset_path.read_bytes()).hexdigest()
|
|
2509
|
+
|
|
2510
|
+
|
|
2511
|
+
def _short_hash(value: str) -> str:
|
|
2512
|
+
return hashlib.sha256(value.encode("utf-8")).hexdigest()[:16]
|
|
2513
|
+
|
|
2514
|
+
|
|
2515
|
+
def _case_hash(raw: Mapping[str, object]) -> str:
|
|
2516
|
+
encoded = json.dumps(raw, sort_keys=True, default=str)
|
|
2517
|
+
return hashlib.sha256(encoded.encode("utf-8")).hexdigest()[:16]
|
|
2518
|
+
|
|
2519
|
+
|
|
2520
|
+
def _normalize_text(value: str) -> str:
|
|
2521
|
+
return " ".join(value.casefold().split())
|
|
2522
|
+
|
|
2523
|
+
|
|
2524
|
+
def _unique(values: Iterable[str]) -> list[str]:
|
|
2525
|
+
seen: set[str] = set()
|
|
2526
|
+
unique_values: list[str] = []
|
|
2527
|
+
for value in values:
|
|
2528
|
+
normalized = _normalize_text(value)
|
|
2529
|
+
if normalized and normalized not in seen:
|
|
2530
|
+
seen.add(normalized)
|
|
2531
|
+
unique_values.append(value.strip())
|
|
2532
|
+
return unique_values
|