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,164 @@
|
|
|
1
|
+
"""Cognee operational memory client — Phase 4 (with PostgreSQL fallback)."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import uuid
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from knowledge_base.config import cfg
|
|
10
|
+
from knowledge_base.db.client import get_conn
|
|
11
|
+
from knowledge_base.memory.memory_types import MemoryItem, MemoryType
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
_client: "CogneeClient | None" = None
|
|
16
|
+
|
|
17
|
+
_MEMORY_TABLE_SQL = """
|
|
18
|
+
CREATE TABLE IF NOT EXISTS operational_memory (
|
|
19
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
20
|
+
memory_type TEXT NOT NULL,
|
|
21
|
+
content TEXT NOT NULL,
|
|
22
|
+
feature_area TEXT NOT NULL DEFAULT 'Unknown',
|
|
23
|
+
metadata JSONB DEFAULT '{}'::jsonb,
|
|
24
|
+
curator_id TEXT,
|
|
25
|
+
promotion_id UUID,
|
|
26
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
27
|
+
);
|
|
28
|
+
CREATE INDEX IF NOT EXISTS idx_op_memory_feature ON operational_memory(feature_area);
|
|
29
|
+
CREATE INDEX IF NOT EXISTS idx_op_memory_type ON operational_memory(memory_type);
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class CogneeClient:
|
|
34
|
+
"""
|
|
35
|
+
Operational memory client.
|
|
36
|
+
Uses Cognee OSS when COGNEE_ENABLED and package installed; otherwise PostgreSQL store.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self) -> None:
|
|
40
|
+
self._cognee = None
|
|
41
|
+
self._use_cognee = False
|
|
42
|
+
self._ensure_table()
|
|
43
|
+
if cfg.COGNEE_ENABLED:
|
|
44
|
+
try:
|
|
45
|
+
import cognee # noqa: F401
|
|
46
|
+
self._cognee = cognee
|
|
47
|
+
self._use_cognee = True
|
|
48
|
+
logger.info("Cognee: using cognee OSS backend")
|
|
49
|
+
except ImportError:
|
|
50
|
+
logger.info("Cognee: package not installed — using PostgreSQL fallback")
|
|
51
|
+
|
|
52
|
+
def _ensure_table(self) -> None:
|
|
53
|
+
with get_conn() as conn:
|
|
54
|
+
with conn.cursor() as cur:
|
|
55
|
+
cur.execute(_MEMORY_TABLE_SQL)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def available(self) -> bool:
|
|
59
|
+
return True
|
|
60
|
+
|
|
61
|
+
def health_check(self) -> dict[str, Any]:
|
|
62
|
+
return {
|
|
63
|
+
"status": "healthy",
|
|
64
|
+
"backend": "cognee" if self._use_cognee else "postgresql_fallback",
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
def add_memory(
|
|
68
|
+
self,
|
|
69
|
+
content: str,
|
|
70
|
+
memory_type: MemoryType | str,
|
|
71
|
+
feature_area: str = "Unknown",
|
|
72
|
+
metadata: dict | None = None,
|
|
73
|
+
curator_id: str | None = None,
|
|
74
|
+
promotion_id: str | None = None,
|
|
75
|
+
) -> str:
|
|
76
|
+
"""Add curated memory item (promotion gate enforced at ingest layer)."""
|
|
77
|
+
mtype = memory_type.value if isinstance(memory_type, MemoryType) else str(memory_type)
|
|
78
|
+
mem_id = str(uuid.uuid4())
|
|
79
|
+
|
|
80
|
+
if self._use_cognee and self._cognee:
|
|
81
|
+
try:
|
|
82
|
+
self._cognee.add(content, metadata={
|
|
83
|
+
"memory_type": mtype,
|
|
84
|
+
"feature_area": feature_area,
|
|
85
|
+
**(metadata or {}),
|
|
86
|
+
})
|
|
87
|
+
except Exception as e:
|
|
88
|
+
logger.warning("Cognee add failed, using PG fallback: %s", e)
|
|
89
|
+
|
|
90
|
+
sql = """
|
|
91
|
+
INSERT INTO operational_memory (id, memory_type, content, feature_area, metadata, curator_id, promotion_id)
|
|
92
|
+
VALUES (%s::uuid, %s, %s, %s, %s::jsonb, %s, %s::uuid)
|
|
93
|
+
"""
|
|
94
|
+
with get_conn() as conn:
|
|
95
|
+
with conn.cursor() as cur:
|
|
96
|
+
cur.execute(sql, (
|
|
97
|
+
mem_id, mtype, content, feature_area,
|
|
98
|
+
json.dumps(metadata or {}),
|
|
99
|
+
curator_id,
|
|
100
|
+
promotion_id,
|
|
101
|
+
))
|
|
102
|
+
return mem_id
|
|
103
|
+
|
|
104
|
+
def search(
|
|
105
|
+
self,
|
|
106
|
+
query: str,
|
|
107
|
+
memory_types: list[str] | None = None,
|
|
108
|
+
feature_area: str | None = None,
|
|
109
|
+
top_k: int = 5,
|
|
110
|
+
min_relevance: float = 0.5,
|
|
111
|
+
) -> list[MemoryItem]:
|
|
112
|
+
"""Search operational memory with strict feature_area isolation."""
|
|
113
|
+
clauses = ["content ILIKE %s OR feature_area ILIKE %s"]
|
|
114
|
+
params: list[Any] = [f"%{query[:100]}%", f"%{query[:50]}%"]
|
|
115
|
+
|
|
116
|
+
if memory_types:
|
|
117
|
+
clauses.append("memory_type = ANY(%s)")
|
|
118
|
+
params.append(memory_types)
|
|
119
|
+
if feature_area and feature_area != "Unknown":
|
|
120
|
+
clauses.append("feature_area ILIKE %s")
|
|
121
|
+
params.append(f"%{feature_area}%")
|
|
122
|
+
|
|
123
|
+
sql = f"""
|
|
124
|
+
SELECT id::text, memory_type, content, feature_area, metadata, curator_id, created_at
|
|
125
|
+
FROM operational_memory
|
|
126
|
+
WHERE ({clauses[0]})
|
|
127
|
+
{" AND " + " AND ".join(clauses[1:]) if len(clauses) > 1 else ""}
|
|
128
|
+
ORDER BY created_at DESC
|
|
129
|
+
LIMIT %s
|
|
130
|
+
"""
|
|
131
|
+
params.append(top_k)
|
|
132
|
+
|
|
133
|
+
with get_conn() as conn:
|
|
134
|
+
with conn.cursor() as cur:
|
|
135
|
+
cur.execute(sql, tuple(params))
|
|
136
|
+
rows = cur.fetchall()
|
|
137
|
+
|
|
138
|
+
items = []
|
|
139
|
+
for row in rows:
|
|
140
|
+
score = 0.8 if feature_area and feature_area.lower() in (row[3] or "").lower() else 0.6
|
|
141
|
+
if score < min_relevance:
|
|
142
|
+
continue
|
|
143
|
+
try:
|
|
144
|
+
mtype = MemoryType(row[1])
|
|
145
|
+
except ValueError:
|
|
146
|
+
mtype = MemoryType.PATTERN
|
|
147
|
+
items.append(MemoryItem(
|
|
148
|
+
memory_id=row[0],
|
|
149
|
+
memory_type=mtype,
|
|
150
|
+
content=row[2],
|
|
151
|
+
feature_area=row[3] or "Unknown",
|
|
152
|
+
relevance_score=score,
|
|
153
|
+
applied_at=row[6].isoformat() if row[6] else None,
|
|
154
|
+
curator=row[5],
|
|
155
|
+
metadata=row[4] if isinstance(row[4], dict) else {},
|
|
156
|
+
))
|
|
157
|
+
return items
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def get_cognee_client() -> CogneeClient:
|
|
161
|
+
global _client
|
|
162
|
+
if _client is None:
|
|
163
|
+
_client = CogneeClient()
|
|
164
|
+
return _client
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Ingest curated feedback promotions into operational memory — Phase 4."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from knowledge_base.db.feedback_schema import list_promotions
|
|
7
|
+
from knowledge_base.memory.cognee_client import get_cognee_client
|
|
8
|
+
from knowledge_base.memory.memory_types import MemoryType
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def ingest_curated_feedback(*, limit: int = 50, dry_run: bool = False) -> dict:
|
|
14
|
+
"""
|
|
15
|
+
Ingest only APPROVED+applied feedback from feedback_promotions.
|
|
16
|
+
Cognee NEVER receives raw feedback_events — curation is mandatory.
|
|
17
|
+
"""
|
|
18
|
+
promotions = list_promotions(status="applied", limit=limit)
|
|
19
|
+
client = get_cognee_client()
|
|
20
|
+
ingested = 0
|
|
21
|
+
skipped = 0
|
|
22
|
+
|
|
23
|
+
for promo in promotions:
|
|
24
|
+
content = promo.get("curator_notes") or ""
|
|
25
|
+
promoted = promo.get("promoted_content") or {}
|
|
26
|
+
if isinstance(promoted, dict):
|
|
27
|
+
content = content or promoted.get("curator_notes") or str(promoted)
|
|
28
|
+
|
|
29
|
+
if not content.strip():
|
|
30
|
+
skipped += 1
|
|
31
|
+
continue
|
|
32
|
+
|
|
33
|
+
feature_area = _infer_feature(promo.get("query", ""))
|
|
34
|
+
if dry_run:
|
|
35
|
+
ingested += 1
|
|
36
|
+
continue
|
|
37
|
+
|
|
38
|
+
client.add_memory(
|
|
39
|
+
content=content,
|
|
40
|
+
memory_type=MemoryType.FEEDBACK,
|
|
41
|
+
feature_area=feature_area,
|
|
42
|
+
metadata={"feedback_event_id": promo["feedback_event_id"], "promotion_id": promo["id"]},
|
|
43
|
+
curator_id=promo.get("curator_id"),
|
|
44
|
+
promotion_id=promo["id"],
|
|
45
|
+
)
|
|
46
|
+
ingested += 1
|
|
47
|
+
|
|
48
|
+
logger.info("Memory ingest: %d ingested, %d skipped", ingested, skipped)
|
|
49
|
+
return {"ingested": ingested, "skipped": skipped, "dry_run": dry_run}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _infer_feature(query: str) -> str:
|
|
53
|
+
q = query.lower()
|
|
54
|
+
if "entra" in q or "sso" in q:
|
|
55
|
+
return "Entra ID SSO"
|
|
56
|
+
if "wifi" in q:
|
|
57
|
+
return "Network WiFi 802.1x"
|
|
58
|
+
if "icg" in q or "cert" in q:
|
|
59
|
+
return "ICG Certificate Management"
|
|
60
|
+
return "IGEL OS General"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Memory category definitions — Phase 4."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from enum import Enum
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MemoryType(str, Enum):
|
|
9
|
+
FEEDBACK = "FeedbackMemory"
|
|
10
|
+
EXECUTION = "ExecutionMemory"
|
|
11
|
+
PATTERN = "PatternMemory"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class MemoryItem:
|
|
16
|
+
memory_id: str
|
|
17
|
+
memory_type: MemoryType
|
|
18
|
+
content: str
|
|
19
|
+
feature_area: str
|
|
20
|
+
relevance_score: float = 0.0
|
|
21
|
+
applied_at: str | None = None
|
|
22
|
+
curator: str | None = None
|
|
23
|
+
metadata: dict | None = None
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import List, Optional, Dict, Any, Literal
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
|
|
6
|
+
# ==========================================
|
|
7
|
+
# 1. Requirement Understanding
|
|
8
|
+
# ==========================================
|
|
9
|
+
class RequirementUnderstanding(BaseModel):
|
|
10
|
+
"""AI extracted metadata from a Jira Requirement."""
|
|
11
|
+
actors: List[str] = Field(default_factory=list, description="Users, systems, or roles involved.")
|
|
12
|
+
entities: List[str] = Field(default_factory=list, description="Core business objects (e.g., 'Device', 'Profile').")
|
|
13
|
+
business_rules: List[str] = Field(default_factory=list, description="Explicit rules stated in the requirement.")
|
|
14
|
+
validations: List[str] = Field(default_factory=list, description="Required data or state validations.")
|
|
15
|
+
constraints: List[str] = Field(default_factory=list, description="Technical or business limitations.")
|
|
16
|
+
dependencies: List[str] = Field(default_factory=list, description="Pre-requisites or downstream impacts.")
|
|
17
|
+
|
|
18
|
+
# ==========================================
|
|
19
|
+
# 2. Xray Core Models (Requirement -> Scenario)
|
|
20
|
+
# ==========================================
|
|
21
|
+
class TestStep(BaseModel):
|
|
22
|
+
"""Represents a single step in an Xray Test (IGEL Scenario)."""
|
|
23
|
+
step_number: int
|
|
24
|
+
action: str
|
|
25
|
+
test_data: Optional[str] = None
|
|
26
|
+
expected_result: Optional[str] = None
|
|
27
|
+
|
|
28
|
+
class TestRequirement(BaseModel):
|
|
29
|
+
"""Represents an Xray Test Issue (IGEL Test Requirement)."""
|
|
30
|
+
jira_key: str
|
|
31
|
+
summary: str
|
|
32
|
+
description: Optional[str] = None
|
|
33
|
+
labels: List[str] = Field(default_factory=list)
|
|
34
|
+
components: List[str] = Field(default_factory=list)
|
|
35
|
+
steps: List[TestStep] = Field(default_factory=list)
|
|
36
|
+
understanding: Optional[RequirementUnderstanding] = None
|
|
37
|
+
|
|
38
|
+
# ==========================================
|
|
39
|
+
# 3. Business Rules Layer
|
|
40
|
+
# ==========================================
|
|
41
|
+
class BusinessRule(BaseModel):
|
|
42
|
+
"""Generated business rule covering a specific aspect of a scenario."""
|
|
43
|
+
rule_type: Literal["positive", "negative", "edge"]
|
|
44
|
+
description: str
|
|
45
|
+
|
|
46
|
+
class ScenarioWithRules(BaseModel):
|
|
47
|
+
"""A scenario mapped to its exhaustive business rules."""
|
|
48
|
+
scenario: TestStep
|
|
49
|
+
business_rules: List[BusinessRule]
|
|
50
|
+
|
|
51
|
+
# ==========================================
|
|
52
|
+
# 4. Generated Artifacts
|
|
53
|
+
# ==========================================
|
|
54
|
+
class ManualTestStep(BaseModel):
|
|
55
|
+
"""A detailed step for a manual test case execution."""
|
|
56
|
+
step_number: int
|
|
57
|
+
action: str
|
|
58
|
+
data: str
|
|
59
|
+
expected_result: str
|
|
60
|
+
|
|
61
|
+
class GeneratedTestCase(BaseModel):
|
|
62
|
+
"""A concrete test case generated from a business rule."""
|
|
63
|
+
tc_reference: str
|
|
64
|
+
business_rule: BusinessRule
|
|
65
|
+
title: str
|
|
66
|
+
test_type: Literal["positive", "negative", "edge"]
|
|
67
|
+
priority: Literal["critical", "high", "medium", "low"]
|
|
68
|
+
preconditions: List[str] = Field(default_factory=list)
|
|
69
|
+
steps: List[ManualTestStep] = Field(default_factory=list)
|
|
70
|
+
|
|
71
|
+
class ConfidenceMetrics(BaseModel):
|
|
72
|
+
"""Deterministic hybrid confidence score breakdown."""
|
|
73
|
+
coverage_score: float = 0.0
|
|
74
|
+
traceability_score: float = 0.0
|
|
75
|
+
completeness_score: float = 0.0
|
|
76
|
+
historical_similarity_score: float = 0.0
|
|
77
|
+
automation_feasibility_score: float = 0.0
|
|
78
|
+
total_score: float = 0.0
|
|
79
|
+
|
|
80
|
+
class TestCaseWithConfidence(BaseModel):
|
|
81
|
+
"""Test case bundled with its validation and confidence metrics."""
|
|
82
|
+
test_case: GeneratedTestCase
|
|
83
|
+
confidence: ConfidenceMetrics
|
|
84
|
+
is_approved: bool = False
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CSV / Excel Parser
|
|
3
|
+
Strategy:
|
|
4
|
+
- Each sheet = one logical document group
|
|
5
|
+
- Rows are grouped into chunks by logical context (not arbitrary row counts)
|
|
6
|
+
- Column headers are always preserved in every chunk (critical for context)
|
|
7
|
+
- Defect/Jira CSVs: group by module/component column if present
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
import re
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from knowledge_base.parsers.markdown_parser import Section
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
_GROUP_COLUMNS = ["module", "component", "area", "feature", "category", "type", "status"]
|
|
16
|
+
_ROWS_PER_CHUNK = 20 # max rows per chunk when no grouping column found
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def parse_csv(file_path: Path) -> list[Section]:
|
|
20
|
+
import pandas as pd
|
|
21
|
+
suffix = file_path.suffix.lower()
|
|
22
|
+
|
|
23
|
+
if suffix in (".xlsx", ".xls"):
|
|
24
|
+
xl = pd.ExcelFile(str(file_path))
|
|
25
|
+
sections: list[Section] = []
|
|
26
|
+
for sheet_name in xl.sheet_names:
|
|
27
|
+
df = xl.parse(sheet_name)
|
|
28
|
+
sections.extend(_dataframe_to_sections(df, f"{file_path.stem} - {sheet_name}"))
|
|
29
|
+
return sections
|
|
30
|
+
|
|
31
|
+
elif suffix == ".csv":
|
|
32
|
+
try:
|
|
33
|
+
df = pd.read_csv(str(file_path), encoding="utf-8")
|
|
34
|
+
except UnicodeDecodeError:
|
|
35
|
+
df = pd.read_csv(str(file_path), encoding="latin-1")
|
|
36
|
+
return _dataframe_to_sections(df, file_path.stem)
|
|
37
|
+
|
|
38
|
+
raise ValueError(f"Unsupported tabular type: {suffix}")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _dataframe_to_sections(df, source_name: str) -> list[Section]:
|
|
42
|
+
import pandas as pd
|
|
43
|
+
|
|
44
|
+
# Clean up: drop fully empty rows/columns
|
|
45
|
+
df = df.dropna(how="all").dropna(axis=1, how="all")
|
|
46
|
+
df = df.fillna("")
|
|
47
|
+
df.columns = [str(c).strip() for c in df.columns]
|
|
48
|
+
|
|
49
|
+
if df.empty:
|
|
50
|
+
return []
|
|
51
|
+
|
|
52
|
+
# Find grouping column
|
|
53
|
+
group_col = _find_group_column(df.columns.tolist())
|
|
54
|
+
|
|
55
|
+
if group_col:
|
|
56
|
+
return _group_by_column(df, group_col, source_name)
|
|
57
|
+
else:
|
|
58
|
+
return _chunk_by_rows(df, source_name)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _find_group_column(columns: list[str]) -> str | None:
|
|
62
|
+
col_lower = {c.lower(): c for c in columns}
|
|
63
|
+
for key in _GROUP_COLUMNS:
|
|
64
|
+
if key in col_lower:
|
|
65
|
+
return col_lower[key]
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _group_by_column(df, group_col: str, source_name: str) -> list[Section]:
|
|
70
|
+
sections: list[Section] = []
|
|
71
|
+
headers = df.columns.tolist()
|
|
72
|
+
header_line = "| " + " | ".join(headers) + " |"
|
|
73
|
+
divider = "| " + " | ".join(["---"] * len(headers)) + " |"
|
|
74
|
+
|
|
75
|
+
for group_val, group_df in df.groupby(group_col, sort=False):
|
|
76
|
+
if group_df.empty:
|
|
77
|
+
continue
|
|
78
|
+
rows_md = _rows_to_markdown(group_df, headers)
|
|
79
|
+
content = f"{header_line}\n{divider}\n{rows_md}"
|
|
80
|
+
sections.append(Section(
|
|
81
|
+
title=f"{source_name} - {group_col}: {group_val}",
|
|
82
|
+
level=2,
|
|
83
|
+
content=content,
|
|
84
|
+
metadata={
|
|
85
|
+
"is_table": True,
|
|
86
|
+
"row_count": len(group_df),
|
|
87
|
+
"group_by": group_col,
|
|
88
|
+
"group_value": str(group_val),
|
|
89
|
+
},
|
|
90
|
+
))
|
|
91
|
+
|
|
92
|
+
return sections
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _chunk_by_rows(df, source_name: str) -> list[Section]:
|
|
96
|
+
sections: list[Section] = []
|
|
97
|
+
headers = df.columns.tolist()
|
|
98
|
+
header_line = "| " + " | ".join(headers) + " |"
|
|
99
|
+
divider = "| " + " | ".join(["---"] * len(headers)) + " |"
|
|
100
|
+
|
|
101
|
+
for start in range(0, len(df), _ROWS_PER_CHUNK):
|
|
102
|
+
chunk_df = df.iloc[start:start + _ROWS_PER_CHUNK]
|
|
103
|
+
rows_md = _rows_to_markdown(chunk_df, headers)
|
|
104
|
+
content = f"{header_line}\n{divider}\n{rows_md}"
|
|
105
|
+
end = min(start + _ROWS_PER_CHUNK, len(df))
|
|
106
|
+
sections.append(Section(
|
|
107
|
+
title=f"{source_name} (rows {start+1}-{end})",
|
|
108
|
+
level=2,
|
|
109
|
+
content=content,
|
|
110
|
+
metadata={
|
|
111
|
+
"is_table": True,
|
|
112
|
+
"row_count": len(chunk_df),
|
|
113
|
+
"row_start": start + 1,
|
|
114
|
+
"row_end": end,
|
|
115
|
+
},
|
|
116
|
+
))
|
|
117
|
+
|
|
118
|
+
return sections
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _rows_to_markdown(df, headers: list[str]) -> str:
|
|
122
|
+
lines = []
|
|
123
|
+
for _, row in df.iterrows():
|
|
124
|
+
cells = [str(row[col]).strip().replace("\n", " ") for col in headers]
|
|
125
|
+
lines.append("| " + " | ".join(cells) + " |")
|
|
126
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DOC / DOCX Parser
|
|
3
|
+
Strategy:
|
|
4
|
+
.docx → python-docx (best fidelity, preserves headings/tables)
|
|
5
|
+
.doc → mammoth → fallback → win32com (Windows COM via installed MS Word)
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
import re
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from knowledge_base.parsers.markdown_parser import Section
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def parse_doc(file_path: Path) -> list[Section]:
|
|
14
|
+
suffix = file_path.suffix.lower()
|
|
15
|
+
if suffix == ".docx":
|
|
16
|
+
return _parse_docx(file_path)
|
|
17
|
+
elif suffix == ".doc":
|
|
18
|
+
return _parse_doc_binary(file_path)
|
|
19
|
+
raise ValueError(f"Unsupported doc type: {suffix}")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ── DOCX ─────────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
def _parse_docx(path: Path) -> list[Section]:
|
|
25
|
+
from docx import Document
|
|
26
|
+
doc = Document(str(path))
|
|
27
|
+
|
|
28
|
+
sections: list[Section] = []
|
|
29
|
+
current_title = path.stem
|
|
30
|
+
current_level = 0
|
|
31
|
+
current_paragraphs: list[str] = []
|
|
32
|
+
|
|
33
|
+
for para in doc.paragraphs:
|
|
34
|
+
style_name = para.style.name if para.style else ""
|
|
35
|
+
text = para.text.strip()
|
|
36
|
+
|
|
37
|
+
if not text:
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
# Detect headings by style
|
|
41
|
+
heading_match = re.match(r"Heading (\d+)", style_name)
|
|
42
|
+
if heading_match:
|
|
43
|
+
# Flush current section
|
|
44
|
+
content = "\n\n".join(current_paragraphs).strip()
|
|
45
|
+
if content:
|
|
46
|
+
sections.append(Section(
|
|
47
|
+
title=current_title,
|
|
48
|
+
level=current_level,
|
|
49
|
+
content=content,
|
|
50
|
+
))
|
|
51
|
+
current_title = text
|
|
52
|
+
current_level = int(heading_match.group(1))
|
|
53
|
+
current_paragraphs = []
|
|
54
|
+
else:
|
|
55
|
+
current_paragraphs.append(text)
|
|
56
|
+
|
|
57
|
+
# Flush last section
|
|
58
|
+
content = "\n\n".join(current_paragraphs).strip()
|
|
59
|
+
if content:
|
|
60
|
+
sections.append(Section(title=current_title, level=current_level, content=content))
|
|
61
|
+
|
|
62
|
+
# Extract tables and append as extra sections
|
|
63
|
+
for i, table in enumerate(doc.tables):
|
|
64
|
+
md_table = _docx_table_to_markdown(table)
|
|
65
|
+
if md_table:
|
|
66
|
+
sections.append(Section(
|
|
67
|
+
title=f"Table {i+1}",
|
|
68
|
+
level=3,
|
|
69
|
+
content=md_table,
|
|
70
|
+
metadata={"is_table": True},
|
|
71
|
+
))
|
|
72
|
+
|
|
73
|
+
return sections if sections else [Section(title=path.stem, level=0, content=_docx_full_text(doc))]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _docx_full_text(doc) -> str:
|
|
77
|
+
return "\n\n".join(p.text.strip() for p in doc.paragraphs if p.text.strip())
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _docx_table_to_markdown(table) -> str:
|
|
81
|
+
rows = []
|
|
82
|
+
for row in table.rows:
|
|
83
|
+
cells = [cell.text.strip().replace("\n", " ") for cell in row.cells]
|
|
84
|
+
rows.append(cells)
|
|
85
|
+
if not rows:
|
|
86
|
+
return ""
|
|
87
|
+
cols = max(len(r) for r in rows)
|
|
88
|
+
rows = [r + [""] * (cols - len(r)) for r in rows]
|
|
89
|
+
header = "| " + " | ".join(rows[0]) + " |"
|
|
90
|
+
divider = "| " + " | ".join(["---"] * cols) + " |"
|
|
91
|
+
body = ["| " + " | ".join(r) + " |" for r in rows[1:]]
|
|
92
|
+
return "\n".join([header, divider] + body)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ── DOC (binary) ──────────────────────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
def _parse_doc_binary(path: Path) -> list[Section]:
|
|
98
|
+
text = ""
|
|
99
|
+
|
|
100
|
+
# Attempt 1: mammoth (handles some .doc files)
|
|
101
|
+
try:
|
|
102
|
+
import mammoth
|
|
103
|
+
with open(str(path), "rb") as f:
|
|
104
|
+
result = mammoth.extract_raw_text(f)
|
|
105
|
+
text = result.value.strip()
|
|
106
|
+
if len(text) > 200:
|
|
107
|
+
return _text_to_sections(text, path.stem)
|
|
108
|
+
except Exception:
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
# Attempt 2: Windows COM (requires MS Word installed)
|
|
112
|
+
try:
|
|
113
|
+
import win32com.client
|
|
114
|
+
import pythoncom
|
|
115
|
+
pythoncom.CoInitialize()
|
|
116
|
+
word = win32com.client.Dispatch("Word.Application")
|
|
117
|
+
word.Visible = False
|
|
118
|
+
doc_obj = word.Documents.Open(str(path.resolve()))
|
|
119
|
+
text = doc_obj.Content.Text.strip()
|
|
120
|
+
doc_obj.Close(False)
|
|
121
|
+
word.Quit()
|
|
122
|
+
if text:
|
|
123
|
+
return _text_to_sections(text, path.stem)
|
|
124
|
+
except Exception:
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
# Attempt 3: read raw bytes and extract printable text (last resort)
|
|
128
|
+
try:
|
|
129
|
+
raw = path.read_bytes()
|
|
130
|
+
# DOC files store text in the Word Binary Format — extract ASCII runs
|
|
131
|
+
text = _extract_text_from_binary(raw)
|
|
132
|
+
if len(text) > 100:
|
|
133
|
+
return _text_to_sections(text, path.stem)
|
|
134
|
+
except Exception:
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
raise RuntimeError(f"Could not parse .doc file: {path.name}. "
|
|
138
|
+
"Try converting to .docx using Microsoft Word or LibreOffice first.")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _extract_text_from_binary(raw: bytes) -> str:
|
|
142
|
+
"""Extract readable text runs from DOC binary format (crude but works for plain content)."""
|
|
143
|
+
import re
|
|
144
|
+
# Extract ASCII sequences of 4+ printable chars
|
|
145
|
+
chunks = re.findall(rb"[\x20-\x7E]{4,}", raw)
|
|
146
|
+
text = " ".join(c.decode("ascii", errors="ignore") for c in chunks)
|
|
147
|
+
# Remove excessive whitespace
|
|
148
|
+
text = re.sub(r"\s{3,}", "\n\n", text)
|
|
149
|
+
return text.strip()
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _text_to_sections(text: str, source_name: str) -> list[Section]:
|
|
153
|
+
"""Convert flat text into sections by detecting paragraph boundaries."""
|
|
154
|
+
paragraphs = [p.strip() for p in re.split(r"\n{2,}", text) if p.strip()]
|
|
155
|
+
sections: list[Section] = []
|
|
156
|
+
current_title = source_name
|
|
157
|
+
current_paras: list[str] = []
|
|
158
|
+
|
|
159
|
+
for para in paragraphs:
|
|
160
|
+
# Heuristic: short line that looks like a heading
|
|
161
|
+
if len(para) < 80 and not para.endswith(".") and para[0].isupper():
|
|
162
|
+
if current_paras:
|
|
163
|
+
sections.append(Section(
|
|
164
|
+
title=current_title,
|
|
165
|
+
level=2,
|
|
166
|
+
content="\n\n".join(current_paras),
|
|
167
|
+
))
|
|
168
|
+
current_title = para
|
|
169
|
+
current_paras = []
|
|
170
|
+
else:
|
|
171
|
+
current_paras.append(para)
|
|
172
|
+
|
|
173
|
+
if current_paras:
|
|
174
|
+
sections.append(Section(title=current_title, level=2, content="\n\n".join(current_paras)))
|
|
175
|
+
|
|
176
|
+
return sections if sections else [Section(title=source_name, level=0, content=text)]
|