Spaces:
Sleeping
Sleeping
Download services/model_interface.py from Seyomi/biolens-dashboard: direct link, hf CLI and curl.
- Browser
- Download file 17.1 kB
-
https://huggingface.co/spaces/Seyomi/biolens-dashboard/resolve/main/services/model_interface.py
- Command line
-
hf download hf://spaces/Seyomi/biolens-dashboard/services/model_interface.py
-
curl -L -o model_interface.py https://huggingface.co/spaces/Seyomi/biolens-dashboard/resolve/main/services/model_interface.py
17.1 kB
| from __future__ import annotations | |
| import hashlib | |
| import json | |
| import os | |
| from typing import Any | |
| from urllib import error, request | |
| from services.constants import RISK_LEVELS | |
| DNA_ALPHABET = set("ACGTN") | |
| PROTEIN_ALPHABET = set("ABCDEFGHIKLMNPQRSTVWXYZ*") | |
| CATEGORY_BANK = { | |
| "DNA": { | |
| "SAFE": ( | |
| "Routine metabolic gene signature", | |
| "Common structural cassette", | |
| "Low-concern regulatory context", | |
| ), | |
| "REVIEW": ( | |
| "Ambiguous host-interaction signal", | |
| "Regulatory activity worth analyst review", | |
| "Unresolved functional control pattern", | |
| ), | |
| "HIGH": ( | |
| "Elevated host-interaction signature", | |
| "Escalation-priority functional signal", | |
| "High-concern regulation-linked pattern", | |
| ), | |
| }, | |
| "PROTEIN": { | |
| "SAFE": ( | |
| "Routine enzyme-like profile", | |
| "Low-concern scaffold signature", | |
| "Common cellular maintenance pattern", | |
| ), | |
| "REVIEW": ( | |
| "Ambiguous membrane-associated profile", | |
| "Unresolved signaling-like pattern", | |
| "Review-level interaction motif cluster", | |
| ), | |
| "HIGH": ( | |
| "Elevated interaction-associated profile", | |
| "Escalation-priority effector-like pattern", | |
| "High-concern modulation signature", | |
| ), | |
| }, | |
| } | |
| def _clamp(value: float, lower: float = 0.0, upper: float = 1.0) -> float: | |
| return max(lower, min(upper, value)) | |
| def _normalize_sequence(sequence: str) -> str: | |
| return "".join(sequence.split()).upper() | |
| def _hash_unit(*parts: str) -> float: | |
| digest = hashlib.sha256("|".join(parts).encode("utf-8")).hexdigest() | |
| return int(digest[:8], 16) / 0xFFFFFFFF | |
| def _longest_run(sequence: str) -> int: | |
| if not sequence: | |
| return 0 | |
| longest = 1 | |
| current = 1 | |
| for index in range(1, len(sequence)): | |
| if sequence[index] == sequence[index - 1]: | |
| current += 1 | |
| longest = max(longest, current) | |
| else: | |
| current = 1 | |
| return longest | |
| def _error_response(model_name: str, error_text: str, data_source: str = "unknown") -> dict[str, Any]: | |
| return { | |
| "ok": False, | |
| "hazard_score": None, | |
| "risk_level": None, | |
| "confidence": None, | |
| "category": None, | |
| "explanation": None, | |
| "baseline_result": None, | |
| "model_name": model_name, | |
| "error": error_text, | |
| "data_source": data_source, | |
| } | |
| def _validate_sequence(sequence: str, seq_type: str) -> tuple[str | None, str | None]: | |
| normalized = _normalize_sequence(sequence) | |
| if not normalized: | |
| return None, "empty_sequence_input" | |
| if seq_type not in {"DNA", "PROTEIN"}: | |
| return None, "unsupported_sequence_type" | |
| if len(normalized) > 50_000: | |
| return None, ( | |
| "sequence_too_long: Sequences over 50 kb exceed this prototype's scope. " | |
| "Please split into smaller chunks and screen each separately." | |
| ) | |
| allowed = DNA_ALPHABET if seq_type == "DNA" else PROTEIN_ALPHABET | |
| invalid = sorted({character for character in normalized if character not in allowed}) | |
| if invalid: | |
| return None, f"invalid_{seq_type.lower()}_characters:{''.join(invalid[:8])}" | |
| return normalized, None | |
| def _risk_level_from_score(score: float) -> str: | |
| if score >= 0.72: | |
| return "HIGH" | |
| if score >= 0.42: | |
| return "REVIEW" | |
| return "SAFE" | |
| def _pick_category(seq_type: str, risk_level: str, sequence: str) -> str: | |
| bank = CATEGORY_BANK[seq_type][risk_level] | |
| index = int(_hash_unit(seq_type, risk_level, sequence) * len(bank)) % len(bank) | |
| return bank[index] | |
| def _baseline_result(risk_level: str) -> str: | |
| if risk_level == "SAFE": | |
| return "Heuristic baseline: routine profile, no alert." | |
| if risk_level == "REVIEW": | |
| return "Heuristic baseline: partial signal overlap, manual review recommended." | |
| return "Heuristic baseline: inconclusive; function-aware adapter retained a high-priority flag." | |
| def _offline_explanation(seq_type: str, risk_level: str) -> str: | |
| display_type = "DNA" if seq_type == "DNA" else "protein" | |
| if risk_level == "SAFE": | |
| return f"BioLens heuristic {display_type} screening found a low-concern functional profile with no elevated review signals." | |
| if risk_level == "REVIEW": | |
| return f"BioLens heuristic {display_type} screening found ambiguous functional cues — manual analyst review recommended." | |
| return f"BioLens heuristic {display_type} screening found multiple elevated functional cues — this case should be escalated." | |
| def _screen_offline(sequence: str, seq_type: str, model_name: str, data_source: str = "biolens-offline") -> dict[str, Any]: | |
| length = len(sequence) | |
| hash_factor = _hash_unit(sequence, seq_type) | |
| if seq_type == "DNA": | |
| gc_fraction = (sequence.count("G") + sequence.count("C")) / length | |
| unknown_fraction = sequence.count("N") / length | |
| motif_hits = sum(sequence.count(motif) for motif in ("ATG", "TATA", "CGCG", "GGG")) | |
| repeat_factor = min(max(_longest_run(sequence) - 3, 0) * 0.018, 0.18) | |
| score = _clamp( | |
| 0.12 | |
| + min(length / 1800, 0.22) | |
| + abs(gc_fraction - 0.5) * 0.30 | |
| + min(motif_hits * 0.028, 0.18) | |
| + repeat_factor | |
| + min(unknown_fraction * 0.45, 0.12) | |
| + hash_factor * 0.15 | |
| ) | |
| confidence = _clamp(0.58 + min(length / 900, 0.20) + (1 - abs(gc_fraction - 0.5)) * 0.15 + hash_factor * 0.07) | |
| pathogenicity = _clamp(0.2 + abs(gc_fraction - 0.5) * 0.4 + min(motif_hits * 0.05, 0.3)) | |
| evasion_potential = _clamp(0.1 + repeat_factor * 2 + hash_factor * 0.2) | |
| synthesis_feasibility = _clamp(0.9 - min(length / 5000, 0.8)) | |
| environmental_resilience = _clamp(0.3 + gc_fraction * 0.4) | |
| host_range = _clamp(0.4 + hash_factor * 0.3) | |
| attr_positions = [i for i, c in enumerate(sequence) if c in "GC" and i % 7 == 0] | |
| attr_scores = [round(_clamp(0.4 + hash_factor * 0.6), 3) for _ in attr_positions] | |
| regions = [{"start": 0, "end": min(30, length), "label": "GC-rich motif region", "score": round(pathogenicity, 3)}] | |
| else: | |
| hydrophobic_fraction = sum(character in "AVILMFWYC" for character in sequence) / length | |
| charged_fraction = sum(character in "KRDEH" for character in sequence) / length | |
| motif_hits = sum(sequence.count(motif) for motif in ("KK", "RR", "KR", "GP", "GG")) | |
| low_complexity = 1 - (len(set(sequence)) / max(1, min(20, length))) | |
| repeat_factor = min(max(_longest_run(sequence) - 2, 0) * 0.022, 0.20) | |
| score = _clamp( | |
| 0.14 | |
| + min(length / 1200, 0.20) | |
| + abs(hydrophobic_fraction - 0.34) * 0.38 | |
| + abs(charged_fraction - 0.22) * 0.24 | |
| + min(motif_hits * 0.024, 0.16) | |
| + min(low_complexity * 0.22, 0.18) | |
| + repeat_factor | |
| + hash_factor * 0.15 | |
| ) | |
| confidence = _clamp( | |
| 0.56 + min(length / 700, 0.20) + (1 - abs(charged_fraction - 0.22)) * 0.12 + hash_factor * 0.08 | |
| ) | |
| pathogenicity = _clamp(0.3 + abs(charged_fraction - 0.2) * 0.5 + min(motif_hits * 0.05, 0.3)) | |
| evasion_potential = _clamp(0.2 + low_complexity * 0.4 + hash_factor * 0.2) | |
| synthesis_feasibility = _clamp(0.8 - min(length / 2000, 0.7)) | |
| environmental_resilience = _clamp(0.2 + hydrophobic_fraction * 0.5) | |
| host_range = _clamp(0.5 + hash_factor * 0.2) | |
| attr_positions = [i for i, c in enumerate(sequence) if c in "KRDEH" and i % 3 == 0] | |
| attr_scores = [round(_clamp(0.5 + hash_factor * 0.5), 3) for _ in attr_positions] | |
| regions = [{"start": 0, "end": min(20, length), "label": "Charged cluster", "score": round(pathogenicity, 3)}] | |
| risk_level = _risk_level_from_score(score) | |
| category = _pick_category(seq_type, risk_level, sequence) | |
| return { | |
| "ok": True, | |
| "hazard_score": round(score, 3), | |
| "risk_level": risk_level, | |
| "confidence": round(confidence, 3), | |
| "category": category, | |
| "explanation": _offline_explanation(seq_type, risk_level), | |
| "baseline_result": _baseline_result(risk_level), | |
| "model_name": model_name, | |
| "error": None, | |
| "data_source": "biolens-offline", | |
| "threat_breakdown": { | |
| "pathogenicity": round(pathogenicity, 3), | |
| "evasion_potential": round(evasion_potential, 3), | |
| "synthesis_feasibility": round(synthesis_feasibility, 3), | |
| "environmental_resilience": round(environmental_resilience, 3), | |
| "host_range": round(host_range, 3), | |
| }, | |
| "attribution_data": { | |
| "positions": attr_positions, | |
| "scores": attr_scores, | |
| "regions": regions, | |
| } | |
| } | |
| def _coerce_integrated_response(payload: dict[str, Any], fallback_model_name: str) -> dict[str, Any]: | |
| try: | |
| if not payload.get("ok"): | |
| return _error_response( | |
| str(payload.get("model_name") or fallback_model_name), | |
| str(payload.get("error") or "integration_failure"), | |
| data_source="synthguard-api", | |
| ) | |
| risk_level = str(payload.get("risk_level")) | |
| if risk_level not in RISK_LEVELS: | |
| raise ValueError("invalid_risk_level") | |
| hazard_score = _clamp(float(payload.get("hazard_score"))) | |
| confidence = _clamp(float(payload.get("confidence"))) | |
| return { | |
| "ok": True, | |
| "hazard_score": round(hazard_score, 3), | |
| "risk_level": risk_level, | |
| "confidence": round(confidence, 3), | |
| "category": str(payload.get("category") or "Unspecified"), | |
| "explanation": str(payload.get("explanation") or "No explanation provided."), | |
| "baseline_result": str(payload.get("baseline_result")) if payload.get("baseline_result") else None, | |
| "model_name": str(payload.get("model_name") or fallback_model_name), | |
| "error": None, | |
| "data_source": "synthguard-api", | |
| "threat_breakdown": payload.get("threat_breakdown"), | |
| "attribution_data": payload.get("attribution_data"), | |
| } | |
| except (TypeError, ValueError) as exc: | |
| return _error_response(fallback_model_name, f"invalid_integrated_response:{exc}", data_source="synthguard-api") | |
| _DEFAULT_ENDPOINT = "https://seyomi-synthguard-api.hf.space/biolens/screen" | |
| def get_base_url() -> str: | |
| """Derive the base API URL from the configured endpoint.""" | |
| endpoint = os.getenv("SYNTHSCREEN_ENDPOINT", _DEFAULT_ENDPOINT) | |
| for suffix in ("/biolens/screen", "/screen", "/protein/screen"): | |
| if endpoint.endswith(suffix): | |
| return endpoint[: -len(suffix)] | |
| return endpoint.rstrip("/") | |
| def get_api_health() -> dict[str, Any]: | |
| """GET /health""" | |
| url = f"{get_base_url()}/health" | |
| try: | |
| req = request.Request(url, method="GET") | |
| with request.urlopen(req, timeout=5) as resp: | |
| return {"ok": resp.status == 200, "status": resp.status} | |
| except Exception as exc: | |
| return {"ok": False, "error": str(exc)} | |
| def screen_dna_direct(sequence: str) -> dict[str, Any]: | |
| """POST /screen""" | |
| url = f"{get_base_url()}/screen" | |
| payload = json.dumps({"sequence": sequence}).encode("utf-8") | |
| req = request.Request(url, data=payload, headers={"Content-Type": "application/json"}, method="POST") | |
| try: | |
| with request.urlopen(req, timeout=30) as resp: | |
| return json.loads(resp.read().decode("utf-8")) | |
| except Exception as exc: | |
| return {"ok": False, "error": str(exc)} | |
| def screen_protein_direct(sequence: str) -> dict[str, Any]: | |
| """POST /protein/screen""" | |
| url = f"{get_base_url()}/protein/screen" | |
| payload = json.dumps({"sequence": sequence}).encode("utf-8") | |
| req = request.Request(url, data=payload, headers={"Content-Type": "application/json"}, method="POST") | |
| try: | |
| with request.urlopen(req, timeout=30) as resp: | |
| return json.loads(resp.read().decode("utf-8")) | |
| except Exception as exc: | |
| return {"ok": False, "error": str(exc)} | |
| def screen_batch(sequences: list[str]) -> dict[str, Any]: | |
| """POST /screen/batch""" | |
| url = f"{get_base_url()}/screen/batch" | |
| payload = json.dumps({"sequences": sequences}).encode("utf-8") | |
| req = request.Request(url, data=payload, headers={"Content-Type": "application/json"}, method="POST") | |
| try: | |
| with request.urlopen(req, timeout=60) as resp: | |
| return json.loads(resp.read().decode("utf-8")) | |
| except Exception as exc: | |
| return {"ok": False, "error": str(exc)} | |
| def submit_split_order(sequences: list[str]) -> dict[str, Any]: | |
| """POST /split/submit""" | |
| url = f"{get_base_url()}/split/submit" | |
| payload = json.dumps({"sequences": sequences}).encode("utf-8") | |
| req = request.Request(url, data=payload, headers={"Content-Type": "application/json"}, method="POST") | |
| try: | |
| with request.urlopen(req, timeout=60) as resp: | |
| return json.loads(resp.read().decode("utf-8")) | |
| except Exception as exc: | |
| return {"ok": False, "error": str(exc)} | |
| def get_model_info() -> dict[str, Any]: | |
| """GET /model/info""" | |
| url = f"{get_base_url()}/model/info" | |
| try: | |
| req = request.Request(url, method="GET") | |
| with request.urlopen(req, timeout=10) as resp: | |
| return json.loads(resp.read().decode("utf-8")) | |
| except Exception as exc: | |
| return {"ok": False, "error": str(exc)} | |
| def _screen_online(sequence: str, seq_type: str, model_name: str) -> dict[str, Any]: | |
| """Call the SynthGuard /biolens/screen endpoint for DNA or PROTEIN sequences.""" | |
| endpoint = f"{get_base_url()}/biolens/screen" | |
| payload = json.dumps({"sequence": sequence, "seq_type": seq_type}).encode("utf-8") | |
| timeout_seconds = float(os.getenv("SYNTHSCREEN_TIMEOUT_SECONDS", "15")) | |
| request_object = request.Request( | |
| endpoint, | |
| data=payload, | |
| headers={"Content-Type": "application/json"}, | |
| method="POST", | |
| ) | |
| try: | |
| with request.urlopen(request_object, timeout=timeout_seconds) as response: | |
| body = response.read().decode("utf-8") | |
| return _coerce_integrated_response(json.loads(body), model_name) | |
| except error.HTTPError as exc: | |
| return _error_response(model_name, f"api_http_error:{exc.code}", data_source="synthguard-api") | |
| except error.URLError as exc: | |
| if isinstance(exc.reason, TimeoutError) or "timed out" in str(exc.reason).lower(): | |
| return _error_response( | |
| model_name, | |
| "api_timeout:The SynthGuard API is waking up or overloaded. Switch to Offline mode to use the local heuristic engine.", | |
| data_source="synthguard-api", | |
| ) | |
| return _error_response(model_name, f"api_connection_error:{exc.reason}", data_source="synthguard-api") | |
| except (TimeoutError, ValueError, json.JSONDecodeError) as exc: | |
| return _error_response(model_name, f"api_parse_error:{exc}", data_source="synthguard-api") | |
| def screen_sequence(sequence: str, seq_type: str) -> dict[str, Any]: | |
| """ | |
| Screen a DNA or protein sequence through the BioLens adapter contract. | |
| Modes: | |
| offline — BioLens built-in heuristic engine, no internet required. | |
| online — Live SynthGuard API (/biolens/screen) for both DNA and PROTEIN. | |
| """ | |
| mode = os.getenv("BIOLENS_MODE", "offline").strip().lower() or "offline" | |
| # Accept legacy 'integrated'/'demo' values gracefully. | |
| if mode in {"integrated", "demo"}: | |
| mode = "online" if mode == "integrated" else "offline" | |
| model_name = f"biolens-{mode}-adapter" | |
| normalized, validation_error = _validate_sequence(sequence, seq_type) | |
| if validation_error: | |
| return _error_response(model_name, validation_error, data_source="biolens-validation") | |
| short_seq = len(normalized) < 20 | |
| if mode == "offline": | |
| result = _screen_offline(normalized, seq_type, model_name) | |
| elif mode == "online": | |
| # SynthGuard /biolens/screen handles both DNA and PROTEIN natively. | |
| result = _screen_online(normalized, seq_type, model_name) | |
| else: | |
| return _error_response(model_name, f"unsupported_runtime_mode:{mode}", data_source="biolens-config") | |
| if short_seq and result.get("ok"): | |
| result["explanation"] = ( | |
| "Short sequences carry higher false-positive uncertainty. " | |
| + (result.get("explanation") or "") | |
| ) | |
| return result | |