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,93 @@
|
|
|
1
|
+
"""Apply curated feedback to the knowledge base — Phase 2."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import re
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from knowledge_base.db.client import get_conn
|
|
9
|
+
from knowledge_base.db.feedback_schema import get_feedback_event, list_promotions
|
|
10
|
+
from knowledge_base.db.schema import ensure_knowledge_graph_tables
|
|
11
|
+
from knowledge_base.feedback.handler import apply_feedback, apply_promotion
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
_REGISTRY_RE = re.compile(r"\b([a-z][a-z0-9_]*(?:\.[a-z0-9_]+)+)\b", re.I)
|
|
16
|
+
_UI_PATH_RE = re.compile(r"(UMS[^>\n]{3,200})", re.I)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def apply_curated_feedback(event_id: str, actor_id: str | None = None) -> dict[str, Any]:
|
|
20
|
+
"""
|
|
21
|
+
Apply an approved feedback event to the system.
|
|
22
|
+
|
|
23
|
+
Strategies:
|
|
24
|
+
- registry_key corrections → upsert into KG
|
|
25
|
+
- ui_path corrections → upsert into KG
|
|
26
|
+
- pattern corrections → stored for Cognee ingestion (Phase 4)
|
|
27
|
+
"""
|
|
28
|
+
event = get_feedback_event(event_id)
|
|
29
|
+
if not event:
|
|
30
|
+
raise ValueError(f"Event not found: {event_id}")
|
|
31
|
+
if event["status"] != "approved":
|
|
32
|
+
raise ValueError(f"Event must be approved before apply (status={event['status']})")
|
|
33
|
+
|
|
34
|
+
ensure_knowledge_graph_tables()
|
|
35
|
+
applied: list[str] = []
|
|
36
|
+
feedback_data = event.get("feedback_data") or {}
|
|
37
|
+
|
|
38
|
+
registry_key = feedback_data.get("registry_key") or feedback_data.get("corrected_registry_key")
|
|
39
|
+
ui_path = feedback_data.get("ui_path") or feedback_data.get("corrected_ui_path")
|
|
40
|
+
|
|
41
|
+
if registry_key:
|
|
42
|
+
_upsert_kg_node("registry_key", registry_key)
|
|
43
|
+
applied.append(f"registry_key:{registry_key}")
|
|
44
|
+
|
|
45
|
+
if ui_path:
|
|
46
|
+
_upsert_kg_node("ui_path", ui_path)
|
|
47
|
+
applied.append(f"ui_path:{ui_path}")
|
|
48
|
+
|
|
49
|
+
correction_text = feedback_data.get("correction") or feedback_data.get("notes") or ""
|
|
50
|
+
if correction_text and not registry_key:
|
|
51
|
+
for key in _REGISTRY_RE.findall(correction_text):
|
|
52
|
+
if key.count(".") >= 2:
|
|
53
|
+
_upsert_kg_node("registry_key", key)
|
|
54
|
+
applied.append(f"registry_key:{key}")
|
|
55
|
+
|
|
56
|
+
if correction_text and not ui_path:
|
|
57
|
+
for path in _UI_PATH_RE.findall(correction_text):
|
|
58
|
+
if ">" in path or "Console" in path:
|
|
59
|
+
_upsert_kg_node("ui_path", path.strip())
|
|
60
|
+
applied.append(f"ui_path:{path.strip()}")
|
|
61
|
+
|
|
62
|
+
action = "updated_kg" if applied else "refined_prompt"
|
|
63
|
+
apply_feedback(event_id, action, actor_id=actor_id)
|
|
64
|
+
|
|
65
|
+
return {"event_id": event_id, "applied_action": action, "changes": applied}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def apply_pending_promotions(limit: int = 10, actor_id: str | None = None) -> list[dict]:
|
|
69
|
+
"""Apply all pending promotions (batch)."""
|
|
70
|
+
promotions = list_promotions(status="pending", limit=limit)
|
|
71
|
+
results = []
|
|
72
|
+
for promo in promotions:
|
|
73
|
+
try:
|
|
74
|
+
result = apply_curated_feedback(promo["feedback_event_id"], actor_id=actor_id)
|
|
75
|
+
apply_promotion(promo["id"], actor_id=actor_id)
|
|
76
|
+
result["promotion_id"] = promo["id"]
|
|
77
|
+
results.append(result)
|
|
78
|
+
except Exception as e:
|
|
79
|
+
logger.error("Failed to apply promotion %s: %s", promo["id"], e)
|
|
80
|
+
results.append({"promotion_id": promo["id"], "error": str(e)})
|
|
81
|
+
return results
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _upsert_kg_node(node_type: str, value: str) -> None:
|
|
85
|
+
normalized = value.lower()[:2000]
|
|
86
|
+
sql = """
|
|
87
|
+
INSERT INTO knowledge_graph_nodes (node_type, value, normalized)
|
|
88
|
+
VALUES (%s, %s, %s)
|
|
89
|
+
ON CONFLICT (node_type, normalized) DO UPDATE SET value = EXCLUDED.value
|
|
90
|
+
"""
|
|
91
|
+
with get_conn() as conn:
|
|
92
|
+
with conn.cursor() as cur:
|
|
93
|
+
cur.execute(sql, (node_type, value[:4000], normalized))
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Feedback curation logic — Phase 2 human-in-the-loop workflow."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from knowledge_base.db.feedback_schema import get_feedback_event, list_feedback_events, list_promotions
|
|
7
|
+
from knowledge_base.feedback.handler import apply_feedback, apply_promotion, curate_feedback
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def list_pending(limit: int = 10, offset: int = 0) -> list[dict]:
|
|
13
|
+
return list_feedback_events(status="pending", limit=limit, offset=offset)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_event(event_id: str) -> dict | None:
|
|
17
|
+
return get_feedback_event(event_id)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def approve(event_id: str, curator_id: str, notes: str | None = None) -> None:
|
|
21
|
+
"""Approve feedback and create promotion record."""
|
|
22
|
+
event = get_feedback_event(event_id)
|
|
23
|
+
if not event:
|
|
24
|
+
raise ValueError(f"Feedback event not found: {event_id}")
|
|
25
|
+
if event["status"] not in ("pending", "reviewed"):
|
|
26
|
+
raise ValueError(f"Cannot approve event in status '{event['status']}'")
|
|
27
|
+
|
|
28
|
+
curate_feedback(event_id, "approved", curator_id, notes)
|
|
29
|
+
logger.info("Feedback approved: %s by %s", event_id, curator_id)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def reject(event_id: str, curator_id: str, notes: str | None = None) -> None:
|
|
33
|
+
event = get_feedback_event(event_id)
|
|
34
|
+
if not event:
|
|
35
|
+
raise ValueError(f"Feedback event not found: {event_id}")
|
|
36
|
+
curate_feedback(event_id, "rejected", curator_id, notes)
|
|
37
|
+
logger.info("Feedback rejected: %s by %s", event_id, curator_id)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def mark_reviewed(event_id: str, curator_id: str, notes: str | None = None) -> None:
|
|
41
|
+
curate_feedback(event_id, "reviewed", curator_id, notes)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def list_approved_promotions(limit: int = 20) -> list[dict]:
|
|
45
|
+
return list_promotions(status="pending", limit=limit)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def list_applied_promotions(limit: int = 20) -> list[dict]:
|
|
49
|
+
return list_promotions(status="applied", limit=limit)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Feedback capture and processing for IGEL KB System (Phase 1+).
|
|
3
|
+
|
|
4
|
+
Two-step flow: Capture → Curate → Promote → Apply
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from knowledge_base.db.feedback_schema import (
|
|
12
|
+
insert_feedback_audit,
|
|
13
|
+
insert_feedback_event,
|
|
14
|
+
insert_feedback_promotion,
|
|
15
|
+
mark_feedback_applied,
|
|
16
|
+
mark_promotion_applied,
|
|
17
|
+
update_feedback_curation,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
VALID_FEEDBACK_TYPES = frozenset({
|
|
23
|
+
"rating",
|
|
24
|
+
"correction",
|
|
25
|
+
"edit",
|
|
26
|
+
"rejection",
|
|
27
|
+
"execution_failure",
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def capture_feedback(
|
|
32
|
+
query: str,
|
|
33
|
+
generated_output: dict,
|
|
34
|
+
feedback_type: str,
|
|
35
|
+
feedback_data: dict,
|
|
36
|
+
*,
|
|
37
|
+
generation_metadata: dict | None = None,
|
|
38
|
+
user_id: str | None = None,
|
|
39
|
+
session_id: str | None = None,
|
|
40
|
+
source_context: dict | None = None,
|
|
41
|
+
) -> str:
|
|
42
|
+
"""
|
|
43
|
+
Capture a user feedback event. Returns feedback event UUID.
|
|
44
|
+
"""
|
|
45
|
+
if feedback_type not in VALID_FEEDBACK_TYPES:
|
|
46
|
+
raise ValueError(
|
|
47
|
+
f"Invalid feedback_type '{feedback_type}'. "
|
|
48
|
+
f"Must be one of: {', '.join(sorted(VALID_FEEDBACK_TYPES))}"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
event_id = insert_feedback_event(
|
|
52
|
+
query=query,
|
|
53
|
+
generated_output=generated_output,
|
|
54
|
+
feedback_type=feedback_type,
|
|
55
|
+
feedback_data=feedback_data,
|
|
56
|
+
generation_metadata=generation_metadata,
|
|
57
|
+
user_id=user_id,
|
|
58
|
+
session_id=session_id,
|
|
59
|
+
source_context=source_context,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
insert_feedback_audit(
|
|
63
|
+
feedback_event_id=event_id,
|
|
64
|
+
action="feedback_captured",
|
|
65
|
+
actor_id=user_id,
|
|
66
|
+
details={"feedback_type": feedback_type},
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
logger.info("Feedback captured: id=%s type=%s", event_id, feedback_type)
|
|
70
|
+
return event_id
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def curate_feedback(
|
|
74
|
+
event_id: str,
|
|
75
|
+
status: str,
|
|
76
|
+
curator_id: str,
|
|
77
|
+
curator_notes: str | None = None,
|
|
78
|
+
) -> None:
|
|
79
|
+
"""
|
|
80
|
+
Update curation status: reviewed, approved, rejected.
|
|
81
|
+
"""
|
|
82
|
+
if status not in ("reviewed", "approved", "rejected"):
|
|
83
|
+
raise ValueError(f"Invalid curation status: {status}")
|
|
84
|
+
|
|
85
|
+
update_feedback_curation(
|
|
86
|
+
event_id=event_id,
|
|
87
|
+
status=status,
|
|
88
|
+
curator_id=curator_id,
|
|
89
|
+
curator_notes=curator_notes,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
insert_feedback_audit(
|
|
93
|
+
feedback_event_id=event_id,
|
|
94
|
+
action=f"feedback_{status}",
|
|
95
|
+
actor_id=curator_id,
|
|
96
|
+
details={"curator_notes": curator_notes},
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if status == "approved":
|
|
100
|
+
insert_feedback_promotion(
|
|
101
|
+
feedback_event_id=event_id,
|
|
102
|
+
curator_id=curator_id,
|
|
103
|
+
promoted_content={"curator_notes": curator_notes},
|
|
104
|
+
curator_notes=curator_notes,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def apply_feedback(
|
|
109
|
+
event_id: str,
|
|
110
|
+
applied_action: str,
|
|
111
|
+
actor_id: str | None = None,
|
|
112
|
+
) -> None:
|
|
113
|
+
"""
|
|
114
|
+
Mark feedback as applied to the system (KG update, prompt refinement, etc.).
|
|
115
|
+
"""
|
|
116
|
+
mark_feedback_applied(event_id=event_id, applied_action=applied_action)
|
|
117
|
+
insert_feedback_audit(
|
|
118
|
+
feedback_event_id=event_id,
|
|
119
|
+
action="feedback_applied",
|
|
120
|
+
actor_id=actor_id,
|
|
121
|
+
details={"applied_action": applied_action},
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def apply_promotion(promotion_id: str, actor_id: str | None = None) -> None:
|
|
126
|
+
"""Mark a curated promotion as applied (ready for Cognee ingestion in Phase 4)."""
|
|
127
|
+
mark_promotion_applied(promotion_id=promotion_id, actor_id=actor_id)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def build_generation_output_payload(
|
|
131
|
+
*,
|
|
132
|
+
test_id: str,
|
|
133
|
+
topic: str,
|
|
134
|
+
feature_area: str,
|
|
135
|
+
markdown_content: str | None,
|
|
136
|
+
python_content: str | None,
|
|
137
|
+
kb_sources: list[str],
|
|
138
|
+
confidence: dict | None = None,
|
|
139
|
+
warnings: list[str] | None = None,
|
|
140
|
+
) -> dict[str, Any]:
|
|
141
|
+
"""Build a JSON-serializable payload for feedback storage."""
|
|
142
|
+
return {
|
|
143
|
+
"test_id": test_id,
|
|
144
|
+
"topic": topic,
|
|
145
|
+
"feature_area": feature_area,
|
|
146
|
+
"markdown": markdown_content,
|
|
147
|
+
"python": python_content,
|
|
148
|
+
"kb_sources": kb_sources,
|
|
149
|
+
"confidence": confidence,
|
|
150
|
+
"warnings": warnings or [],
|
|
151
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Classify user/test-case inputs for routing: transform (gold TC), expand (partial), or synthesize (topic-only).
|
|
3
|
+
|
|
4
|
+
Also picks target detail level (concise / detailed) for generation prompts.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from enum import Enum
|
|
11
|
+
|
|
12
|
+
from knowledge_base.generation.manual_tc_parser import parse_manual_tc, count_parsed_steps
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class InputMode(str, Enum):
|
|
16
|
+
"""How the pipeline should treat the primary input."""
|
|
17
|
+
|
|
18
|
+
TRANSFORM = "transform" # Full gold TC — preserve structure
|
|
19
|
+
EXPAND = "expand" # Partial TC — fill gaps only
|
|
20
|
+
SYNTHESIZE = "synthesize" # Topic/query only — RAG synthesis
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DetailLevel(str, Enum):
|
|
24
|
+
CONCISE = "concise"
|
|
25
|
+
DETAILED = "detailed"
|
|
26
|
+
AUTO = "auto"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class Classification:
|
|
31
|
+
mode: InputMode
|
|
32
|
+
detail_level: DetailLevel
|
|
33
|
+
reason: str
|
|
34
|
+
parsed_step_count: int
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
_JIRA_KEY = re.compile(
|
|
38
|
+
r"\b(QCL|QCAPPS|QC|TC)-?\d+\b",
|
|
39
|
+
re.IGNORECASE,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def classify_input(
|
|
44
|
+
topic: str,
|
|
45
|
+
source_tc: str | None,
|
|
46
|
+
*,
|
|
47
|
+
detail_level: DetailLevel | str = DetailLevel.AUTO,
|
|
48
|
+
) -> Classification:
|
|
49
|
+
"""
|
|
50
|
+
Decide pipeline mode from topic + optional pasted manual test case text.
|
|
51
|
+
|
|
52
|
+
Rules:
|
|
53
|
+
- If `source_tc` is non-empty and parses as 3+ structured steps → TRANSFORM.
|
|
54
|
+
- If `source_tc` is non-empty but 1–2 steps or missing expected results → EXPAND.
|
|
55
|
+
- If only `topic` with structured steps inside topic (rare) → TRANSFORM if parse succeeds.
|
|
56
|
+
- Else → SYNTHESIZE.
|
|
57
|
+
"""
|
|
58
|
+
raw = (source_tc or "").strip()
|
|
59
|
+
topic_clean = (topic or "").strip()
|
|
60
|
+
|
|
61
|
+
# Explicit detail override
|
|
62
|
+
if isinstance(detail_level, str):
|
|
63
|
+
try:
|
|
64
|
+
dl = DetailLevel(detail_level.lower())
|
|
65
|
+
except ValueError:
|
|
66
|
+
dl = DetailLevel.AUTO
|
|
67
|
+
else:
|
|
68
|
+
dl = detail_level
|
|
69
|
+
|
|
70
|
+
# Heuristic: partial = has JIRA id + narrative but few steps
|
|
71
|
+
if raw or (not raw and _looks_like_pasted_tc(topic_clean)):
|
|
72
|
+
combined = raw or topic_clean
|
|
73
|
+
has_jira = bool(_JIRA_KEY.search(combined))
|
|
74
|
+
has_expected_col = "expected result" in combined.lower()
|
|
75
|
+
steps = parse_manual_tc(combined)
|
|
76
|
+
n = count_parsed_steps(steps)
|
|
77
|
+
|
|
78
|
+
if n >= 3 and _steps_look_complete(steps, has_expected_col):
|
|
79
|
+
reason = f"Structured TC with {n} steps — transform mode (preserve fidelity)."
|
|
80
|
+
return Classification(
|
|
81
|
+
InputMode.TRANSFORM,
|
|
82
|
+
_resolve_detail(dl, n, topic_clean),
|
|
83
|
+
reason,
|
|
84
|
+
n,
|
|
85
|
+
)
|
|
86
|
+
if n >= 1 or (has_jira and len(combined) > 200):
|
|
87
|
+
reason = (
|
|
88
|
+
f"Partial TC ({n} parsed steps) — expand mode with KB fill-in."
|
|
89
|
+
if n
|
|
90
|
+
else "JIRA-style text with little step structure — expand mode."
|
|
91
|
+
)
|
|
92
|
+
return Classification(
|
|
93
|
+
InputMode.EXPAND,
|
|
94
|
+
_resolve_detail(dl, n, topic_clean or combined[:80]),
|
|
95
|
+
reason,
|
|
96
|
+
n,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Topic-only synthesis
|
|
100
|
+
return Classification(
|
|
101
|
+
InputMode.SYNTHESIZE,
|
|
102
|
+
_resolve_detail(dl, 0, topic_clean),
|
|
103
|
+
"No structured manual TC provided — synthesize from KB.",
|
|
104
|
+
0,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _looks_like_pasted_tc(text: str) -> bool:
|
|
109
|
+
if not text or len(text) < 80:
|
|
110
|
+
return False
|
|
111
|
+
low = text.lower()
|
|
112
|
+
if "test data" in low and ("expected result" in low or "step" in low):
|
|
113
|
+
return True
|
|
114
|
+
if "#,step,test data" in low.replace(" ", "").lower():
|
|
115
|
+
return True
|
|
116
|
+
if re.search(r"(?m)^\s*\"?#\"?\s*,\s*Step\s*,\s*Test Data", text, re.I):
|
|
117
|
+
return True
|
|
118
|
+
# Many numbered lines like "1,Verify"
|
|
119
|
+
numbered = len(re.findall(r"(?m)^\s*\d+\s*,\s*[^\n]", text))
|
|
120
|
+
return numbered >= 3
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _steps_look_complete(steps: list, has_expected_column_hint: bool) -> bool:
|
|
124
|
+
if not steps:
|
|
125
|
+
return False
|
|
126
|
+
# JIRA/Confluence exports often list "Expected Result" in the header but cells are not parsed.
|
|
127
|
+
if has_expected_column_hint and len(steps) >= 3:
|
|
128
|
+
return True
|
|
129
|
+
with_exp = sum(1 for s in steps if (s.expected_result or "").strip())
|
|
130
|
+
if has_expected_column_hint and with_exp < max(1, len(steps) // 3):
|
|
131
|
+
return False
|
|
132
|
+
titled = sum(1 for s in steps if (s.step_title or "").strip())
|
|
133
|
+
return titled >= max(1, len(steps) // 2)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _resolve_detail(dl: DetailLevel, step_count: int, topic: str) -> DetailLevel:
|
|
137
|
+
if dl != DetailLevel.AUTO:
|
|
138
|
+
return dl
|
|
139
|
+
low = topic.lower()
|
|
140
|
+
if "brief" in low or "concise" in low or "short" in low:
|
|
141
|
+
return DetailLevel.CONCISE
|
|
142
|
+
if "detailed" in low or "novice" in low or "verbose" in low:
|
|
143
|
+
return DetailLevel.DETAILED
|
|
144
|
+
if step_count >= 12:
|
|
145
|
+
return DetailLevel.DETAILED
|
|
146
|
+
if step_count >= 4:
|
|
147
|
+
return DetailLevel.CONCISE
|
|
148
|
+
return DetailLevel.DETAILED
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Parse pasted IGEL manual test cases (CSV export or JIRA-ish markdown) into structured steps.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import csv
|
|
7
|
+
import io
|
|
8
|
+
import re
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class ParsedStep:
|
|
14
|
+
step_id: str
|
|
15
|
+
step_title: str
|
|
16
|
+
test_data: str
|
|
17
|
+
expected_result: str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def parse_manual_tc(text: str) -> list[ParsedStep]:
|
|
21
|
+
text = (text or "").strip()
|
|
22
|
+
if not text:
|
|
23
|
+
return []
|
|
24
|
+
|
|
25
|
+
low_head = text[:800].lower()
|
|
26
|
+
if "test data" in low_head and "expected result" in low_head and _is_csv_like(text):
|
|
27
|
+
parsed = _parse_csv_tc(text)
|
|
28
|
+
if parsed:
|
|
29
|
+
return parsed
|
|
30
|
+
|
|
31
|
+
if re.search(r"(?m)^\s*\"?#\"?\s*,\s*step\s*,", text[:2000], re.I):
|
|
32
|
+
parsed = _parse_csv_tc(text)
|
|
33
|
+
if parsed:
|
|
34
|
+
return parsed
|
|
35
|
+
|
|
36
|
+
jira_steps = _parse_jira_id_column_layout(text)
|
|
37
|
+
if len(jira_steps) >= 3:
|
|
38
|
+
return jira_steps
|
|
39
|
+
|
|
40
|
+
return _parse_loose_numbered_tc(text)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def count_parsed_steps(steps: list[ParsedStep]) -> int:
|
|
44
|
+
return len(steps)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _is_csv_like(text: str) -> bool:
|
|
48
|
+
first = text.split("\n", 1)[0]
|
|
49
|
+
return first.count(",") >= 2
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _parse_csv_tc(text: str) -> list[ParsedStep]:
|
|
53
|
+
"""Handle TestSteps*.csv style: #,Step,Test Data,Expected Result"""
|
|
54
|
+
stream = io.StringIO(text)
|
|
55
|
+
try:
|
|
56
|
+
reader = csv.DictReader(stream)
|
|
57
|
+
except Exception:
|
|
58
|
+
return []
|
|
59
|
+
if not reader.fieldnames:
|
|
60
|
+
return []
|
|
61
|
+
|
|
62
|
+
lower_to_key: dict[str, str] = {}
|
|
63
|
+
for k in reader.fieldnames:
|
|
64
|
+
lower_to_key[k.strip().strip('"').lower()] = k
|
|
65
|
+
|
|
66
|
+
def pick(*cands: str) -> str | None:
|
|
67
|
+
for c in cands:
|
|
68
|
+
lk = c.lower().strip()
|
|
69
|
+
if lk in lower_to_key:
|
|
70
|
+
return lower_to_key[lk]
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
k_id = pick("#", "step id", "id")
|
|
74
|
+
k_step = pick("step")
|
|
75
|
+
k_data = pick("test data", "testdata")
|
|
76
|
+
k_exp = pick("expected result", "expected")
|
|
77
|
+
|
|
78
|
+
steps: list[ParsedStep] = []
|
|
79
|
+
for row in reader:
|
|
80
|
+
if not row:
|
|
81
|
+
continue
|
|
82
|
+
if not any((row.get(k) or "").strip() for k in reader.fieldnames):
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
def gv(k: str | None) -> str:
|
|
86
|
+
if not k:
|
|
87
|
+
return ""
|
|
88
|
+
return (row.get(k) or "").strip()
|
|
89
|
+
|
|
90
|
+
sid = gv(k_id)
|
|
91
|
+
title = gv(k_step)
|
|
92
|
+
data = gv(k_data)
|
|
93
|
+
exp = gv(k_exp)
|
|
94
|
+
if not data and title:
|
|
95
|
+
data = title
|
|
96
|
+
if not sid and not title and not data:
|
|
97
|
+
continue
|
|
98
|
+
steps.append(
|
|
99
|
+
ParsedStep(
|
|
100
|
+
step_id=sid or str(len(steps) + 1),
|
|
101
|
+
step_title=title or (data[:120] if data else f"Step {len(steps) + 1}"),
|
|
102
|
+
test_data=data or title,
|
|
103
|
+
expected_result=exp,
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
return steps
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _parse_jira_id_column_layout(text: str) -> list[ParsedStep]:
|
|
110
|
+
"""
|
|
111
|
+
JIRA / Confluence exports: step id alone on a line, following lines are step content until next id.
|
|
112
|
+
"""
|
|
113
|
+
lines = text.splitlines()
|
|
114
|
+
steps: list[ParsedStep] = []
|
|
115
|
+
i = 0
|
|
116
|
+
while i < len(lines):
|
|
117
|
+
raw = lines[i]
|
|
118
|
+
stripped = raw.strip()
|
|
119
|
+
if re.match(r"^\d{1,3}$", stripped):
|
|
120
|
+
sid = stripped
|
|
121
|
+
i += 1
|
|
122
|
+
buf: list[str] = []
|
|
123
|
+
while i < len(lines):
|
|
124
|
+
nxt = lines[i].strip()
|
|
125
|
+
if re.match(r"^\d{1,3}$", nxt) and not buf:
|
|
126
|
+
break
|
|
127
|
+
if re.match(r"^\d{1,3}$", nxt) and buf:
|
|
128
|
+
break
|
|
129
|
+
buf.append(lines[i].rstrip())
|
|
130
|
+
i += 1
|
|
131
|
+
blob = "\n".join(buf).strip()
|
|
132
|
+
if blob and len(blob) > 3:
|
|
133
|
+
title = blob.split("\n", 1)[0].strip()[:200]
|
|
134
|
+
steps.append(
|
|
135
|
+
ParsedStep(
|
|
136
|
+
step_id=sid,
|
|
137
|
+
step_title=title,
|
|
138
|
+
test_data=blob,
|
|
139
|
+
expected_result="",
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
continue
|
|
143
|
+
i += 1
|
|
144
|
+
return steps
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _parse_loose_numbered_tc(text: str) -> list[ParsedStep]:
|
|
148
|
+
"""
|
|
149
|
+
Best-effort for JIRA PDF/markdown exports: find lines starting with an integer step id.
|
|
150
|
+
"""
|
|
151
|
+
steps: list[ParsedStep] = []
|
|
152
|
+
# Pattern: start of line, digits, whitespace, rest of line
|
|
153
|
+
parts = re.split(r"(?m)^(\d{1,3})\s{2,}(.+?)\s*$", text)
|
|
154
|
+
# re.split with groups returns alternating chunks; skip preamble
|
|
155
|
+
i = 1
|
|
156
|
+
while i < len(parts):
|
|
157
|
+
if i + 1 >= len(parts):
|
|
158
|
+
break
|
|
159
|
+
sid = parts[i]
|
|
160
|
+
first_line = parts[i + 1].strip()
|
|
161
|
+
i += 2
|
|
162
|
+
block = first_line
|
|
163
|
+
# Attach following non-numbered lines until next step
|
|
164
|
+
# This is heuristic — export formats vary
|
|
165
|
+
steps.append(
|
|
166
|
+
ParsedStep(
|
|
167
|
+
step_id=sid,
|
|
168
|
+
step_title=first_line[:120],
|
|
169
|
+
test_data=block,
|
|
170
|
+
expected_result="",
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
if len(steps) >= 2:
|
|
174
|
+
return steps
|
|
175
|
+
|
|
176
|
+
# Fallback: lines like "1,Step title,..." CSV without header
|
|
177
|
+
for m in re.finditer(r"(?m)^\s*(\d+)\s*,\s*([^,\n]+)(?:\s*,\s*([^\n]*))?", text):
|
|
178
|
+
sid, t1, t2 = m.group(1), m.group(2).strip(), (m.group(3) or "").strip()
|
|
179
|
+
steps.append(ParsedStep(step_id=sid, step_title=t1, test_data=t2 or t1, expected_result=""))
|
|
180
|
+
return steps
|