dhee 7.2.7__tar.gz → 7.3.0__tar.gz
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.
- {dhee-7.2.7/dhee.egg-info → dhee-7.3.0}/PKG-INFO +1 -1
- {dhee-7.2.7 → dhee-7.3.0}/dhee/__init__.py +1 -1
- dhee-7.3.0/dhee/corpus/__init__.py +72 -0
- dhee-7.3.0/dhee/corpus/extract.py +352 -0
- dhee-7.3.0/dhee/corpus/index.py +344 -0
- dhee-7.3.0/dhee/corpus/search.py +229 -0
- dhee-7.3.0/dhee/corpus/store.py +666 -0
- dhee-7.3.0/dhee/corpus/tree.py +376 -0
- dhee-7.3.0/dhee/embeddings/openrouter.py +168 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/reranker.py +72 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/utils/factory.py +4 -0
- {dhee-7.2.7 → dhee-7.3.0/dhee.egg-info}/PKG-INFO +1 -1
- {dhee-7.2.7 → dhee-7.3.0}/dhee.egg-info/SOURCES.txt +9 -0
- {dhee-7.2.7 → dhee-7.3.0}/pyproject.toml +1 -1
- dhee-7.3.0/tests/test_corpus_index.py +333 -0
- dhee-7.3.0/tests/test_corpus_tree.py +238 -0
- {dhee-7.2.7 → dhee-7.3.0}/LICENSE +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/MANIFEST.in +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/README.md +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/adapters/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/adapters/gstack.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/adapters/gstack_parser.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/auth.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/client.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/events.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/models.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/policy.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/run.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/server.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/agent_runtime/tools.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/arc_agi.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/decades.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/hippocamp.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/longmemeval.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/phase0_context_audit.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/portability.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/raw_extractors.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/replay_corpus.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/benchmarks/router_replay.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/checkpoint_runtime.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/cli.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/cli_config.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/cli_mcp.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/cli_onboard.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/cli_pretty.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/cli_setup.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/cli_update.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/configs/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/configs/active.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/configs/base.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/configs/presets.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/context_ir.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/context_kernel.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/context_state.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/contract_runtime.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/contract_supervisor.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/alaya.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/answer_orchestration.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/artifacts.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/belief.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/buddhi.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/category.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/codex_artifacts.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/codex_stream.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/cognition.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/cognition_kernel.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/conflict.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/context_bootstrap.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/contrastive.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/decay.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/distillation.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/echo.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/engram.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/engram_consolidator.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/engram_extractor.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/engram_tiering.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/engram_verification.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/enrichment.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/episode.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/episodic_index.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/evolution.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/file_baseline.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/file_read_tracker.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/forgetting.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/fusion.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/graph.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/handoff_snapshot.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/heuristic.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/intent.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/intention.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/kernel.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/learnings.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/live_context.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/log_parser.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/meta_buddhi.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/pattern_detector.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/policy.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/profile.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/provenance.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/resolvers.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/retrieval.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/samskara.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/scene.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/session_tracker.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/shared_tasks.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/strategy.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/task_state.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/thread_state.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/traces.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/trigger.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/viveka.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/workspace_line.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/core/workspace_line_bus.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/db/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/db/sqlite.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/db/sqlite_analytics.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/db/sqlite_artifacts.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/db/sqlite_common.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/db/sqlite_domains.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/debugger_api.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/demo.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/doctor.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/base.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/gemini.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/nvidia.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/ollama.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/openai.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/qwen.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/embeddings/simple.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/exceptions.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/fs/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/fs/mounts.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/fs/types.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/fs/uri.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/fs/workspace.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/graph_projection_hardening.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/graph_proof_bundle.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/harness/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/harness/base.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/harness/claude_code.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/harness/codex.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/harness/install.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/__main__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/assembler.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/chunker.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/ingest.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/install.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/migrate.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/privacy.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/renderer.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/claude_code/signal.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/hooks/scene_world.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/incremental_context.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/install_cleanup.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/integrations/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/integrations/context_sources.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/integrations/hermes.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/integrations/hermes_provider/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/integrations/hermes_provider/provider.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/anthropic.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/base.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/dhee.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/gemini.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/mock.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/nvidia.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/ollama.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/openai.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/llms/teacher_logger.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/mcp_registry.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/mcp_server.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/mcp_slim.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/admission.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/base.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/core.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/cost.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/episodic.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/main.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/narrative_scene.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/orchestration.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/parallel.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/projects.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/quality.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/retrieval_helpers.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/scene_profile.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/scoping.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/search_pipeline.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/smart.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/tasks.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/utils.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/vectors.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/memory/write_pipeline.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/mini/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/mini/karma_evaluator.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/mini/progressive_trainer.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/mini/replay_gate.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/observability.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/patch_families.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/plugin.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/profiles/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/profiles/elevenlabs.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/profiles/gemini.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/profiles/openai.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/protocol/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/protocol/v1.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/provider_defaults.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/providers/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/providers/base.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/providers/elevenlabs.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/providers/gemini.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/providers/openai.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/release_hygiene.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/replay_branching.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/repo_intelligence.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/repo_link.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/agent_digest.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/bash_digest.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/bootstrap.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/critical_surface.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/digest.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/edit_ledger.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/grep_digest.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/handlers.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/install.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/intent.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/policy.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/pre_tool_gate.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/ptr_store.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/quality_report.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/stats.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/router/tune.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/runtime.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/runtime_daemon.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/runtime_io.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/schemas/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/schemas/narrative.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/schemas/scene_card.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/secret_store.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/simple.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/discovery.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/executor.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/hashing.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/miner.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/outcomes.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/schema.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/store.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/structure.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/skills/trajectory.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/task_contracts.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/temporal_fact_integration.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/temporal_fact_ledger.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/temporal_scenes.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/training/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/training/data_formatter.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/training/karma.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/training/nididhyasana.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/training/smrti.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/training/train.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/cli.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/server.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/dist/assets/CanvasView-Cl1HxIK0.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/dist/assets/index-BboZhdsv.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/dist/assets/index-CIsLOp4u.css +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/dist/dhee-logo.png +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/dist/index.html +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/index.html +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/package-lock.json +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/package.json +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/public/dhee-logo.png +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/App.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/App.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/api.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/api.ts +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/AssetDrawer.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/AssetDrawer.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ChatMessage.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ChatMessage.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/FirstRunPanel.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/FirstRunPanel.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/LinePanel.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/LinePanel.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/NavRail.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/NavRail.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/OrgDrawer.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/OrgDrawer.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/TopBar.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/TopBar.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/TweaksPanel.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/TweaksPanel.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/WorkspaceManagerModal.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/WorkspaceManagerModal.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/CanvasControls.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/CanvasControls.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/CanvasSkeleton.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/CanvasSkeleton.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/DirectionHints.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/DirectionHints.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/Minimap.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/Minimap.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/NodeCard.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/NodeCard.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/forceLayout.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/forceLayout.ts +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/layout.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/layout.ts +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/treeLayout.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/treeLayout.ts +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/useInfiniteCanvas.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/canvas/useInfiniteCanvas.ts +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/cards/Cards.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/cards/Cards.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/graph/CanvasNodeCard.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/graph/CanvasNodeCard.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/DecayBar.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/DecayBar.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/Markdown.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/Markdown.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/SectionHeader.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/SectionHeader.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/StatPill.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/StatPill.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/TierBadge.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/components/ui/TierBadge.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/main.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/main.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/styles.css +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/types.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/types.ts +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/CanvasView.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/CanvasView.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ChannelView.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ChannelView.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ConflictView.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ConflictView.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ContextView.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ContextView.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/EvolutionView.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/EvolutionView.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/LaunchView.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/LaunchView.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/MemoryView.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/MemoryView.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/NotepadView.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/NotepadView.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/OrgCanvas.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/OrgCanvas.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ProductViews.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/ProductViews.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/RouterView.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/RouterView.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/TasksView.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/TasksView.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/WorkspaceView.js +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/src/views/WorkspaceView.tsx +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/tsconfig.json +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/ui/web/vite.config.ts +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/update_capsules.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/utils/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/utils/math.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/utils/prompts.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/utils/repo_identity.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/vector_stores/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/vector_stores/base.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/vector_stores/memory.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/vector_stores/sqlite_vec.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/vector_stores/zvec_store.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/verification_runner.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/verifier_engine.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/webhooks/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/webhooks/elevenlabs.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/capture_store.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/causal_graph.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/encoder.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/gem_extractor.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/predictor.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/schema.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/service.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/session_graph.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee/world_memory/store.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee.egg-info/dependency_links.txt +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee.egg-info/entry_points.txt +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee.egg-info/requires.txt +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee.egg-info/top_level.txt +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee_shared/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/dhee_shared/model_paths.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/engram/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/engram-bus/engram_bus/__init__.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/engram-bus/engram_bus/bus.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/engram-bus/engram_bus/pubsub.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/engram-bus/engram_bus/server.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/engram-bus/engram_bus/store.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/engram-bus/engram_bus/workspace.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/setup.cfg +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_accel.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_accel_benchmark.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_agent_digest.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_agent_provider_examples.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_agent_runtime_before.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_agent_runtime_http_auth.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_agent_runtime_lifecycle.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_agent_runtime_tool.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_agent_runtime_voice_policy.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_artifacts.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_auto_lifecycle.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_backward_compat.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_batch.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_belief_debugger.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_causal_scene_memory.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_claude_code_hooks.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_cli_config.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_cli_harness_commands.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_cli_onboard_update.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_cli_pretty.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_codex_stream.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_cognition_evals.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_cognition_kernel.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_cognition_v3.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_context_state.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_contract_runtime_recovery.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_core_memory.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_cosine_similarity.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_data_dir_paths.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_decades_eval.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_dedup.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_deferred_enrichment.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_demo.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_dhee_init.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_dhee_model_paths.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_dheefs.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_distillation.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_e2e_all_features.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_elevenlabs_cli.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_elevenlabs_correction_memory.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_elevenlabs_no_secret_storage.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_elevenlabs_profile.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_elevenlabs_two_call_memory.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_elevenlabs_webhook.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_engram_tiering.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_engram_tiering_promotion.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_evolution_meta_loop.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_evolution_session_gate.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_forgetting.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_gemini_provider.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_graph_proof_bundle.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_gstack_adapter.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_handoff_snapshot.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_harness_adapters.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_harness_install.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_hashing.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_hermes_provider.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_hippocamp_benchmark.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_incremental_context.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_install_cleanup.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_intent.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_karma_evaluator.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_learnings.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_locomo_plus_runner.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_log_parser.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_longmemeval_config.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_mcp_artifact_tools.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_mcp_tools_slim.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_memory_admission.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_memory_os_service.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_memory_quality_contract.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_memory_types.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_meta_buddhi_group_relative.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_migration.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_miner.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_narrative_scene_intelligence.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_nvidia_embedder.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_nvidia_llm.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_openai_provider.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_openclaw.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_orchestration_core.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_oss_61_regressions.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_packaging.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_parallel.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_patch_families.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_plugin_learnings.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_portability_eval.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_power_packages.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_presets.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_profile.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_progressive_trainer.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_projects.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_protocol_v1.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_provider_runtime_profiles.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_query_cache.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_release_hygiene.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_replay_branching.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_replay_corpus_export.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_replay_gate.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_repo_intelligence.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_repo_link.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_reranker_defaults.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_router.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_router_replay.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_router_stats_agents.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_runtime.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_runtime_hardening.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_scene.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_scene_maintenance.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_scene_world_hook.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_secret_store.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_session_tracker.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_shared_tasks.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_simple_zero_config.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_skills.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_smart_memory.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_sqlite_connection_pool.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_sqlite_vec.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_structural.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_structured_resolution.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_task_contracts.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_tasks.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_temporal_fact_integration.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_temporal_fact_ledger.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_temporal_scenes.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_thread_state.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_traces.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_trajectory.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_ui.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_unified_enrichment.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_update_capsules.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_vector_store_factory.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_verification_runner.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_verifier_engine.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_workspace_line_agent_emit.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_workspace_line_bus.py +0 -0
- {dhee-7.2.7 → dhee-7.3.0}/tests/test_zvec_store.py +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Dhee's document corpus — a third lane, beside beliefs and world memory.
|
|
2
|
+
|
|
3
|
+
Belief memory answers "what is true about the user and the work". A corpus answers
|
|
4
|
+
"what does this folder of documents say", and the two must not share a store: Dhee's
|
|
5
|
+
quality pipeline deliberately treats document chunks as noise so that a thousand
|
|
6
|
+
fragments of a PDF never drown a handful of real beliefs. This package is where those
|
|
7
|
+
fragments are supposed to live instead.
|
|
8
|
+
|
|
9
|
+
Typical use::
|
|
10
|
+
|
|
11
|
+
from dhee.corpus import CorpusIndex, CorpusSearch, CorpusStore
|
|
12
|
+
|
|
13
|
+
index = CorpusIndex(store=store, vector_store=vectors, embedder=embedder,
|
|
14
|
+
extractor=my_ocr_extractor)
|
|
15
|
+
corpus_id = index.attach("~/Documents/Cases")
|
|
16
|
+
index.sync(corpus_id)
|
|
17
|
+
|
|
18
|
+
hits = CorpusSearch(store=store, vector_store=vectors, embedder=embedder,
|
|
19
|
+
reranker=reranker).search(corpus_id, "what is the penalty clause?")
|
|
20
|
+
|
|
21
|
+
Extraction is injected, never imported: OCR belongs to the host application, so Dhee
|
|
22
|
+
still installs and runs on its own.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from dhee.corpus.extract import (
|
|
26
|
+
Chunk,
|
|
27
|
+
ExtractedDoc,
|
|
28
|
+
ExtractedLine,
|
|
29
|
+
ExtractedPage,
|
|
30
|
+
ExtractorChain,
|
|
31
|
+
PlainTextExtractor,
|
|
32
|
+
TextExtractor,
|
|
33
|
+
chunk_document,
|
|
34
|
+
)
|
|
35
|
+
from dhee.corpus.index import CorpusIndex, SyncProgress, SyncReport
|
|
36
|
+
from dhee.corpus.search import Citation, CorpusSearch, SearchHit, SearchResult
|
|
37
|
+
from dhee.corpus.store import ChunkRecord, CorpusRecord, CorpusStore
|
|
38
|
+
from dhee.corpus.tree import (
|
|
39
|
+
BlobEntry,
|
|
40
|
+
FolderDiff,
|
|
41
|
+
FolderSnapshot,
|
|
42
|
+
changed_subtrees,
|
|
43
|
+
diff,
|
|
44
|
+
scan,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
__all__ = [
|
|
48
|
+
"BlobEntry",
|
|
49
|
+
"Chunk",
|
|
50
|
+
"ChunkRecord",
|
|
51
|
+
"Citation",
|
|
52
|
+
"CorpusIndex",
|
|
53
|
+
"CorpusRecord",
|
|
54
|
+
"CorpusSearch",
|
|
55
|
+
"CorpusStore",
|
|
56
|
+
"ExtractedDoc",
|
|
57
|
+
"ExtractedLine",
|
|
58
|
+
"ExtractedPage",
|
|
59
|
+
"ExtractorChain",
|
|
60
|
+
"FolderDiff",
|
|
61
|
+
"FolderSnapshot",
|
|
62
|
+
"PlainTextExtractor",
|
|
63
|
+
"SearchHit",
|
|
64
|
+
"SearchResult",
|
|
65
|
+
"SyncProgress",
|
|
66
|
+
"SyncReport",
|
|
67
|
+
"TextExtractor",
|
|
68
|
+
"changed_subtrees",
|
|
69
|
+
"chunk_document",
|
|
70
|
+
"diff",
|
|
71
|
+
"scan",
|
|
72
|
+
]
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
"""Turning a file into pages of text, and pages of text into citable chunks.
|
|
2
|
+
|
|
3
|
+
Two responsibilities that stay deliberately separate:
|
|
4
|
+
|
|
5
|
+
**Extraction** is pluggable and lives outside Dhee. Dhee ships a default that reads
|
|
6
|
+
plain text and PDFs that already carry a text layer — enough to be useful alone, which
|
|
7
|
+
is the standing rule for this package. Anything harder (a scan, a photograph of a page,
|
|
8
|
+
a Devanagari order) needs OCR, and OCR is a 220MB bundle that belongs to the host
|
|
9
|
+
application, not to a library. So `TextExtractor` is a Protocol the host implements and
|
|
10
|
+
injects. Dhee never imports the host.
|
|
11
|
+
|
|
12
|
+
**Chunking** is Dhee's, and it is page-aware on purpose. A retrieval hit is only worth
|
|
13
|
+
as much as the citation attached to it: "this is in the folder somewhere" is not an
|
|
14
|
+
answer, "page 4 of the March order" is. So chunks never straddle a page boundary, and
|
|
15
|
+
every chunk carries the page it came from all the way through to the answer.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import hashlib
|
|
21
|
+
import re
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any, Dict, List, Optional, Protocol, Sequence, Tuple, runtime_checkable
|
|
25
|
+
|
|
26
|
+
# Roughly 300 tokens. Small enough that a hit points at a paragraph rather than a page,
|
|
27
|
+
# large enough that a clause keeps the sentences that qualify it.
|
|
28
|
+
DEFAULT_CHUNK_CHARS = 1200
|
|
29
|
+
# One or two sentences of carry-over, so a clause split across a boundary is still
|
|
30
|
+
# retrievable from either side.
|
|
31
|
+
DEFAULT_CHUNK_OVERLAP_CHARS = 160
|
|
32
|
+
MIN_CHUNK_CHARS = 60
|
|
33
|
+
|
|
34
|
+
EXTRACTION_OK = "extracted"
|
|
35
|
+
EXTRACTION_EMPTY = "empty"
|
|
36
|
+
EXTRACTION_UNSUPPORTED = "unsupported"
|
|
37
|
+
EXTRACTION_FAILED = "failed"
|
|
38
|
+
|
|
39
|
+
_PARAGRAPH_BREAK = re.compile(r"\n\s*\n")
|
|
40
|
+
_SENTENCE_END = re.compile(r"(?<=[.!?।])\s+")
|
|
41
|
+
_WHITESPACE = re.compile(r"[ \t]+")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True, slots=True)
|
|
45
|
+
class ExtractedLine:
|
|
46
|
+
"""One recognised line, and where it sat on the page.
|
|
47
|
+
|
|
48
|
+
The box is optional because a text-layer PDF has no geometry worth keeping, but an
|
|
49
|
+
OCR engine does — and a citation into a scan should be able to highlight the region
|
|
50
|
+
it came from rather than only name the page.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
text: str
|
|
54
|
+
confidence: float = 1.0
|
|
55
|
+
box: Optional[Tuple[float, float, float, float]] = None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(frozen=True, slots=True)
|
|
59
|
+
class ExtractedPage:
|
|
60
|
+
page_no: int
|
|
61
|
+
text: str
|
|
62
|
+
lines: Tuple[ExtractedLine, ...] = ()
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def is_empty(self) -> bool:
|
|
66
|
+
return not self.text.strip()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass(frozen=True, slots=True)
|
|
70
|
+
class ExtractedDoc:
|
|
71
|
+
"""Everything one file yielded, plus an honest account of how it went."""
|
|
72
|
+
|
|
73
|
+
pages: Tuple[ExtractedPage, ...] = ()
|
|
74
|
+
engine: str = ""
|
|
75
|
+
status: str = EXTRACTION_OK
|
|
76
|
+
error: str = ""
|
|
77
|
+
was_scanned: bool = False
|
|
78
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def page_count(self) -> int:
|
|
82
|
+
return len(self.pages)
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def text(self) -> str:
|
|
86
|
+
return "\n\n".join(page.text for page in self.pages if page.text.strip())
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def is_usable(self) -> bool:
|
|
90
|
+
return self.status == EXTRACTION_OK and bool(self.text.strip())
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
def failure(cls, reason: str, *, engine: str = "", status: str = EXTRACTION_FAILED) -> "ExtractedDoc":
|
|
94
|
+
return cls(engine=engine, status=status, error=reason)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@runtime_checkable
|
|
98
|
+
class TextExtractor(Protocol):
|
|
99
|
+
"""What the host plugs in. One method to decide, one to do."""
|
|
100
|
+
|
|
101
|
+
def supports(self, path: Path) -> bool:
|
|
102
|
+
...
|
|
103
|
+
|
|
104
|
+
def extract(self, path: Path) -> ExtractedDoc:
|
|
105
|
+
...
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass(frozen=True, slots=True)
|
|
109
|
+
class Chunk:
|
|
110
|
+
"""A retrievable span, addressed by the content it came from.
|
|
111
|
+
|
|
112
|
+
``chunk_id`` is derived from the blob hash and the chunk's own text, so the same
|
|
113
|
+
passage in two copies of the same document is one chunk with one embedding, and a
|
|
114
|
+
chunk's identity never depends on the path it was found at.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
chunk_id: str
|
|
118
|
+
blob_sha: str
|
|
119
|
+
chunk_index: int
|
|
120
|
+
page_no: int
|
|
121
|
+
text: str
|
|
122
|
+
char_start: int
|
|
123
|
+
char_end: int
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def char_count(self) -> int:
|
|
127
|
+
return len(self.text)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def chunk_id_for(blob_sha: str, chunk_index: int, text: str) -> str:
|
|
131
|
+
digest = hashlib.sha256()
|
|
132
|
+
digest.update(blob_sha.encode("ascii"))
|
|
133
|
+
digest.update(b"\0")
|
|
134
|
+
digest.update(str(chunk_index).encode("ascii"))
|
|
135
|
+
digest.update(b"\0")
|
|
136
|
+
digest.update(text.encode("utf-8"))
|
|
137
|
+
return digest.hexdigest()
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def normalise_text(value: str) -> str:
|
|
141
|
+
"""Collapse the noise OCR and PDF layers add, keeping paragraph structure.
|
|
142
|
+
|
|
143
|
+
Runs of spaces become one space and trailing space is dropped, because they change
|
|
144
|
+
a chunk's hash without changing its meaning — and a chunk whose hash moves for no
|
|
145
|
+
reason is a chunk that gets re-embedded for no reason.
|
|
146
|
+
"""
|
|
147
|
+
lines = [_WHITESPACE.sub(" ", line).strip() for line in value.splitlines()]
|
|
148
|
+
collapsed: List[str] = []
|
|
149
|
+
blank_run = 0
|
|
150
|
+
for line in lines:
|
|
151
|
+
if line:
|
|
152
|
+
blank_run = 0
|
|
153
|
+
collapsed.append(line)
|
|
154
|
+
else:
|
|
155
|
+
blank_run += 1
|
|
156
|
+
if blank_run == 1:
|
|
157
|
+
collapsed.append("")
|
|
158
|
+
return "\n".join(collapsed).strip()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def chunk_page(
|
|
162
|
+
*,
|
|
163
|
+
blob_sha: str,
|
|
164
|
+
page: ExtractedPage,
|
|
165
|
+
start_index: int,
|
|
166
|
+
max_chars: int = DEFAULT_CHUNK_CHARS,
|
|
167
|
+
overlap_chars: int = DEFAULT_CHUNK_OVERLAP_CHARS,
|
|
168
|
+
) -> List[Chunk]:
|
|
169
|
+
"""Split one page, preferring to break where the author already broke.
|
|
170
|
+
|
|
171
|
+
Paragraph boundaries first, then sentence boundaries, then a hard cut. Breaking
|
|
172
|
+
mid-sentence is the worst outcome for retrieval quality, so it is what we fall back
|
|
173
|
+
to rather than what we do.
|
|
174
|
+
"""
|
|
175
|
+
text = normalise_text(page.text)
|
|
176
|
+
if len(text) < MIN_CHUNK_CHARS:
|
|
177
|
+
return []
|
|
178
|
+
|
|
179
|
+
spans = _split_spans(text, max_chars=max_chars, overlap_chars=overlap_chars)
|
|
180
|
+
chunks: List[Chunk] = []
|
|
181
|
+
for offset, (start, end) in enumerate(spans):
|
|
182
|
+
body = text[start:end].strip()
|
|
183
|
+
if len(body) < MIN_CHUNK_CHARS:
|
|
184
|
+
continue
|
|
185
|
+
index = start_index + len(chunks)
|
|
186
|
+
chunks.append(
|
|
187
|
+
Chunk(
|
|
188
|
+
chunk_id=chunk_id_for(blob_sha, index, body),
|
|
189
|
+
blob_sha=blob_sha,
|
|
190
|
+
chunk_index=index,
|
|
191
|
+
page_no=page.page_no,
|
|
192
|
+
text=body,
|
|
193
|
+
char_start=start,
|
|
194
|
+
char_end=end,
|
|
195
|
+
)
|
|
196
|
+
)
|
|
197
|
+
return chunks
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def chunk_document(
|
|
201
|
+
*,
|
|
202
|
+
blob_sha: str,
|
|
203
|
+
doc: ExtractedDoc,
|
|
204
|
+
max_chars: int = DEFAULT_CHUNK_CHARS,
|
|
205
|
+
overlap_chars: int = DEFAULT_CHUNK_OVERLAP_CHARS,
|
|
206
|
+
) -> List[Chunk]:
|
|
207
|
+
chunks: List[Chunk] = []
|
|
208
|
+
for page in doc.pages:
|
|
209
|
+
chunks.extend(
|
|
210
|
+
chunk_page(
|
|
211
|
+
blob_sha=blob_sha,
|
|
212
|
+
page=page,
|
|
213
|
+
start_index=len(chunks),
|
|
214
|
+
max_chars=max_chars,
|
|
215
|
+
overlap_chars=overlap_chars,
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
return chunks
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _split_spans(text: str, *, max_chars: int, overlap_chars: int) -> List[Tuple[int, int]]:
|
|
222
|
+
if len(text) <= max_chars:
|
|
223
|
+
return [(0, len(text))]
|
|
224
|
+
|
|
225
|
+
boundaries = _candidate_boundaries(text)
|
|
226
|
+
spans: List[Tuple[int, int]] = []
|
|
227
|
+
start = 0
|
|
228
|
+
length = len(text)
|
|
229
|
+
|
|
230
|
+
while start < length:
|
|
231
|
+
hard_end = min(start + max_chars, length)
|
|
232
|
+
if hard_end >= length:
|
|
233
|
+
spans.append((start, length))
|
|
234
|
+
break
|
|
235
|
+
|
|
236
|
+
# The last boundary that fits, provided it leaves a chunk worth having.
|
|
237
|
+
cut = max(
|
|
238
|
+
(pos for pos in boundaries if start + MIN_CHUNK_CHARS < pos <= hard_end),
|
|
239
|
+
default=hard_end,
|
|
240
|
+
)
|
|
241
|
+
spans.append((start, cut))
|
|
242
|
+
next_start = cut - overlap_chars
|
|
243
|
+
start = cut if next_start <= start else next_start
|
|
244
|
+
|
|
245
|
+
return spans
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _candidate_boundaries(text: str) -> List[int]:
|
|
249
|
+
"""Offsets it is safe to break at, best first — paragraphs, then sentences."""
|
|
250
|
+
positions = {match.end() for match in _PARAGRAPH_BREAK.finditer(text)}
|
|
251
|
+
positions.update(match.end() for match in _SENTENCE_END.finditer(text))
|
|
252
|
+
positions.add(len(text))
|
|
253
|
+
return sorted(positions)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class PlainTextExtractor:
|
|
257
|
+
"""Dhee's built-in floor: text files, and PDFs that already carry a text layer.
|
|
258
|
+
|
|
259
|
+
This exists so the corpus works with no host and no bundle at all. It deliberately
|
|
260
|
+
does not attempt OCR — a scanned page returns ``EXTRACTION_EMPTY`` with a reason
|
|
261
|
+
rather than silently indexing nothing, so the caller can see that a real extractor
|
|
262
|
+
is needed rather than wonder why the document is unsearchable.
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
TEXT_SUFFIXES = frozenset({".txt", ".md", ".markdown", ".rst", ".csv", ".tsv", ".json", ".yaml", ".yml", ".log"})
|
|
266
|
+
PDF_SUFFIXES = frozenset({".pdf"})
|
|
267
|
+
|
|
268
|
+
name = "plain_text"
|
|
269
|
+
|
|
270
|
+
def supports(self, path: Path) -> bool:
|
|
271
|
+
suffix = path.suffix.lower()
|
|
272
|
+
return suffix in self.TEXT_SUFFIXES or suffix in self.PDF_SUFFIXES
|
|
273
|
+
|
|
274
|
+
def extract(self, path: Path) -> ExtractedDoc:
|
|
275
|
+
suffix = path.suffix.lower()
|
|
276
|
+
if suffix in self.TEXT_SUFFIXES:
|
|
277
|
+
return self._extract_text(path)
|
|
278
|
+
if suffix in self.PDF_SUFFIXES:
|
|
279
|
+
return self._extract_pdf(path)
|
|
280
|
+
return ExtractedDoc.failure(f"unsupported suffix {suffix}", engine=self.name, status=EXTRACTION_UNSUPPORTED)
|
|
281
|
+
|
|
282
|
+
def _extract_text(self, path: Path) -> ExtractedDoc:
|
|
283
|
+
try:
|
|
284
|
+
raw = path.read_text(encoding="utf-8", errors="replace")
|
|
285
|
+
except OSError as exc:
|
|
286
|
+
return ExtractedDoc.failure(str(exc), engine=self.name)
|
|
287
|
+
body = normalise_text(raw)
|
|
288
|
+
if not body:
|
|
289
|
+
return ExtractedDoc(engine=self.name, status=EXTRACTION_EMPTY, error="file is empty")
|
|
290
|
+
return ExtractedDoc(pages=(ExtractedPage(page_no=1, text=body),), engine=self.name)
|
|
291
|
+
|
|
292
|
+
def _extract_pdf(self, path: Path) -> ExtractedDoc:
|
|
293
|
+
try:
|
|
294
|
+
from pypdf import PdfReader
|
|
295
|
+
except ImportError:
|
|
296
|
+
return ExtractedDoc.failure(
|
|
297
|
+
"pypdf is not installed, so PDF text extraction is unavailable",
|
|
298
|
+
engine=self.name,
|
|
299
|
+
status=EXTRACTION_UNSUPPORTED,
|
|
300
|
+
)
|
|
301
|
+
try:
|
|
302
|
+
reader = PdfReader(str(path))
|
|
303
|
+
pages: List[ExtractedPage] = []
|
|
304
|
+
for number, page in enumerate(reader.pages, start=1):
|
|
305
|
+
body = normalise_text(page.extract_text() or "")
|
|
306
|
+
if body:
|
|
307
|
+
pages.append(ExtractedPage(page_no=number, text=body))
|
|
308
|
+
except Exception as exc: # pypdf raises a wide range on damaged files
|
|
309
|
+
return ExtractedDoc.failure(f"could not read pdf: {exc}", engine=self.name)
|
|
310
|
+
|
|
311
|
+
if not pages:
|
|
312
|
+
# Almost always a scan. Say so, because the fix is an OCR extractor.
|
|
313
|
+
return ExtractedDoc(
|
|
314
|
+
engine=self.name,
|
|
315
|
+
status=EXTRACTION_EMPTY,
|
|
316
|
+
error="pdf has no text layer — it is probably scanned and needs OCR",
|
|
317
|
+
was_scanned=True,
|
|
318
|
+
)
|
|
319
|
+
return ExtractedDoc(pages=tuple(pages), engine=self.name)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
class ExtractorChain:
|
|
323
|
+
"""Try each extractor in order and take the first that both supports and succeeds.
|
|
324
|
+
|
|
325
|
+
A registry rather than branching, so the host adds OCR by putting it at the front
|
|
326
|
+
of the tuple. Falling through on an *unusable* result (not merely an unsupported
|
|
327
|
+
one) is what lets a text-layer reader sit ahead of an expensive OCR pass: the cheap
|
|
328
|
+
engine gets first refusal, and the expensive one only runs when the cheap one came
|
|
329
|
+
back empty.
|
|
330
|
+
"""
|
|
331
|
+
|
|
332
|
+
def __init__(self, extractors: Sequence[TextExtractor]):
|
|
333
|
+
if not extractors:
|
|
334
|
+
raise ValueError("ExtractorChain needs at least one extractor")
|
|
335
|
+
self._extractors = tuple(extractors)
|
|
336
|
+
|
|
337
|
+
def supports(self, path: Path) -> bool:
|
|
338
|
+
return any(extractor.supports(path) for extractor in self._extractors)
|
|
339
|
+
|
|
340
|
+
def extract(self, path: Path) -> ExtractedDoc:
|
|
341
|
+
last: Optional[ExtractedDoc] = None
|
|
342
|
+
for extractor in self._extractors:
|
|
343
|
+
if not extractor.supports(path):
|
|
344
|
+
continue
|
|
345
|
+
result = extractor.extract(path)
|
|
346
|
+
if result.is_usable:
|
|
347
|
+
return result
|
|
348
|
+
last = last or result
|
|
349
|
+
return last or ExtractedDoc.failure(
|
|
350
|
+
f"no extractor handles {path.suffix or 'this file'}",
|
|
351
|
+
status=EXTRACTION_UNSUPPORTED,
|
|
352
|
+
)
|