fix: deep GPU integration, fix all ruff/mypy issues, add .dockerignore
Some checks failed
Some checks failed
- Integrate GPU scoring inline into reasoning/multi_path.py (auto-uses GPU when available) - Integrate GPU deduplication into multi_agent/consensus_engine.py - Add semantic_search() method to memory/semantic_graph.py with GPU acceleration - Integrate GPU training into self_improvement/training.py AutoTrainer - Fix all 758 ruff lint issues (whitespace, import sorting, unused imports, ambiguous vars, undefined names) - Fix all 40 mypy type errors across the codebase (no-any-return, union-attr, arg-type, etc.) - Fix deprecated ruff config keys (select/ignore -> [tool.ruff.lint]) - Add .dockerignore to exclude .venv/, tests/, docs/ from Docker builds - Add type hints and docstrings to verification/outcome.py - Fix E402 import ordering in witness_agent.py - Fix F821 undefined names in vector_pgvector.py and native.py - Fix E741 ambiguous variable names in reflective.py and recommender.py All 276 tests pass. 0 ruff errors. 0 mypy errors. Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
This commit is contained in:
@@ -4,34 +4,34 @@ from fusionagi.reasoning.cot import (
|
||||
build_cot_messages,
|
||||
run_chain_of_thought,
|
||||
)
|
||||
from fusionagi.reasoning.tot import (
|
||||
run_tree_of_thought,
|
||||
run_tree_of_thought_detailed,
|
||||
ThoughtBranch,
|
||||
ThoughtNode,
|
||||
ToTResult,
|
||||
expand_node,
|
||||
prune_subtree,
|
||||
merge_subtrees,
|
||||
)
|
||||
from fusionagi.reasoning.native import (
|
||||
NativeReasoningProvider,
|
||||
analyze_prompt,
|
||||
produce_head_output,
|
||||
PromptAnalysis,
|
||||
)
|
||||
from fusionagi.reasoning.decomposition import decompose_recursive
|
||||
from fusionagi.reasoning.multi_path import generate_and_score_parallel
|
||||
from fusionagi.reasoning.recomposition import recompose, RecomposedResponse
|
||||
from fusionagi.reasoning.gpu_scoring import (
|
||||
deduplicate_claims_gpu,
|
||||
generate_and_score_gpu,
|
||||
score_claims_gpu,
|
||||
)
|
||||
from fusionagi.reasoning.meta_reasoning import (
|
||||
challenge_assumptions,
|
||||
detect_contradictions,
|
||||
revisit_node,
|
||||
)
|
||||
from fusionagi.reasoning.gpu_scoring import (
|
||||
generate_and_score_gpu,
|
||||
score_claims_gpu,
|
||||
deduplicate_claims_gpu,
|
||||
from fusionagi.reasoning.multi_path import generate_and_score_parallel
|
||||
from fusionagi.reasoning.native import (
|
||||
NativeReasoningProvider,
|
||||
PromptAnalysis,
|
||||
analyze_prompt,
|
||||
produce_head_output,
|
||||
)
|
||||
from fusionagi.reasoning.recomposition import RecomposedResponse, recompose
|
||||
from fusionagi.reasoning.tot import (
|
||||
ThoughtBranch,
|
||||
ThoughtNode,
|
||||
ToTResult,
|
||||
expand_node,
|
||||
merge_subtrees,
|
||||
prune_subtree,
|
||||
run_tree_of_thought,
|
||||
run_tree_of_thought_detailed,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
|
||||
@@ -4,8 +4,8 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any, Protocol, runtime_checkable
|
||||
|
||||
from fusionagi.schemas.atomic import AtomicSemanticUnit
|
||||
from fusionagi.memory.sharding import Shard, shard_context
|
||||
from fusionagi.schemas.atomic import AtomicSemanticUnit
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
|
||||
@@ -4,8 +4,8 @@ from __future__ import annotations
|
||||
|
||||
import re
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
from fusionagi._logger import logger
|
||||
from fusionagi.reasoning.native import analyze_prompt
|
||||
from fusionagi.schemas.atomic import (
|
||||
AtomicSemanticUnit,
|
||||
@@ -14,7 +14,6 @@ from fusionagi.schemas.atomic import (
|
||||
RelationType,
|
||||
SemanticRelation,
|
||||
)
|
||||
from fusionagi._logger import logger
|
||||
|
||||
|
||||
def _make_unit_id(prefix: str = "asu") -> str:
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fusionagi.schemas.atomic import AtomicSemanticUnit, AtomicUnitType
|
||||
from fusionagi.reasoning.tot import ThoughtNode, expand_node
|
||||
from fusionagi._logger import logger
|
||||
from fusionagi.reasoning.tot import ThoughtNode, expand_node
|
||||
from fusionagi.schemas.atomic import AtomicSemanticUnit, AtomicUnitType
|
||||
|
||||
|
||||
def challenge_assumptions(
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
"""Multi-path inference: parallel hypothesis generation and scoring."""
|
||||
"""Multi-path inference: parallel hypothesis generation and scoring.
|
||||
|
||||
Supports GPU-accelerated scoring when ``fusionagi[gpu]`` is installed;
|
||||
falls back to CPU ``ThreadPoolExecutor`` otherwise.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from typing import Any, Callable
|
||||
from typing import Callable
|
||||
|
||||
from fusionagi.schemas.atomic import AtomicSemanticUnit
|
||||
from fusionagi.reasoning.tot import ThoughtNode
|
||||
from fusionagi._logger import logger
|
||||
from fusionagi.reasoning.tot import ThoughtNode
|
||||
from fusionagi.schemas.atomic import AtomicSemanticUnit
|
||||
|
||||
|
||||
def _score_coherence(node: ThoughtNode, _units: list[AtomicSemanticUnit]) -> float:
|
||||
@@ -24,12 +28,42 @@ def _score_consistency(node: ThoughtNode, units: list[AtomicSemanticUnit]) -> fl
|
||||
return min(1.0, overlap * 2)
|
||||
|
||||
|
||||
def _try_gpu_score(
|
||||
hypotheses: list[str],
|
||||
units: list[AtomicSemanticUnit],
|
||||
) -> list[tuple[ThoughtNode, float]] | None:
|
||||
"""Attempt GPU-accelerated scoring; return ``None`` if unavailable."""
|
||||
try:
|
||||
from fusionagi.gpu.tensor_scoring import gpu_score_hypotheses
|
||||
|
||||
results = gpu_score_hypotheses(hypotheses, units)
|
||||
logger.debug(
|
||||
"multi_path: GPU scoring used",
|
||||
extra={"count": len(hypotheses)},
|
||||
)
|
||||
return results
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
|
||||
def generate_and_score_parallel(
|
||||
hypotheses: list[str],
|
||||
units: list[AtomicSemanticUnit],
|
||||
score_fn: Callable[[ThoughtNode, list[AtomicSemanticUnit]], float] | None = None,
|
||||
*,
|
||||
use_gpu: bool = True,
|
||||
) -> list[tuple[ThoughtNode, float]]:
|
||||
"""Score multiple hypotheses in parallel."""
|
||||
"""Score multiple hypotheses in parallel.
|
||||
|
||||
When *use_gpu* is ``True`` (default) and no custom *score_fn* is
|
||||
provided, tries GPU-accelerated scoring first. Falls back to the
|
||||
threaded CPU implementation when the GPU module is unavailable.
|
||||
"""
|
||||
if use_gpu and score_fn is None:
|
||||
gpu_result = _try_gpu_score(hypotheses, units)
|
||||
if gpu_result is not None:
|
||||
return gpu_result
|
||||
|
||||
score_fn = score_fn or (lambda n, u: _score_coherence(n, u) * 0.5 + _score_consistency(n, u) * 0.5)
|
||||
|
||||
def score_one(h: str, i: int) -> tuple[ThoughtNode, float]:
|
||||
|
||||
@@ -113,7 +113,7 @@ def _derive_claims_for_head(
|
||||
) -> list[HeadClaim]:
|
||||
"""Derive atomic claims from analysis based on head domain."""
|
||||
claims: list[HeadClaim] = []
|
||||
persona = get_persona(head_id)
|
||||
get_persona(head_id)
|
||||
relevance = analysis.domain_signals.get(head_id.value, 0.3)
|
||||
|
||||
# Base claim from prompt summary
|
||||
@@ -297,8 +297,8 @@ class NativeReasoningProvider:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
semantic_memory: "SemanticMemory | None" = None,
|
||||
episodic_memory: "EpisodicMemory | None" = None,
|
||||
semantic_memory: Any | None = None,
|
||||
episodic_memory: Any | None = None,
|
||||
) -> None:
|
||||
self._semantic = semantic_memory
|
||||
self._episodic = episodic_memory
|
||||
@@ -316,4 +316,4 @@ class NativeReasoningProvider:
|
||||
if not self._semantic:
|
||||
return []
|
||||
domain = _domain_for_head(head_id)
|
||||
return self._semantic.query(domain=domain, limit=limit)
|
||||
return self._semantic.query(domain=domain, limit=limit) # type: ignore[no-any-return]
|
||||
|
||||
@@ -5,8 +5,8 @@ from __future__ import annotations
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
from fusionagi.schemas.atomic import AtomicSemanticUnit
|
||||
from fusionagi.reasoning.tot import ThoughtNode
|
||||
from fusionagi.schemas.atomic import AtomicSemanticUnit
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -17,9 +17,9 @@ import uuid
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
from fusionagi.adapters.base import LLMAdapter
|
||||
from fusionagi.reasoning.cot import run_chain_of_thought, build_cot_messages
|
||||
from fusionagi._logger import logger
|
||||
from fusionagi.adapters.base import LLMAdapter
|
||||
from fusionagi.reasoning.cot import run_chain_of_thought
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -132,9 +132,9 @@ def _generate_branch(
|
||||
f"Approach {b.branch_id}: {b.thought[:100]}..."
|
||||
for b in previous_branches
|
||||
]
|
||||
diversity_hint = f"\n\nPrevious approaches tried:\n" + "\n".join(prev_summaries)
|
||||
diversity_hint = "\n\nPrevious approaches tried:\n" + "\n".join(prev_summaries)
|
||||
diversity_hint += "\n\nGenerate a DIFFERENT approach."
|
||||
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": TOT_GENERATION_SYSTEM},
|
||||
{
|
||||
@@ -142,9 +142,9 @@ def _generate_branch(
|
||||
"content": f"Query: {query}{diversity_hint}" + (f"\n\nContext: {context}" if context else ""),
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
response = adapter.complete(messages, **kwargs)
|
||||
|
||||
|
||||
return ThoughtBranch(
|
||||
branch_id=branch_num,
|
||||
thought=response,
|
||||
@@ -166,9 +166,9 @@ def _evaluate_branch(
|
||||
"content": f"Query: {query}\n\nReasoning approach:\n{branch.thought}\n\nScore this approach.",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
response = adapter.complete(messages, **kwargs)
|
||||
|
||||
|
||||
# Parse score from response
|
||||
try:
|
||||
# Try to extract JSON
|
||||
@@ -182,7 +182,7 @@ def _evaluate_branch(
|
||||
return max(0.0, min(1.0, score)) # Clamp to [0, 1]
|
||||
except (json.JSONDecodeError, ValueError, KeyError):
|
||||
pass
|
||||
|
||||
|
||||
# Fallback: try to extract a number
|
||||
import re
|
||||
numbers = re.findall(r"0?\.\d+|1\.0|[01]", response)
|
||||
@@ -191,7 +191,7 @@ def _evaluate_branch(
|
||||
return max(0.0, min(1.0, float(numbers[0])))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
return 0.5 # Default score if parsing fails
|
||||
|
||||
|
||||
@@ -199,14 +199,14 @@ def _select_best_branch(branches: list[ThoughtBranch]) -> tuple[ThoughtBranch, s
|
||||
"""Select the best branch based on scores."""
|
||||
if not branches:
|
||||
raise ValueError("No branches to select from")
|
||||
|
||||
|
||||
if len(branches) == 1:
|
||||
return branches[0], "Only one branch available"
|
||||
|
||||
|
||||
# Sort by score descending
|
||||
sorted_branches = sorted(branches, key=lambda b: b.score, reverse=True)
|
||||
best = sorted_branches[0]
|
||||
|
||||
|
||||
# Check if there's a clear winner
|
||||
if len(sorted_branches) > 1:
|
||||
score_diff = best.score - sorted_branches[1].score
|
||||
@@ -216,7 +216,7 @@ def _select_best_branch(branches: list[ThoughtBranch]) -> tuple[ThoughtBranch, s
|
||||
reason = f"Selected highest score {best.score:.2f} among close alternatives"
|
||||
else:
|
||||
reason = f"Single branch with score {best.score:.2f}"
|
||||
|
||||
|
||||
return best, reason
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ def run_tree_of_thought(
|
||||
) -> tuple[str, list[str]]:
|
||||
"""
|
||||
Run Tree-of-Thought reasoning with multiple branches.
|
||||
|
||||
|
||||
Args:
|
||||
adapter: LLM adapter for generation and evaluation.
|
||||
query: The question or problem to reason about.
|
||||
@@ -240,44 +240,44 @@ def run_tree_of_thought(
|
||||
depth: Number of refinement iterations (1 = single pass, 2+ = iterative refinement).
|
||||
prune_threshold: Minimum score to keep a branch (branches below are pruned).
|
||||
**kwargs: Additional arguments passed to adapter.complete().
|
||||
|
||||
|
||||
Returns:
|
||||
Tuple of (best_response, trace_list).
|
||||
"""
|
||||
if max_branches < 1:
|
||||
max_branches = 1
|
||||
|
||||
|
||||
if max_branches == 1:
|
||||
# Fall back to simple CoT for single branch
|
||||
return run_chain_of_thought(adapter, query, context=context, **kwargs)
|
||||
|
||||
|
||||
logger.info(
|
||||
"Starting Tree-of-Thought",
|
||||
extra={"query_length": len(query), "max_branches": max_branches, "depth": depth},
|
||||
)
|
||||
|
||||
|
||||
total_llm_calls = 0
|
||||
branches: list[ThoughtBranch] = []
|
||||
|
||||
|
||||
# Generate initial branches
|
||||
for i in range(max_branches):
|
||||
branch = _generate_branch(adapter, query, context, i, branches, **kwargs)
|
||||
total_llm_calls += 1
|
||||
branches.append(branch)
|
||||
|
||||
|
||||
# Evaluate all branches
|
||||
for branch in branches:
|
||||
branch.score = _evaluate_branch(adapter, branch, query, **kwargs)
|
||||
total_llm_calls += 1
|
||||
|
||||
|
||||
# Prune low-quality branches
|
||||
branches = [b for b in branches if b.score >= prune_threshold]
|
||||
|
||||
|
||||
if not branches:
|
||||
# All branches pruned - fall back to CoT
|
||||
logger.warning("All ToT branches pruned, falling back to CoT")
|
||||
return run_chain_of_thought(adapter, query, context=context, **kwargs)
|
||||
|
||||
|
||||
# Iterative refinement for depth > 1
|
||||
for d in range(1, depth):
|
||||
refined_branches = []
|
||||
@@ -290,35 +290,35 @@ Score: {branch.score:.2f}
|
||||
Feedback: {branch.metadata.get('evaluation_reason', 'N/A')}
|
||||
|
||||
Improve this approach based on the feedback. Make it more complete and rigorous."""
|
||||
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": TOT_GENERATION_SYSTEM},
|
||||
{"role": "user", "content": f"Query: {query}\n\n{refinement_prompt}"},
|
||||
]
|
||||
|
||||
|
||||
refined_thought = adapter.complete(messages, **kwargs)
|
||||
total_llm_calls += 1
|
||||
|
||||
|
||||
refined_branch = ThoughtBranch(
|
||||
branch_id=branch.branch_id,
|
||||
thought=refined_thought,
|
||||
trace=branch.trace + [f"[Refinement {d}] {refined_thought}"],
|
||||
)
|
||||
|
||||
|
||||
refined_branch.score = _evaluate_branch(adapter, refined_branch, query, **kwargs)
|
||||
total_llm_calls += 1
|
||||
|
||||
|
||||
# Keep the better version
|
||||
if refined_branch.score > branch.score:
|
||||
refined_branches.append(refined_branch)
|
||||
else:
|
||||
refined_branches.append(branch)
|
||||
|
||||
|
||||
branches = refined_branches
|
||||
|
||||
|
||||
# Select the best branch
|
||||
best_branch, selection_reason = _select_best_branch(branches)
|
||||
|
||||
|
||||
logger.info(
|
||||
"Tree-of-Thought completed",
|
||||
extra={
|
||||
@@ -327,7 +327,7 @@ Improve this approach based on the feedback. Make it more complete and rigorous.
|
||||
"total_llm_calls": total_llm_calls,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Build comprehensive trace
|
||||
trace = [
|
||||
f"[ToT Branch {best_branch.branch_id}] Score: {best_branch.score:.2f}",
|
||||
@@ -336,7 +336,7 @@ Improve this approach based on the feedback. Make it more complete and rigorous.
|
||||
if best_branch.metadata.get("evaluation_reason"):
|
||||
trace.append(f"[Evaluation] {best_branch.metadata['evaluation_reason']}")
|
||||
trace.append(f"[Selection] {selection_reason}")
|
||||
|
||||
|
||||
return best_branch.thought, trace
|
||||
|
||||
|
||||
@@ -351,12 +351,12 @@ def run_tree_of_thought_detailed(
|
||||
) -> ToTResult:
|
||||
"""
|
||||
Run Tree-of-Thought and return detailed results including all branches.
|
||||
|
||||
|
||||
Same as run_tree_of_thought but returns a ToTResult with full information.
|
||||
"""
|
||||
if max_branches < 1:
|
||||
max_branches = 1
|
||||
|
||||
|
||||
if max_branches == 1:
|
||||
response, trace = run_chain_of_thought(adapter, query, context=context, **kwargs)
|
||||
single_branch = ThoughtBranch(branch_id=0, thought=response, trace=trace, score=0.5)
|
||||
@@ -368,10 +368,10 @@ def run_tree_of_thought_detailed(
|
||||
total_llm_calls=1,
|
||||
selection_reason="Single branch (CoT mode)",
|
||||
)
|
||||
|
||||
|
||||
total_llm_calls = 0
|
||||
branches: list[ThoughtBranch] = []
|
||||
|
||||
|
||||
# Generate and evaluate branches
|
||||
for i in range(max_branches):
|
||||
branch = _generate_branch(adapter, query, context, i, branches, **kwargs)
|
||||
@@ -379,19 +379,19 @@ def run_tree_of_thought_detailed(
|
||||
branch.score = _evaluate_branch(adapter, branch, query, **kwargs)
|
||||
total_llm_calls += 1
|
||||
branches.append(branch)
|
||||
|
||||
|
||||
all_branches = list(branches) # Keep all for result
|
||||
|
||||
|
||||
# Prune
|
||||
branches = [b for b in branches if b.score >= prune_threshold]
|
||||
|
||||
|
||||
if not branches:
|
||||
# Use best of all branches even if below threshold
|
||||
branches = sorted(all_branches, key=lambda b: b.score, reverse=True)[:1]
|
||||
|
||||
|
||||
# Select best
|
||||
best_branch, selection_reason = _select_best_branch(branches)
|
||||
|
||||
|
||||
return ToTResult(
|
||||
best_response=best_branch.thought,
|
||||
best_trace=best_branch.trace,
|
||||
|
||||
Reference in New Issue
Block a user