fable-engine 1.3.1__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 (104) hide show
  1. fable_compressor.py +356 -0
  2. fable_engine/__init__.py +1 -0
  3. fable_engine/actions/__init__.py +291 -0
  4. fable_engine/actions/cas.py +182 -0
  5. fable_engine/actions/deliberation.py +523 -0
  6. fable_engine/actions/fleet.py +807 -0
  7. fable_engine/actions/lifecycle.py +298 -0
  8. fable_engine/actions/scrapers.py +116 -0
  9. fable_engine/actions/system3.py +815 -0
  10. fable_engine/browser.py +824 -0
  11. fable_engine/cas.py +974 -0
  12. fable_engine/fable_session.json +510 -0
  13. fable_engine/guards.py +283 -0
  14. fable_engine/schema.py +714 -0
  15. fable_engine/scrapers/__init__.py +32 -0
  16. fable_engine/scrapers/arxiv.py +115 -0
  17. fable_engine/scrapers/base.py +386 -0
  18. fable_engine/scrapers/github.py +129 -0
  19. fable_engine/scrapers/reddit.py +154 -0
  20. fable_engine/scrapers/web.py +120 -0
  21. fable_engine/scrapers/x.py +125 -0
  22. fable_engine/scrapers/youtube.py +132 -0
  23. fable_engine/server.py +414 -0
  24. fable_engine/session.py +1819 -0
  25. fable_engine/test_server.py +1362 -0
  26. fable_engine/updater.py +541 -0
  27. fable_engine-1.3.1.dist-info/LICENSE +22 -0
  28. fable_engine-1.3.1.dist-info/METADATA +173 -0
  29. fable_engine-1.3.1.dist-info/RECORD +104 -0
  30. fable_engine-1.3.1.dist-info/WHEEL +5 -0
  31. fable_engine-1.3.1.dist-info/entry_points.txt +5 -0
  32. fable_engine-1.3.1.dist-info/top_level.txt +6 -0
  33. fable_mode/__init__.py +3 -0
  34. fable_mode/__main__.py +4 -0
  35. fable_mode/adapters.py +1014 -0
  36. fable_mode/installer.py +553 -0
  37. fable_mode/launcher.py +437 -0
  38. fable_mode/manifest.py +142 -0
  39. fable_mode/resources.json +114 -0
  40. fable_mode/safety.py +103 -0
  41. fable_mode_entry.py +10 -0
  42. fable_v2/__init__.py +146 -0
  43. fable_v2/adapters.py +151 -0
  44. fable_v2/coder_fleet/__init__.py +100 -0
  45. fable_v2/coder_fleet/ast_tools.py +158 -0
  46. fable_v2/coder_fleet/compute.py +199 -0
  47. fable_v2/coder_fleet/design_engine.py +1316 -0
  48. fable_v2/coder_fleet/diagnostics.py +293 -0
  49. fable_v2/coder_fleet/fleet_dispatcher.py +214 -0
  50. fable_v2/coder_fleet/mock_auditor.py +306 -0
  51. fable_v2/coder_fleet/mutation.py +216 -0
  52. fable_v2/coder_fleet/property_oracle.py +260 -0
  53. fable_v2/coder_fleet/receipt_attestor.py +122 -0
  54. fable_v2/coder_fleet/red_team_swarm.py +908 -0
  55. fable_v2/coder_fleet/test_harness.py +198 -0
  56. fable_v2/coder_fleet/vector_engine.py +1287 -0
  57. fable_v2/coder_fleet/visual.py +357 -0
  58. fable_v2/coder_fleet/workspace.py +153 -0
  59. fable_v2/cortical/__init__.py +20 -0
  60. fable_v2/cortical/plasticity_engine.py +992 -0
  61. fable_v2/execution_broker.py +811 -0
  62. fable_v2/proof_engine.py +1141 -0
  63. fable_v2/protocol.py +485 -0
  64. fable_v2/runtime.py +1010 -0
  65. fable_v2/system3/__init__.py +204 -0
  66. fable_v2/system3/causal.py +558 -0
  67. fable_v2/system3/dialectical.py +577 -0
  68. fable_v2/system3/evolution.py +503 -0
  69. fable_v2/system3/executive.py +338 -0
  70. fable_v2/system3/free_energy.py +479 -0
  71. fable_v2/system3/hyperbolic.py +555 -0
  72. fable_v2/system3/induction.py +336 -0
  73. fable_v2/system3/kripke.py +548 -0
  74. fable_v2/system3/oracle.py +745 -0
  75. fable_v2/verifiers.py +72 -0
  76. tests/__init__.py +1 -0
  77. tests/test_anti_loop_circuit_breaker.py +64 -0
  78. tests/test_auto_updater.py +407 -0
  79. tests/test_coder_fleet.py +535 -0
  80. tests/test_delegation_compiler.py +54 -0
  81. tests/test_descriptor_boundaries.py +126 -0
  82. tests/test_design_engine.py +603 -0
  83. tests/test_epistemic_evidence_validator.py +66 -0
  84. tests/test_execution_broker.py +233 -0
  85. tests/test_fable_v2.py +406 -0
  86. tests/test_fleet_transitions.py +116 -0
  87. tests/test_fsm_redteam_evolution.py +406 -0
  88. tests/test_goal_rubric_and_pipeline.py +367 -0
  89. tests/test_hebbian_plasticity.py +585 -0
  90. tests/test_packaging_runtime.py +194 -0
  91. tests/test_proof_engine.py +259 -0
  92. tests/test_red_team_swarm.py +645 -0
  93. tests/test_redteam_remediation.py +169 -0
  94. tests/test_registration_transaction.py +375 -0
  95. tests/test_requested_regressions.py +467 -0
  96. tests/test_scrapers.py +370 -0
  97. tests/test_server_actions.py +93 -0
  98. tests/test_server_frontier_actions.py +269 -0
  99. tests/test_server_protocol.py +88 -0
  100. tests/test_stealth_browser.py +970 -0
  101. tests/test_system3.py +381 -0
  102. tests/test_system3_deep_integration.py +385 -0
  103. tests/test_system3_frontier.py +436 -0
  104. tests/test_vector_engine.py +608 -0
@@ -0,0 +1,158 @@
1
+ """Tree-Sitter / AST Codemod Engine for Structural Queries and Refactoring.
2
+
3
+ Pure-Python implementation utilizing standard library ast and tokenize modules
4
+ with zero mandatory external C-dependencies.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import ast
9
+ import io
10
+ import json
11
+ import tokenize
12
+ from typing import Any
13
+
14
+
15
+ class TreeSitterCodemodEngine:
16
+ """Engine for syntax verification, AST structural queries, and safe symbol renaming."""
17
+
18
+ def query_ast(self, source_code: str, node_type: str = "FunctionDef") -> list[dict[str, Any]]:
19
+ """Query AST nodes matching node_type with line numbers, arguments, and metadata."""
20
+ try:
21
+ tree = ast.parse(source_code)
22
+ except SyntaxError:
23
+ return []
24
+
25
+ results: list[dict[str, Any]] = []
26
+
27
+ for node in ast.walk(tree):
28
+ if type(node).__name__ == node_type:
29
+ item: dict[str, Any] = {
30
+ "node_type": node_type,
31
+ "name": getattr(node, "name", None),
32
+ "line": getattr(node, "lineno", 1),
33
+ "end_line": getattr(node, "end_lineno", getattr(node, "lineno", 1)),
34
+ "col": getattr(node, "col_offset", 0),
35
+ }
36
+
37
+ # Extract arguments if FunctionDef or AsyncFunctionDef
38
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
39
+ args_list: list[str] = [arg.arg for arg in node.args.args]
40
+ if node.args.vararg:
41
+ args_list.append(f"*{node.args.vararg.arg}")
42
+ for kw in node.args.kwonlyargs:
43
+ args_list.append(kw.arg)
44
+ if node.args.kwarg:
45
+ args_list.append(f"**{node.args.kwarg.arg}")
46
+ item["args"] = args_list
47
+
48
+ # Decorators
49
+ item["decorators"] = [
50
+ ast.unparse(d) if hasattr(ast, "unparse") else getattr(d, "id", "decorator")
51
+ for d in node.decorator_list
52
+ ]
53
+
54
+ # Return annotation
55
+ if node.returns:
56
+ item["returns"] = ast.unparse(node.returns) if hasattr(ast, "unparse") else str(node.returns)
57
+
58
+ # Docstring
59
+ item["docstring"] = ast.get_docstring(node)
60
+
61
+ elif isinstance(node, ast.ClassDef):
62
+ item["bases"] = [
63
+ ast.unparse(b) if hasattr(ast, "unparse") else getattr(b, "id", "base")
64
+ for b in node.bases
65
+ ]
66
+ item["docstring"] = ast.get_docstring(node)
67
+
68
+ elif isinstance(node, ast.Call):
69
+ func_name = ast.unparse(node.func) if hasattr(ast, "unparse") else getattr(node.func, "id", "call")
70
+ item["func_name"] = func_name
71
+ item["arg_count"] = len(node.args)
72
+
73
+ results.append(item)
74
+
75
+ results.sort(key=lambda x: (x.get("line", 0), x.get("col", 0)))
76
+ return results
77
+
78
+ def rename_symbol(self, source_code: str, old_name: str, new_name: str) -> tuple[str, int]:
79
+ """AST / tokenizer-based identifier rename that preserves string literals and comments
80
+
81
+ without false substring matches.
82
+ """
83
+ if not source_code or old_name == new_name:
84
+ return source_code, 0
85
+
86
+ try:
87
+ tokens = list(tokenize.tokenize(io.BytesIO(source_code.encode("utf-8")).readline))
88
+ except (tokenize.TokenError, IndentationError):
89
+ return source_code, 0
90
+
91
+ # Collect exact NAME tokens matching old_name
92
+ replacements: list[tuple[tuple[int, int], tuple[int, int]]] = []
93
+ for tok in tokens:
94
+ if tok.type == tokenize.NAME and tok.string == old_name:
95
+ replacements.append((tok.start, tok.end))
96
+
97
+ if not replacements:
98
+ return source_code, 0
99
+
100
+ # Apply replacements backwards so line/column coordinates remain valid
101
+ lines = source_code.splitlines(keepends=True)
102
+ for (s_row, s_col), (e_row, e_col) in sorted(replacements, reverse=True):
103
+ if 1 <= s_row <= len(lines):
104
+ line = lines[s_row - 1]
105
+ new_line = line[:s_col] + new_name + line[e_col:]
106
+ lines[s_row - 1] = new_line
107
+
108
+ return "".join(lines), len(replacements)
109
+
110
+ def verify_syntax(self, source_code: str, language: str = "python") -> dict[str, Any]:
111
+ """Verify code syntax, returning {valid: bool, error: str, line: int}."""
112
+ lang = language.lower()
113
+
114
+ if lang == "python":
115
+ try:
116
+ ast.parse(source_code)
117
+ return {
118
+ "valid": True,
119
+ "error": None,
120
+ "line": None,
121
+ "col": None,
122
+ "language": "python",
123
+ }
124
+ except SyntaxError as exc:
125
+ return {
126
+ "valid": False,
127
+ "error": exc.msg,
128
+ "line": exc.lineno or 1,
129
+ "col": exc.offset or 0,
130
+ "language": "python",
131
+ }
132
+
133
+ elif lang == "json":
134
+ try:
135
+ json.loads(source_code)
136
+ return {
137
+ "valid": True,
138
+ "error": None,
139
+ "line": None,
140
+ "col": None,
141
+ "language": "json",
142
+ }
143
+ except json.JSONDecodeError as exc:
144
+ return {
145
+ "valid": False,
146
+ "error": exc.msg,
147
+ "line": exc.lineno,
148
+ "col": exc.colno,
149
+ "language": "json",
150
+ }
151
+
152
+ return {
153
+ "valid": False,
154
+ "error": f"Unsupported language '{language}' for syntax verification",
155
+ "line": None,
156
+ "col": None,
157
+ "language": language,
158
+ }
@@ -0,0 +1,199 @@
1
+ """Compute Orchestrator Engine for Thinking Budgets, MCTS Exploration, and Best-of-N Consensus.
2
+
3
+ Calculates model token velocity profiles, performs deterministic Monte Carlo Tree Search,
4
+ and synthesizes multi-candidate consensus rankings.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import math
9
+ from typing import Any
10
+
11
+
12
+ class MCTSNode:
13
+ """A node in the Monte Carlo Tree Search tree."""
14
+
15
+ def __init__(self, name: str, parent: MCTSNode | None = None, depth: int = 0) -> None:
16
+ self.name = name
17
+ self.parent = parent
18
+ self.children: list[MCTSNode] = []
19
+ self.visits: int = 0
20
+ self.value: float = 0.0
21
+ self.depth: int = depth
22
+
23
+ def is_leaf(self) -> bool:
24
+ return len(self.children) == 0
25
+
26
+ def ucb1(self, exploration_constant: float = 1.414) -> float:
27
+ if self.visits == 0:
28
+ return float("inf")
29
+ parent_visits = self.parent.visits if self.parent else self.visits
30
+ exploit = self.value / self.visits
31
+ explore = exploration_constant * math.sqrt(math.log(max(1, parent_visits)) / self.visits)
32
+ return exploit + explore
33
+
34
+
35
+ class ComputeOrchestratorEngine:
36
+ """Engine for compute budgeting, MCTS decision search, and candidate consensus evaluation."""
37
+
38
+ def calculate_thinking_budget(self, complexity_score: float, failure_count: int) -> dict[str, Any]:
39
+ """Calculate recommended thinking tokens (4k-64k) and model tier based on complexity
40
+
41
+ and previous failure iterations.
42
+ """
43
+ clamped_complexity = max(0.0, min(10.0, float(complexity_score)))
44
+ clamped_failures = max(0, int(failure_count))
45
+
46
+ # Base token range: 4,096 to 65,536
47
+ min_tokens = 4096
48
+ max_tokens = 65536
49
+
50
+ # Difficulty scaling
51
+ factor = (clamped_complexity / 10.0) ** 1.2
52
+ failure_bonus = clamped_failures * 8192
53
+ raw_tokens = min_tokens + int((max_tokens - min_tokens) * factor) + failure_bonus
54
+ recommended_tokens = max(min_tokens, min(max_tokens, raw_tokens))
55
+ # Round to nearest 1024
56
+ recommended_tokens = (recommended_tokens // 1024) * 1024
57
+
58
+ # Model tier determination
59
+ if clamped_complexity <= 3.0 and clamped_failures == 0:
60
+ model_tier = "flash"
61
+ strategy = "direct_single_pass"
62
+ elif clamped_complexity <= 7.0 and clamped_failures <= 1:
63
+ model_tier = "pro"
64
+ strategy = "dual_pass_verification"
65
+ else:
66
+ model_tier = "deepthink"
67
+ strategy = "system3_mcts_redteam"
68
+
69
+ return {
70
+ "complexity_score": clamped_complexity,
71
+ "failure_count": clamped_failures,
72
+ "recommended_tokens": recommended_tokens,
73
+ "model_tier": model_tier,
74
+ "strategy": strategy,
75
+ }
76
+
77
+ def mcts_explore(self, problem_spec: dict[str, Any], branches: int = 4, depth: int = 2) -> dict[str, Any]:
78
+ """Simulate branch exploration and value backpropagation using Monte Carlo Tree Search."""
79
+ root = MCTSNode(name="root", depth=0)
80
+ candidate_strategies = problem_spec.get("strategies") or [f"strategy_branch_{i+1}" for i in range(branches)]
81
+
82
+ # Expand root
83
+ for strat in candidate_strategies[:branches]:
84
+ child = MCTSNode(name=str(strat), parent=root, depth=1)
85
+ root.children.append(child)
86
+
87
+ # Simulation iterations
88
+ total_simulations = max(20, branches * depth * 6)
89
+
90
+ for _ in range(total_simulations):
91
+ # 1. Selection
92
+ curr = root
93
+ while not curr.is_leaf() and curr.depth < depth:
94
+ curr = max(curr.children, key=lambda n: n.ucb1())
95
+
96
+ # 2. Expansion if not at max depth
97
+ if curr.depth < depth and curr.visits > 0:
98
+ for sub_i in range(min(2, branches)):
99
+ sub_child = MCTSNode(name=f"{curr.name}.step_{sub_i+1}", parent=curr, depth=curr.depth + 1)
100
+ curr.children.append(sub_child)
101
+ curr = curr.children[0]
102
+
103
+ # 3. Simulation / Rollout evaluation
104
+ # Heuristic score based on candidate attributes or pseudo-deterministic fitness
105
+ base_fitness = 0.5
106
+ strat_bonus = (hash(curr.name) % 100) / 200.0 # [0.0, 0.5]
107
+ depth_penalty = curr.depth * 0.02
108
+ reward = max(0.0, min(1.0, base_fitness + strat_bonus - depth_penalty))
109
+
110
+ # 4. Backpropagation
111
+ back = curr
112
+ while back is not None:
113
+ back.visits += 1
114
+ back.value += reward
115
+ back = back.parent
116
+
117
+ # Select best branch from root
118
+ best_child = max(root.children, key=lambda n: n.visits)
119
+
120
+ # Build recommended path
121
+ path = [best_child.name]
122
+ trav = best_child
123
+ while trav.children:
124
+ trav = max(trav.children, key=lambda n: n.visits)
125
+ path.append(trav.name)
126
+
127
+ tree_summary = [
128
+ {
129
+ "name": ch.name,
130
+ "visits": ch.visits,
131
+ "avg_value": round(ch.value / max(1, ch.visits), 4),
132
+ "ucb1": round(ch.ucb1(), 4),
133
+ }
134
+ for ch in root.children
135
+ ]
136
+
137
+ return {
138
+ "root_visits": root.visits,
139
+ "best_branch": {
140
+ "name": best_child.name,
141
+ "visits": best_child.visits,
142
+ "avg_value": round(best_child.value / max(1, best_child.visits), 4),
143
+ },
144
+ "branches_explored": len(root.children),
145
+ "tree_summary": tree_summary,
146
+ "recommended_path": path,
147
+ }
148
+
149
+ def best_of_n_consensus(self, candidate_results: list[dict[str, Any]]) -> dict[str, Any]:
150
+ """Select highest scoring candidate via differential comparison and consensus scoring."""
151
+ if not candidate_results:
152
+ return {
153
+ "selected_candidate": None,
154
+ "consensus_score": 0.0,
155
+ "rankings": [],
156
+ "differential_notes": ["No candidate results provided for evaluation"],
157
+ }
158
+
159
+ ranked_candidates: list[dict[str, Any]] = []
160
+
161
+ for idx, cand in enumerate(candidate_results):
162
+ cand_id = cand.get("id", f"candidate_{idx+1}")
163
+ pass_rate = float(cand.get("test_pass_rate", cand.get("pass_rate", 1.0)))
164
+ raw_score = float(cand.get("score", 0.5))
165
+ complexity = float(cand.get("complexity", 5.0))
166
+ # Simplicity bonus (lower complexity is better)
167
+ simplicity = max(0.0, 1.0 - (complexity / 10.0))
168
+
169
+ # Composite weighted fitness
170
+ fitness = (pass_rate * 0.50) + (raw_score * 0.35) + (simplicity * 0.15)
171
+ ranked_candidates.append({
172
+ "candidate": cand,
173
+ "id": cand_id,
174
+ "fitness": round(fitness, 4),
175
+ "pass_rate": pass_rate,
176
+ "raw_score": raw_score,
177
+ "simplicity": simplicity,
178
+ })
179
+
180
+ ranked_candidates.sort(key=lambda x: x["fitness"], reverse=True)
181
+ winner = ranked_candidates[0]["candidate"]
182
+
183
+ # Calculate consensus (agreement among top candidates on pass rate / score)
184
+ high_performers = [c for c in ranked_candidates if c["fitness"] >= 0.7]
185
+ consensus_score = round(len(high_performers) / len(ranked_candidates), 4)
186
+
187
+ differential_notes: list[str] = [
188
+ f"Selected winner '{ranked_candidates[0]['id']}' with top composite fitness {ranked_candidates[0]['fitness']}",
189
+ ]
190
+ if len(ranked_candidates) > 1:
191
+ margin = round(ranked_candidates[0]["fitness"] - ranked_candidates[1]["fitness"], 4)
192
+ differential_notes.append(f"Fitness margin over runner-up: +{margin}")
193
+
194
+ return {
195
+ "selected_candidate": winner,
196
+ "consensus_score": consensus_score,
197
+ "rankings": ranked_candidates,
198
+ "differential_notes": differential_notes,
199
+ }