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.
Files changed (306) hide show
  1. CRCA.py +172 -7
  2. MODEL_CARD.md +53 -0
  3. PKG-INFO +8 -2
  4. RELEASE_NOTES.md +17 -0
  5. STABILITY.md +19 -0
  6. architecture/hybrid/consistency_engine.py +362 -0
  7. architecture/hybrid/conversation_manager.py +421 -0
  8. architecture/hybrid/explanation_generator.py +452 -0
  9. architecture/hybrid/few_shot_learner.py +533 -0
  10. architecture/hybrid/graph_compressor.py +286 -0
  11. architecture/hybrid/hybrid_agent.py +4398 -0
  12. architecture/hybrid/language_compiler.py +623 -0
  13. architecture/hybrid/main,py +0 -0
  14. architecture/hybrid/reasoning_tracker.py +322 -0
  15. architecture/hybrid/self_verifier.py +524 -0
  16. architecture/hybrid/task_decomposer.py +567 -0
  17. architecture/hybrid/text_corrector.py +341 -0
  18. benchmark_results/crca_core_benchmarks.json +178 -0
  19. branches/crca_sd/crca_sd_realtime.py +6 -2
  20. branches/general_agent/__init__.py +102 -0
  21. branches/general_agent/general_agent.py +1400 -0
  22. branches/general_agent/personality.py +169 -0
  23. branches/general_agent/utils/__init__.py +19 -0
  24. branches/general_agent/utils/prompt_builder.py +170 -0
  25. {crca-1.4.0.dist-info → crca-1.5.0.dist-info}/METADATA +8 -2
  26. {crca-1.4.0.dist-info → crca-1.5.0.dist-info}/RECORD +303 -20
  27. crca_core/__init__.py +35 -0
  28. crca_core/benchmarks/__init__.py +14 -0
  29. crca_core/benchmarks/synthetic_scm.py +103 -0
  30. crca_core/core/__init__.py +23 -0
  31. crca_core/core/api.py +120 -0
  32. crca_core/core/estimate.py +208 -0
  33. crca_core/core/godclass.py +72 -0
  34. crca_core/core/intervention_design.py +174 -0
  35. crca_core/core/lifecycle.py +48 -0
  36. crca_core/discovery/__init__.py +9 -0
  37. crca_core/discovery/tabular.py +193 -0
  38. crca_core/identify/__init__.py +171 -0
  39. crca_core/identify/backdoor.py +39 -0
  40. crca_core/identify/frontdoor.py +48 -0
  41. crca_core/identify/graph.py +106 -0
  42. crca_core/identify/id_algorithm.py +43 -0
  43. crca_core/identify/iv.py +48 -0
  44. crca_core/models/__init__.py +67 -0
  45. crca_core/models/provenance.py +56 -0
  46. crca_core/models/refusal.py +39 -0
  47. crca_core/models/result.py +83 -0
  48. crca_core/models/spec.py +151 -0
  49. crca_core/models/validation.py +68 -0
  50. crca_core/scm/__init__.py +9 -0
  51. crca_core/scm/linear_gaussian.py +198 -0
  52. crca_core/timeseries/__init__.py +6 -0
  53. crca_core/timeseries/pcmci.py +181 -0
  54. crca_llm/__init__.py +12 -0
  55. crca_llm/client.py +85 -0
  56. crca_llm/coauthor.py +118 -0
  57. crca_llm/orchestrator.py +289 -0
  58. crca_llm/types.py +21 -0
  59. crca_reasoning/__init__.py +16 -0
  60. crca_reasoning/critique.py +54 -0
  61. crca_reasoning/godclass.py +206 -0
  62. crca_reasoning/memory.py +24 -0
  63. crca_reasoning/rationale.py +10 -0
  64. crca_reasoning/react_controller.py +81 -0
  65. crca_reasoning/tool_router.py +97 -0
  66. crca_reasoning/types.py +40 -0
  67. crca_sd/__init__.py +15 -0
  68. crca_sd/crca_sd_core.py +2 -0
  69. crca_sd/crca_sd_governance.py +2 -0
  70. crca_sd/crca_sd_mpc.py +2 -0
  71. crca_sd/crca_sd_realtime.py +2 -0
  72. crca_sd/crca_sd_tui.py +2 -0
  73. cuda-keyring_1.1-1_all.deb +0 -0
  74. cuda-keyring_1.1-1_all.deb.1 +0 -0
  75. docs/IMAGE_ANNOTATION_USAGE.md +539 -0
  76. docs/INSTALL_DEEPSPEED.md +125 -0
  77. docs/api/branches/crca-cg.md +19 -0
  78. docs/api/branches/crca-q.md +27 -0
  79. docs/api/branches/crca-sd.md +37 -0
  80. docs/api/branches/general-agent.md +24 -0
  81. docs/api/branches/overview.md +19 -0
  82. docs/api/crca/agent-methods.md +62 -0
  83. docs/api/crca/operations.md +79 -0
  84. docs/api/crca/overview.md +32 -0
  85. docs/api/image-annotation/engine.md +52 -0
  86. docs/api/image-annotation/overview.md +17 -0
  87. docs/api/schemas/annotation.md +34 -0
  88. docs/api/schemas/core-schemas.md +82 -0
  89. docs/api/schemas/overview.md +32 -0
  90. docs/api/schemas/policy.md +30 -0
  91. docs/api/utils/conversation.md +22 -0
  92. docs/api/utils/graph-reasoner.md +32 -0
  93. docs/api/utils/overview.md +21 -0
  94. docs/api/utils/router.md +19 -0
  95. docs/api/utils/utilities.md +97 -0
  96. docs/architecture/causal-graphs.md +41 -0
  97. docs/architecture/data-flow.md +29 -0
  98. docs/architecture/design-principles.md +33 -0
  99. docs/architecture/hybrid-agent/components.md +38 -0
  100. docs/architecture/hybrid-agent/consistency.md +26 -0
  101. docs/architecture/hybrid-agent/overview.md +44 -0
  102. docs/architecture/hybrid-agent/reasoning.md +22 -0
  103. docs/architecture/llm-integration.md +26 -0
  104. docs/architecture/modular-structure.md +37 -0
  105. docs/architecture/overview.md +69 -0
  106. docs/architecture/policy-engine-arch.md +29 -0
  107. docs/branches/crca-cg/corposwarm.md +39 -0
  108. docs/branches/crca-cg/esg-scoring.md +30 -0
  109. docs/branches/crca-cg/multi-agent.md +35 -0
  110. docs/branches/crca-cg/overview.md +40 -0
  111. docs/branches/crca-q/alternative-data.md +55 -0
  112. docs/branches/crca-q/architecture.md +71 -0
  113. docs/branches/crca-q/backtesting.md +45 -0
  114. docs/branches/crca-q/causal-engine.md +33 -0
  115. docs/branches/crca-q/execution.md +39 -0
  116. docs/branches/crca-q/market-data.md +60 -0
  117. docs/branches/crca-q/overview.md +58 -0
  118. docs/branches/crca-q/philosophy.md +60 -0
  119. docs/branches/crca-q/portfolio-optimization.md +66 -0
  120. docs/branches/crca-q/risk-management.md +102 -0
  121. docs/branches/crca-q/setup.md +65 -0
  122. docs/branches/crca-q/signal-generation.md +61 -0
  123. docs/branches/crca-q/signal-validation.md +43 -0
  124. docs/branches/crca-sd/core.md +84 -0
  125. docs/branches/crca-sd/governance.md +53 -0
  126. docs/branches/crca-sd/mpc-solver.md +65 -0
  127. docs/branches/crca-sd/overview.md +59 -0
  128. docs/branches/crca-sd/realtime.md +28 -0
  129. docs/branches/crca-sd/tui.md +20 -0
  130. docs/branches/general-agent/overview.md +37 -0
  131. docs/branches/general-agent/personality.md +36 -0
  132. docs/branches/general-agent/prompt-builder.md +30 -0
  133. docs/changelog/index.md +79 -0
  134. docs/contributing/code-style.md +69 -0
  135. docs/contributing/documentation.md +43 -0
  136. docs/contributing/overview.md +29 -0
  137. docs/contributing/testing.md +29 -0
  138. docs/core/crcagent/async-operations.md +65 -0
  139. docs/core/crcagent/automatic-extraction.md +107 -0
  140. docs/core/crcagent/batch-prediction.md +80 -0
  141. docs/core/crcagent/bayesian-inference.md +60 -0
  142. docs/core/crcagent/causal-graph.md +92 -0
  143. docs/core/crcagent/counterfactuals.md +96 -0
  144. docs/core/crcagent/deterministic-simulation.md +78 -0
  145. docs/core/crcagent/dual-mode-operation.md +82 -0
  146. docs/core/crcagent/initialization.md +88 -0
  147. docs/core/crcagent/optimization.md +65 -0
  148. docs/core/crcagent/overview.md +63 -0
  149. docs/core/crcagent/time-series.md +57 -0
  150. docs/core/schemas/annotation.md +30 -0
  151. docs/core/schemas/core-schemas.md +82 -0
  152. docs/core/schemas/overview.md +30 -0
  153. docs/core/schemas/policy.md +41 -0
  154. docs/core/templates/base-agent.md +31 -0
  155. docs/core/templates/feature-mixins.md +31 -0
  156. docs/core/templates/overview.md +29 -0
  157. docs/core/templates/templates-guide.md +75 -0
  158. docs/core/tools/mcp-client.md +34 -0
  159. docs/core/tools/overview.md +24 -0
  160. docs/core/utils/conversation.md +27 -0
  161. docs/core/utils/graph-reasoner.md +29 -0
  162. docs/core/utils/overview.md +27 -0
  163. docs/core/utils/router.md +27 -0
  164. docs/core/utils/utilities.md +97 -0
  165. docs/css/custom.css +84 -0
  166. docs/examples/basic-usage.md +57 -0
  167. docs/examples/general-agent/general-agent-examples.md +50 -0
  168. docs/examples/hybrid-agent/hybrid-agent-examples.md +56 -0
  169. docs/examples/image-annotation/image-annotation-examples.md +54 -0
  170. docs/examples/integration/integration-examples.md +58 -0
  171. docs/examples/overview.md +37 -0
  172. docs/examples/trading/trading-examples.md +46 -0
  173. docs/features/causal-reasoning/advanced-topics.md +101 -0
  174. docs/features/causal-reasoning/counterfactuals.md +43 -0
  175. docs/features/causal-reasoning/do-calculus.md +50 -0
  176. docs/features/causal-reasoning/overview.md +47 -0
  177. docs/features/causal-reasoning/structural-models.md +52 -0
  178. docs/features/hybrid-agent/advanced-components.md +55 -0
  179. docs/features/hybrid-agent/core-components.md +64 -0
  180. docs/features/hybrid-agent/overview.md +34 -0
  181. docs/features/image-annotation/engine.md +82 -0
  182. docs/features/image-annotation/features.md +113 -0
  183. docs/features/image-annotation/integration.md +75 -0
  184. docs/features/image-annotation/overview.md +53 -0
  185. docs/features/image-annotation/quickstart.md +73 -0
  186. docs/features/policy-engine/doctrine-ledger.md +105 -0
  187. docs/features/policy-engine/monitoring.md +44 -0
  188. docs/features/policy-engine/mpc-control.md +89 -0
  189. docs/features/policy-engine/overview.md +46 -0
  190. docs/getting-started/configuration.md +225 -0
  191. docs/getting-started/first-agent.md +164 -0
  192. docs/getting-started/installation.md +144 -0
  193. docs/getting-started/quickstart.md +137 -0
  194. docs/index.md +118 -0
  195. docs/js/mathjax.js +13 -0
  196. docs/lrm/discovery_proof_notes.md +25 -0
  197. docs/lrm/finetune_full.md +83 -0
  198. docs/lrm/math_appendix.md +120 -0
  199. docs/lrm/overview.md +32 -0
  200. docs/mkdocs.yml +238 -0
  201. docs/stylesheets/extra.css +21 -0
  202. docs_generated/crca_core/CounterfactualResult.md +12 -0
  203. docs_generated/crca_core/DiscoveryHypothesisResult.md +13 -0
  204. docs_generated/crca_core/DraftSpec.md +13 -0
  205. docs_generated/crca_core/EstimateResult.md +13 -0
  206. docs_generated/crca_core/IdentificationResult.md +17 -0
  207. docs_generated/crca_core/InterventionDesignResult.md +12 -0
  208. docs_generated/crca_core/LockedSpec.md +15 -0
  209. docs_generated/crca_core/RefusalResult.md +12 -0
  210. docs_generated/crca_core/ValidationReport.md +9 -0
  211. docs_generated/crca_core/index.md +13 -0
  212. examples/general_agent_example.py +277 -0
  213. examples/general_agent_quickstart.py +202 -0
  214. examples/general_agent_simple.py +92 -0
  215. examples/hybrid_agent_auto_extraction.py +84 -0
  216. examples/hybrid_agent_dictionary_demo.py +104 -0
  217. examples/hybrid_agent_enhanced.py +179 -0
  218. examples/hybrid_agent_general_knowledge.py +107 -0
  219. examples/image_annotation_quickstart.py +328 -0
  220. examples/test_hybrid_fixes.py +77 -0
  221. image_annotation/__init__.py +27 -0
  222. image_annotation/annotation_engine.py +2593 -0
  223. install_cuda_wsl2.sh +59 -0
  224. install_deepspeed.sh +56 -0
  225. install_deepspeed_simple.sh +87 -0
  226. mkdocs.yml +252 -0
  227. ollama/Modelfile +8 -0
  228. prompts/__init__.py +2 -1
  229. prompts/default_crca.py +9 -1
  230. prompts/general_agent.py +227 -0
  231. prompts/image_annotation.py +56 -0
  232. pyproject.toml +17 -2
  233. requirements-docs.txt +10 -0
  234. requirements.txt +21 -2
  235. schemas/__init__.py +26 -1
  236. schemas/annotation.py +222 -0
  237. schemas/conversation.py +193 -0
  238. schemas/hybrid.py +211 -0
  239. schemas/reasoning.py +276 -0
  240. schemas_export/crca_core/CounterfactualResult.schema.json +108 -0
  241. schemas_export/crca_core/DiscoveryHypothesisResult.schema.json +113 -0
  242. schemas_export/crca_core/DraftSpec.schema.json +635 -0
  243. schemas_export/crca_core/EstimateResult.schema.json +113 -0
  244. schemas_export/crca_core/IdentificationResult.schema.json +145 -0
  245. schemas_export/crca_core/InterventionDesignResult.schema.json +111 -0
  246. schemas_export/crca_core/LockedSpec.schema.json +646 -0
  247. schemas_export/crca_core/RefusalResult.schema.json +90 -0
  248. schemas_export/crca_core/ValidationReport.schema.json +62 -0
  249. scripts/build_lrm_dataset.py +80 -0
  250. scripts/export_crca_core_schemas.py +54 -0
  251. scripts/export_hf_lrm.py +37 -0
  252. scripts/export_ollama_gguf.py +45 -0
  253. scripts/generate_changelog.py +157 -0
  254. scripts/generate_crca_core_docs_from_schemas.py +86 -0
  255. scripts/run_crca_core_benchmarks.py +163 -0
  256. scripts/run_full_finetune.py +198 -0
  257. scripts/run_lrm_eval.py +31 -0
  258. templates/graph_management.py +29 -0
  259. tests/conftest.py +9 -0
  260. tests/test_core.py +2 -3
  261. tests/test_crca_core_discovery_tabular.py +15 -0
  262. tests/test_crca_core_estimate_dowhy.py +36 -0
  263. tests/test_crca_core_identify.py +18 -0
  264. tests/test_crca_core_intervention_design.py +36 -0
  265. tests/test_crca_core_linear_gaussian_scm.py +69 -0
  266. tests/test_crca_core_spec.py +25 -0
  267. tests/test_crca_core_timeseries_pcmci.py +15 -0
  268. tests/test_crca_llm_coauthor.py +12 -0
  269. tests/test_crca_llm_orchestrator.py +80 -0
  270. tests/test_hybrid_agent_llm_enhanced.py +556 -0
  271. tests/test_image_annotation_demo.py +376 -0
  272. tests/test_image_annotation_operational.py +408 -0
  273. tests/test_image_annotation_unit.py +551 -0
  274. tests/test_training_moe.py +13 -0
  275. training/__init__.py +42 -0
  276. training/datasets.py +140 -0
  277. training/deepspeed_zero2_0_5b.json +22 -0
  278. training/deepspeed_zero2_1_5b.json +22 -0
  279. training/deepspeed_zero3_0_5b.json +28 -0
  280. training/deepspeed_zero3_14b.json +28 -0
  281. training/deepspeed_zero3_h100_3gpu.json +20 -0
  282. training/deepspeed_zero3_offload.json +28 -0
  283. training/eval.py +92 -0
  284. training/finetune.py +516 -0
  285. training/public_datasets.py +89 -0
  286. training_data/react_train.jsonl +7473 -0
  287. utils/agent_discovery.py +311 -0
  288. utils/batch_processor.py +317 -0
  289. utils/conversation.py +78 -0
  290. utils/edit_distance.py +118 -0
  291. utils/formatter.py +33 -0
  292. utils/graph_reasoner.py +530 -0
  293. utils/rate_limiter.py +283 -0
  294. utils/router.py +2 -2
  295. utils/tool_discovery.py +307 -0
  296. webui/__init__.py +10 -0
  297. webui/app.py +229 -0
  298. webui/config.py +104 -0
  299. webui/static/css/style.css +332 -0
  300. webui/static/js/main.js +284 -0
  301. webui/templates/index.html +42 -0
  302. tests/test_crca_excel.py +0 -166
  303. tests/test_data_broker.py +0 -424
  304. tests/test_palantir.py +0 -349
  305. {crca-1.4.0.dist-info → crca-1.5.0.dist-info}/WHEEL +0 -0
  306. {crca-1.4.0.dist-info → crca-1.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,408 @@
1
+ """
2
+ Operational tests for image annotation engine - testing basic functionality with simplified API.
3
+ """
4
+
5
+ import os
6
+ import sys
7
+ import numpy as np
8
+ import pytest
9
+ import tempfile
10
+ from pathlib import Path
11
+ from PIL import Image
12
+ import cv2
13
+
14
+ # Add parent directory to path
15
+ sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
16
+
17
+ # Image annotation operational tests can be slow and may require extra deps/LLM access.
18
+ if os.environ.get("CRCA_RUN_IMAGE_ANNOTATION_TESTS") != "1":
19
+ pytest.skip("Set CRCA_RUN_IMAGE_ANNOTATION_TESTS=1 to run image annotation tests", allow_module_level=True)
20
+
21
+ try:
22
+ from image_annotation.annotation_engine import ImageAnnotationEngine
23
+ from image_annotation import AnnotationResult
24
+ IMAGE_ANNOTATION_AVAILABLE = True
25
+ except ImportError:
26
+ IMAGE_ANNOTATION_AVAILABLE = False
27
+ pytest.skip("Image annotation not available", allow_module_level=True)
28
+
29
+
30
+ def create_test_image(width=500, height=500, image_type="simple"):
31
+ """Create a test image for testing."""
32
+ img = np.zeros((height, width, 3), dtype=np.uint8)
33
+
34
+ if image_type == "simple":
35
+ # Simple geometric shapes
36
+ cv2.rectangle(img, (100, 100), (400, 400), (255, 255, 255), 2)
37
+ cv2.circle(img, (250, 250), 50, (255, 255, 255), 2)
38
+ cv2.line(img, (0, 0), (500, 500), (255, 255, 255), 2)
39
+ elif image_type == "circuit":
40
+ # Circuit-like diagram
41
+ for i in range(0, width, 50):
42
+ cv2.line(img, (i, 0), (i, height), (255, 255, 255), 1)
43
+ for i in range(50, width, 100):
44
+ cv2.circle(img, (i, height//2), 20, (255, 255, 255), 2)
45
+ elif image_type == "architectural":
46
+ # Architectural drawing style
47
+ for i in range(0, width, 20):
48
+ cv2.line(img, (0, i), (width, i), (255, 255, 255), 1)
49
+ cv2.line(img, (i, 0), (i, height), (255, 255, 255), 1)
50
+
51
+ return img
52
+
53
+
54
+ class TestSimplifiedAPI:
55
+ """Test the simplified/standardized API usage."""
56
+
57
+ def test_annotate_file_path(self):
58
+ """Test annotate with file path - simplest usage."""
59
+ engine = ImageAnnotationEngine()
60
+
61
+ # Create temporary image file
62
+ img = create_test_image()
63
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
64
+ cv2.imwrite(f.name, img)
65
+ temp_path = f.name
66
+
67
+ try:
68
+ # Simplest usage - just pass file path
69
+ # Should return overlay (numpy array) by default for simplified API
70
+ result = engine.annotate(temp_path)
71
+
72
+ # File paths return overlay by default (simplified API)
73
+ assert isinstance(result, np.ndarray)
74
+ assert len(result.shape) == 3 # BGR image
75
+
76
+ # Can also request AnnotationResult explicitly
77
+ result_full = engine.annotate(temp_path, output="all")
78
+ assert isinstance(result_full, AnnotationResult)
79
+ assert result_full.annotation_graph is not None
80
+ assert result_full.processing_time > 0
81
+ finally:
82
+ os.unlink(temp_path)
83
+
84
+ def test_annotate_numpy_array(self):
85
+ """Test annotate with numpy array - backward compatible."""
86
+ engine = ImageAnnotationEngine()
87
+ img = create_test_image()
88
+
89
+ # Old API still works
90
+ result = engine.annotate(img)
91
+
92
+ assert isinstance(result, AnnotationResult)
93
+ assert len(result.annotation_graph.entities) >= 0
94
+
95
+ def test_annotate_output_overlay(self):
96
+ """Test annotate with overlay output format."""
97
+ engine = ImageAnnotationEngine()
98
+ img = create_test_image()
99
+
100
+ # Request overlay format - should return numpy array
101
+ overlay = engine.annotate(img, output="overlay")
102
+
103
+ assert isinstance(overlay, np.ndarray)
104
+ assert len(overlay.shape) == 3 # BGR image
105
+
106
+ def test_annotate_output_json(self):
107
+ """Test annotate with JSON output format."""
108
+ engine = ImageAnnotationEngine()
109
+ img = create_test_image()
110
+
111
+ # Request JSON format
112
+ json_output = engine.annotate(img, output="json")
113
+
114
+ assert isinstance(json_output, dict)
115
+ assert "entities" in json_output or "error" in json_output
116
+
117
+ def test_annotate_output_report(self):
118
+ """Test annotate with report output format."""
119
+ engine = ImageAnnotationEngine()
120
+ img = create_test_image()
121
+
122
+ # Request report format
123
+ report = engine.annotate(img, output="report")
124
+
125
+ assert isinstance(report, str)
126
+ assert len(report) > 0
127
+
128
+ def test_annotate_batch_auto_detection(self):
129
+ """Test automatic batch processing detection."""
130
+ engine = ImageAnnotationEngine()
131
+
132
+ # Create multiple test images
133
+ images = []
134
+ temp_files = []
135
+ for i in range(3):
136
+ img = create_test_image()
137
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
138
+ cv2.imwrite(f.name, img)
139
+ images.append(f.name)
140
+ temp_files.append(f.name)
141
+
142
+ try:
143
+ # Pass list - should auto-detect batch processing
144
+ results = engine.annotate(images)
145
+
146
+ assert isinstance(results, list)
147
+ assert len(results) == 3
148
+ # Results should be in requested format (default is overlay for new API)
149
+ # But since we passed file paths, it might return AnnotationResult
150
+ assert all(isinstance(r, (AnnotationResult, np.ndarray, dict, str)) for r in results)
151
+ finally:
152
+ for f in temp_files:
153
+ if os.path.exists(f):
154
+ os.unlink(f)
155
+
156
+ def test_annotate_batch_mixed_inputs(self):
157
+ """Test batch processing with mixed input types."""
158
+ engine = ImageAnnotationEngine()
159
+
160
+ # Create mix of file path and numpy array
161
+ img1 = create_test_image()
162
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
163
+ cv2.imwrite(f.name, img1)
164
+ temp_path = f.name
165
+
166
+ img2 = create_test_image()
167
+
168
+ try:
169
+ # Mix file path and numpy array
170
+ results = engine.annotate([temp_path, img2])
171
+
172
+ assert isinstance(results, list)
173
+ assert len(results) == 2
174
+ finally:
175
+ if os.path.exists(temp_path):
176
+ os.unlink(temp_path)
177
+
178
+ def test_query_simple(self):
179
+ """Test query interface with simple query."""
180
+ engine = ImageAnnotationEngine()
181
+ img = create_test_image()
182
+
183
+ # Save to temp file for query
184
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
185
+ cv2.imwrite(f.name, img)
186
+ temp_path = f.name
187
+
188
+ try:
189
+ # Simple query
190
+ result = engine.query(temp_path, "find all circles")
191
+
192
+ assert isinstance(result, dict)
193
+ assert "answer" in result
194
+ assert "entities" in result
195
+ assert "measurements" in result
196
+ assert "confidence" in result
197
+ finally:
198
+ if os.path.exists(temp_path):
199
+ os.unlink(temp_path)
200
+
201
+ def test_query_with_measurements(self):
202
+ """Test query interface requesting measurements."""
203
+ engine = ImageAnnotationEngine()
204
+ img = create_test_image()
205
+
206
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
207
+ cv2.imwrite(f.name, img)
208
+ temp_path = f.name
209
+
210
+ try:
211
+ # Query requesting measurements
212
+ result = engine.query(temp_path, "find the largest circle and measure its area")
213
+
214
+ assert isinstance(result, dict)
215
+ assert "measurements" in result
216
+ # May or may not have measurements depending on what was found
217
+ finally:
218
+ if os.path.exists(temp_path):
219
+ os.unlink(temp_path)
220
+
221
+ def test_query_count_objects(self):
222
+ """Test query interface for counting objects."""
223
+ engine = ImageAnnotationEngine()
224
+ img = create_test_image()
225
+
226
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
227
+ cv2.imwrite(f.name, img)
228
+ temp_path = f.name
229
+
230
+ try:
231
+ # Count query
232
+ result = engine.query(temp_path, "count how many lines are in this image")
233
+
234
+ assert isinstance(result, dict)
235
+ assert "answer" in result
236
+ assert "entities" in result
237
+ finally:
238
+ if os.path.exists(temp_path):
239
+ os.unlink(temp_path)
240
+
241
+
242
+ class TestAutoFeatures:
243
+ """Test automatic features (type detection, parameter tuning, etc.)."""
244
+
245
+ def test_auto_image_type_detection(self):
246
+ """Test automatic image type detection."""
247
+ engine = ImageAnnotationEngine()
248
+ engine.config.auto_detect_type = True
249
+
250
+ # Circuit-like image
251
+ circuit_img = create_test_image(image_type="circuit")
252
+ result = engine.annotate(circuit_img, output="all")
253
+
254
+ # Should have detected image type in metadata
255
+ if isinstance(result, AnnotationResult):
256
+ detected_type = result.annotation_graph.metadata.get("image_type", "unknown")
257
+ assert detected_type in ["circuit", "general", "technical", "unknown"]
258
+
259
+ def test_auto_parameter_tuning(self):
260
+ """Test automatic parameter tuning."""
261
+ engine = ImageAnnotationEngine()
262
+ engine.config.auto_tune_params = True
263
+
264
+ img = create_test_image()
265
+ result = engine.annotate(img)
266
+
267
+ # Should complete without errors (tuning happens internally)
268
+ assert isinstance(result, AnnotationResult)
269
+
270
+ def test_auto_retry_on_failure(self):
271
+ """Test automatic retry logic."""
272
+ engine = ImageAnnotationEngine()
273
+ engine.config.auto_retry = True
274
+ engine.config.max_retries = 2
275
+
276
+ # Very simple image that might need retry
277
+ img = np.zeros((100, 100, 3), dtype=np.uint8)
278
+ result = engine.annotate(img)
279
+
280
+ # Should complete (may retry internally)
281
+ assert isinstance(result, AnnotationResult)
282
+
283
+ def test_caching_enabled(self):
284
+ """Test that caching works."""
285
+ engine = ImageAnnotationEngine()
286
+ engine.config.cache_enabled = True
287
+
288
+ img = create_test_image()
289
+
290
+ # First annotation
291
+ result1 = engine.annotate(img)
292
+
293
+ # Second annotation (should use cache for primitives)
294
+ result2 = engine.annotate(img)
295
+
296
+ # Both should complete
297
+ assert isinstance(result1, AnnotationResult)
298
+ assert isinstance(result2, AnnotationResult)
299
+
300
+
301
+ class TestErrorHandling:
302
+ """Test error handling in simplified API."""
303
+
304
+ def test_invalid_file_path(self):
305
+ """Test handling of invalid file path."""
306
+ engine = ImageAnnotationEngine()
307
+
308
+ # Non-existent file
309
+ result = engine.annotate("nonexistent_file.png", output="all")
310
+
311
+ # Should return error result, not crash
312
+ if isinstance(result, AnnotationResult):
313
+ assert "error" in result.json_output or len(result.annotation_graph.entities) == 0
314
+
315
+ def test_empty_image(self):
316
+ """Test handling of empty/blank image."""
317
+ engine = ImageAnnotationEngine()
318
+
319
+ # Empty image
320
+ img = np.zeros((100, 100, 3), dtype=np.uint8)
321
+ result = engine.annotate(img)
322
+
323
+ # Should complete without crashing
324
+ assert isinstance(result, AnnotationResult)
325
+
326
+ def test_batch_with_errors(self):
327
+ """Test batch processing continues on individual errors."""
328
+ engine = ImageAnnotationEngine()
329
+
330
+ img = create_test_image()
331
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
332
+ cv2.imwrite(f.name, img)
333
+ temp_path = f.name
334
+
335
+ try:
336
+ # Mix valid and invalid inputs
337
+ results = engine.annotate([temp_path, "nonexistent.png", img])
338
+
339
+ # Should return results for all (some may be errors)
340
+ assert isinstance(results, list)
341
+ assert len(results) == 3
342
+ finally:
343
+ if os.path.exists(temp_path):
344
+ os.unlink(temp_path)
345
+
346
+
347
+ class TestConfiguration:
348
+ """Test configuration options."""
349
+
350
+ def test_custom_config(self):
351
+ """Test using custom configuration."""
352
+ from image_annotation.annotation_engine import AnnotationConfig
353
+
354
+ config = AnnotationConfig(
355
+ gpt_model="gpt-4o-mini",
356
+ cache_enabled=False,
357
+ auto_retry=False,
358
+ output_format="json"
359
+ )
360
+
361
+ engine = ImageAnnotationEngine(config=config)
362
+
363
+ assert engine.config.cache_enabled is False
364
+ assert engine.config.auto_retry is False
365
+ assert engine.config.output_format == "json"
366
+
367
+ def test_config_override(self):
368
+ """Test overriding config parameters."""
369
+ engine = ImageAnnotationEngine(
370
+ gpt_model="gpt-4o",
371
+ cache_enabled=False
372
+ )
373
+
374
+ assert engine.config.gpt_model == "gpt-4o"
375
+ assert engine.config.cache_enabled is False
376
+
377
+
378
+ class TestIntegration:
379
+ """Test integration with CR-CA."""
380
+
381
+ def test_crca_agent_with_image_tools(self):
382
+ """Test CR-CA agent can use image annotation tools."""
383
+ try:
384
+ from CRCA import CRCAAgent
385
+ except ImportError:
386
+ pytest.skip("CR-CA not available")
387
+
388
+ # Create agent with image annotation enabled
389
+ agent = CRCAAgent(
390
+ model_name="gpt-4o-mini",
391
+ use_image_annotation=True
392
+ )
393
+
394
+ # Check that tools are available
395
+ tool_names = []
396
+ if hasattr(agent, 'tools_list_dictionary') and agent.tools_list_dictionary:
397
+ for tool in agent.tools_list_dictionary:
398
+ if isinstance(tool, dict):
399
+ func_name = tool.get("function", {}).get("name", "")
400
+ if func_name:
401
+ tool_names.append(func_name)
402
+
403
+ # Image annotation tools should be available
404
+ assert "annotate_image" in tool_names or "query_image" in tool_names or len(tool_names) == 0
405
+
406
+
407
+ if __name__ == "__main__":
408
+ pytest.main([__file__, "-v"])