petter2025 commited on
Commit
693bb03
·
verified ·
1 Parent(s): 4e92bf6

Update hf_demo.py

Browse files
Files changed (1) hide show
  1. hf_demo.py +46 -231
hf_demo.py CHANGED
@@ -1,51 +1,40 @@
1
  """
2
- ARF OSS v3.3.9 - Enterprise Lead Generation Engine
3
- Compatible with Gradio 4.44.1 and Pydantic V2
4
  """
5
 
6
  import os
7
- # 🔥 CRITICAL: Force Gradio to use port 7860 for Hugging Face Spaces
8
- os.environ['GRADIO_SERVER_PORT'] = '7860'
9
- os.environ['GRADIO_SERVER_NAME'] = '0.0.0.0'
10
- # 🔥 Prevent Gradio from auto-launching its own server
11
- os.environ['GRADIO_ANALYTICS_ENABLED'] = 'False'
12
-
13
  import json
14
  import uuid
15
- import hmac
16
  import hashlib
17
  import logging
18
- import asyncio
19
  import sqlite3
20
  import requests
21
- import fcntl # <-- NEW: for file locking
22
- import sys
23
- from datetime import datetime, timedelta
24
  from typing import Dict, List, Optional, Any, Tuple
25
  from contextlib import contextmanager
26
- from dataclasses import dataclass, asdict
27
  from enum import Enum
28
 
29
- import gradio as gr
30
- # 🔥 Close any existing Gradio instances immediately after import
31
- gr.close_all()
32
-
33
  from fastapi import FastAPI, HTTPException, Depends, status
34
  from fastapi.middleware.cors import CORSMiddleware
35
  from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
36
- from pydantic import BaseModel, Field, field_validator, ConfigDict # <-- NEW: ConfigDict
37
- from pydantic_settings import BaseSettings, SettingsConfigDict # <-- NEW: SettingsConfigDict
38
- from gradio import mount_gradio_app
39
 
40
- # ============== SINGLE INSTANCE LOCK ==============
41
- LOCK_FILE = '/tmp/arf_app.lock'
 
42
  try:
43
  lock_fd = open(LOCK_FILE, 'w')
44
  fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
45
  except (IOError, OSError):
46
- print("Another instance is already running. Exiting.")
47
  sys.exit(1)
48
- # ==================================================
49
 
50
  # ============== CONFIGURATION (Pydantic V2) ==============
51
  class Settings(BaseSettings):
@@ -61,7 +50,7 @@ class Settings(BaseSettings):
61
  alias='DATA_DIR'
62
  )
63
 
64
- # Lead generation
65
  lead_email: str = "petter2025us@outlook.com"
66
  calendly_url: str = "https://calendly.com/petter2025us/arf-demo"
67
 
@@ -81,15 +70,14 @@ class Settings(BaseSettings):
81
 
82
  # Pydantic V2 configuration
83
  model_config = SettingsConfigDict(
84
- populate_by_name=True, # allows use of field names or aliases
85
- extra='ignore', # ignore extra env vars
86
- env_prefix='', # no prefix
87
- case_sensitive=False # case-insensitive matching
88
  )
89
 
90
  def __init__(self, **kwargs):
91
  super().__init__(**kwargs)
92
- # Ensure data directory exists
93
  os.makedirs(self.data_dir, exist_ok=True)
94
 
95
  settings = Settings()
@@ -125,18 +113,12 @@ class LeadSignal(str, Enum):
125
  CONFIDENCE_LOW = "confidence_low"
126
  REPEATED_FAILURE = "repeated_failure"
127
 
128
- # ============== REAL ARF BAYESIAN ENGINE ==============
129
  class BayesianRiskEngine:
130
- """
131
- True Bayesian inference with conjugate priors
132
- Matches ARF OSS production implementation
133
- """
134
-
135
  def __init__(self):
136
- # Beta-Binomial conjugate prior
137
  self.prior_alpha = 2.0
138
  self.prior_beta = 5.0
139
-
140
  self.action_priors = {
141
  'database': {'alpha': 1.5, 'beta': 8.0},
142
  'network': {'alpha': 3.0, 'beta': 4.0},
@@ -144,12 +126,10 @@ class BayesianRiskEngine:
144
  'security': {'alpha': 2.0, 'beta': 6.0},
145
  'default': {'alpha': 2.0, 'beta': 5.0}
146
  }
147
-
148
  self.evidence_db = f"{settings.data_dir}/evidence.db"
149
  self._init_db()
150
 
151
  def _init_db(self):
152
- """Initialize SQLite DB for evidence storage"""
153
  try:
154
  with self._get_db() as conn:
155
  conn.execute('''
@@ -163,10 +143,7 @@ class BayesianRiskEngine:
163
  metadata TEXT
164
  )
165
  ''')
166
- conn.execute('''
167
- CREATE INDEX IF NOT EXISTS idx_action_hash
168
- ON evidence(action_hash)
169
- ''')
170
  except sqlite3.Error as e:
171
  logger.error(f"Failed to initialize evidence database: {e}")
172
  raise RuntimeError("Could not initialize evidence storage") from e
@@ -214,24 +191,17 @@ class BayesianRiskEngine:
214
  logger.error(f"Failed to retrieve evidence: {e}")
215
  return (0, 0)
216
 
217
- def calculate_posterior(self,
218
- action_text: str,
219
- context: Dict[str, Any]) -> Dict[str, Any]:
220
  action_type = self.classify_action(action_text)
221
  alpha0, beta0 = self.get_prior(action_type)
222
-
223
  action_hash = hashlib.sha256(action_text.encode()).hexdigest()
224
  successes, trials = self.get_evidence(action_hash)
225
-
226
  alpha_n = alpha0 + successes
227
  beta_n = beta0 + (trials - successes)
228
-
229
  posterior_mean = alpha_n / (alpha_n + beta_n)
230
  context_multiplier = self._context_likelihood(context)
231
-
232
  risk_score = posterior_mean * context_multiplier
233
  risk_score = min(0.99, max(0.01, risk_score))
234
-
235
  variance = (alpha_n * beta_n) / ((alpha_n + beta_n)**2 * (alpha_n + beta_n + 1))
236
  std_dev = variance ** 0.5
237
  ci_lower = max(0.01, posterior_mean - 1.96 * std_dev)
@@ -299,7 +269,7 @@ class BayesianRiskEngine:
299
  except sqlite3.Error as e:
300
  logger.error(f"Failed to record outcome: {e}")
301
 
302
- # ============== POLICY ENGINE ==============
303
  class PolicyEngine:
304
  def __init__(self):
305
  self.config = {
@@ -325,10 +295,8 @@ class PolicyEngine:
325
  "require_rollback": True
326
  }
327
 
328
- def evaluate(self,
329
- action: str,
330
- risk: Dict[str, Any],
331
- confidence: float) -> Dict[str, Any]:
332
  gates = []
333
 
334
  # Gate 1: Confidence threshold
@@ -347,7 +315,6 @@ class PolicyEngine:
347
  max_idx = risk_levels.index(RiskLevel(self.config["max_autonomous_risk"]))
348
  action_idx = risk_levels.index(risk["level"])
349
  risk_passed = action_idx <= max_idx
350
-
351
  gates.append({
352
  "gate": "risk_assessment",
353
  "passed": risk_passed,
@@ -355,19 +322,11 @@ class PolicyEngine:
355
  "actual": risk["level"].value,
356
  "reason": f"Risk level {risk['level'].value} {'≤' if risk_passed else '>'} max autonomous {self.config['max_autonomous_risk']}",
357
  "type": "categorical",
358
- "metadata": {
359
- "risk_score": risk["score"],
360
- "credible_interval": risk["credible_interval"]
361
- }
362
  })
363
 
364
  # Gate 3: Destructive check
365
- import re
366
- is_destructive = any(
367
- re.search(pattern, action.lower())
368
- for pattern in self.config["destructive_patterns"]
369
- )
370
-
371
  gates.append({
372
  "gate": "destructive_check",
373
  "passed": not is_destructive,
@@ -379,7 +338,6 @@ class PolicyEngine:
379
 
380
  # Gate 4: Human review requirement
381
  requires_human = risk["level"] in self.config["require_human"]
382
-
383
  gates.append({
384
  "gate": "human_review",
385
  "passed": not requires_human,
@@ -388,7 +346,7 @@ class PolicyEngine:
388
  "type": "boolean"
389
  })
390
 
391
- # Gate 5: OSS license (always passes in OSS)
392
  gates.append({
393
  "gate": "license_check",
394
  "passed": True,
@@ -423,7 +381,7 @@ class PolicyEngine:
423
  return True
424
  return False
425
 
426
- # ============== RAG MEMORY WITH PERSISTENCE ==============
427
  class RAGMemory:
428
  def __init__(self):
429
  self.db_path = f"{settings.data_dir}/memory.db"
@@ -482,13 +440,11 @@ class RAGMemory:
482
  def _simple_embedding(self, text: str) -> List[float]:
483
  if text in self.embedding_cache:
484
  return self.embedding_cache[text]
485
-
486
  words = text.lower().split()
487
  trigrams = set()
488
  for word in words:
489
  for i in range(len(word) - 2):
490
  trigrams.add(word[i:i+3])
491
-
492
  vector = [hash(t) % 1000 / 1000.0 for t in sorted(trigrams)[:100]]
493
  while len(vector) < 100:
494
  vector.append(0.0)
@@ -496,13 +452,8 @@ class RAGMemory:
496
  self.embedding_cache[text] = vector
497
  return vector
498
 
499
- def store_incident(self,
500
- action: str,
501
- risk_score: float,
502
- risk_level: RiskLevel,
503
- confidence: float,
504
- allowed: bool,
505
- gates: List[Dict]):
506
  action_hash = hashlib.sha256(action.encode()).hexdigest()[:50]
507
  embedding = json.dumps(self._simple_embedding(action))
508
  try:
@@ -531,11 +482,7 @@ class RAGMemory:
531
  query_embedding = self._simple_embedding(action)
532
  try:
533
  with self._get_db() as conn:
534
- cursor = conn.execute('''
535
- SELECT * FROM incidents
536
- ORDER BY timestamp DESC
537
- LIMIT 100
538
- ''')
539
  incidents = []
540
  for row in cursor.fetchall():
541
  stored_embedding = json.loads(row['embedding'])
@@ -559,11 +506,8 @@ class RAGMemory:
559
  logger.error(f"Failed to find similar incidents: {e}")
560
  return []
561
 
562
- def track_enterprise_signal(self,
563
- signal_type: LeadSignal,
564
- action: str,
565
- risk_score: float,
566
- metadata: Dict = None):
567
  signal = {
568
  'id': str(uuid.uuid4()),
569
  'signal_type': signal_type.value,
@@ -615,11 +559,7 @@ class RAGMemory:
615
  def get_uncontacted_signals(self) -> List[Dict]:
616
  try:
617
  with self._get_db() as conn:
618
- cursor = conn.execute('''
619
- SELECT * FROM signals
620
- WHERE contacted = 0
621
- ORDER BY timestamp DESC
622
- ''')
623
  signals = []
624
  for row in cursor.fetchall():
625
  signals.append({
@@ -704,9 +644,9 @@ class LeadSignalResponse(BaseModel):
704
 
705
  # ============== FASTAPI SETUP ==============
706
  app = FastAPI(
707
- title="ARF OSS Real Engine",
708
  version="3.3.9",
709
- description="Real ARF OSS components for enterprise lead generation",
710
  contact={
711
  "name": "ARF Sales",
712
  "email": settings.lead_email,
@@ -726,11 +666,11 @@ risk_engine = BayesianRiskEngine()
726
  policy_engine = PolicyEngine()
727
  memory = RAGMemory()
728
 
729
- # ============== API ENDPOINTS (with authentication) ==============
730
 
731
  @app.get("/health")
732
  async def health_check():
733
- """Public health check endpoint (no auth required)"""
734
  return {
735
  "status": "healthy",
736
  "version": "3.3.9",
@@ -741,7 +681,7 @@ async def health_check():
741
 
742
  @app.get("/api/v1/config", dependencies=[Depends(verify_api_key)])
743
  async def get_config():
744
- """Get current ARF configuration (protected)"""
745
  return {
746
  "confidenceThreshold": policy_engine.config["confidence_threshold"],
747
  "maxAutonomousRisk": policy_engine.config["max_autonomous_risk"],
@@ -752,7 +692,7 @@ async def get_config():
752
 
753
  @app.post("/api/v1/config", dependencies=[Depends(verify_api_key)])
754
  async def update_config(config: ConfigUpdateRequest):
755
- """Update ARF configuration (protected)"""
756
  if config.confidenceThreshold:
757
  policy_engine.update_config("confidence_threshold", config.confidenceThreshold)
758
  if config.maxAutonomousRisk:
@@ -762,7 +702,7 @@ async def update_config(config: ConfigUpdateRequest):
762
  @app.post("/api/v1/evaluate", dependencies=[Depends(verify_api_key)], response_model=EvaluationResponse)
763
  async def evaluate_action(request: ActionRequest):
764
  """
765
- Real ARF OSS evaluation pipeline (protected)
766
  """
767
  try:
768
  context = {
@@ -855,7 +795,7 @@ async def evaluate_action(request: ActionRequest):
855
  @app.get("/api/v1/enterprise/signals", dependencies=[Depends(verify_api_key)])
856
  async def get_enterprise_signals(contacted: bool = False):
857
  """
858
- Get enterprise lead signals (protected endpoint)
859
  """
860
  try:
861
  if contacted:
@@ -898,143 +838,18 @@ async def record_outcome(action: str, success: bool):
898
  risk_engine.record_outcome(action, success)
899
  return {"status": "success", "message": "Outcome recorded"}
900
 
901
- # ============== GRADIO LEAD GENERATION UI ==============
902
- def create_lead_gen_ui():
903
- """Professional lead generation interface (no auth needed for UI)"""
904
- with gr.Blocks(title="ARF OSS - Enterprise Reliability Intelligence") as ui:
905
-
906
- gr.HTML(f"""
907
- <div style="padding: 2rem; border-radius: 1rem; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; text-align: center;">
908
- <h1 style="font-size: 3em; margin-bottom: 0.5rem;">🤖 ARF OSS v3.3.9</h1>
909
- <h2 style="font-size: 1.5em; font-weight: 300; margin-bottom: 2rem;">
910
- Real Bayesian Reliability Intelligence
911
- </h2>
912
- <div style="display: inline-block; background: rgba(255,255,255,0.2); padding: 0.5rem 1rem;
913
- border-radius: 2rem; margin-bottom: 2rem;">
914
- ⚡ Running REAL ARF OSS Components • No Simulation
915
- </div>
916
- </div>
917
- """)
918
-
919
- with gr.Row():
920
- with gr.Column():
921
- gr.HTML("""
922
- <div style="text-align: center; padding: 2rem;">
923
- <h3 style="color: #333; font-size: 2em;">From Bayesian Analysis to Autonomous Execution</h3>
924
- <p style="color: #666; font-size: 1.2em; max-width: 800px; margin: 1rem auto;">
925
- This demo uses real ARF OSS components for risk assessment.
926
- Enterprise adds mechanical gates, learning loops, and governed execution.
927
- </p>
928
- </div>
929
- """)
930
-
931
- with gr.Row():
932
- with gr.Column():
933
- gr.HTML("""
934
- <div style="padding: 1.5rem; border-radius: 0.5rem; background: #f8f9fa; border-left: 4px solid #667eea; height: 100%;">
935
- <h4>🧮 True Bayesian Inference</h4>
936
- <p>Beta-Binomial conjugate priors with evidence updates</p>
937
- </div>
938
- """)
939
- with gr.Column():
940
- gr.HTML("""
941
- <div style="padding: 1.5rem; border-radius: 0.5rem; background: #f8f9fa; border-left: 4px solid #667eea; height: 100%;">
942
- <h4>🛡️ Deterministic Policies</h4>
943
- <p>5 mechanical gates with live configuration</p>
944
- </div>
945
- """)
946
-
947
- with gr.Row():
948
- with gr.Column():
949
- gr.HTML("""
950
- <div style="padding: 1.5rem; border-radius: 0.5rem; background: #f8f9fa; border-left: 4px solid #667eea; height: 100%;">
951
- <h4>💾 Persistent RAG Memory</h4>
952
- <p>SQLite + vector embeddings for incident recall</p>
953
- </div>
954
- """)
955
- with gr.Column():
956
- gr.HTML("""
957
- <div style="padding: 1.5rem; border-radius: 0.5rem; background: #f8f9fa; border-left: 4px solid #667eea; height: 100%;">
958
- <h4>📊 Lead Intelligence</h4>
959
- <p>Automatic enterprise signal detection</p>
960
- </div>
961
- """)
962
-
963
- demo_stats = gr.JSON(
964
- label="📊 Live Demo Statistics",
965
- value={
966
- "active_since": datetime.utcnow().strftime("%Y-%m-%d %H:%M"),
967
- "bayesian_prior": "Beta(2.0, 5.0)",
968
- "memory_size": len(memory.get_uncontacted_signals()),
969
- "enterprise_signals": len(memory.get_uncontacted_signals())
970
- }
971
- )
972
-
973
- gr.HTML(f"""
974
- <div style="margin: 3rem 0; padding: 3rem; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
975
- border-radius: 1rem; text-align: center; color: white;">
976
- <h2 style="font-size: 2.5em; margin-bottom: 1rem;">🚀 Ready for Autonomous Operations?</h2>
977
- <p style="font-size: 1.3em; margin-bottom: 2rem;">
978
- See ARF Enterprise with mechanical gates and execution
979
- </p>
980
-
981
- <div style="display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap;">
982
- <a href="mailto:{settings.lead_email}?subject=ARF%20Enterprise%20Demo%20Request&body=I%20saw%20the%20real%20ARF%20OSS%20demo%20and%20would%20like%20to%20discuss%20Enterprise%20capabilities."
983
- style="background: white; color: #667eea; padding: 1rem 2rem; border-radius: 2rem; font-weight: bold; text-decoration: none; display: inline-block; margin: 0.5rem;">
984
- 📧 {settings.lead_email}
985
- </a>
986
- <a href="{settings.calendly_url}" target="_blank"
987
- style="background: #FFD700; color: #333; padding: 1rem 2rem; border-radius: 2rem; font-weight: bold; text-decoration: none; display: inline-block; margin: 0.5rem;">
988
- 📅 Schedule Technical Demo
989
- </a>
990
- </div>
991
-
992
- <p style="margin-top: 2rem; font-size: 0.9em; opacity: 0.9;">
993
- ⚡ 30-min technical deep-dive • Live autonomous execution • Enterprise pricing<br>
994
- 🔒 All demos confidential and tailored to your infrastructure
995
- </p>
996
- </div>
997
- """)
998
-
999
- gr.HTML(f"""
1000
- <div style="text-align: center; padding: 2rem; color: #666; border-top: 1px solid #eee;">
1001
- <p>
1002
- 📧 <a href="mailto:{settings.lead_email}" style="color: #667eea;">{settings.lead_email}</a> •
1003
- 🐙 <a href="https://github.com/petterjuan/agentic-reliability-framework" style="color: #667eea;">GitHub</a>
1004
- </p>
1005
- <p style="font-size: 0.9rem;">
1006
- © 2026 ARF - Open Source Intelligence, Enterprise Execution<br>
1007
- <span style="font-size: 0.8rem; color: #999;">
1008
- v3.3.9 • Real Bayesian Inference • Persistent RAG • Lead Intelligence
1009
- </span>
1010
- </p>
1011
- </div>
1012
- """)
1013
-
1014
- return ui
1015
-
1016
- # ============== MOUNT GRADIO ON FASTAPI ==============
1017
- gradio_ui = create_lead_gen_ui()
1018
- app = mount_gradio_app(app, gradio_ui, path="/")
1019
-
1020
  # ============== MAIN ENTRY POINT ==============
1021
  if __name__ == "__main__":
1022
  import uvicorn
1023
 
1024
  port = int(os.environ.get('PORT', 7860))
1025
 
1026
- # 🔥 Ensure any lingering Gradio servers are closed before starting
1027
- try:
1028
- gr.close_all()
1029
- except:
1030
- pass
1031
-
1032
  logger.info("="*60)
1033
- logger.info("🚀 ARF OSS v3.3.9 Starting")
1034
  logger.info(f"📊 Data directory: {settings.data_dir}")
1035
  logger.info(f"📧 Lead email: {settings.lead_email}")
1036
  logger.info(f"🔑 API Key: {settings.api_key[:8]}... (set in HF secrets)")
1037
- logger.info(f"🌐 Serving at: http://0.0.0.0:{port}")
1038
  logger.info("="*60)
1039
 
1040
  uvicorn.run(
 
1
  """
2
+ ARF OSS v3.3.9 - Enterprise Lead Generation Engine (API Only)
3
+ Compatible with Pydantic V2
4
  """
5
 
6
  import os
7
+ # 🔥 CRITICAL: Ensure we use the correct port from environment
8
+ import sys
 
 
 
 
9
  import json
10
  import uuid
 
11
  import hashlib
12
  import logging
 
13
  import sqlite3
14
  import requests
15
+ import fcntl
16
+ from datetime import datetime
 
17
  from typing import Dict, List, Optional, Any, Tuple
18
  from contextlib import contextmanager
 
19
  from enum import Enum
20
 
21
+ # FastAPI and Pydantic
 
 
 
22
  from fastapi import FastAPI, HTTPException, Depends, status
23
  from fastapi.middleware.cors import CORSMiddleware
24
  from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
25
+ from pydantic import BaseModel, Field, field_validator
26
+ from pydantic_settings import BaseSettings, SettingsConfigDict
 
27
 
28
+ # ============== SINGLE INSTANCE LOCK (per port) ==============
29
+ PORT = int(os.environ.get('PORT', 7860))
30
+ LOCK_FILE = f'/tmp/arf_app_{PORT}.lock'
31
  try:
32
  lock_fd = open(LOCK_FILE, 'w')
33
  fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
34
  except (IOError, OSError):
35
+ print(f"Another instance is already running on port {PORT}. Exiting.")
36
  sys.exit(1)
37
+ # ==============================================================
38
 
39
  # ============== CONFIGURATION (Pydantic V2) ==============
40
  class Settings(BaseSettings):
 
50
  alias='DATA_DIR'
51
  )
52
 
53
+ # Lead generation (kept for reference, but UI removed)
54
  lead_email: str = "petter2025us@outlook.com"
55
  calendly_url: str = "https://calendly.com/petter2025us/arf-demo"
56
 
 
70
 
71
  # Pydantic V2 configuration
72
  model_config = SettingsConfigDict(
73
+ populate_by_name=True,
74
+ extra='ignore',
75
+ env_prefix='',
76
+ case_sensitive=False
77
  )
78
 
79
  def __init__(self, **kwargs):
80
  super().__init__(**kwargs)
 
81
  os.makedirs(self.data_dir, exist_ok=True)
82
 
83
  settings = Settings()
 
113
  CONFIDENCE_LOW = "confidence_low"
114
  REPEATED_FAILURE = "repeated_failure"
115
 
116
+ # ============== BAYESIAN ENGINE (unchanged) ==============
117
  class BayesianRiskEngine:
118
+ # ... (keep the full class as before) ...
 
 
 
 
119
  def __init__(self):
 
120
  self.prior_alpha = 2.0
121
  self.prior_beta = 5.0
 
122
  self.action_priors = {
123
  'database': {'alpha': 1.5, 'beta': 8.0},
124
  'network': {'alpha': 3.0, 'beta': 4.0},
 
126
  'security': {'alpha': 2.0, 'beta': 6.0},
127
  'default': {'alpha': 2.0, 'beta': 5.0}
128
  }
 
129
  self.evidence_db = f"{settings.data_dir}/evidence.db"
130
  self._init_db()
131
 
132
  def _init_db(self):
 
133
  try:
134
  with self._get_db() as conn:
135
  conn.execute('''
 
143
  metadata TEXT
144
  )
145
  ''')
146
+ conn.execute('CREATE INDEX IF NOT EXISTS idx_action_hash ON evidence(action_hash)')
 
 
 
147
  except sqlite3.Error as e:
148
  logger.error(f"Failed to initialize evidence database: {e}")
149
  raise RuntimeError("Could not initialize evidence storage") from e
 
191
  logger.error(f"Failed to retrieve evidence: {e}")
192
  return (0, 0)
193
 
194
+ def calculate_posterior(self, action_text: str, context: Dict[str, Any]) -> Dict[str, Any]:
 
 
195
  action_type = self.classify_action(action_text)
196
  alpha0, beta0 = self.get_prior(action_type)
 
197
  action_hash = hashlib.sha256(action_text.encode()).hexdigest()
198
  successes, trials = self.get_evidence(action_hash)
 
199
  alpha_n = alpha0 + successes
200
  beta_n = beta0 + (trials - successes)
 
201
  posterior_mean = alpha_n / (alpha_n + beta_n)
202
  context_multiplier = self._context_likelihood(context)
 
203
  risk_score = posterior_mean * context_multiplier
204
  risk_score = min(0.99, max(0.01, risk_score))
 
205
  variance = (alpha_n * beta_n) / ((alpha_n + beta_n)**2 * (alpha_n + beta_n + 1))
206
  std_dev = variance ** 0.5
207
  ci_lower = max(0.01, posterior_mean - 1.96 * std_dev)
 
269
  except sqlite3.Error as e:
270
  logger.error(f"Failed to record outcome: {e}")
271
 
272
+ # ============== POLICY ENGINE (unchanged) ==============
273
  class PolicyEngine:
274
  def __init__(self):
275
  self.config = {
 
295
  "require_rollback": True
296
  }
297
 
298
+ def evaluate(self, action: str, risk: Dict[str, Any], confidence: float) -> Dict[str, Any]:
299
+ import re
 
 
300
  gates = []
301
 
302
  # Gate 1: Confidence threshold
 
315
  max_idx = risk_levels.index(RiskLevel(self.config["max_autonomous_risk"]))
316
  action_idx = risk_levels.index(risk["level"])
317
  risk_passed = action_idx <= max_idx
 
318
  gates.append({
319
  "gate": "risk_assessment",
320
  "passed": risk_passed,
 
322
  "actual": risk["level"].value,
323
  "reason": f"Risk level {risk['level'].value} {'≤' if risk_passed else '>'} max autonomous {self.config['max_autonomous_risk']}",
324
  "type": "categorical",
325
+ "metadata": {"risk_score": risk["score"], "credible_interval": risk["credible_interval"]}
 
 
 
326
  })
327
 
328
  # Gate 3: Destructive check
329
+ is_destructive = any(re.search(pattern, action.lower()) for pattern in self.config["destructive_patterns"])
 
 
 
 
 
330
  gates.append({
331
  "gate": "destructive_check",
332
  "passed": not is_destructive,
 
338
 
339
  # Gate 4: Human review requirement
340
  requires_human = risk["level"] in self.config["require_human"]
 
341
  gates.append({
342
  "gate": "human_review",
343
  "passed": not requires_human,
 
346
  "type": "boolean"
347
  })
348
 
349
+ # Gate 5: OSS license (always passes)
350
  gates.append({
351
  "gate": "license_check",
352
  "passed": True,
 
381
  return True
382
  return False
383
 
384
+ # ============== RAG MEMORY (unchanged) ==============
385
  class RAGMemory:
386
  def __init__(self):
387
  self.db_path = f"{settings.data_dir}/memory.db"
 
440
  def _simple_embedding(self, text: str) -> List[float]:
441
  if text in self.embedding_cache:
442
  return self.embedding_cache[text]
 
443
  words = text.lower().split()
444
  trigrams = set()
445
  for word in words:
446
  for i in range(len(word) - 2):
447
  trigrams.add(word[i:i+3])
 
448
  vector = [hash(t) % 1000 / 1000.0 for t in sorted(trigrams)[:100]]
449
  while len(vector) < 100:
450
  vector.append(0.0)
 
452
  self.embedding_cache[text] = vector
453
  return vector
454
 
455
+ def store_incident(self, action: str, risk_score: float, risk_level: RiskLevel,
456
+ confidence: float, allowed: bool, gates: List[Dict]):
 
 
 
 
 
457
  action_hash = hashlib.sha256(action.encode()).hexdigest()[:50]
458
  embedding = json.dumps(self._simple_embedding(action))
459
  try:
 
482
  query_embedding = self._simple_embedding(action)
483
  try:
484
  with self._get_db() as conn:
485
+ cursor = conn.execute('SELECT * FROM incidents ORDER BY timestamp DESC LIMIT 100')
 
 
 
 
486
  incidents = []
487
  for row in cursor.fetchall():
488
  stored_embedding = json.loads(row['embedding'])
 
506
  logger.error(f"Failed to find similar incidents: {e}")
507
  return []
508
 
509
+ def track_enterprise_signal(self, signal_type: LeadSignal, action: str,
510
+ risk_score: float, metadata: Dict = None):
 
 
 
511
  signal = {
512
  'id': str(uuid.uuid4()),
513
  'signal_type': signal_type.value,
 
559
  def get_uncontacted_signals(self) -> List[Dict]:
560
  try:
561
  with self._get_db() as conn:
562
+ cursor = conn.execute('SELECT * FROM signals WHERE contacted = 0 ORDER BY timestamp DESC')
 
 
 
 
563
  signals = []
564
  for row in cursor.fetchall():
565
  signals.append({
 
644
 
645
  # ============== FASTAPI SETUP ==============
646
  app = FastAPI(
647
+ title="ARF OSS Real Engine (API Only)",
648
  version="3.3.9",
649
+ description="Real ARF OSS components for enterprise lead generation - API only",
650
  contact={
651
  "name": "ARF Sales",
652
  "email": settings.lead_email,
 
666
  policy_engine = PolicyEngine()
667
  memory = RAGMemory()
668
 
669
+ # ============== API ENDPOINTS ==============
670
 
671
  @app.get("/health")
672
  async def health_check():
673
+ """Public health check endpoint"""
674
  return {
675
  "status": "healthy",
676
  "version": "3.3.9",
 
681
 
682
  @app.get("/api/v1/config", dependencies=[Depends(verify_api_key)])
683
  async def get_config():
684
+ """Get current ARF configuration"""
685
  return {
686
  "confidenceThreshold": policy_engine.config["confidence_threshold"],
687
  "maxAutonomousRisk": policy_engine.config["max_autonomous_risk"],
 
692
 
693
  @app.post("/api/v1/config", dependencies=[Depends(verify_api_key)])
694
  async def update_config(config: ConfigUpdateRequest):
695
+ """Update ARF configuration"""
696
  if config.confidenceThreshold:
697
  policy_engine.update_config("confidence_threshold", config.confidenceThreshold)
698
  if config.maxAutonomousRisk:
 
702
  @app.post("/api/v1/evaluate", dependencies=[Depends(verify_api_key)], response_model=EvaluationResponse)
703
  async def evaluate_action(request: ActionRequest):
704
  """
705
+ Real ARF OSS evaluation pipeline
706
  """
707
  try:
708
  context = {
 
795
  @app.get("/api/v1/enterprise/signals", dependencies=[Depends(verify_api_key)])
796
  async def get_enterprise_signals(contacted: bool = False):
797
  """
798
+ Get enterprise lead signals
799
  """
800
  try:
801
  if contacted:
 
838
  risk_engine.record_outcome(action, success)
839
  return {"status": "success", "message": "Outcome recorded"}
840
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
841
  # ============== MAIN ENTRY POINT ==============
842
  if __name__ == "__main__":
843
  import uvicorn
844
 
845
  port = int(os.environ.get('PORT', 7860))
846
 
 
 
 
 
 
 
847
  logger.info("="*60)
848
+ logger.info("🚀 ARF OSS v3.3.9 (API Only) Starting")
849
  logger.info(f"📊 Data directory: {settings.data_dir}")
850
  logger.info(f"📧 Lead email: {settings.lead_email}")
851
  logger.info(f"🔑 API Key: {settings.api_key[:8]}... (set in HF secrets)")
852
+ logger.info(f"🌐 Serving API at: http://0.0.0.0:{port}")
853
  logger.info("="*60)
854
 
855
  uvicorn.run(