Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/gradata/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,6 @@ def _cloud_sync_session(
try:
import hashlib
import os

from pathlib import Path

# 1. Resolve cloud credentials: ~/.gradata/config.toml or env var
Expand Down
2 changes: 1 addition & 1 deletion src/gradata/_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ class RetainOrchestrator:
position on the next call.
"""

def __init__(self, brain_dir: "str | Path") -> None:
def __init__(self, brain_dir: str | Path) -> None:
from pathlib import Path as _Path
self.brain_dir = _Path(brain_dir)
self.events_path = self.brain_dir / "events.jsonl"
Expand Down
28 changes: 14 additions & 14 deletions src/gradata/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from __future__ import annotations

import argparse
import contextlib
import json
import logging
import sys
Expand Down Expand Up @@ -516,7 +517,7 @@ def _sanitize_toml_value(val: str) -> str:
return val.replace("\n", "").replace("\r", "").replace("[", "").replace("]", "").replace('"', "").replace("\\", "").strip()


def _check_config_permissions(config_path: "Path") -> None:
def _check_config_permissions(config_path: Path) -> None:
"""Finding 4: warn if config file is world-readable (Unix only)."""
import os
import stat
Expand All @@ -538,17 +539,18 @@ def cmd_login(args):
import os
import time
import webbrowser
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError

api_url = os.environ.get("GRADATA_API_URL", "https://api.gradata.ai/api/v1")

# Finding 5: reject non-HTTPS API URLs (allow localhost for development)
if not api_url.startswith("https://"):
if not (api_url.startswith("http://localhost") or api_url.startswith("http://127.0.0.1")):
print(f"Error: GRADATA_API_URL must use HTTPS (got: {api_url})")
print(" HTTP is only allowed for localhost/127.0.0.1 during development.")
sys.exit(1)
if not api_url.startswith("https://") and not (
api_url.startswith("http://localhost") or api_url.startswith("http://127.0.0.1")
):
print(f"Error: GRADATA_API_URL must use HTTPS (got: {api_url})")
print(" HTTP is only allowed for localhost/127.0.0.1 during development.")
sys.exit(1)

# Step 1: Request a device code
try:
Expand Down Expand Up @@ -614,10 +616,9 @@ def cmd_login(args):
config_path.parent.mkdir(parents=True, exist_ok=True)

# Finding 4: restrict directory permissions (Unix)
try:
# Windows — os.chmod has limited effect, suppress the error
with contextlib.suppress(OSError, AttributeError):
os.chmod(config_path.parent, 0o700)
except (OSError, AttributeError):
pass # Windows — os.chmod has limited effect

# Finding 12: sanitize values before writing TOML
safe_key = _sanitize_toml_value(api_key)
Expand All @@ -634,16 +635,15 @@ def cmd_login(args):
)

# Finding 4: restrict file permissions (Unix)
try:
# Windows — os.chmod has limited effect, suppress the error
with contextlib.suppress(OSError, AttributeError):
os.chmod(config_path, 0o600)
except (OSError, AttributeError):
pass # Windows — os.chmod has limited effect

# Also set in the environment for the current process (so
# subsequent brain operations in the same shell can auto-sync)
os.environ["GRADATA_API_KEY"] = api_key

print(f" Connected! Your brain will sync to app.gradata.ai")
print(" Connected! Your brain will sync to app.gradata.ai")
print(f" Config saved to {config_path}")

# Finding 4: check permissions on startup
Expand Down
5 changes: 2 additions & 3 deletions src/gradata/correction_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from __future__ import annotations

import re
from dataclasses import dataclass, field
from dataclasses import dataclass
from enum import StrEnum
from typing import Any

Expand Down Expand Up @@ -149,7 +149,7 @@ def to_dict(self) -> dict[str, Any]:
}

@classmethod
def from_dict(cls, data: dict[str, Any]) -> "StructuredCorrection":
def from_dict(cls, data: dict[str, Any]) -> StructuredCorrection:
"""Deserialize from a plain dict."""
return cls(
what_wrong=data.get("what_wrong", ""),
Expand Down Expand Up @@ -416,7 +416,6 @@ def extract_structured_correction(
# one/both of draft/final are empty. Nothing to extract.
return None

correction_text = final or context
full_text = " ".join(filter(None, [context, final]))

what_wrong = _extract_what_wrong(draft, final)
Expand Down
6 changes: 3 additions & 3 deletions src/gradata/enhancements/bandits/collaborative_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

import hashlib
import math
from dataclasses import dataclass, field
from dataclasses import dataclass


@dataclass
Expand All @@ -61,7 +61,7 @@ class BrainFingerprint:
category_distribution: dict[str, int] # category -> rule count

@classmethod
def from_lessons(cls, lessons: list, domain: str = "", total_sessions: int = 0) -> "BrainFingerprint":
def from_lessons(cls, lessons: list, domain: str = "", total_sessions: int = 0) -> BrainFingerprint:
"""Build a fingerprint from a list of Lesson objects."""

rules = []
Expand Down Expand Up @@ -122,7 +122,7 @@ def compute_brain_similarity(a: BrainFingerprint, b: BrainFingerprint) -> float:
vec_b = [b.category_distribution.get(c, 0) for c in sorted(all_cats)]

# Cosine similarity
dot = sum(x * y for x, y in zip(vec_a, vec_b))
dot = sum(x * y for x, y in zip(vec_a, vec_b, strict=True))
mag_a = math.sqrt(sum(x * x for x in vec_a))
mag_b = math.sqrt(sum(x * x for x in vec_b))

Expand Down
2 changes: 1 addition & 1 deletion src/gradata/enhancements/carl.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Backward-compatibility shim — all code moved to behavioral_engine.py."""

from gradata.enhancements.behavioral_engine import ( # noqa: F401
from gradata.enhancements.behavioral_engine import (
Comment on lines 1 to +3
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether this file (and optionally other SDK Python files) includes the required future import.
set -euo pipefail

echo "Checking src/gradata/enhancements/carl.py..."
sed -n '1,20p' src/gradata/enhancements/carl.py

echo
echo "Scanning all SDK Python files missing 'from __future__ import annotations'..."
fd -e py . src/gradata | while read -r f; do
  if ! rg -n "^from __future__ import annotations$" "$f" >/dev/null; then
    echo "$f"
  fi
done

Repository: Gradata/gradata

Length of output: 1686


Add required from __future__ import annotations at module top.

This SDK file is missing the required future-annotations import for type safety. Add it directly below the module docstring.

Proposed fix
 """Backward-compatibility shim — all code moved to behavioral_engine.py."""
+
+from __future__ import annotations
 
 from gradata.enhancements.behavioral_engine import (
     BehavioralContract,

Per coding guidelines for src/gradata/**/*.py: type safety requires from __future__ import annotations.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"""Backward-compatibility shim — all code moved to behavioral_engine.py."""
from gradata.enhancements.behavioral_engine import ( # noqa: F401
from gradata.enhancements.behavioral_engine import (
"""Backward-compatibility shim — all code moved to behavioral_engine.py."""
from __future__ import annotations
from gradata.enhancements.behavioral_engine import (
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/gradata/enhancements/carl.py` around lines 1 - 3, The module is missing
the required future import for postponed evaluation of annotations; insert the
line "from __future__ import annotations" immediately below the module docstring
at the top of the file (above the existing import from
gradata.enhancements.behavioral_engine) so that type annotations in this shim
(and any references in imports like gradata.enhancements.behavioral_engine) use
postponed evaluation for type safety.

BehavioralContract,
ConstraintViolation,
ContractRegistry,
Expand Down
2 changes: 1 addition & 1 deletion src/gradata/enhancements/clustering.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def promote_instinct_clusters(
by_category[lesson.category].append(lesson)

promoted: list[str] = []
for category, members in by_category.items():
for _category, members in by_category.items():
if len(members) < min_cluster_size:
continue

Expand Down
21 changes: 10 additions & 11 deletions src/gradata/enhancements/graduation/agent_graduation.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,26 +48,25 @@
import json
import re
from dataclasses import dataclass, field
from datetime import datetime, timezone
from datetime import UTC, datetime
from pathlib import Path

# Import graduation constants from self_improvement (same research-backed values)
from gradata.enhancements.self_improvement import (
SURVIVAL_BONUS,
ACCEPTANCE_BONUS,
MIN_APPLICATIONS_FOR_PATTERN,
MIN_APPLICATIONS_FOR_RULE,
MISFIRE_PENALTY,
PATTERN_THRESHOLD,
RULE_THRESHOLD,
MIN_APPLICATIONS_FOR_PATTERN,
MIN_APPLICATIONS_FOR_RULE,
LessonState,
SURVIVAL_BONUS,
Lesson,
parse_lessons,
LessonState,
format_lessons,
get_maturity_phase,
parse_lessons,
)


# ---------------------------------------------------------------------------
# Approval Gate Thresholds
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -250,7 +249,7 @@ def compile_deterministic_rule(lesson: Lesson) -> DeterministicRule | None:


def _now() -> str:
return datetime.now(timezone.utc).isoformat()
return datetime.now(UTC).isoformat()


# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -450,7 +449,7 @@ def _extract_agent_lesson(
category = "AGENT_" + profile.agent_type.upper()
if edit_category:
category = edit_category.upper()
today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
today = datetime.now(UTC).strftime("%Y-%m-%d")

# Check for duplicate (same edit pattern already captured)
edit_lower = edits.lower()[:100]
Expand Down Expand Up @@ -773,7 +772,7 @@ def compute_quality_scores(self) -> dict:
"best_agent": best,
}

def get_deterministic_rules(self, agent_type: str, task_type: str = "") -> list["DeterministicRule"]:
def get_deterministic_rules(self, agent_type: str, task_type: str = "") -> list[DeterministicRule]:
"""Get RULE-tier lessons compiled into enforceable guard logic.

Only RULE-tier lessons with an enforceable pattern are returned.
Expand Down Expand Up @@ -820,7 +819,7 @@ def get_deterministic_rules(self, agent_type: str, task_type: str = "") -> list[

return rules

def enforce_rules(self, agent_type: str, output: str, task_type: str = "") -> "EnforcementResult":
def enforce_rules(self, agent_type: str, output: str, task_type: str = "") -> EnforcementResult:
"""Apply all deterministic RULE-tier guards to agent output.

Returns an EnforcementResult with pass/fail and details of any violations.
Expand Down
5 changes: 2 additions & 3 deletions src/gradata/enhancements/graduation/judgment_decay.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,11 @@
from typing import Any

from gradata.enhancements.self_improvement import (
LessonState,
Lesson,
PATTERN_THRESHOLD,
Lesson,
LessonState,
)


# Decay constants
DECAY_PER_IDLE_SESSION = 0.02
CONFIDENCE_FLOOR = 0.10
Expand Down
2 changes: 1 addition & 1 deletion src/gradata/enhancements/meta_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ def _try_llm_principle(rules: list[Lesson], category: str) -> str | None:
api_base=b,
model=os.environ.get("GRADATA_LLM_MODEL", "gpt-4o-mini"),
)
except Exception as exc: # noqa: BLE001 -- degrade to deterministic
except Exception as exc:
_log.debug("OpenAI-compat synthesis failed for %s: %s", category, exc)
return None

Expand Down
5 changes: 2 additions & 3 deletions src/gradata/enhancements/profiling/tone_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
from dataclasses import dataclass, field
from typing import Any


# ---------------------------------------------------------------------------
# Tone Feature Extraction
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -118,7 +117,7 @@ def to_dict(self) -> dict[str, Any]:
}

@classmethod
def from_dict(cls, d: dict[str, Any]) -> "ToneFeatures":
def from_dict(cls, d: dict[str, Any]) -> ToneFeatures:
"""Deserialize from dict."""
return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})

Expand Down Expand Up @@ -250,7 +249,7 @@ def to_dict(self) -> dict[str, Any]:
}

@classmethod
def from_dict(cls, d: dict[str, Any]) -> "ToneProfile":
def from_dict(cls, d: dict[str, Any]) -> ToneProfile:
features = ToneFeatures.from_dict(d.get("features", {}))
return cls(
task_type=d["task_type"],
Expand Down
39 changes: 26 additions & 13 deletions src/gradata/enhancements/rule_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ def _generate_skill_file(
return skill_path


def review_generated_skill(skill_path: "Path") -> dict:
def review_generated_skill(skill_path: Path) -> dict:
"""Review a generated skill file for quality issues.

Returns dict with:
Expand Down Expand Up @@ -370,8 +370,9 @@ def run_rule_pipeline(
)
if already_exists:
continue
from gradata._types import Lesson as _Lesson
from datetime import date as _date

from gradata._types import Lesson as _Lesson
candidate = _Lesson(
date=_date.today().isoformat(),
state=LessonState.INSTINCT,
Expand Down Expand Up @@ -404,18 +405,26 @@ def run_rule_pipeline(
# ── Phase 2: Atomic writes ────────────────────────────────────────────────
# Graduate rules, update confidence, create meta-rules.
for lesson in all_lessons:
if lesson.state.name == "INSTINCT" and lesson.confidence >= PATTERN_THRESHOLD:
if lesson.fire_count >= MIN_APPLICATIONS_FOR_PATTERN:
lesson.state = LessonState.PATTERN
result.graduated.append(f"{lesson.category}:{lesson.description[:30]}")
elif lesson.state.name == "PATTERN" and lesson.confidence >= RULE_THRESHOLD:
if lesson.fire_count >= MIN_APPLICATIONS_FOR_RULE:
lesson.state = LessonState.RULE
result.graduated.append(f"{lesson.category}:{lesson.description[:30]}")
if (
lesson.state.name == "INSTINCT"
and lesson.confidence >= PATTERN_THRESHOLD
and lesson.fire_count >= MIN_APPLICATIONS_FOR_PATTERN
):
lesson.state = LessonState.PATTERN
result.graduated.append(f"{lesson.category}:{lesson.description[:30]}")
elif (
lesson.state.name == "PATTERN"
and lesson.confidence >= RULE_THRESHOLD
and lesson.fire_count >= MIN_APPLICATIONS_FOR_RULE
):
lesson.state = LessonState.RULE
result.graduated.append(f"{lesson.category}:{lesson.description[:30]}")

# Synthesize meta-rules from graduated rules
try:
from gradata.enhancements.meta_rules import synthesize_meta_rules_agentic # type: ignore[import]
from gradata.enhancements.meta_rules import (
synthesize_meta_rules_agentic, # type: ignore[import]
)
from gradata.enhancements.meta_rules_storage import ( # type: ignore[import]
load_meta_rules,
save_meta_rules,
Expand Down Expand Up @@ -470,7 +479,9 @@ def run_rule_pipeline(

# Disposition updates from this session's corrections
try:
from gradata.enhancements.behavioral_engine import DispositionTracker # type: ignore[import]
from gradata.enhancements.behavioral_engine import (
DispositionTracker, # type: ignore[import]
)

tracker = DispositionTracker()
disp_path = lessons_path.parent / "disposition.json"
Expand Down Expand Up @@ -613,7 +624,9 @@ def build_knowledge_graph(lessons_path: Path, db_path: Path) -> dict:

# Cross-domain candidates
try:
from gradata.enhancements.meta_rules import detect_cross_domain_candidates # type: ignore[import]
from gradata.enhancements.meta_rules import (
detect_cross_domain_candidates, # type: ignore[import]
)
graph["cross_domain"] = detect_cross_domain_candidates(lessons)
except (ImportError, Exception):
pass
Expand Down
7 changes: 1 addition & 6 deletions src/gradata/enhancements/scoring/brain_scores.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@
import sqlite3
from dataclasses import dataclass, field
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
pass


# ---------------------------------------------------------------------------
# Data model
Expand Down Expand Up @@ -233,7 +228,7 @@ def compute_brain_scores(
"""
# Primary path: delegate to the authoritative implementation
try:
import gradata._events as _events # noqa: PLC0415
import gradata._events as _events

_fn = getattr(_events, "compute_brain_scores", None)
if _fn is None:
Expand Down
3 changes: 1 addition & 2 deletions src/gradata/enhancements/scoring/calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@

from __future__ import annotations

import math
from dataclasses import dataclass, field
from dataclasses import dataclass


@dataclass
Expand Down
Loading
Loading