crca 1.4.0__py3-none-any.whl → 1.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- CRCA.py +172 -7
- MODEL_CARD.md +53 -0
- PKG-INFO +8 -2
- RELEASE_NOTES.md +17 -0
- STABILITY.md +19 -0
- architecture/hybrid/consistency_engine.py +362 -0
- architecture/hybrid/conversation_manager.py +421 -0
- architecture/hybrid/explanation_generator.py +452 -0
- architecture/hybrid/few_shot_learner.py +533 -0
- architecture/hybrid/graph_compressor.py +286 -0
- architecture/hybrid/hybrid_agent.py +4398 -0
- architecture/hybrid/language_compiler.py +623 -0
- architecture/hybrid/main,py +0 -0
- architecture/hybrid/reasoning_tracker.py +322 -0
- architecture/hybrid/self_verifier.py +524 -0
- architecture/hybrid/task_decomposer.py +567 -0
- architecture/hybrid/text_corrector.py +341 -0
- benchmark_results/crca_core_benchmarks.json +178 -0
- branches/crca_sd/crca_sd_realtime.py +6 -2
- branches/general_agent/__init__.py +102 -0
- branches/general_agent/general_agent.py +1400 -0
- branches/general_agent/personality.py +169 -0
- branches/general_agent/utils/__init__.py +19 -0
- branches/general_agent/utils/prompt_builder.py +170 -0
- {crca-1.4.0.dist-info → crca-1.5.0.dist-info}/METADATA +8 -2
- {crca-1.4.0.dist-info → crca-1.5.0.dist-info}/RECORD +303 -20
- crca_core/__init__.py +35 -0
- crca_core/benchmarks/__init__.py +14 -0
- crca_core/benchmarks/synthetic_scm.py +103 -0
- crca_core/core/__init__.py +23 -0
- crca_core/core/api.py +120 -0
- crca_core/core/estimate.py +208 -0
- crca_core/core/godclass.py +72 -0
- crca_core/core/intervention_design.py +174 -0
- crca_core/core/lifecycle.py +48 -0
- crca_core/discovery/__init__.py +9 -0
- crca_core/discovery/tabular.py +193 -0
- crca_core/identify/__init__.py +171 -0
- crca_core/identify/backdoor.py +39 -0
- crca_core/identify/frontdoor.py +48 -0
- crca_core/identify/graph.py +106 -0
- crca_core/identify/id_algorithm.py +43 -0
- crca_core/identify/iv.py +48 -0
- crca_core/models/__init__.py +67 -0
- crca_core/models/provenance.py +56 -0
- crca_core/models/refusal.py +39 -0
- crca_core/models/result.py +83 -0
- crca_core/models/spec.py +151 -0
- crca_core/models/validation.py +68 -0
- crca_core/scm/__init__.py +9 -0
- crca_core/scm/linear_gaussian.py +198 -0
- crca_core/timeseries/__init__.py +6 -0
- crca_core/timeseries/pcmci.py +181 -0
- crca_llm/__init__.py +12 -0
- crca_llm/client.py +85 -0
- crca_llm/coauthor.py +118 -0
- crca_llm/orchestrator.py +289 -0
- crca_llm/types.py +21 -0
- crca_reasoning/__init__.py +16 -0
- crca_reasoning/critique.py +54 -0
- crca_reasoning/godclass.py +206 -0
- crca_reasoning/memory.py +24 -0
- crca_reasoning/rationale.py +10 -0
- crca_reasoning/react_controller.py +81 -0
- crca_reasoning/tool_router.py +97 -0
- crca_reasoning/types.py +40 -0
- crca_sd/__init__.py +15 -0
- crca_sd/crca_sd_core.py +2 -0
- crca_sd/crca_sd_governance.py +2 -0
- crca_sd/crca_sd_mpc.py +2 -0
- crca_sd/crca_sd_realtime.py +2 -0
- crca_sd/crca_sd_tui.py +2 -0
- cuda-keyring_1.1-1_all.deb +0 -0
- cuda-keyring_1.1-1_all.deb.1 +0 -0
- docs/IMAGE_ANNOTATION_USAGE.md +539 -0
- docs/INSTALL_DEEPSPEED.md +125 -0
- docs/api/branches/crca-cg.md +19 -0
- docs/api/branches/crca-q.md +27 -0
- docs/api/branches/crca-sd.md +37 -0
- docs/api/branches/general-agent.md +24 -0
- docs/api/branches/overview.md +19 -0
- docs/api/crca/agent-methods.md +62 -0
- docs/api/crca/operations.md +79 -0
- docs/api/crca/overview.md +32 -0
- docs/api/image-annotation/engine.md +52 -0
- docs/api/image-annotation/overview.md +17 -0
- docs/api/schemas/annotation.md +34 -0
- docs/api/schemas/core-schemas.md +82 -0
- docs/api/schemas/overview.md +32 -0
- docs/api/schemas/policy.md +30 -0
- docs/api/utils/conversation.md +22 -0
- docs/api/utils/graph-reasoner.md +32 -0
- docs/api/utils/overview.md +21 -0
- docs/api/utils/router.md +19 -0
- docs/api/utils/utilities.md +97 -0
- docs/architecture/causal-graphs.md +41 -0
- docs/architecture/data-flow.md +29 -0
- docs/architecture/design-principles.md +33 -0
- docs/architecture/hybrid-agent/components.md +38 -0
- docs/architecture/hybrid-agent/consistency.md +26 -0
- docs/architecture/hybrid-agent/overview.md +44 -0
- docs/architecture/hybrid-agent/reasoning.md +22 -0
- docs/architecture/llm-integration.md +26 -0
- docs/architecture/modular-structure.md +37 -0
- docs/architecture/overview.md +69 -0
- docs/architecture/policy-engine-arch.md +29 -0
- docs/branches/crca-cg/corposwarm.md +39 -0
- docs/branches/crca-cg/esg-scoring.md +30 -0
- docs/branches/crca-cg/multi-agent.md +35 -0
- docs/branches/crca-cg/overview.md +40 -0
- docs/branches/crca-q/alternative-data.md +55 -0
- docs/branches/crca-q/architecture.md +71 -0
- docs/branches/crca-q/backtesting.md +45 -0
- docs/branches/crca-q/causal-engine.md +33 -0
- docs/branches/crca-q/execution.md +39 -0
- docs/branches/crca-q/market-data.md +60 -0
- docs/branches/crca-q/overview.md +58 -0
- docs/branches/crca-q/philosophy.md +60 -0
- docs/branches/crca-q/portfolio-optimization.md +66 -0
- docs/branches/crca-q/risk-management.md +102 -0
- docs/branches/crca-q/setup.md +65 -0
- docs/branches/crca-q/signal-generation.md +61 -0
- docs/branches/crca-q/signal-validation.md +43 -0
- docs/branches/crca-sd/core.md +84 -0
- docs/branches/crca-sd/governance.md +53 -0
- docs/branches/crca-sd/mpc-solver.md +65 -0
- docs/branches/crca-sd/overview.md +59 -0
- docs/branches/crca-sd/realtime.md +28 -0
- docs/branches/crca-sd/tui.md +20 -0
- docs/branches/general-agent/overview.md +37 -0
- docs/branches/general-agent/personality.md +36 -0
- docs/branches/general-agent/prompt-builder.md +30 -0
- docs/changelog/index.md +79 -0
- docs/contributing/code-style.md +69 -0
- docs/contributing/documentation.md +43 -0
- docs/contributing/overview.md +29 -0
- docs/contributing/testing.md +29 -0
- docs/core/crcagent/async-operations.md +65 -0
- docs/core/crcagent/automatic-extraction.md +107 -0
- docs/core/crcagent/batch-prediction.md +80 -0
- docs/core/crcagent/bayesian-inference.md +60 -0
- docs/core/crcagent/causal-graph.md +92 -0
- docs/core/crcagent/counterfactuals.md +96 -0
- docs/core/crcagent/deterministic-simulation.md +78 -0
- docs/core/crcagent/dual-mode-operation.md +82 -0
- docs/core/crcagent/initialization.md +88 -0
- docs/core/crcagent/optimization.md +65 -0
- docs/core/crcagent/overview.md +63 -0
- docs/core/crcagent/time-series.md +57 -0
- docs/core/schemas/annotation.md +30 -0
- docs/core/schemas/core-schemas.md +82 -0
- docs/core/schemas/overview.md +30 -0
- docs/core/schemas/policy.md +41 -0
- docs/core/templates/base-agent.md +31 -0
- docs/core/templates/feature-mixins.md +31 -0
- docs/core/templates/overview.md +29 -0
- docs/core/templates/templates-guide.md +75 -0
- docs/core/tools/mcp-client.md +34 -0
- docs/core/tools/overview.md +24 -0
- docs/core/utils/conversation.md +27 -0
- docs/core/utils/graph-reasoner.md +29 -0
- docs/core/utils/overview.md +27 -0
- docs/core/utils/router.md +27 -0
- docs/core/utils/utilities.md +97 -0
- docs/css/custom.css +84 -0
- docs/examples/basic-usage.md +57 -0
- docs/examples/general-agent/general-agent-examples.md +50 -0
- docs/examples/hybrid-agent/hybrid-agent-examples.md +56 -0
- docs/examples/image-annotation/image-annotation-examples.md +54 -0
- docs/examples/integration/integration-examples.md +58 -0
- docs/examples/overview.md +37 -0
- docs/examples/trading/trading-examples.md +46 -0
- docs/features/causal-reasoning/advanced-topics.md +101 -0
- docs/features/causal-reasoning/counterfactuals.md +43 -0
- docs/features/causal-reasoning/do-calculus.md +50 -0
- docs/features/causal-reasoning/overview.md +47 -0
- docs/features/causal-reasoning/structural-models.md +52 -0
- docs/features/hybrid-agent/advanced-components.md +55 -0
- docs/features/hybrid-agent/core-components.md +64 -0
- docs/features/hybrid-agent/overview.md +34 -0
- docs/features/image-annotation/engine.md +82 -0
- docs/features/image-annotation/features.md +113 -0
- docs/features/image-annotation/integration.md +75 -0
- docs/features/image-annotation/overview.md +53 -0
- docs/features/image-annotation/quickstart.md +73 -0
- docs/features/policy-engine/doctrine-ledger.md +105 -0
- docs/features/policy-engine/monitoring.md +44 -0
- docs/features/policy-engine/mpc-control.md +89 -0
- docs/features/policy-engine/overview.md +46 -0
- docs/getting-started/configuration.md +225 -0
- docs/getting-started/first-agent.md +164 -0
- docs/getting-started/installation.md +144 -0
- docs/getting-started/quickstart.md +137 -0
- docs/index.md +118 -0
- docs/js/mathjax.js +13 -0
- docs/lrm/discovery_proof_notes.md +25 -0
- docs/lrm/finetune_full.md +83 -0
- docs/lrm/math_appendix.md +120 -0
- docs/lrm/overview.md +32 -0
- docs/mkdocs.yml +238 -0
- docs/stylesheets/extra.css +21 -0
- docs_generated/crca_core/CounterfactualResult.md +12 -0
- docs_generated/crca_core/DiscoveryHypothesisResult.md +13 -0
- docs_generated/crca_core/DraftSpec.md +13 -0
- docs_generated/crca_core/EstimateResult.md +13 -0
- docs_generated/crca_core/IdentificationResult.md +17 -0
- docs_generated/crca_core/InterventionDesignResult.md +12 -0
- docs_generated/crca_core/LockedSpec.md +15 -0
- docs_generated/crca_core/RefusalResult.md +12 -0
- docs_generated/crca_core/ValidationReport.md +9 -0
- docs_generated/crca_core/index.md +13 -0
- examples/general_agent_example.py +277 -0
- examples/general_agent_quickstart.py +202 -0
- examples/general_agent_simple.py +92 -0
- examples/hybrid_agent_auto_extraction.py +84 -0
- examples/hybrid_agent_dictionary_demo.py +104 -0
- examples/hybrid_agent_enhanced.py +179 -0
- examples/hybrid_agent_general_knowledge.py +107 -0
- examples/image_annotation_quickstart.py +328 -0
- examples/test_hybrid_fixes.py +77 -0
- image_annotation/__init__.py +27 -0
- image_annotation/annotation_engine.py +2593 -0
- install_cuda_wsl2.sh +59 -0
- install_deepspeed.sh +56 -0
- install_deepspeed_simple.sh +87 -0
- mkdocs.yml +252 -0
- ollama/Modelfile +8 -0
- prompts/__init__.py +2 -1
- prompts/default_crca.py +9 -1
- prompts/general_agent.py +227 -0
- prompts/image_annotation.py +56 -0
- pyproject.toml +17 -2
- requirements-docs.txt +10 -0
- requirements.txt +21 -2
- schemas/__init__.py +26 -1
- schemas/annotation.py +222 -0
- schemas/conversation.py +193 -0
- schemas/hybrid.py +211 -0
- schemas/reasoning.py +276 -0
- schemas_export/crca_core/CounterfactualResult.schema.json +108 -0
- schemas_export/crca_core/DiscoveryHypothesisResult.schema.json +113 -0
- schemas_export/crca_core/DraftSpec.schema.json +635 -0
- schemas_export/crca_core/EstimateResult.schema.json +113 -0
- schemas_export/crca_core/IdentificationResult.schema.json +145 -0
- schemas_export/crca_core/InterventionDesignResult.schema.json +111 -0
- schemas_export/crca_core/LockedSpec.schema.json +646 -0
- schemas_export/crca_core/RefusalResult.schema.json +90 -0
- schemas_export/crca_core/ValidationReport.schema.json +62 -0
- scripts/build_lrm_dataset.py +80 -0
- scripts/export_crca_core_schemas.py +54 -0
- scripts/export_hf_lrm.py +37 -0
- scripts/export_ollama_gguf.py +45 -0
- scripts/generate_changelog.py +157 -0
- scripts/generate_crca_core_docs_from_schemas.py +86 -0
- scripts/run_crca_core_benchmarks.py +163 -0
- scripts/run_full_finetune.py +198 -0
- scripts/run_lrm_eval.py +31 -0
- templates/graph_management.py +29 -0
- tests/conftest.py +9 -0
- tests/test_core.py +2 -3
- tests/test_crca_core_discovery_tabular.py +15 -0
- tests/test_crca_core_estimate_dowhy.py +36 -0
- tests/test_crca_core_identify.py +18 -0
- tests/test_crca_core_intervention_design.py +36 -0
- tests/test_crca_core_linear_gaussian_scm.py +69 -0
- tests/test_crca_core_spec.py +25 -0
- tests/test_crca_core_timeseries_pcmci.py +15 -0
- tests/test_crca_llm_coauthor.py +12 -0
- tests/test_crca_llm_orchestrator.py +80 -0
- tests/test_hybrid_agent_llm_enhanced.py +556 -0
- tests/test_image_annotation_demo.py +376 -0
- tests/test_image_annotation_operational.py +408 -0
- tests/test_image_annotation_unit.py +551 -0
- tests/test_training_moe.py +13 -0
- training/__init__.py +42 -0
- training/datasets.py +140 -0
- training/deepspeed_zero2_0_5b.json +22 -0
- training/deepspeed_zero2_1_5b.json +22 -0
- training/deepspeed_zero3_0_5b.json +28 -0
- training/deepspeed_zero3_14b.json +28 -0
- training/deepspeed_zero3_h100_3gpu.json +20 -0
- training/deepspeed_zero3_offload.json +28 -0
- training/eval.py +92 -0
- training/finetune.py +516 -0
- training/public_datasets.py +89 -0
- training_data/react_train.jsonl +7473 -0
- utils/agent_discovery.py +311 -0
- utils/batch_processor.py +317 -0
- utils/conversation.py +78 -0
- utils/edit_distance.py +118 -0
- utils/formatter.py +33 -0
- utils/graph_reasoner.py +530 -0
- utils/rate_limiter.py +283 -0
- utils/router.py +2 -2
- utils/tool_discovery.py +307 -0
- webui/__init__.py +10 -0
- webui/app.py +229 -0
- webui/config.py +104 -0
- webui/static/css/style.css +332 -0
- webui/static/js/main.js +284 -0
- webui/templates/index.html +42 -0
- tests/test_crca_excel.py +0 -166
- tests/test_data_broker.py +0 -424
- tests/test_palantir.py +0 -349
- {crca-1.4.0.dist-info → crca-1.5.0.dist-info}/WHEEL +0 -0
- {crca-1.4.0.dist-info → crca-1.5.0.dist-info}/licenses/LICENSE +0 -0
webui/app.py
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Flask application for CRCA Web UI.
|
|
3
|
+
|
|
4
|
+
Provides a simple REST API and serves the web interface.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from typing import Dict, Any, Optional
|
|
9
|
+
from flask import Flask, request, jsonify, render_template, session
|
|
10
|
+
from loguru import logger
|
|
11
|
+
|
|
12
|
+
from webui.config import AGENTS, get_default_agent_key, get_agent_info, list_agents
|
|
13
|
+
|
|
14
|
+
# Initialize Flask app
|
|
15
|
+
app = Flask(__name__)
|
|
16
|
+
app.secret_key = "crca-webui-secret-key-change-in-production" # Change in production
|
|
17
|
+
|
|
18
|
+
# In-memory agent storage (session-based)
|
|
19
|
+
# In production, consider using Redis or database
|
|
20
|
+
_agent_instances: Dict[str, Any] = {}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_agent_instance(agent_key: str) -> Optional[Any]:
|
|
24
|
+
"""
|
|
25
|
+
Get or create an agent instance.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
agent_key: Agent registry key
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Agent instance or None if creation fails
|
|
32
|
+
"""
|
|
33
|
+
# Check if we already have an instance for this session
|
|
34
|
+
session_id = session.get("session_id", "default")
|
|
35
|
+
cache_key = f"{session_id}_{agent_key}"
|
|
36
|
+
|
|
37
|
+
if cache_key in _agent_instances:
|
|
38
|
+
return _agent_instances[cache_key]
|
|
39
|
+
|
|
40
|
+
# Get agent info
|
|
41
|
+
agent_info = get_agent_info(agent_key)
|
|
42
|
+
if not agent_info:
|
|
43
|
+
logger.error(f"Agent '{agent_key}' not found in registry")
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
# Create new instance
|
|
47
|
+
agent_class = agent_info["class"]
|
|
48
|
+
try:
|
|
49
|
+
if agent_key == "general":
|
|
50
|
+
# GeneralAgent can be initialized with no args (uses defaults)
|
|
51
|
+
instance = agent_class()
|
|
52
|
+
elif agent_key == "hybrid":
|
|
53
|
+
# HybridAgent can be initialized with no args (uses defaults)
|
|
54
|
+
instance = agent_class()
|
|
55
|
+
else:
|
|
56
|
+
# Generic initialization for other agents
|
|
57
|
+
instance = agent_class()
|
|
58
|
+
|
|
59
|
+
_agent_instances[cache_key] = instance
|
|
60
|
+
logger.info(f"Created {agent_info['name']} instance for session {session_id}")
|
|
61
|
+
return instance
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logger.error(f"Failed to create agent instance: {e}")
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@app.route("/")
|
|
69
|
+
def index() -> str:
|
|
70
|
+
"""Serve the main web UI page."""
|
|
71
|
+
return render_template("index.html")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@app.route("/api/agents", methods=["GET"])
|
|
75
|
+
def api_agents() -> Any:
|
|
76
|
+
"""
|
|
77
|
+
Get list of available agents.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
JSON response with agent list
|
|
81
|
+
"""
|
|
82
|
+
agents = list_agents()
|
|
83
|
+
|
|
84
|
+
# Format for frontend
|
|
85
|
+
agent_list = []
|
|
86
|
+
for key, info in agents.items():
|
|
87
|
+
agent_list.append({
|
|
88
|
+
"key": key,
|
|
89
|
+
"name": info["name"],
|
|
90
|
+
"description": info["description"],
|
|
91
|
+
"requires_llm": info.get("requires_llm", True),
|
|
92
|
+
"default": info.get("default", False),
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
return jsonify({
|
|
96
|
+
"agents": agent_list,
|
|
97
|
+
"default": get_default_agent_key(),
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@app.route("/api/chat", methods=["POST"])
|
|
102
|
+
def api_chat() -> Any:
|
|
103
|
+
"""
|
|
104
|
+
Handle chat message and return agent response.
|
|
105
|
+
|
|
106
|
+
Request body:
|
|
107
|
+
{
|
|
108
|
+
"message": str,
|
|
109
|
+
"agent_key": str (optional, uses default if not provided)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
JSON response with agent reply
|
|
114
|
+
"""
|
|
115
|
+
# Initialize session if needed
|
|
116
|
+
if "session_id" not in session:
|
|
117
|
+
import uuid
|
|
118
|
+
session["session_id"] = str(uuid.uuid4())
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
data = request.get_json()
|
|
122
|
+
if not data:
|
|
123
|
+
return jsonify({"error": "No JSON data provided"}), 400
|
|
124
|
+
|
|
125
|
+
message = data.get("message", "").strip()
|
|
126
|
+
if not message:
|
|
127
|
+
return jsonify({"error": "Message cannot be empty"}), 400
|
|
128
|
+
|
|
129
|
+
# Get agent key (use default if not provided)
|
|
130
|
+
agent_key = data.get("agent_key") or get_default_agent_key()
|
|
131
|
+
if not agent_key:
|
|
132
|
+
return jsonify({"error": "No agents available"}), 500
|
|
133
|
+
|
|
134
|
+
# Get agent instance
|
|
135
|
+
agent = get_agent_instance(agent_key)
|
|
136
|
+
if not agent:
|
|
137
|
+
return jsonify({"error": f"Failed to initialize agent '{agent_key}'"}), 500
|
|
138
|
+
|
|
139
|
+
# Get agent info for response
|
|
140
|
+
agent_info = get_agent_info(agent_key)
|
|
141
|
+
agent_name = agent_info["name"] if agent_info else agent_key
|
|
142
|
+
|
|
143
|
+
# Run agent
|
|
144
|
+
try:
|
|
145
|
+
response = agent.run(message)
|
|
146
|
+
|
|
147
|
+
# Ensure response is a string
|
|
148
|
+
if not isinstance(response, str):
|
|
149
|
+
response = str(response)
|
|
150
|
+
|
|
151
|
+
return jsonify({
|
|
152
|
+
"response": response,
|
|
153
|
+
"agent_type": agent_key,
|
|
154
|
+
"agent_name": agent_name,
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
except Exception as e:
|
|
158
|
+
logger.error(f"Error running agent: {e}")
|
|
159
|
+
return jsonify({
|
|
160
|
+
"error": f"Agent execution failed: {str(e)}",
|
|
161
|
+
"agent_type": agent_key,
|
|
162
|
+
}), 500
|
|
163
|
+
|
|
164
|
+
except Exception as e:
|
|
165
|
+
logger.error(f"Error in chat endpoint: {e}")
|
|
166
|
+
return jsonify({"error": f"Internal server error: {str(e)}"}), 500
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@app.route("/api/switch", methods=["POST"])
|
|
170
|
+
def api_switch() -> Any:
|
|
171
|
+
"""
|
|
172
|
+
Switch to a different agent.
|
|
173
|
+
|
|
174
|
+
Request body:
|
|
175
|
+
{
|
|
176
|
+
"agent_key": str
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
JSON response confirming switch
|
|
181
|
+
"""
|
|
182
|
+
try:
|
|
183
|
+
data = request.get_json()
|
|
184
|
+
if not data:
|
|
185
|
+
return jsonify({"error": "No JSON data provided"}), 400
|
|
186
|
+
|
|
187
|
+
agent_key = data.get("agent_key")
|
|
188
|
+
if not agent_key:
|
|
189
|
+
return jsonify({"error": "agent_key is required"}), 400
|
|
190
|
+
|
|
191
|
+
# Verify agent exists
|
|
192
|
+
agent_info = get_agent_info(agent_key)
|
|
193
|
+
if not agent_info:
|
|
194
|
+
return jsonify({"error": f"Agent '{agent_key}' not found"}), 404
|
|
195
|
+
|
|
196
|
+
# Clear cached instance for this session to force recreation
|
|
197
|
+
session_id = session.get("session_id", "default")
|
|
198
|
+
cache_key = f"{session_id}_{agent_key}"
|
|
199
|
+
if cache_key in _agent_instances:
|
|
200
|
+
del _agent_instances[cache_key]
|
|
201
|
+
|
|
202
|
+
return jsonify({
|
|
203
|
+
"success": True,
|
|
204
|
+
"agent_key": agent_key,
|
|
205
|
+
"agent_name": agent_info["name"],
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
except Exception as e:
|
|
209
|
+
logger.error(f"Error switching agent: {e}")
|
|
210
|
+
return jsonify({"error": f"Internal server error: {str(e)}"}), 500
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@app.errorhandler(404)
|
|
214
|
+
def not_found(error: Any) -> Any:
|
|
215
|
+
"""Handle 404 errors."""
|
|
216
|
+
return jsonify({"error": "Not found"}), 404
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@app.errorhandler(500)
|
|
220
|
+
def internal_error(error: Any) -> Any:
|
|
221
|
+
"""Handle 500 errors."""
|
|
222
|
+
return jsonify({"error": "Internal server error"}), 500
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
if __name__ == "__main__":
|
|
226
|
+
# Run development server
|
|
227
|
+
logger.info("Starting CRCA Web UI server...")
|
|
228
|
+
logger.info(f"Available agents: {list(AGENTS.keys())}")
|
|
229
|
+
app.run(host="0.0.0.0", port=5000, debug=True)
|
webui/config.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration registry for agents and tools.
|
|
3
|
+
|
|
4
|
+
This module provides an extensible registry system for adding new agents
|
|
5
|
+
and tools to the web UI without modifying core application code.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Dict, Any, Optional, Type
|
|
9
|
+
from loguru import logger
|
|
10
|
+
|
|
11
|
+
# Agent registry - add new agents here
|
|
12
|
+
AGENTS: Dict[str, Dict[str, Any]] = {}
|
|
13
|
+
|
|
14
|
+
# Tool registry - add new tools here
|
|
15
|
+
TOOLS: Dict[str, Dict[str, Any]] = {}
|
|
16
|
+
|
|
17
|
+
# Lazy imports to handle optional dependencies
|
|
18
|
+
try:
|
|
19
|
+
from branches.general_agent import GeneralAgent
|
|
20
|
+
GENERAL_AGENT_AVAILABLE = True
|
|
21
|
+
except ImportError as e:
|
|
22
|
+
GeneralAgent = None
|
|
23
|
+
GENERAL_AGENT_AVAILABLE = False
|
|
24
|
+
logger.warning(f"GeneralAgent not available: {e}")
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
from architecture.hybrid.hybrid_agent import HybridAgent
|
|
28
|
+
HYBRID_AGENT_AVAILABLE = True
|
|
29
|
+
except ImportError as e:
|
|
30
|
+
HybridAgent = None
|
|
31
|
+
HYBRID_AGENT_AVAILABLE = False
|
|
32
|
+
logger.warning(f"HybridAgent not available: {e}")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _register_agents() -> None:
|
|
36
|
+
"""Register available agents in the AGENTS dictionary."""
|
|
37
|
+
global AGENTS
|
|
38
|
+
|
|
39
|
+
# Register GeneralAgent if available
|
|
40
|
+
if GENERAL_AGENT_AVAILABLE and GeneralAgent is not None:
|
|
41
|
+
AGENTS["general"] = {
|
|
42
|
+
"name": "General Agent",
|
|
43
|
+
"class": GeneralAgent,
|
|
44
|
+
"description": "LLM-powered general-purpose agent with causal reasoning capabilities",
|
|
45
|
+
"requires_llm": True,
|
|
46
|
+
"default": True, # This is the default agent
|
|
47
|
+
}
|
|
48
|
+
logger.info("Registered GeneralAgent")
|
|
49
|
+
|
|
50
|
+
# Register HybridAgent if available
|
|
51
|
+
if HYBRID_AGENT_AVAILABLE and HybridAgent is not None:
|
|
52
|
+
AGENTS["hybrid"] = {
|
|
53
|
+
"name": "Hybrid Agent",
|
|
54
|
+
"class": HybridAgent,
|
|
55
|
+
"description": "Symbolic-statistical agent (no LLM required)",
|
|
56
|
+
"requires_llm": False,
|
|
57
|
+
"default": False,
|
|
58
|
+
}
|
|
59
|
+
logger.info("Registered HybridAgent")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_default_agent_key() -> Optional[str]:
|
|
63
|
+
"""
|
|
64
|
+
Get the key of the default agent.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Agent key string or None if no agents available
|
|
68
|
+
"""
|
|
69
|
+
for key, agent_info in AGENTS.items():
|
|
70
|
+
if agent_info.get("default", False):
|
|
71
|
+
return key
|
|
72
|
+
|
|
73
|
+
# If no default marked, return first available agent
|
|
74
|
+
if AGENTS:
|
|
75
|
+
return list(AGENTS.keys())[0]
|
|
76
|
+
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def get_agent_info(key: str) -> Optional[Dict[str, Any]]:
|
|
81
|
+
"""
|
|
82
|
+
Get agent information by key.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
key: Agent registry key
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Agent info dictionary or None if not found
|
|
89
|
+
"""
|
|
90
|
+
return AGENTS.get(key)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def list_agents() -> Dict[str, Dict[str, Any]]:
|
|
94
|
+
"""
|
|
95
|
+
List all registered agents.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Dictionary of agent keys to agent info
|
|
99
|
+
"""
|
|
100
|
+
return AGENTS.copy()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# Initialize agent registry on module import
|
|
104
|
+
_register_agents()
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/* CRCA Web UI - European Professional Style */
|
|
2
|
+
/* Muted greys, whites, subtle blues - no bright colors, no rounded corners */
|
|
3
|
+
|
|
4
|
+
* {
|
|
5
|
+
margin: 0;
|
|
6
|
+
padding: 0;
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
body {
|
|
11
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
12
|
+
background-color: #f8f9fa;
|
|
13
|
+
color: #2c2c2c;
|
|
14
|
+
line-height: 1.6;
|
|
15
|
+
height: 100vh;
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
font-size: 14px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Header */
|
|
22
|
+
.header {
|
|
23
|
+
background-color: #4a5568;
|
|
24
|
+
color: #ffffff;
|
|
25
|
+
padding: 1.25rem 2rem;
|
|
26
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
|
|
27
|
+
border-bottom: 2px solid #2d3748;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.header h1 {
|
|
31
|
+
font-size: 1.5rem;
|
|
32
|
+
font-weight: 600;
|
|
33
|
+
letter-spacing: 0.5px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.header p {
|
|
37
|
+
font-size: 0.875rem;
|
|
38
|
+
opacity: 0.9;
|
|
39
|
+
margin-top: 0.25rem;
|
|
40
|
+
font-weight: 300;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* Controls Bar */
|
|
44
|
+
.controls {
|
|
45
|
+
background-color: #ffffff;
|
|
46
|
+
padding: 1rem 2rem;
|
|
47
|
+
border-bottom: 1px solid #e2e8f0;
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
gap: 1.5rem;
|
|
51
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.agent-selector {
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
gap: 0.75rem;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.agent-selector label {
|
|
61
|
+
font-weight: 500;
|
|
62
|
+
font-size: 0.875rem;
|
|
63
|
+
color: #4a5568;
|
|
64
|
+
text-transform: uppercase;
|
|
65
|
+
letter-spacing: 0.5px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.agent-selector select {
|
|
69
|
+
padding: 0.5rem 1rem;
|
|
70
|
+
border: 1px solid #cbd5e0;
|
|
71
|
+
background-color: #ffffff;
|
|
72
|
+
font-size: 0.875rem;
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
color: #2d3748;
|
|
75
|
+
font-family: inherit;
|
|
76
|
+
min-width: 200px;
|
|
77
|
+
transition: border-color 0.2s ease;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.agent-selector select:hover {
|
|
81
|
+
border-color: #718096;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.agent-selector select:focus {
|
|
85
|
+
outline: none;
|
|
86
|
+
border-color: #4a5568;
|
|
87
|
+
box-shadow: 0 0 0 2px rgba(74, 85, 104, 0.1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.agent-info {
|
|
91
|
+
font-size: 0.8125rem;
|
|
92
|
+
color: #718096;
|
|
93
|
+
font-style: normal;
|
|
94
|
+
padding-left: 1rem;
|
|
95
|
+
border-left: 1px solid #e2e8f0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Chat Container */
|
|
99
|
+
.chat-container {
|
|
100
|
+
flex: 1;
|
|
101
|
+
display: flex;
|
|
102
|
+
flex-direction: column;
|
|
103
|
+
overflow: hidden;
|
|
104
|
+
background-color: #ffffff;
|
|
105
|
+
margin: 1rem;
|
|
106
|
+
border: 1px solid #e2e8f0;
|
|
107
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.chat-messages {
|
|
111
|
+
flex: 1;
|
|
112
|
+
overflow-y: auto;
|
|
113
|
+
padding: 1.5rem;
|
|
114
|
+
display: flex;
|
|
115
|
+
flex-direction: column;
|
|
116
|
+
gap: 1rem;
|
|
117
|
+
background-color: #fafbfc;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.chat-messages::-webkit-scrollbar {
|
|
121
|
+
width: 8px;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.chat-messages::-webkit-scrollbar-track {
|
|
125
|
+
background: #f1f5f9;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.chat-messages::-webkit-scrollbar-thumb {
|
|
129
|
+
background: #cbd5e0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.chat-messages::-webkit-scrollbar-thumb:hover {
|
|
133
|
+
background: #a0aec0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* Messages */
|
|
137
|
+
.message {
|
|
138
|
+
max-width: 75%;
|
|
139
|
+
padding: 0.875rem 1.125rem;
|
|
140
|
+
word-wrap: break-word;
|
|
141
|
+
border: 1px solid transparent;
|
|
142
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.message.user {
|
|
146
|
+
align-self: flex-end;
|
|
147
|
+
background-color: #4a5568;
|
|
148
|
+
color: #ffffff;
|
|
149
|
+
border-color: #2d3748;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.message.agent {
|
|
153
|
+
align-self: flex-start;
|
|
154
|
+
background-color: #ffffff;
|
|
155
|
+
color: #2d3748;
|
|
156
|
+
border-color: #e2e8f0;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.message.error {
|
|
160
|
+
align-self: flex-start;
|
|
161
|
+
background-color: #c53030;
|
|
162
|
+
color: #ffffff;
|
|
163
|
+
border-color: #9b2c2c;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.message-header {
|
|
167
|
+
font-size: 0.75rem;
|
|
168
|
+
opacity: 0.85;
|
|
169
|
+
margin-bottom: 0.375rem;
|
|
170
|
+
font-weight: 500;
|
|
171
|
+
text-transform: uppercase;
|
|
172
|
+
letter-spacing: 0.5px;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.message.user .message-header {
|
|
176
|
+
color: #e2e8f0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.message.agent .message-header {
|
|
180
|
+
color: #718096;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.message-content {
|
|
184
|
+
white-space: pre-wrap;
|
|
185
|
+
line-height: 1.5;
|
|
186
|
+
font-size: 0.9375rem;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.message.user .message-content {
|
|
190
|
+
color: #ffffff;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.message.agent .message-content {
|
|
194
|
+
color: #2d3748;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* Input Container */
|
|
198
|
+
.chat-input-container {
|
|
199
|
+
padding: 1.125rem 1.5rem;
|
|
200
|
+
border-top: 1px solid #e2e8f0;
|
|
201
|
+
display: flex;
|
|
202
|
+
gap: 0.75rem;
|
|
203
|
+
background-color: #ffffff;
|
|
204
|
+
align-items: flex-end;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.chat-input {
|
|
208
|
+
flex: 1;
|
|
209
|
+
padding: 0.75rem 1rem;
|
|
210
|
+
border: 1px solid #cbd5e0;
|
|
211
|
+
background-color: #ffffff;
|
|
212
|
+
font-size: 0.9375rem;
|
|
213
|
+
font-family: inherit;
|
|
214
|
+
resize: none;
|
|
215
|
+
min-height: 50px;
|
|
216
|
+
max-height: 150px;
|
|
217
|
+
color: #2d3748;
|
|
218
|
+
line-height: 1.5;
|
|
219
|
+
transition: border-color 0.2s ease;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.chat-input:focus {
|
|
223
|
+
outline: none;
|
|
224
|
+
border-color: #4a5568;
|
|
225
|
+
box-shadow: 0 0 0 2px rgba(74, 85, 104, 0.1);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.chat-input:disabled {
|
|
229
|
+
background-color: #f7fafc;
|
|
230
|
+
color: #a0aec0;
|
|
231
|
+
cursor: not-allowed;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.chat-input::placeholder {
|
|
235
|
+
color: #a0aec0;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.send-button {
|
|
239
|
+
padding: 0.75rem 1.75rem;
|
|
240
|
+
background-color: #4a5568;
|
|
241
|
+
color: #ffffff;
|
|
242
|
+
border: 1px solid #2d3748;
|
|
243
|
+
font-size: 0.9375rem;
|
|
244
|
+
cursor: pointer;
|
|
245
|
+
font-weight: 500;
|
|
246
|
+
transition: background-color 0.2s ease, border-color 0.2s ease;
|
|
247
|
+
text-transform: uppercase;
|
|
248
|
+
letter-spacing: 0.5px;
|
|
249
|
+
font-size: 0.8125rem;
|
|
250
|
+
min-width: 100px;
|
|
251
|
+
height: 50px;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.send-button:hover:not(:disabled) {
|
|
255
|
+
background-color: #2d3748;
|
|
256
|
+
border-color: #1a202c;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.send-button:active:not(:disabled) {
|
|
260
|
+
background-color: #1a202c;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.send-button:disabled {
|
|
264
|
+
background-color: #cbd5e0;
|
|
265
|
+
border-color: #a0aec0;
|
|
266
|
+
color: #718096;
|
|
267
|
+
cursor: not-allowed;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.send-button:focus {
|
|
271
|
+
outline: none;
|
|
272
|
+
box-shadow: 0 0 0 2px rgba(74, 85, 104, 0.2);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* Loading Indicator */
|
|
276
|
+
.loading {
|
|
277
|
+
display: inline-block;
|
|
278
|
+
width: 12px;
|
|
279
|
+
height: 12px;
|
|
280
|
+
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
281
|
+
border-top: 2px solid #ffffff;
|
|
282
|
+
animation: spin 0.8s linear infinite;
|
|
283
|
+
margin-right: 0.5rem;
|
|
284
|
+
vertical-align: middle;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
@keyframes spin {
|
|
288
|
+
0% {
|
|
289
|
+
transform: rotate(0deg);
|
|
290
|
+
}
|
|
291
|
+
100% {
|
|
292
|
+
transform: rotate(360deg);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* Empty State */
|
|
297
|
+
.empty-state {
|
|
298
|
+
text-align: center;
|
|
299
|
+
color: #a0aec0;
|
|
300
|
+
padding: 3rem;
|
|
301
|
+
font-style: italic;
|
|
302
|
+
font-size: 0.9375rem;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/* Typography Enhancements */
|
|
306
|
+
h1, h2, h3, h4, h5, h6 {
|
|
307
|
+
font-weight: 600;
|
|
308
|
+
color: #2d3748;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/* Focus States */
|
|
312
|
+
*:focus {
|
|
313
|
+
outline: none;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
select:focus,
|
|
317
|
+
input:focus,
|
|
318
|
+
textarea:focus,
|
|
319
|
+
button:focus {
|
|
320
|
+
outline: none;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* Selection */
|
|
324
|
+
::selection {
|
|
325
|
+
background-color: #4a5568;
|
|
326
|
+
color: #ffffff;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
::-moz-selection {
|
|
330
|
+
background-color: #4a5568;
|
|
331
|
+
color: #ffffff;
|
|
332
|
+
}
|