igel-qe-core 1.0.0
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.
- package/dist/cli/index.js +38 -0
- package/dist/mcp/server.js +104 -0
- package/knowledge_base/__init__.py +3 -0
- package/knowledge_base/__pycache__/__init__.cpython-314.pyc +0 -0
- package/knowledge_base/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/__pycache__/confidence.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/config.cpython-314.pyc +0 -0
- package/knowledge_base/__pycache__/config.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/config.cpython-38.pyc +0 -0
- package/knowledge_base/__pycache__/context_fusion.cpython-314.pyc +0 -0
- package/knowledge_base/__pycache__/context_fusion.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/feedback_analytics.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/feedback_applier.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/feedback_cli.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/feedback_curator.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/feedback_handler.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/generate.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/generate_qcapps134.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/input_classifier.cpython-314.pyc +0 -0
- package/knowledge_base/__pycache__/input_classifier.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/kg_extractor.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/knowledge_graph.cpython-314.pyc +0 -0
- package/knowledge_base/__pycache__/knowledge_graph.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/manual_tc_parser.cpython-314.pyc +0 -0
- package/knowledge_base/__pycache__/manual_tc_parser.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/precedence.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/prompts.cpython-314.pyc +0 -0
- package/knowledge_base/__pycache__/prompts.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/reranker.cpython-314.pyc +0 -0
- package/knowledge_base/__pycache__/reranker.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/retrieval_debug.cpython-314.pyc +0 -0
- package/knowledge_base/__pycache__/retrieval_debug.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/retriever.cpython-314.pyc +0 -0
- package/knowledge_base/__pycache__/retriever.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/server.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/tc_transformer.cpython-314.pyc +0 -0
- package/knowledge_base/__pycache__/tc_transformer.cpython-37.pyc +0 -0
- package/knowledge_base/__pycache__/test_generator.cpython-314.pyc +0 -0
- package/knowledge_base/__pycache__/test_generator.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__init__.py +6 -0
- package/knowledge_base/agents/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/agents/__pycache__/fusion_agent.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__pycache__/fusion_agent.cpython-38.pyc +0 -0
- package/knowledge_base/agents/__pycache__/graph.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__pycache__/graph.cpython-38.pyc +0 -0
- package/knowledge_base/agents/__pycache__/healing_agent.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__pycache__/intent_agent.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__pycache__/intent_agent.cpython-38.pyc +0 -0
- package/knowledge_base/agents/__pycache__/kg_agent.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__pycache__/kg_agent.cpython-38.pyc +0 -0
- package/knowledge_base/agents/__pycache__/memory_agent.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__pycache__/memory_agent.cpython-38.pyc +0 -0
- package/knowledge_base/agents/__pycache__/rca_agent.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__pycache__/reflection_agent.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__pycache__/reflection_agent.cpython-38.pyc +0 -0
- package/knowledge_base/agents/__pycache__/regression_agent.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__pycache__/retrieval_agent.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__pycache__/retrieval_agent.cpython-38.pyc +0 -0
- package/knowledge_base/agents/__pycache__/script_agent.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__pycache__/script_agent.cpython-38.pyc +0 -0
- package/knowledge_base/agents/__pycache__/state.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__pycache__/state.cpython-38.pyc +0 -0
- package/knowledge_base/agents/__pycache__/temporal_agent.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__pycache__/temporal_agent.cpython-38.pyc +0 -0
- package/knowledge_base/agents/__pycache__/validation_agent.cpython-37.pyc +0 -0
- package/knowledge_base/agents/__pycache__/validation_agent.cpython-38.pyc +0 -0
- package/knowledge_base/agents/automation_design_agent.py +123 -0
- package/knowledge_base/agents/code_intelligence_agent.py +147 -0
- package/knowledge_base/agents/confidence_agent.py +94 -0
- package/knowledge_base/agents/execution_validation_agent.py +129 -0
- package/knowledge_base/agents/feedback_agent.py +56 -0
- package/knowledge_base/agents/fusion_agent.py +113 -0
- package/knowledge_base/agents/graph.py +152 -0
- package/knowledge_base/agents/healing_agent.py +62 -0
- package/knowledge_base/agents/intent_agent.py +76 -0
- package/knowledge_base/agents/kg_agent.py +91 -0
- package/knowledge_base/agents/memory_agent.py +62 -0
- package/knowledge_base/agents/rca_agent.py +53 -0
- package/knowledge_base/agents/reflection_agent.py +62 -0
- package/knowledge_base/agents/regression_agent.py +81 -0
- package/knowledge_base/agents/requirement_agent.py +71 -0
- package/knowledge_base/agents/retrieval_agent.py +66 -0
- package/knowledge_base/agents/scenario_agent.py +81 -0
- package/knowledge_base/agents/script_agent.py +121 -0
- package/knowledge_base/agents/script_generator_agent.py +77 -0
- package/knowledge_base/agents/state.py +53 -0
- package/knowledge_base/agents/temporal_agent.py +48 -0
- package/knowledge_base/agents/testcase_agent.py +111 -0
- package/knowledge_base/agents/validation_agent.py +49 -0
- package/knowledge_base/agents/workflow_engine.py +87 -0
- package/knowledge_base/alembic/README.md +104 -0
- package/knowledge_base/alembic/__pycache__/env.cpython-37.pyc +0 -0
- package/knowledge_base/alembic/__pycache__/env.cpython-38.pyc +0 -0
- package/knowledge_base/alembic/env.py +76 -0
- package/knowledge_base/alembic/script.py.mako +5 -0
- package/knowledge_base/alembic/versions/.gitkeep +26 -0
- package/knowledge_base/alembic/versions/001_phase0_feedback.py +122 -0
- package/knowledge_base/alembic/versions/002_feedback_promotions.py +60 -0
- package/knowledge_base/alembic/versions/003_enterprise_hardening.py +74 -0
- package/knowledge_base/alembic/versions/__pycache__/001_phase0_feedback.cpython-37.pyc +0 -0
- package/knowledge_base/alembic/versions/__pycache__/001_phase0_feedback.cpython-38.pyc +0 -0
- package/knowledge_base/alembic/versions/__pycache__/002_feedback_promotions.cpython-37.pyc +0 -0
- package/knowledge_base/alembic/versions/__pycache__/002_feedback_promotions.cpython-38.pyc +0 -0
- package/knowledge_base/api/__init__.py +1 -0
- package/knowledge_base/api/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/api/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/api/__pycache__/middleware.cpython-38.pyc +0 -0
- package/knowledge_base/api/__pycache__/server.cpython-37.pyc +0 -0
- package/knowledge_base/api/__pycache__/server.cpython-38.pyc +0 -0
- package/knowledge_base/api/middleware.py +78 -0
- package/knowledge_base/api/server.py +778 -0
- package/knowledge_base/chunking/__init__.py +0 -0
- package/knowledge_base/chunking/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/chunking/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/chunking/__pycache__/chunker.cpython-37.pyc +0 -0
- package/knowledge_base/chunking/__pycache__/chunker.cpython-38.pyc +0 -0
- package/knowledge_base/chunking/__pycache__/validators.cpython-37.pyc +0 -0
- package/knowledge_base/chunking/__pycache__/validators.cpython-38.pyc +0 -0
- package/knowledge_base/chunking/chunker.py +402 -0
- package/knowledge_base/chunking/validators.py +144 -0
- package/knowledge_base/cli/__init__.py +1 -0
- package/knowledge_base/cli/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/cli/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/cli/__pycache__/feedback_cli.cpython-37.pyc +0 -0
- package/knowledge_base/cli/__pycache__/feedback_cli.cpython-38.pyc +0 -0
- package/knowledge_base/cli/__pycache__/generate.cpython-37.pyc +0 -0
- package/knowledge_base/cli/__pycache__/generate.cpython-38.pyc +0 -0
- package/knowledge_base/cli/__pycache__/generate_qcapps134.cpython-37.pyc +0 -0
- package/knowledge_base/cli/feedback_cli.py +121 -0
- package/knowledge_base/cli/generate.py +308 -0
- package/knowledge_base/cli/generate_qcapps134.py +391 -0
- package/knowledge_base/config/.env +100 -0
- package/knowledge_base/config/.env.example +75 -0
- package/knowledge_base/config/__init__.py +5 -0
- package/knowledge_base/config/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/config/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/config/__pycache__/settings.cpython-37.pyc +0 -0
- package/knowledge_base/config/__pycache__/settings.cpython-38.pyc +0 -0
- package/knowledge_base/config/settings.py +280 -0
- package/knowledge_base/connectors/__init__.py +5 -0
- package/knowledge_base/connectors/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/base.cpython-37.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/base.cpython-38.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/bitbucket_connector.cpython-37.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/bitbucket_connector.cpython-38.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/community_connector.cpython-38.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/confluence_connector.cpython-37.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/confluence_connector.cpython-38.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/jira_connector.cpython-37.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/jira_connector.cpython-38.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/kb_igel_connector.cpython-38.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/playwright_fetcher.cpython-38.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/query_bridge.cpython-38.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/search_backends.cpython-38.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/serviceNow_connector.cpython-37.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/serviceNow_connector.cpython-38.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/sync_scheduler.cpython-37.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/sync_scheduler.cpython-38.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/teamcity_connector.cpython-37.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/teamcity_connector.cpython-38.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/testRail_connector.cpython-37.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/testRail_connector.cpython-38.pyc +0 -0
- package/knowledge_base/connectors/__pycache__/web_fetcher.cpython-38.pyc +0 -0
- package/knowledge_base/connectors/base.py +44 -0
- package/knowledge_base/connectors/bitbucket_connector.py +25 -0
- package/knowledge_base/connectors/community_connector.py +112 -0
- package/knowledge_base/connectors/confluence_connector.py +165 -0
- package/knowledge_base/connectors/jira_connector.py +174 -0
- package/knowledge_base/connectors/kb_igel_connector.py +145 -0
- package/knowledge_base/connectors/playwright_fetcher.py +50 -0
- package/knowledge_base/connectors/query_bridge.py +170 -0
- package/knowledge_base/connectors/search_backends.py +368 -0
- package/knowledge_base/connectors/serviceNow_connector.py +25 -0
- package/knowledge_base/connectors/sync_scheduler.py +111 -0
- package/knowledge_base/connectors/teamcity_connector.py +25 -0
- package/knowledge_base/connectors/testRail_connector.py +25 -0
- package/knowledge_base/connectors/web_fetcher.py +280 -0
- package/knowledge_base/core/__init__.py +8 -0
- package/knowledge_base/core/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/core/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/core/__pycache__/cache.cpython-38.pyc +0 -0
- package/knowledge_base/core/__pycache__/circuit_breaker.cpython-38.pyc +0 -0
- package/knowledge_base/core/__pycache__/confidence.cpython-37.pyc +0 -0
- package/knowledge_base/core/__pycache__/confidence.cpython-38.pyc +0 -0
- package/knowledge_base/core/__pycache__/logging_setup.cpython-38.pyc +0 -0
- package/knowledge_base/core/__pycache__/metrics.cpython-38.pyc +0 -0
- package/knowledge_base/core/__pycache__/precedence.cpython-37.pyc +0 -0
- package/knowledge_base/core/__pycache__/precedence.cpython-38.pyc +0 -0
- package/knowledge_base/core/__pycache__/request_context.cpython-38.pyc +0 -0
- package/knowledge_base/core/cache.py +98 -0
- package/knowledge_base/core/circuit_breaker.py +33 -0
- package/knowledge_base/core/confidence.py +254 -0
- package/knowledge_base/core/logging_setup.py +39 -0
- package/knowledge_base/core/metrics.py +63 -0
- package/knowledge_base/core/precedence.py +354 -0
- package/knowledge_base/core/request_context.py +54 -0
- package/knowledge_base/db/__init__.py +0 -0
- package/knowledge_base/db/__pycache__/__init__.cpython-314.pyc +0 -0
- package/knowledge_base/db/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/db/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/db/__pycache__/client.cpython-314.pyc +0 -0
- package/knowledge_base/db/__pycache__/client.cpython-37.pyc +0 -0
- package/knowledge_base/db/__pycache__/client.cpython-38.pyc +0 -0
- package/knowledge_base/db/__pycache__/connector_sync.cpython-38.pyc +0 -0
- package/knowledge_base/db/__pycache__/feedback_schema.cpython-37.pyc +0 -0
- package/knowledge_base/db/__pycache__/feedback_schema.cpython-38.pyc +0 -0
- package/knowledge_base/db/__pycache__/kg_introspect.cpython-314.pyc +0 -0
- package/knowledge_base/db/__pycache__/kg_introspect.cpython-37.pyc +0 -0
- package/knowledge_base/db/__pycache__/kg_introspect.cpython-38.pyc +0 -0
- package/knowledge_base/db/__pycache__/schema.cpython-37.pyc +0 -0
- package/knowledge_base/db/__pycache__/schema.cpython-38.pyc +0 -0
- package/knowledge_base/db/client.py +100 -0
- package/knowledge_base/db/connector_sync.py +43 -0
- package/knowledge_base/db/feedback_schema.py +548 -0
- package/knowledge_base/db/igel_schema.py +150 -0
- package/knowledge_base/db/kg_introspect.py +116 -0
- package/knowledge_base/db/migrations.py +22 -0
- package/knowledge_base/db/schema.py +649 -0
- package/knowledge_base/deploy/Dockerfile +23 -0
- package/knowledge_base/deploy/feedback_automation.sh +18 -0
- package/knowledge_base/deploy/firecrawl-docker.sh +50 -0
- package/knowledge_base/deploy/igel-testgen.service +26 -0
- package/knowledge_base/deploy/install_reranker.sh +92 -0
- package/knowledge_base/deploy/requirements.txt +68 -0
- package/knowledge_base/deploy/run_server.sh +35 -0
- package/knowledge_base/docs/TEST_GENERATION_GUIDE.md +174 -0
- package/knowledge_base/embeddings/__init__.py +0 -0
- package/knowledge_base/embeddings/__pycache__/__init__.cpython-314.pyc +0 -0
- package/knowledge_base/embeddings/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/embeddings/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/embeddings/__pycache__/azure_embedder.cpython-314.pyc +0 -0
- package/knowledge_base/embeddings/__pycache__/azure_embedder.cpython-37.pyc +0 -0
- package/knowledge_base/embeddings/__pycache__/azure_embedder.cpython-38.pyc +0 -0
- package/knowledge_base/embeddings/azure_embedder.py +99 -0
- package/knowledge_base/eval/__init__.py +33 -0
- package/knowledge_base/eval/__main__.py +172 -0
- package/knowledge_base/eval/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/eval/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/eval/__pycache__/__main__.cpython-37.pyc +0 -0
- package/knowledge_base/eval/__pycache__/baseline.cpython-37.pyc +0 -0
- package/knowledge_base/eval/__pycache__/baseline.cpython-38.pyc +0 -0
- package/knowledge_base/eval/__pycache__/harness.cpython-37.pyc +0 -0
- package/knowledge_base/eval/__pycache__/harness.cpython-38.pyc +0 -0
- package/knowledge_base/eval/__pycache__/scorers.cpython-37.pyc +0 -0
- package/knowledge_base/eval/__pycache__/scorers.cpython-38.pyc +0 -0
- package/knowledge_base/eval/baseline.py +213 -0
- package/knowledge_base/eval/harness.py +400 -0
- package/knowledge_base/eval/queries_50.json +302 -0
- package/knowledge_base/eval/scorers.py +494 -0
- package/knowledge_base/exemplars/README.md +7 -0
- package/knowledge_base/exemplars/__pycache__/pytest_reference_snippet.cpython-37.pyc +0 -0
- package/knowledge_base/exemplars/gold_manual_concise_snippet.md +8 -0
- package/knowledge_base/exemplars/gold_manual_detailed_snippet.md +7 -0
- package/knowledge_base/exemplars/pytest_reference_snippet.py +37 -0
- package/knowledge_base/feedback/__init__.py +10 -0
- package/knowledge_base/feedback/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/feedback/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/feedback/__pycache__/analytics.cpython-37.pyc +0 -0
- package/knowledge_base/feedback/__pycache__/analytics.cpython-38.pyc +0 -0
- package/knowledge_base/feedback/__pycache__/applier.cpython-37.pyc +0 -0
- package/knowledge_base/feedback/__pycache__/applier.cpython-38.pyc +0 -0
- package/knowledge_base/feedback/__pycache__/curator.cpython-37.pyc +0 -0
- package/knowledge_base/feedback/__pycache__/curator.cpython-38.pyc +0 -0
- package/knowledge_base/feedback/__pycache__/handler.cpython-37.pyc +0 -0
- package/knowledge_base/feedback/__pycache__/handler.cpython-38.pyc +0 -0
- package/knowledge_base/feedback/analytics.py +72 -0
- package/knowledge_base/feedback/applier.py +93 -0
- package/knowledge_base/feedback/curator.py +49 -0
- package/knowledge_base/feedback/handler.py +151 -0
- package/knowledge_base/generation/__init__.py +5 -0
- package/knowledge_base/generation/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/generation/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/generation/__pycache__/input_classifier.cpython-37.pyc +0 -0
- package/knowledge_base/generation/__pycache__/input_classifier.cpython-38.pyc +0 -0
- package/knowledge_base/generation/__pycache__/manual_tc_parser.cpython-37.pyc +0 -0
- package/knowledge_base/generation/__pycache__/manual_tc_parser.cpython-38.pyc +0 -0
- package/knowledge_base/generation/__pycache__/prompts.cpython-37.pyc +0 -0
- package/knowledge_base/generation/__pycache__/prompts.cpython-38.pyc +0 -0
- package/knowledge_base/generation/__pycache__/tc_transformer.cpython-37.pyc +0 -0
- package/knowledge_base/generation/__pycache__/test_generator.cpython-37.pyc +0 -0
- package/knowledge_base/generation/__pycache__/test_generator.cpython-38.pyc +0 -0
- package/knowledge_base/generation/input_classifier.py +148 -0
- package/knowledge_base/generation/manual_tc_parser.py +180 -0
- package/knowledge_base/generation/prompts.py +755 -0
- package/knowledge_base/generation/tc_transformer.py +368 -0
- package/knowledge_base/generation/test_generator.py +553 -0
- package/knowledge_base/graph_db/__init__.py +5 -0
- package/knowledge_base/graph_db/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/graph_db/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/graph_db/__pycache__/graphiti_client.cpython-37.pyc +0 -0
- package/knowledge_base/graph_db/__pycache__/kg_traversal.cpython-37.pyc +0 -0
- package/knowledge_base/graph_db/__pycache__/migrate_kg.cpython-37.pyc +0 -0
- package/knowledge_base/graph_db/__pycache__/neo4j_client.cpython-37.pyc +0 -0
- package/knowledge_base/graph_db/__pycache__/neo4j_client.cpython-38.pyc +0 -0
- package/knowledge_base/graph_db/__pycache__/schema.cpython-37.pyc +0 -0
- package/knowledge_base/graph_db/__pycache__/temporal_ingest.cpython-37.pyc +0 -0
- package/knowledge_base/graph_db/graphiti_client.py +127 -0
- package/knowledge_base/graph_db/kg_traversal.py +192 -0
- package/knowledge_base/graph_db/migrate_kg.py +93 -0
- package/knowledge_base/graph_db/neo4j_client.py +116 -0
- package/knowledge_base/graph_db/schema.py +22 -0
- package/knowledge_base/graph_db/temporal_ingest.py +59 -0
- package/knowledge_base/ingestion/__init__.py +1 -0
- package/knowledge_base/ingestion/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/ingestion/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/ingestion/__pycache__/ingest.cpython-37.pyc +0 -0
- package/knowledge_base/ingestion/__pycache__/ingest.cpython-38.pyc +0 -0
- package/knowledge_base/ingestion/__pycache__/ingest_images.cpython-37.pyc +0 -0
- package/knowledge_base/ingestion/__pycache__/ingest_images.cpython-38.pyc +0 -0
- package/knowledge_base/ingestion/__pycache__/kg_extractor.cpython-37.pyc +0 -0
- package/knowledge_base/ingestion/__pycache__/kg_extractor.cpython-38.pyc +0 -0
- package/knowledge_base/ingestion/__pycache__/web_ingest.cpython-38.pyc +0 -0
- package/knowledge_base/ingestion/ingest.py +346 -0
- package/knowledge_base/ingestion/ingest_images.py +330 -0
- package/knowledge_base/ingestion/kg_extractor.py +358 -0
- package/knowledge_base/ingestion/web_ingest.py +83 -0
- package/knowledge_base/intelligence/__init__.py +1 -0
- package/knowledge_base/intelligence/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/intelligence/__pycache__/execution_ingest.cpython-37.pyc +0 -0
- package/knowledge_base/intelligence/__pycache__/predictive_engine.cpython-37.pyc +0 -0
- package/knowledge_base/intelligence/__pycache__/risk_scorer.cpython-37.pyc +0 -0
- package/knowledge_base/intelligence/execution_ingest.py +34 -0
- package/knowledge_base/intelligence/predictive_engine.py +7 -0
- package/knowledge_base/intelligence/risk_scorer.py +8 -0
- package/knowledge_base/memory/__init__.py +6 -0
- package/knowledge_base/memory/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/memory/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/memory/__pycache__/cognee_client.cpython-37.pyc +0 -0
- package/knowledge_base/memory/__pycache__/cognee_client.cpython-38.pyc +0 -0
- package/knowledge_base/memory/__pycache__/ingest_memory.cpython-37.pyc +0 -0
- package/knowledge_base/memory/__pycache__/memory_types.cpython-37.pyc +0 -0
- package/knowledge_base/memory/__pycache__/memory_types.cpython-38.pyc +0 -0
- package/knowledge_base/memory/cognee_client.py +164 -0
- package/knowledge_base/memory/ingest_memory.py +60 -0
- package/knowledge_base/memory/memory_types.py +23 -0
- package/knowledge_base/models/test_requirement.py +84 -0
- package/knowledge_base/parsers/__init__.py +0 -0
- package/knowledge_base/parsers/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/parsers/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/parsers/__pycache__/csv_parser.cpython-37.pyc +0 -0
- package/knowledge_base/parsers/__pycache__/csv_parser.cpython-38.pyc +0 -0
- package/knowledge_base/parsers/__pycache__/doc_parser.cpython-37.pyc +0 -0
- package/knowledge_base/parsers/__pycache__/doc_parser.cpython-38.pyc +0 -0
- package/knowledge_base/parsers/__pycache__/html_parser.cpython-38.pyc +0 -0
- package/knowledge_base/parsers/__pycache__/image_extractor.cpython-37.pyc +0 -0
- package/knowledge_base/parsers/__pycache__/image_extractor.cpython-38.pyc +0 -0
- package/knowledge_base/parsers/__pycache__/markdown_parser.cpython-37.pyc +0 -0
- package/knowledge_base/parsers/__pycache__/markdown_parser.cpython-38.pyc +0 -0
- package/knowledge_base/parsers/__pycache__/pdf_parser.cpython-37.pyc +0 -0
- package/knowledge_base/parsers/__pycache__/pdf_parser.cpython-38.pyc +0 -0
- package/knowledge_base/parsers/csv_parser.py +126 -0
- package/knowledge_base/parsers/doc_parser.py +176 -0
- package/knowledge_base/parsers/html_parser.py +151 -0
- package/knowledge_base/parsers/image_extractor.py +370 -0
- package/knowledge_base/parsers/markdown_parser.py +116 -0
- package/knowledge_base/parsers/pdf_parser.py +178 -0
- package/knowledge_base/retrieval/__init__.py +1 -0
- package/knowledge_base/retrieval/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/retrieval/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/retrieval/__pycache__/context_fusion.cpython-37.pyc +0 -0
- package/knowledge_base/retrieval/__pycache__/context_fusion.cpython-38.pyc +0 -0
- package/knowledge_base/retrieval/__pycache__/knowledge_graph.cpython-37.pyc +0 -0
- package/knowledge_base/retrieval/__pycache__/knowledge_graph.cpython-38.pyc +0 -0
- package/knowledge_base/retrieval/__pycache__/reranker.cpython-37.pyc +0 -0
- package/knowledge_base/retrieval/__pycache__/reranker.cpython-38.pyc +0 -0
- package/knowledge_base/retrieval/__pycache__/retrieval_debug.cpython-37.pyc +0 -0
- package/knowledge_base/retrieval/__pycache__/retrieval_debug.cpython-38.pyc +0 -0
- package/knowledge_base/retrieval/__pycache__/retriever.cpython-37.pyc +0 -0
- package/knowledge_base/retrieval/__pycache__/retriever.cpython-38.pyc +0 -0
- package/knowledge_base/retrieval/__pycache__/unified_query.cpython-38.pyc +0 -0
- package/knowledge_base/retrieval/__pycache__/web_retriever.cpython-38.pyc +0 -0
- package/knowledge_base/retrieval/context_fusion.py +264 -0
- package/knowledge_base/retrieval/knowledge_graph.py +267 -0
- package/knowledge_base/retrieval/reranker.py +308 -0
- package/knowledge_base/retrieval/retrieval_debug.py +69 -0
- package/knowledge_base/retrieval/retriever.py +885 -0
- package/knowledge_base/retrieval/unified_query.py +189 -0
- package/knowledge_base/retrieval/web_retriever.py +337 -0
- package/knowledge_base/validators/__init__.py +1 -0
- package/knowledge_base/validators/__pycache__/__init__.cpython-314.pyc +0 -0
- package/knowledge_base/validators/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/validators/__pycache__/step_fidelity.cpython-314.pyc +0 -0
- package/knowledge_base/validators/__pycache__/step_fidelity.cpython-37.pyc +0 -0
- package/knowledge_base/validators/step_fidelity.py +88 -0
- package/knowledge_base/vision/__init__.py +0 -0
- package/knowledge_base/vision/__pycache__/__init__.cpython-37.pyc +0 -0
- package/knowledge_base/vision/__pycache__/__init__.cpython-38.pyc +0 -0
- package/knowledge_base/vision/__pycache__/image_describer.cpython-37.pyc +0 -0
- package/knowledge_base/vision/__pycache__/image_describer.cpython-38.pyc +0 -0
- package/knowledge_base/vision/image_describer.py +202 -0
- package/package.json +31 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PDF Parser — three-tier strategy:
|
|
3
|
+
Tier 1: If a pre-converted markdown file exists in markdown_output/, use it.
|
|
4
|
+
(Higher quality — processed by marker-pdf + Azure OpenAI LLM)
|
|
5
|
+
Tier 2: Direct pdfplumber extraction.
|
|
6
|
+
Tier 3: PyMuPDF (fitz) fallback — handles PDFs pdfplumber can't (encrypted
|
|
7
|
+
XRefs, exotic fonts, malformed cross-reference tables).
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
import logging
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from knowledge_base.parsers.markdown_parser import Section, parse_markdown_file, find_markdown_for_pdf
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def parse_pdf(pdf_path: Path, markdown_output_dir: Path) -> tuple[list[Section], str]:
|
|
18
|
+
"""
|
|
19
|
+
Returns (sections, parse_method) where parse_method is one of:
|
|
20
|
+
'markdown', 'pdfplumber', 'pymupdf'.
|
|
21
|
+
"""
|
|
22
|
+
# Tier 1: use pre-converted markdown
|
|
23
|
+
md_path = find_markdown_for_pdf(pdf_path.name, markdown_output_dir)
|
|
24
|
+
if md_path:
|
|
25
|
+
sections = parse_markdown_file(md_path)
|
|
26
|
+
if sections:
|
|
27
|
+
return sections, "markdown"
|
|
28
|
+
|
|
29
|
+
# Tier 2: pdfplumber
|
|
30
|
+
try:
|
|
31
|
+
sections = _parse_with_pdfplumber(pdf_path)
|
|
32
|
+
if sections and any(s.content.strip() for s in sections):
|
|
33
|
+
return sections, "pdfplumber"
|
|
34
|
+
logger.info("pdfplumber returned empty sections for %s — trying PyMuPDF", pdf_path.name)
|
|
35
|
+
except Exception as e:
|
|
36
|
+
logger.warning("pdfplumber failed on %s: %s — falling back to PyMuPDF", pdf_path.name, e)
|
|
37
|
+
|
|
38
|
+
# Tier 3: PyMuPDF
|
|
39
|
+
sections = _parse_with_pymupdf(pdf_path)
|
|
40
|
+
return sections, "pymupdf"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _parse_with_pymupdf(pdf_path: Path) -> list[Section]:
|
|
44
|
+
import fitz
|
|
45
|
+
sections: list[Section] = []
|
|
46
|
+
try:
|
|
47
|
+
doc = fitz.open(str(pdf_path))
|
|
48
|
+
except Exception as e:
|
|
49
|
+
logger.error("PyMuPDF could not open %s: %s", pdf_path.name, e)
|
|
50
|
+
return [Section(title=pdf_path.stem, level=0, content="", page_hint=1)]
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
for page_idx in range(len(doc)):
|
|
54
|
+
page = doc[page_idx]
|
|
55
|
+
text = page.get_text("text") or ""
|
|
56
|
+
text = text.strip()
|
|
57
|
+
if not text:
|
|
58
|
+
continue
|
|
59
|
+
sections.append(Section(
|
|
60
|
+
title=pdf_path.stem,
|
|
61
|
+
level=0,
|
|
62
|
+
content=text,
|
|
63
|
+
page_hint=page_idx + 1,
|
|
64
|
+
))
|
|
65
|
+
finally:
|
|
66
|
+
doc.close()
|
|
67
|
+
|
|
68
|
+
return sections if sections else [Section(title=pdf_path.stem, level=0, content="", page_hint=1)]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _parse_with_pdfplumber(pdf_path: Path) -> list[Section]:
|
|
72
|
+
import pdfplumber
|
|
73
|
+
import re
|
|
74
|
+
|
|
75
|
+
all_sections: list[Section] = []
|
|
76
|
+
current_title = pdf_path.stem
|
|
77
|
+
current_level = 0
|
|
78
|
+
current_lines: list[str] = []
|
|
79
|
+
current_page = 1
|
|
80
|
+
full_text_lines: list[str] = []
|
|
81
|
+
|
|
82
|
+
with pdfplumber.open(str(pdf_path)) as pdf:
|
|
83
|
+
for page_num, page in enumerate(pdf.pages, start=1):
|
|
84
|
+
# Extract tables first — preserve as markdown
|
|
85
|
+
tables = page.extract_tables()
|
|
86
|
+
table_md_blocks = [_table_to_markdown(t) for t in tables if t]
|
|
87
|
+
|
|
88
|
+
text = page.extract_text(x_tolerance=3, y_tolerance=3) or ""
|
|
89
|
+
lines = text.split("\n")
|
|
90
|
+
|
|
91
|
+
# Strip header/footer (first and last line are usually page number / product name)
|
|
92
|
+
if len(lines) > 3:
|
|
93
|
+
lines = lines[1:-1]
|
|
94
|
+
|
|
95
|
+
for line in lines:
|
|
96
|
+
line = line.strip()
|
|
97
|
+
if not line:
|
|
98
|
+
full_text_lines.append("")
|
|
99
|
+
continue
|
|
100
|
+
|
|
101
|
+
# Heading detection by ALL_CAPS short lines or common IGEL heading patterns
|
|
102
|
+
if _is_heading(line):
|
|
103
|
+
# Flush current section
|
|
104
|
+
content = "\n".join(current_lines).strip()
|
|
105
|
+
if content:
|
|
106
|
+
all_sections.append(Section(
|
|
107
|
+
title=current_title,
|
|
108
|
+
level=current_level,
|
|
109
|
+
content=content,
|
|
110
|
+
page_hint=current_page,
|
|
111
|
+
))
|
|
112
|
+
current_title = line.rstrip(":")
|
|
113
|
+
current_level = _estimate_heading_level(line)
|
|
114
|
+
current_lines = []
|
|
115
|
+
current_page = page_num
|
|
116
|
+
else:
|
|
117
|
+
current_lines.append(line)
|
|
118
|
+
full_text_lines.append(line)
|
|
119
|
+
|
|
120
|
+
# Append tables as a block at end of page content
|
|
121
|
+
for table_md in table_md_blocks:
|
|
122
|
+
current_lines.append(table_md)
|
|
123
|
+
|
|
124
|
+
# Flush final section
|
|
125
|
+
content = "\n".join(current_lines).strip()
|
|
126
|
+
if content:
|
|
127
|
+
all_sections.append(Section(
|
|
128
|
+
title=current_title,
|
|
129
|
+
level=current_level,
|
|
130
|
+
content=content,
|
|
131
|
+
page_hint=current_page,
|
|
132
|
+
))
|
|
133
|
+
|
|
134
|
+
# If no structure detected, treat whole doc as one section
|
|
135
|
+
if len(all_sections) <= 1:
|
|
136
|
+
all_text = "\n".join(full_text_lines).strip()
|
|
137
|
+
return [Section(title=pdf_path.stem, level=0, content=all_text, page_hint=1)]
|
|
138
|
+
|
|
139
|
+
return all_sections
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _is_heading(line: str) -> bool:
|
|
143
|
+
import re
|
|
144
|
+
if len(line) > 120:
|
|
145
|
+
return False
|
|
146
|
+
# Short ALL CAPS lines
|
|
147
|
+
if line.isupper() and 3 < len(line) < 80:
|
|
148
|
+
return True
|
|
149
|
+
# Numbered heading: "1.2 Title" or "3. Title"
|
|
150
|
+
if re.match(r"^\d+(\.\d+)*\.?\s+[A-Z]", line):
|
|
151
|
+
return True
|
|
152
|
+
# Lines ending with colon that look like section titles
|
|
153
|
+
if line.endswith(":") and len(line) < 60 and line[0].isupper():
|
|
154
|
+
return True
|
|
155
|
+
return False
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _estimate_heading_level(line: str) -> int:
|
|
159
|
+
import re
|
|
160
|
+
m = re.match(r"^(\d+)(\.\d+)*", line)
|
|
161
|
+
if m:
|
|
162
|
+
depth = line[:line.index(" ")].count(".")
|
|
163
|
+
return min(depth + 1, 3)
|
|
164
|
+
return 2
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _table_to_markdown(table: list) -> str:
|
|
168
|
+
if not table or not table[0]:
|
|
169
|
+
return ""
|
|
170
|
+
rows = [[str(cell or "").strip() for cell in row] for row in table]
|
|
171
|
+
# Find max columns
|
|
172
|
+
cols = max(len(r) for r in rows)
|
|
173
|
+
rows = [r + [""] * (cols - len(r)) for r in rows]
|
|
174
|
+
|
|
175
|
+
header = "| " + " | ".join(rows[0]) + " |"
|
|
176
|
+
divider = "| " + " | ".join(["---"] * cols) + " |"
|
|
177
|
+
body = ["| " + " | ".join(r) + " |" for r in rows[1:]]
|
|
178
|
+
return "\n".join([header, divider] + body)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Retrieval, reranking, and context fusion."""
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""Fuse RAG hits, KG text, and image snippets under a dynamic token budget."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from knowledge_base.config import cfg
|
|
9
|
+
from knowledge_base.core.precedence import (
|
|
10
|
+
apply_precedence_filter,
|
|
11
|
+
detect_precedence_conflicts,
|
|
12
|
+
infer_source_type,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from knowledge_base.retrieval.retriever import SearchResult
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
_CHARS_PER_TOKEN = 3.8
|
|
21
|
+
|
|
22
|
+
_EXCLUDE_SOURCE_MAP = {
|
|
23
|
+
"jira": "jira_tc",
|
|
24
|
+
"defects": "execution_history",
|
|
25
|
+
"defect": "execution_history",
|
|
26
|
+
"confluence": "confluence",
|
|
27
|
+
"screenshots": "screenshot",
|
|
28
|
+
"execution": "execution_history",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _chunk_content(chunk) -> str:
|
|
33
|
+
"""Extract searchable text from a chunk object."""
|
|
34
|
+
for attr in ("parent_content", "content", "child_content"):
|
|
35
|
+
val = getattr(chunk, attr, None)
|
|
36
|
+
if val:
|
|
37
|
+
return str(val)
|
|
38
|
+
return ""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _chunk_metadata(chunk) -> dict:
|
|
42
|
+
meta = getattr(chunk, "metadata", None)
|
|
43
|
+
if isinstance(meta, dict) and meta:
|
|
44
|
+
return meta
|
|
45
|
+
return {
|
|
46
|
+
"file_path": getattr(chunk, "file_name", "") or "",
|
|
47
|
+
"product": getattr(chunk, "product", "") or "",
|
|
48
|
+
"section_title": getattr(chunk, "section_title", "") or "",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _attach_chunk_helpers(chunk):
|
|
53
|
+
"""Ensure precedence helpers can read content/metadata from SearchResult."""
|
|
54
|
+
if not getattr(chunk, "metadata", None):
|
|
55
|
+
chunk.metadata = _chunk_metadata(chunk)
|
|
56
|
+
if not getattr(chunk, "content", None):
|
|
57
|
+
chunk.content = _chunk_content(chunk)
|
|
58
|
+
return chunk
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def filter_excluded_sources(results: list) -> list:
|
|
62
|
+
"""Remove chunks from excluded source types (RETRIEVAL_EXCLUDE_SOURCES)."""
|
|
63
|
+
excluded = cfg.RETRIEVAL_EXCLUDE_SOURCES
|
|
64
|
+
if not excluded or not results:
|
|
65
|
+
return results
|
|
66
|
+
|
|
67
|
+
excluded_types = set()
|
|
68
|
+
for label in excluded:
|
|
69
|
+
mapped = _EXCLUDE_SOURCE_MAP.get(label.lower(), label.lower())
|
|
70
|
+
excluded_types.add(mapped)
|
|
71
|
+
if "defect" in label.lower():
|
|
72
|
+
excluded_types.add("execution_history")
|
|
73
|
+
|
|
74
|
+
kept: list = []
|
|
75
|
+
removed = 0
|
|
76
|
+
for chunk in results:
|
|
77
|
+
chunk = _attach_chunk_helpers(chunk)
|
|
78
|
+
source = infer_source_type(_chunk_metadata(chunk))
|
|
79
|
+
file_path = (_chunk_metadata(chunk).get("file_path") or "").lower()
|
|
80
|
+
if source.name in excluded_types:
|
|
81
|
+
removed += 1
|
|
82
|
+
continue
|
|
83
|
+
if "defect" in excluded and "defect" in file_path:
|
|
84
|
+
removed += 1
|
|
85
|
+
continue
|
|
86
|
+
kept.append(chunk)
|
|
87
|
+
|
|
88
|
+
if removed:
|
|
89
|
+
logger.info("context_fusion: excluded %d chunks by source filter %s", removed, excluded)
|
|
90
|
+
return kept
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def apply_precedence_ranking(
|
|
94
|
+
results: list,
|
|
95
|
+
*,
|
|
96
|
+
max_chunks: int | None = None,
|
|
97
|
+
) -> tuple[list, dict, list[dict]]:
|
|
98
|
+
"""
|
|
99
|
+
Rank chunks by source precedence, detect conflicts.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
(filtered_chunks, attribution_metadata, conflicts)
|
|
103
|
+
"""
|
|
104
|
+
if not results:
|
|
105
|
+
return [], {"total_chunks": 0, "source_breakdown": {}}, []
|
|
106
|
+
|
|
107
|
+
prepared = [_attach_chunk_helpers(r) for r in results]
|
|
108
|
+
top_k = max_chunks or len(prepared)
|
|
109
|
+
filtered, attribution = apply_precedence_filter(prepared, top_k=top_k)
|
|
110
|
+
conflicts = detect_precedence_conflicts(filtered)
|
|
111
|
+
if conflicts:
|
|
112
|
+
logger.info("context_fusion: detected %d precedence conflicts", len(conflicts))
|
|
113
|
+
return filtered, attribution, conflicts
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def fuse_retrieval_context(
|
|
117
|
+
results: list,
|
|
118
|
+
kg_text: str,
|
|
119
|
+
image_hits: list | None,
|
|
120
|
+
*,
|
|
121
|
+
token_budget: int,
|
|
122
|
+
reserve_prompt: int,
|
|
123
|
+
kg_ratio: float,
|
|
124
|
+
image_ratio: float,
|
|
125
|
+
min_score: float | None,
|
|
126
|
+
max_chunks: int,
|
|
127
|
+
web_block: str = "",
|
|
128
|
+
web_ratio: float | None = None,
|
|
129
|
+
) -> tuple[list, str, str, str, int, dict, list[dict]]:
|
|
130
|
+
"""
|
|
131
|
+
Full fusion pipeline: source filter → precedence → dynamic budget.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
(selected_results, kg_trimmed, image_block, web_trimmed, rag_tokens, attribution, conflicts)
|
|
135
|
+
"""
|
|
136
|
+
filtered = filter_excluded_sources(results)
|
|
137
|
+
ranked, attribution, conflicts = apply_precedence_ranking(
|
|
138
|
+
filtered,
|
|
139
|
+
max_chunks=max(max_chunks, len(filtered)),
|
|
140
|
+
)
|
|
141
|
+
selected, kg_out, img_block, web_out, rag_used = apply_dynamic_budget(
|
|
142
|
+
ranked,
|
|
143
|
+
kg_text,
|
|
144
|
+
image_hits,
|
|
145
|
+
token_budget=token_budget,
|
|
146
|
+
reserve_prompt=reserve_prompt,
|
|
147
|
+
kg_ratio=kg_ratio,
|
|
148
|
+
image_ratio=image_ratio,
|
|
149
|
+
min_score=min_score,
|
|
150
|
+
max_chunks=max_chunks,
|
|
151
|
+
web_block=web_block,
|
|
152
|
+
web_ratio=web_ratio,
|
|
153
|
+
)
|
|
154
|
+
return selected, kg_out, img_block, web_out, rag_used, attribution, conflicts
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _truncate_words(text: str, max_chars: int) -> str:
|
|
158
|
+
if len(text) <= max_chars:
|
|
159
|
+
return text
|
|
160
|
+
truncated = text[:max_chars]
|
|
161
|
+
last_space = truncated.rfind(" ")
|
|
162
|
+
return truncated[:last_space] if last_space > 0 else truncated
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _est_tokens(text: str) -> int:
|
|
166
|
+
return max(1, int(len(text) / _CHARS_PER_TOKEN))
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _format_image_snippets(image_hits: list, max_images: int = 4) -> str:
|
|
170
|
+
lines: list[str] = []
|
|
171
|
+
for h in image_hits[:max_images]:
|
|
172
|
+
lines.append(
|
|
173
|
+
f"[{h.image_type}] ({h.file_name} p.{h.page_number or '?'}) "
|
|
174
|
+
f"{getattr(h, 'semantic_description', '') or ''}\n"
|
|
175
|
+
f"Visible text: {(getattr(h, 'verbatim_text', '') or '')[:800]}\n"
|
|
176
|
+
f"Test hints: {(getattr(h, 'test_relevance', '') or '')[:800]}"
|
|
177
|
+
)
|
|
178
|
+
if not lines:
|
|
179
|
+
return ""
|
|
180
|
+
return "—— Related documentation images ——\n" + "\n\n".join(lines)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def apply_dynamic_budget(
|
|
184
|
+
results: list,
|
|
185
|
+
kg_text: str,
|
|
186
|
+
image_hits: list | None,
|
|
187
|
+
*,
|
|
188
|
+
token_budget: int,
|
|
189
|
+
reserve_prompt: int,
|
|
190
|
+
kg_ratio: float,
|
|
191
|
+
image_ratio: float,
|
|
192
|
+
min_score: float | None,
|
|
193
|
+
max_chunks: int,
|
|
194
|
+
web_block: str = "",
|
|
195
|
+
web_ratio: float | None = None,
|
|
196
|
+
) -> tuple[list, str, str, str, int]:
|
|
197
|
+
"""
|
|
198
|
+
Select RAG parent sections + trim KG + images + web to fit token_budget.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
(selected_SearchResult_list, kg_trimmed, image_block, web_trimmed, rag_tokens_used_approx)
|
|
202
|
+
"""
|
|
203
|
+
available = max(200, token_budget - reserve_prompt)
|
|
204
|
+
web_ratio_val = web_ratio if web_ratio is not None else float(getattr(cfg, "WEB_BUDGET_RATIO", 0.12))
|
|
205
|
+
web_cap = int(available * web_ratio_val) if web_block else 0
|
|
206
|
+
kg_cap = int(available * kg_ratio)
|
|
207
|
+
img_cap = int(available * image_ratio)
|
|
208
|
+
rag_cap = max(200, available - kg_cap - img_cap - web_cap)
|
|
209
|
+
|
|
210
|
+
filtered: list = []
|
|
211
|
+
for r in results:
|
|
212
|
+
score = getattr(r, "rerank_score", None)
|
|
213
|
+
if score is None:
|
|
214
|
+
score = float(getattr(r, "rrf_score", 0.0))
|
|
215
|
+
if min_score is not None and score < min_score:
|
|
216
|
+
continue
|
|
217
|
+
filtered.append(r)
|
|
218
|
+
|
|
219
|
+
filtered = filtered[: max(1, max_chunks)]
|
|
220
|
+
|
|
221
|
+
selected: list = []
|
|
222
|
+
rag_used = 0
|
|
223
|
+
for r in filtered:
|
|
224
|
+
body = getattr(r, "parent_content", "") or ""
|
|
225
|
+
need = _est_tokens(body)
|
|
226
|
+
if rag_used + need <= rag_cap:
|
|
227
|
+
selected.append(r)
|
|
228
|
+
rag_used += need
|
|
229
|
+
elif rag_cap - rag_used > 150:
|
|
230
|
+
remain = int((rag_cap - rag_used) * _CHARS_PER_TOKEN)
|
|
231
|
+
shortened = _truncate_words(body, max(0, remain))
|
|
232
|
+
import copy as _copy
|
|
233
|
+
|
|
234
|
+
cp = _copy.copy(r)
|
|
235
|
+
cp.parent_content = shortened + "\n[...truncated by context budget]"
|
|
236
|
+
selected.append(cp)
|
|
237
|
+
rag_used += _est_tokens(shortened)
|
|
238
|
+
break
|
|
239
|
+
else:
|
|
240
|
+
break
|
|
241
|
+
|
|
242
|
+
kg_out = (kg_text or "").strip()
|
|
243
|
+
if kg_out and _est_tokens(kg_out) > kg_cap:
|
|
244
|
+
kg_out = _truncate_words(kg_out, int(kg_cap * _CHARS_PER_TOKEN)) + "\n[...kg truncated]"
|
|
245
|
+
|
|
246
|
+
img_block = _format_image_snippets(image_hits or [], max_images=max(1, int(3 + img_cap // 400)))
|
|
247
|
+
if img_block and _est_tokens(img_block) > img_cap:
|
|
248
|
+
img_block = _truncate_words(img_block, int(img_cap * _CHARS_PER_TOKEN)) + "\n[...images truncated]"
|
|
249
|
+
|
|
250
|
+
web_out = (web_block or "").strip()
|
|
251
|
+
if web_out and _est_tokens(web_out) > web_cap:
|
|
252
|
+
web_out = _truncate_words(web_out, int(web_cap * _CHARS_PER_TOKEN)) + "\n[...web truncated]"
|
|
253
|
+
|
|
254
|
+
logger.debug(
|
|
255
|
+
"context_fusion: rag_chunks=%d rag_tok~%d kg_tok~%d img_tok~%d web_tok~%d budget=%d",
|
|
256
|
+
len(selected),
|
|
257
|
+
rag_used,
|
|
258
|
+
_est_tokens(kg_out),
|
|
259
|
+
_est_tokens(img_block),
|
|
260
|
+
_est_tokens(web_out),
|
|
261
|
+
token_budget,
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
return selected, kg_out, img_block, web_out, rag_used
|