algo-cli-runtime 0.14.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 (237) hide show
  1. algo_cli/__init__.py +3 -0
  2. algo_cli/__main__.py +7 -0
  3. algo_cli/_internal/__init__.py +12 -0
  4. algo_cli/_internal/policy_chain.py +259 -0
  5. algo_cli/action_registry.py +1047 -0
  6. algo_cli/agent_blocks.py +550 -0
  7. algo_cli/agent_pipeline.py +1457 -0
  8. algo_cli/agent_threads.py +308 -0
  9. algo_cli/animations.py +316 -0
  10. algo_cli/cache_admission.py +209 -0
  11. algo_cli/capability_mask.py +66 -0
  12. algo_cli/chat_protocol.py +116 -0
  13. algo_cli/chatgpt_auth.py +510 -0
  14. algo_cli/chatgpt_client.py +657 -0
  15. algo_cli/code_rag.py +479 -0
  16. algo_cli/config.py +651 -0
  17. algo_cli/context_budget.py +679 -0
  18. algo_cli/credential_helpers.py +315 -0
  19. algo_cli/deliberation.py +29 -0
  20. algo_cli/display.py +1470 -0
  21. algo_cli/evals/__init__.py +21 -0
  22. algo_cli/evals/algorithm_effectiveness.py +560 -0
  23. algo_cli/evals/competitive_harness_rating.py +702 -0
  24. algo_cli/evals/cot_quality.py +220 -0
  25. algo_cli/evals/harness_retrieval_benchmark.py +401 -0
  26. algo_cli/evals/performance_regression.py +136 -0
  27. algo_cli/evals/scorecard_grading.py +308 -0
  28. algo_cli/evals/session_distribution.py +84 -0
  29. algo_cli/execution_guardrails.py +806 -0
  30. algo_cli/extensions_manifest.py +84 -0
  31. algo_cli/git_evidence.py +227 -0
  32. algo_cli/google_workspace.py +407 -0
  33. algo_cli/google_workspace_auth.py +523 -0
  34. algo_cli/harness.py +2587 -0
  35. algo_cli/identity.py +557 -0
  36. algo_cli/index_compute_lab.py +228 -0
  37. algo_cli/inference_harness.py +70 -0
  38. algo_cli/intelligence/__init__.py +1103 -0
  39. algo_cli/intelligence/acrobat_config.py +307 -0
  40. algo_cli/intelligence/acrobat_manifests.py +338 -0
  41. algo_cli/intelligence/acrobat_models.py +195 -0
  42. algo_cli/intelligence/acrobat_pipeline.py +295 -0
  43. algo_cli/intelligence/acrobat_runtime.py +302 -0
  44. algo_cli/intelligence/acrobat_security.py +261 -0
  45. algo_cli/intelligence/acrobat_workflows.py +226 -0
  46. algo_cli/intelligence/actionability.py +165 -0
  47. algo_cli/intelligence/adversarial_audit.py +136 -0
  48. algo_cli/intelligence/agent_arena.py +92 -0
  49. algo_cli/intelligence/agent_benchmark.py +236 -0
  50. algo_cli/intelligence/agent_runtime.py +171 -0
  51. algo_cli/intelligence/agents_as_tools.py +70 -0
  52. algo_cli/intelligence/artifact_binding.py +80 -0
  53. algo_cli/intelligence/autonomous_engineer.py +1976 -0
  54. algo_cli/intelligence/backpressure.py +99 -0
  55. algo_cli/intelligence/bloom_filter.py +186 -0
  56. algo_cli/intelligence/bonferroni.py +66 -0
  57. algo_cli/intelligence/boundary_compaction.py +98 -0
  58. algo_cli/intelligence/catalog_verifier.py +172 -0
  59. algo_cli/intelligence/cavecrew.py +118 -0
  60. algo_cli/intelligence/changelog.py +176 -0
  61. algo_cli/intelligence/checkpoint_resume.py +92 -0
  62. algo_cli/intelligence/circuit_breaker.py +88 -0
  63. algo_cli/intelligence/clarification_gate.py +101 -0
  64. algo_cli/intelligence/code_graph.py +180 -0
  65. algo_cli/intelligence/coderank.py +97 -0
  66. algo_cli/intelligence/consistent_hash.py +150 -0
  67. algo_cli/intelligence/consortium_synthesis.py +139 -0
  68. algo_cli/intelligence/construction/__init__.py +241 -0
  69. algo_cli/intelligence/construction/common.py +273 -0
  70. algo_cli/intelligence/construction/documents.py +496 -0
  71. algo_cli/intelligence/construction/labor_units.py +1395 -0
  72. algo_cli/intelligence/construction/payments.py +470 -0
  73. algo_cli/intelligence/construction/risk.py +784 -0
  74. algo_cli/intelligence/content_extractor.py +132 -0
  75. algo_cli/intelligence/context_adaptive.py +102 -0
  76. algo_cli/intelligence/context_ops.py +95 -0
  77. algo_cli/intelligence/count_min.py +145 -0
  78. algo_cli/intelligence/cow_state.py +103 -0
  79. algo_cli/intelligence/critic_loop.py +119 -0
  80. algo_cli/intelligence/cross_source.py +113 -0
  81. algo_cli/intelligence/daemon_mode.py +99 -0
  82. algo_cli/intelligence/dag_orchestration.py +151 -0
  83. algo_cli/intelligence/deep_research.py +155 -0
  84. algo_cli/intelligence/degenerate_detector.py +78 -0
  85. algo_cli/intelligence/delta_report.py +92 -0
  86. algo_cli/intelligence/discovery_event_log.py +92 -0
  87. algo_cli/intelligence/document_ingest.py +298 -0
  88. algo_cli/intelligence/dual_layer_validate.py +151 -0
  89. algo_cli/intelligence/echo_fidelity.py +73 -0
  90. algo_cli/intelligence/ema_tuning.py +104 -0
  91. algo_cli/intelligence/event_log.py +92 -0
  92. algo_cli/intelligence/evidence_graph.py +114 -0
  93. algo_cli/intelligence/extension_host.py +162 -0
  94. algo_cli/intelligence/extension_manifest.py +115 -0
  95. algo_cli/intelligence/falsification_suite.py +178 -0
  96. algo_cli/intelligence/finance/__init__.py +169 -0
  97. algo_cli/intelligence/finance/anomalies.py +135 -0
  98. algo_cli/intelligence/finance/ap_ar.py +351 -0
  99. algo_cli/intelligence/finance/cash.py +162 -0
  100. algo_cli/intelligence/finance/close.py +332 -0
  101. algo_cli/intelligence/finance/common.py +244 -0
  102. algo_cli/intelligence/finance/construction.py +135 -0
  103. algo_cli/intelligence/finance/controls.py +172 -0
  104. algo_cli/intelligence/finance/evidence.py +119 -0
  105. algo_cli/intelligence/finance/exceptions.py +157 -0
  106. algo_cli/intelligence/finance/reconciliations.py +254 -0
  107. algo_cli/intelligence/finance/revenue.py +109 -0
  108. algo_cli/intelligence/finance/tax.py +74 -0
  109. algo_cli/intelligence/finance/workpapers.py +111 -0
  110. algo_cli/intelligence/finding_record.py +120 -0
  111. algo_cli/intelligence/flow_dag.py +267 -0
  112. algo_cli/intelligence/gatherer.py +223 -0
  113. algo_cli/intelligence/golden_master.py +98 -0
  114. algo_cli/intelligence/graph_rag.py +195 -0
  115. algo_cli/intelligence/group_chat.py +143 -0
  116. algo_cli/intelligence/hash_dedup.py +145 -0
  117. algo_cli/intelligence/hyperloglog.py +128 -0
  118. algo_cli/intelligence/incremental_index.py +316 -0
  119. algo_cli/intelligence/index_store.py +16 -0
  120. algo_cli/intelligence/iteration_plan.py +133 -0
  121. algo_cli/intelligence/kernel_plugins.py +167 -0
  122. algo_cli/intelligence/lesson_catalog.py +135 -0
  123. algo_cli/intelligence/llm_fallback.py +169 -0
  124. algo_cli/intelligence/log2_histogram.py +267 -0
  125. algo_cli/intelligence/lsp_integration.py +147 -0
  126. algo_cli/intelligence/memory_evolution.py +117 -0
  127. algo_cli/intelligence/minhash_lsh.py +182 -0
  128. algo_cli/intelligence/multi_model_score.py +174 -0
  129. algo_cli/intelligence/multi_tier_grade.py +211 -0
  130. algo_cli/intelligence/negative_controls.py +113 -0
  131. algo_cli/intelligence/numeric_clamp.py +63 -0
  132. algo_cli/intelligence/occ_editor.py +66 -0
  133. algo_cli/intelligence/output_normalize.py +112 -0
  134. algo_cli/intelligence/parallel_delegation.py +98 -0
  135. algo_cli/intelligence/parallel_fanout.py +104 -0
  136. algo_cli/intelligence/permission_modes.py +105 -0
  137. algo_cli/intelligence/pre_push_gate.py +68 -0
  138. algo_cli/intelligence/prefetch.py +171 -0
  139. algo_cli/intelligence/process_framework.py +217 -0
  140. algo_cli/intelligence/project_graph.py +387 -0
  141. algo_cli/intelligence/query_expansion.py +146 -0
  142. algo_cli/intelligence/ralph_loop.py +117 -0
  143. algo_cli/intelligence/rate_limiter.py +153 -0
  144. algo_cli/intelligence/refactor_transaction.py +94 -0
  145. algo_cli/intelligence/research_workspace.py +108 -0
  146. algo_cli/intelligence/retraction_ledger.py +72 -0
  147. algo_cli/intelligence/saga_pattern.py +88 -0
  148. algo_cli/intelligence/session_fork.py +100 -0
  149. algo_cli/intelligence/shadow_editor.py +67 -0
  150. algo_cli/intelligence/shell_session.py +213 -0
  151. algo_cli/intelligence/source_registry.py +143 -0
  152. algo_cli/intelligence/spawn_scales.py +99 -0
  153. algo_cli/intelligence/stat_stability.py +104 -0
  154. algo_cli/intelligence/structural_validator.py +148 -0
  155. algo_cli/intelligence/subagent_spawner.py +111 -0
  156. algo_cli/intelligence/symmetric_verify.py +70 -0
  157. algo_cli/intelligence/task_classifier.py +129 -0
  158. algo_cli/intelligence/team_execution.py +122 -0
  159. algo_cli/intelligence/tiered_access.py +121 -0
  160. algo_cli/intelligence/utility_registry.py +159 -0
  161. algo_cli/intuition_engine.py +560 -0
  162. algo_cli/intuition_injector.py +82 -0
  163. algo_cli/kernels/__init__.py +5 -0
  164. algo_cli/kernels/manifest.py +763 -0
  165. algo_cli/main.py +3903 -0
  166. algo_cli/memory_candidates.py +541 -0
  167. algo_cli/memory_echo_veil.py +394 -0
  168. algo_cli/memory_runtime.py +112 -0
  169. algo_cli/model_info.py +548 -0
  170. algo_cli/model_profile.py +160 -0
  171. algo_cli/model_routing.py +74 -0
  172. algo_cli/oneshot.py +331 -0
  173. algo_cli/perf_telemetry.py +389 -0
  174. algo_cli/plugins.py +245 -0
  175. algo_cli/private_event_store.py +654 -0
  176. algo_cli/quantization/__init__.py +24 -0
  177. algo_cli/quantization/lloyd_max.py +98 -0
  178. algo_cli/quantization/turbo_quant.py +308 -0
  179. algo_cli/reasoning/__init__.py +46 -0
  180. algo_cli/reasoning/combinatorial.py +356 -0
  181. algo_cli/reasoning/graph_of_thought.py +297 -0
  182. algo_cli/reasoning/mcts.py +220 -0
  183. algo_cli/reasoning/neuro_symbolic.py +250 -0
  184. algo_cli/reasoning/react.py +246 -0
  185. algo_cli/reasoning/reflexion.py +225 -0
  186. algo_cli/reasoning/tree_of_thought.py +241 -0
  187. algo_cli/reasoning_bridge.py +150 -0
  188. algo_cli/reconciliation.py +284 -0
  189. algo_cli/reflex.py +385 -0
  190. algo_cli/resources/docs/ALGO.md +13958 -0
  191. algo_cli/resources/docs/algo-cli-algorithm-evidence-contract.md +60 -0
  192. algo_cli/resources/docs/algo-cli-execution-verification-contract.md +59 -0
  193. algo_cli/resources/docs/algo-cli-memory-lifecycle-contract.md +72 -0
  194. algo_cli/resources/docs/harness-extension-cleanup-recommendation.md +41 -0
  195. algo_cli/resources/docs/index-compute-lab-integration.md +32 -0
  196. algo_cli/resources/docs/inference-harness-loop-blueprint-2026-06.md +55 -0
  197. algo_cli/resources/docs/main-split-map.md +35 -0
  198. algo_cli/resources/docs/privacy-and-context.md +48 -0
  199. algo_cli/resources/docs/reflex-loop-v0.2.md +354 -0
  200. algo_cli/resources/skills/README.md +26 -0
  201. algo_cli/resources/skills/algo-cli.md +59 -0
  202. algo_cli/resources/skills/edit-file-precision.md +49 -0
  203. algo_cli/resources/skills/harness-search-first.md +47 -0
  204. algo_cli/resources/skills/memory-recall-ritual.md +51 -0
  205. algo_cli/resources/skills/qol-algorithms.md +224 -0
  206. algo_cli/resources/skills/smart-error-recovery.md +56 -0
  207. algo_cli/resources/skills/tool-selection-cheatsheet.md +65 -0
  208. algo_cli/retrieval_algorithms.py +127 -0
  209. algo_cli/runtime_qos.py +236 -0
  210. algo_cli/runtime_services.py +320 -0
  211. algo_cli/session_commands.py +95 -0
  212. algo_cli/session_mode.py +113 -0
  213. algo_cli/skills.py +430 -0
  214. algo_cli/slash_dispatch.py +1265 -0
  215. algo_cli/small_context.py +206 -0
  216. algo_cli/spawn_budget.py +89 -0
  217. algo_cli/task_ledger.py +84 -0
  218. algo_cli/task_router.py +197 -0
  219. algo_cli/tool_context.py +94 -0
  220. algo_cli/tool_contract.py +99 -0
  221. algo_cli/tool_policy.py +357 -0
  222. algo_cli/tool_runtime.py +647 -0
  223. algo_cli/tools.py +3056 -0
  224. algo_cli/url_scheme.py +174 -0
  225. algo_cli/verify.py +154 -0
  226. algo_cli/version_manifest.py +178 -0
  227. algo_cli/vision_screenshot_verify.py +76 -0
  228. algo_cli/workspace_resolver.py +68 -0
  229. algo_cli/x_account.py +209 -0
  230. algo_cli/xai_auth.py +374 -0
  231. algo_cli/xai_client.py +600 -0
  232. algo_cli_runtime-0.14.0.dist-info/METADATA +369 -0
  233. algo_cli_runtime-0.14.0.dist-info/RECORD +237 -0
  234. algo_cli_runtime-0.14.0.dist-info/WHEEL +4 -0
  235. algo_cli_runtime-0.14.0.dist-info/entry_points.txt +3 -0
  236. algo_cli_runtime-0.14.0.dist-info/licenses/LICENSE +21 -0
  237. ollama_cli/__init__.py +67 -0
@@ -0,0 +1,147 @@
1
+ """B60. LSP Integration for Code Intelligence.
2
+
3
+ Language Server Protocol integration for go-to-definition, hover,
4
+ diagnostics, and references. Source: copilot-cli pattern.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import subprocess
9
+ import json
10
+ from dataclasses import dataclass
11
+ from enum import Enum, auto
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+
16
+ class LSPServerStatus(Enum):
17
+ STOPPED = auto()
18
+ STARTING = auto()
19
+ READY = auto()
20
+ ERROR = auto()
21
+
22
+
23
+ @dataclass
24
+ class LSPDiagnostic:
25
+ severity: str # "error", "warning", "info", "hint"
26
+ message: str
27
+ line: int # 0-based
28
+ col: int
29
+ end_line: int = 0
30
+ end_col: int = 0
31
+ source: str = ""
32
+ code: str = ""
33
+
34
+
35
+ @dataclass
36
+ class LSPDefinition:
37
+ file: str
38
+ line: int
39
+ col: int
40
+
41
+
42
+ @dataclass
43
+ class LSPHover:
44
+ contents: str
45
+ range_line: int = 0
46
+
47
+
48
+ @dataclass
49
+ class LSPServer:
50
+ name: str
51
+ command: list[str]
52
+ languages: list[str]
53
+ status: LSPServerStatus = LSPServerStatus.STOPPED
54
+ process: Any = None
55
+
56
+
57
+ class LSPManager:
58
+ """Manage LSP servers and provide code intelligence."""
59
+
60
+ # Common LSP server configs
61
+ SERVER_CONFIGS: dict[str, LSPServer] = {
62
+ "pyright": LSPServer(
63
+ name="pyright",
64
+ command=["pyright-langserver", "--stdio"],
65
+ languages=["python"],
66
+ ),
67
+ "pylsp": LSPServer(
68
+ name="pylsp",
69
+ command=["pylsp"],
70
+ languages=["python"],
71
+ ),
72
+ "typescript": LSPServer(
73
+ name="typescript-language-server",
74
+ command=["typescript-language-server", "--stdio"],
75
+ languages=["typescript", "javascript"],
76
+ ),
77
+ "rust-analyzer": LSPServer(
78
+ name="rust-analyzer",
79
+ command=["rust-analyzer"],
80
+ languages=["rust"],
81
+ ),
82
+ }
83
+
84
+ def __init__(self) -> None:
85
+ self._servers: dict[str, LSPServer] = {}
86
+ self._file_to_server: dict[str, str] = {}
87
+
88
+ def register_server(self, server: LSPServer) -> None:
89
+ self._servers[server.name] = server
90
+ for lang in server.languages:
91
+ self._file_to_server[lang] = server.name
92
+
93
+ def get_server_for_file(self, file_path: str) -> LSPServer | None:
94
+ ext = Path(file_path).suffix.lower()
95
+ lang_map = {
96
+ ".py": "python",
97
+ ".ts": "typescript",
98
+ ".js": "javascript",
99
+ ".rs": "rust",
100
+ }
101
+ lang = lang_map.get(ext)
102
+ if not lang:
103
+ return None
104
+ server_name = self._file_to_server.get(lang)
105
+ if not server_name:
106
+ return None
107
+ return self._servers.get(server_name)
108
+
109
+ def parse_diagnostics(self, output: str) -> list[LSPDiagnostic]:
110
+ """Parse LSP diagnostic output."""
111
+ diags: list[LSPDiagnostic] = []
112
+ try:
113
+ data = json.loads(output)
114
+ for item in data.get("diagnostics", []):
115
+ diags.append(LSPDiagnostic(
116
+ severity=item.get("severity", "info"),
117
+ message=item.get("message", ""),
118
+ line=item.get("range", {}).get("start", {}).get("line", 0),
119
+ col=item.get("range", {}).get("start", {}).get("character", 0),
120
+ end_line=item.get("range", {}).get("end", {}).get("line", 0),
121
+ end_col=item.get("range", {}).get("end", {}).get("character", 0),
122
+ source=item.get("source", ""),
123
+ code=item.get("code", ""),
124
+ ))
125
+ except (json.JSONDecodeError, KeyError):
126
+ pass
127
+ return diags
128
+
129
+ def check_available(self) -> dict[str, bool]:
130
+ """Check which LSP servers are installed."""
131
+ available: dict[str, bool] = {}
132
+ for name, server in self.SERVER_CONFIGS.items():
133
+ try:
134
+ cmd = server.command[0]
135
+ result = subprocess.run(
136
+ ["where" if _is_windows() else "which", cmd],
137
+ capture_output=True, timeout=5,
138
+ )
139
+ available[name] = result.returncode == 0
140
+ except Exception:
141
+ available[name] = False
142
+ return available
143
+
144
+
145
+ def _is_windows() -> bool:
146
+ import sys
147
+ return sys.platform == "win32"
@@ -0,0 +1,117 @@
1
+ """B56. Memory Skill Evolution.
2
+
3
+ Learn reusable memory skills from task feedback. Evolve from hard cases.
4
+ Source: MemSkill pattern.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import time
9
+ from dataclasses import dataclass, field
10
+ from enum import Enum, auto
11
+
12
+
13
+ class SkillStatus(Enum):
14
+ CANDIDATE = auto()
15
+ ACTIVE = auto()
16
+ DEPRECATED = auto()
17
+ EVOLVED = auto()
18
+
19
+
20
+ @dataclass
21
+ class MemorySkill:
22
+ name: str
23
+ pattern: str # what to remember
24
+ trigger: str # when to apply
25
+ confidence: float = 0.5
26
+ uses: int = 0
27
+ successes: int = 0
28
+ failures: int = 0
29
+ status: SkillStatus = SkillStatus.CANDIDATE
30
+ created_at: float = field(default_factory=time.time)
31
+ evolved_from: str | None = None
32
+ hard_cases: list[str] = field(default_factory=list)
33
+
34
+
35
+ @dataclass
36
+ class TaskFeedback:
37
+ task: str
38
+ memory_used: list[str] # skill names
39
+ outcome: str # "success", "failure", "partial"
40
+ missing_info: str = "" # what was missing?
41
+ wrong_info: str = "" # what was wrong/stale?
42
+
43
+
44
+ class MemorySkillEvolver:
45
+ """Evolve memory skills from task feedback."""
46
+
47
+ def __init__(self, confidence_threshold: float = 0.7,
48
+ deprecation_threshold: float = 0.3) -> None:
49
+ self._skills: dict[str, MemorySkill] = {}
50
+ self._feedback_history: list[TaskFeedback] = []
51
+ self._confidence_threshold = confidence_threshold
52
+ self._deprecation_threshold = deprecation_threshold
53
+
54
+ def register(self, skill: MemorySkill) -> None:
55
+ self._skills[skill.name] = skill
56
+
57
+ def record_feedback(self, feedback: TaskFeedback) -> None:
58
+ self._feedback_history.append(feedback)
59
+ for skill_name in feedback.memory_used:
60
+ skill = self._skills.get(skill_name)
61
+ if not skill:
62
+ continue
63
+ skill.uses += 1
64
+ if feedback.outcome == "success":
65
+ skill.successes += 1
66
+ elif feedback.outcome == "failure":
67
+ skill.failures += 1
68
+ if feedback.wrong_info:
69
+ skill.hard_cases.append(feedback.wrong_info)
70
+
71
+ def _confidence(self, skill: MemorySkill) -> float:
72
+ if skill.uses == 0:
73
+ return skill.confidence
74
+ return skill.successes / skill.uses
75
+
76
+ def evolve(self) -> list[MemorySkill]:
77
+ """Promote/deprecate/evolve skills based on feedback."""
78
+ evolved: list[MemorySkill] = []
79
+ for skill in list(self._skills.values()):
80
+ conf = self._confidence(skill)
81
+ if conf >= self._confidence_threshold and skill.status == SkillStatus.CANDIDATE:
82
+ skill.status = SkillStatus.ACTIVE
83
+ evolved.append(skill)
84
+ elif conf < self._deprecation_threshold and skill.status == SkillStatus.ACTIVE:
85
+ skill.status = SkillStatus.DEPRECATED
86
+ evolved.append(skill)
87
+ elif skill.hard_cases and skill.status == SkillStatus.ACTIVE:
88
+ # Evolve: create a refined version
89
+ evolved_name = f"{skill.name}_v2"
90
+ if evolved_name not in self._skills:
91
+ evolved_skill = MemorySkill(
92
+ name=evolved_name,
93
+ pattern=skill.pattern,
94
+ trigger=skill.trigger,
95
+ confidence=0.5,
96
+ status=SkillStatus.CANDIDATE,
97
+ evolved_from=skill.name,
98
+ hard_cases=list(skill.hard_cases),
99
+ )
100
+ self._skills[evolved_name] = evolved_skill
101
+ skill.status = SkillStatus.EVOLVED
102
+ evolved.append(evolved_skill)
103
+ return evolved
104
+
105
+ def mine_hard_cases(self) -> list[dict]:
106
+ """Find cases where memory was wrong or missing."""
107
+ cases: list[dict] = []
108
+ for fb in self._feedback_history:
109
+ if fb.wrong_info:
110
+ cases.append({"type": "wrong", "info": fb.wrong_info, "task": fb.task})
111
+ if fb.missing_info:
112
+ cases.append({"type": "missing", "info": fb.missing_info, "task": fb.task})
113
+ return cases
114
+
115
+ @property
116
+ def skills(self) -> dict[str, MemorySkill]:
117
+ return dict(self._skills)
@@ -0,0 +1,182 @@
1
+ """MinHash + LSH — approximate near-duplicate detection.
2
+
3
+ MinHash estimates Jaccard similarity between sets using k random hash
4
+ functions. LSH (Locality-Sensitive Hashing) buckets similar signatures
5
+ into the same band-slot for O(1) candidate lookup.
6
+
7
+ Harness use:
8
+ - Detect near-duplicate files (rebranded code, copied configs)
9
+ - Find similar conversation contexts for dedup
10
+ - Cluster similar error messages
11
+ - Detect duplicate harness entries
12
+
13
+ Operations:
14
+ - MinHasher.signature(set): compute k-element signature
15
+ - MinHasher.similarity(sig1, sig2): estimated Jaccard similarity
16
+ - LSHIndex.insert(key, signature): add to LSH buckets
17
+ - LSHIndex.query(signature): return candidate near-duplicate keys
18
+
19
+ Properties:
20
+ - Signature computation: O(k * |set|)
21
+ - Similarity estimation: O(k)
22
+ - LSH query: O(bands) expected, where bands = signature_size / rows_per_band
23
+ - Trade-off: more bands = higher recall, more false positives
24
+ """
25
+ from __future__ import annotations
26
+
27
+ import hashlib
28
+ from typing import Any
29
+
30
+ from dataclasses import dataclass, field
31
+
32
+
33
+ class MinHasher:
34
+ """MinHash signature generator.
35
+
36
+ Args:
37
+ num_hashes: Number of hash functions (signature size).
38
+ Higher = more accurate similarity estimation, more memory.
39
+ seed: Random seed for reproducibility.
40
+ """
41
+
42
+ def __init__(self, num_hashes: int = 128, seed: int = 42) -> None:
43
+ self.num_hashes = num_hashes
44
+ self._seeds = [(seed + i * 2654435761) & 0xFFFFFFFF for i in range(num_hashes)]
45
+
46
+ def _hash(self, item: Any, seed: int) -> int:
47
+ """Hash an item with a given seed to a 32-bit integer."""
48
+ data = f"{seed}:{item}".encode("utf-8")
49
+ return int.from_bytes(hashlib.md5(data).digest()[:4], "big")
50
+
51
+ def signature(self, items: set[Any] | list[Any]) -> list[int]:
52
+ """Compute the MinHash signature of a set of items.
53
+
54
+ Returns a list of num_hashes integers, where each is the minimum
55
+ hash value for that hash function across all items.
56
+ """
57
+ if not items:
58
+ return [0xFFFFFFFF] * self.num_hashes
59
+ items_set = set(items)
60
+ sig = []
61
+ for seed in self._seeds:
62
+ min_val = min(self._hash(item, seed) for item in items_set)
63
+ sig.append(min_val)
64
+ return sig
65
+
66
+ def similarity(self, sig1: list[int], sig2: list[int]) -> float:
67
+ """Estimate Jaccard similarity from two signatures.
68
+
69
+ Jaccard(A, B) = |A ∩ B| / |A ∪ B|
70
+ MinHash estimates this as the fraction of matching hash positions.
71
+ """
72
+ if len(sig1) != len(sig2):
73
+ raise ValueError("Signatures must have the same length")
74
+ if not sig1:
75
+ return 0.0
76
+ matches = sum(1 for a, b in zip(sig1, sig2) if a == b)
77
+ return matches / len(sig1)
78
+
79
+
80
+ @dataclass
81
+ class LSHIndex:
82
+ """Locality-Sensitive Hashing index for MinHash signatures.
83
+
84
+ Splits the signature into bands. Two signatures are candidates if
85
+ they share at least one band hash. This provides sublinear query time.
86
+
87
+ Args:
88
+ num_bands: Number of bands (more = higher recall, more false positives).
89
+ rows_per_band: Rows per band (more = higher precision, lower recall).
90
+ num_bands * rows_per_band should equal signature size.
91
+ """
92
+
93
+ num_bands: int = 32
94
+ rows_per_band: int = 4
95
+ _buckets: dict[tuple[int, int], set[str]] = field(default_factory=dict)
96
+ _signatures: dict[str, list[int]] = field(default_factory=dict)
97
+
98
+ def __post_init__(self) -> None:
99
+ self._expected_sig_len = self.num_bands * self.rows_per_band
100
+
101
+ def _band_hash(self, signature: list[int], band_idx: int) -> int:
102
+ """Hash a band of the signature to an integer."""
103
+ start = band_idx * self.rows_per_band
104
+ end = start + self.rows_per_band
105
+ band = tuple(signature[start:end])
106
+ return hash(band)
107
+
108
+ def insert(self, key: str, signature: list[int]) -> None:
109
+ """Insert a key with its MinHash signature into the LSH index."""
110
+ if len(signature) != self._expected_sig_len:
111
+ raise ValueError(
112
+ f"Signature length {len(signature)} != expected "
113
+ f"{self._expected_sig_len} (bands={self.num_bands} * "
114
+ f"rows={self.rows_per_band})"
115
+ )
116
+ self._signatures[key] = signature
117
+ for band_idx in range(self.num_bands):
118
+ bh = self._band_hash(signature, band_idx)
119
+ bucket_key = (band_idx, bh)
120
+ if bucket_key not in self._buckets:
121
+ self._buckets[bucket_key] = set()
122
+ self._buckets[bucket_key].add(key)
123
+
124
+ def remove(self, key: str) -> None:
125
+ """Remove a key from the LSH index."""
126
+ sig = self._signatures.pop(key, None)
127
+ if sig is None:
128
+ return
129
+ for band_idx in range(self.num_bands):
130
+ bh = self._band_hash(sig, band_idx)
131
+ bucket_key = (band_idx, bh)
132
+ if bucket_key in self._buckets:
133
+ self._buckets[bucket_key].discard(key)
134
+ if not self._buckets[bucket_key]:
135
+ del self._buckets[bucket_key]
136
+
137
+ def query(self, signature: list[int]) -> list[str]:
138
+ """Find candidate near-duplicate keys for a signature.
139
+
140
+ Returns keys that share at least one band hash. These are
141
+ candidates — verify with MinHasher.similarity() to get exact estimate.
142
+ """
143
+ if len(signature) != self._expected_sig_len:
144
+ raise ValueError(
145
+ f"Signature length {len(signature)} != expected "
146
+ f"{self._expected_sig_len}"
147
+ )
148
+ candidates: set[str] = set()
149
+ for band_idx in range(self.num_bands):
150
+ bh = self._band_hash(signature, band_idx)
151
+ bucket_key = (band_idx, bh)
152
+ if bucket_key in self._buckets:
153
+ candidates.update(self._buckets[bucket_key])
154
+ return list(candidates)
155
+
156
+ def query_similar(
157
+ self,
158
+ signature: list[int],
159
+ minhasher: MinHasher,
160
+ threshold: float = 0.5,
161
+ ) -> list[tuple[str, float]]:
162
+ """Find near-duplicates above a similarity threshold.
163
+
164
+ Returns list of (key, similarity) sorted by similarity descending.
165
+ """
166
+ candidates = self.query(signature)
167
+ results: list[tuple[str, float]] = []
168
+ for key in candidates:
169
+ sim = minhasher.similarity(signature, self._signatures[key])
170
+ if sim >= threshold:
171
+ results.append((key, sim))
172
+ results.sort(key=lambda x: -x[1])
173
+ return results
174
+
175
+ def stats(self) -> dict[str, Any]:
176
+ return {
177
+ "num_bands": self.num_bands,
178
+ "rows_per_band": self.rows_per_band,
179
+ "indexed_items": len(self._signatures),
180
+ "num_buckets": len(self._buckets),
181
+ "expected_sig_len": self._expected_sig_len,
182
+ }
@@ -0,0 +1,174 @@
1
+ """H12 — Multi-Model Composite Scoring.
2
+
3
+ Score algorithm candidates across a panel of models.
4
+ Mined from G0DM0D3 ULTRAPLINIAN: query N models, score each response,
5
+ pick the winner.
6
+
7
+ LLM integration: requires model clients to query. Falls back to
8
+ rule-based scoring when no models are available.
9
+ """
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass, field
13
+ from typing import Any
14
+
15
+
16
+ @dataclass
17
+ class ModelResponse:
18
+ """A single model's response to a prompt."""
19
+
20
+ model_name: str
21
+ response: str
22
+ latency_ms: float = 0.0
23
+ error: str | None = None
24
+
25
+
26
+ @dataclass
27
+ class ScoredResponse:
28
+ """A response with its composite score."""
29
+
30
+ model_name: str
31
+ response: str
32
+ score: float
33
+ sub_scores: dict[str, float] = field(default_factory=dict)
34
+ winner: bool = False
35
+
36
+
37
+ # Scoring dimensions
38
+ SCORE_DIMENSIONS = [
39
+ "directness", # How direct and clear is the response?
40
+ "completeness", # Does it address all parts of the prompt?
41
+ "accuracy", # Is the information correct?
42
+ "conciseness", # Is it appropriately concise?
43
+ ]
44
+
45
+
46
+ def _score_dimension(response: str, dimension: str) -> float:
47
+ """Score a single dimension of a response (rule-based fallback)."""
48
+ if not response:
49
+ return 0.0
50
+
51
+ text = response.lower()
52
+
53
+ if dimension == "directness":
54
+ # Penalize hedging language
55
+ hedge_words = ["maybe", "perhaps", "might", "could", "possibly", "i think"]
56
+ hedge_count = sum(text.count(w) for w in hedge_words)
57
+ return max(0.0, 1.0 - hedge_count * 0.1)
58
+
59
+ if dimension == "completeness":
60
+ # Reward longer responses (up to a point)
61
+ length = len(response)
62
+ if length < 50:
63
+ return length / 50.0
64
+ if length > 500:
65
+ return min(1.0, 500 / length + 0.5)
66
+ return 1.0
67
+
68
+ if dimension == "accuracy":
69
+ # Rule-based: penalize obvious errors (empty, repeated chars)
70
+ if not response.strip():
71
+ return 0.0
72
+ if response.strip() == response[0] * len(response.strip()):
73
+ return 0.1
74
+ return 0.8 # Default neutral-positive
75
+
76
+ if dimension == "conciseness":
77
+ # Reward shorter responses
78
+ length = len(response)
79
+ if length < 100:
80
+ return 1.0
81
+ if length > 2000:
82
+ return 0.3
83
+ return max(0.3, 1.0 - (length - 100) / 1900)
84
+
85
+ return 0.5
86
+
87
+
88
+ def score_response(
89
+ response: ModelResponse,
90
+ dimensions: list[str] | None = None,
91
+ weights: dict[str, float] | None = None,
92
+ model_client: Any | None = None,
93
+ ) -> ScoredResponse:
94
+ """Score a single model response across dimensions.
95
+
96
+ Args:
97
+ response: The model response to score.
98
+ dimensions: Which dimensions to score. Defaults to all.
99
+ weights: Optional weights per dimension. Defaults to equal.
100
+ model_client: Optional LLM for richer scoring.
101
+
102
+ Returns:
103
+ ScoredResponse with composite score.
104
+ """
105
+ dims = dimensions or SCORE_DIMENSIONS
106
+ w = weights or {d: 1.0 / len(dims) for d in dims}
107
+
108
+ if response.error:
109
+ return ScoredResponse(
110
+ model_name=response.model_name,
111
+ response=response.response,
112
+ score=0.0,
113
+ sub_scores={d: 0.0 for d in dims},
114
+ )
115
+
116
+ sub_scores: dict[str, float] = {}
117
+ for dim in dims:
118
+ sub_scores[dim] = _score_dimension(response.response, dim)
119
+
120
+ total_weight = sum(w.get(d, 0.0) for d in dims)
121
+ if total_weight == 0:
122
+ composite = 0.0
123
+ else:
124
+ composite = sum(sub_scores[d] * w.get(d, 0.0) for d in dims) / total_weight
125
+
126
+ return ScoredResponse(
127
+ model_name=response.model_name,
128
+ response=response.response,
129
+ score=composite,
130
+ sub_scores=sub_scores,
131
+ )
132
+
133
+
134
+ def score_panel(
135
+ responses: list[ModelResponse],
136
+ dimensions: list[str] | None = None,
137
+ weights: dict[str, float] | None = None,
138
+ model_client: Any | None = None,
139
+ ) -> list[ScoredResponse]:
140
+ """Score a panel of model responses and pick the winner.
141
+
142
+ Args:
143
+ responses: List of model responses.
144
+ dimensions: Which dimensions to score.
145
+ weights: Optional weights per dimension.
146
+ model_client: Optional LLM for richer scoring.
147
+
148
+ Returns:
149
+ List of ScoredResponses, with the winner marked.
150
+ """
151
+ scored = [
152
+ score_response(r, dimensions, weights, model_client)
153
+ for r in responses
154
+ ]
155
+ if scored:
156
+ best_idx = max(range(len(scored)), key=lambda i: scored[i].score)
157
+ scored[best_idx] = ScoredResponse(
158
+ model_name=scored[best_idx].model_name,
159
+ response=scored[best_idx].response,
160
+ score=scored[best_idx].score,
161
+ sub_scores=scored[best_idx].sub_scores,
162
+ winner=True,
163
+ )
164
+ return scored
165
+
166
+
167
+ def pick_winner(scored: list[ScoredResponse]) -> ScoredResponse | None:
168
+ """Get the winning response from a scored panel."""
169
+ for s in scored:
170
+ if s.winner:
171
+ return s
172
+ if not scored:
173
+ return None
174
+ return max(scored, key=lambda s: s.score)