TheRealAIGuy commited on
Commit
7b4c859
·
verified ·
1 Parent(s): 6f41203

app.py Static Label Fix #1

Browse files
Files changed (1) hide show
  1. server/app.py +51 -63
server/app.py CHANGED
@@ -26,13 +26,12 @@ if _CURRENT_DIR not in sys.path:
26
  sys.path.insert(0, _CURRENT_DIR)
27
 
28
  try:
29
- # 1. Import the environment AND the safely loaded C++ module
30
  from fin_auditor_environment import FinAuditorEnvironment, hft_auditor
31
  from models import AuditorAction, AuditorObservation
32
 
33
  HAS_ENV = True
34
  NATIVE_VERIFIED = hft_auditor is not None
35
- hft_mod = hft_auditor # Create the alias app.py expects to use for Difficulty settings
36
 
37
  except ImportError as e:
38
  HAS_ENV = False
@@ -44,11 +43,20 @@ except ImportError as e:
44
  # ==============================================================================
45
  # PHASE 2: SYSTEM STATE & AUTHORITY TRACKING
46
  # ==============================================================================
47
- env = FinAuditorEnvironment() if (HAS_ENV and NATIVE_VERIFIED) else None
48
 
49
- # OpenEnv Compliance: Ensure endpoints exist even in MOCK mode to prevent 404s
50
- if env:
51
- app = create_app(FinAuditorEnvironment, AuditorAction, AuditorObservation)
 
 
 
 
 
 
 
 
 
 
52
  else:
53
  app = FastAPI(title="PayGorn (MOCK MODE)")
54
  @app.post("/reset")
@@ -60,7 +68,6 @@ app_metrics = {"last_step_latency_us": 0.0}
60
 
61
  @app.middleware("http")
62
  async def capture_step_latency(request: Request, call_next):
63
- """TASK 1: Captures true end-to-end latency of the C++ bridge."""
64
  if request.url.path == "/step":
65
  start_ns = time.perf_counter_ns()
66
  response = await call_next(request)
@@ -87,7 +94,7 @@ llm_session = {
87
  }
88
 
89
  class LLMConfig(BaseModel):
90
- api_key: str = "" # Default to empty string to allow default token usage
91
  model_name: Optional[str] = None
92
  base_url: Optional[str] = None
93
 
@@ -134,13 +141,14 @@ async def execute_llm_step(api_key: str, base_url: str, model_name: str, batch_s
134
  # ==============================================================================
135
  @app.get("/state")
136
  async def get_state():
137
- if not env:
 
138
  return {"status": "FALLBACK_MOCK_MODE", "health": system_health, "accuracy": 0.0, "latency_us": 0.0, "throughput_m": 0.0, "buffer_saturation": 0.0, "active_count": 0, "total_ingested": 0, "step_count": 0, "difficulty": "EASY", "metrics": {"tp": 0, "tn": 0, "fp": 0, "fn": 0}}
139
 
140
- tp = getattr(env.state, 'last_tp', 0)
141
- fp = getattr(env.state, 'last_fp', 0)
142
- tn = getattr(env.state, 'last_tn', 0)
143
- fn = getattr(env.state, 'last_fn', 0)
144
 
145
  accuracy = tp / (tp + fp) if (tp + fp) > 0 else 0.0
146
  latency_us = app_metrics["last_step_latency_us"]
@@ -152,13 +160,13 @@ async def get_state():
152
  "latency_us": round(latency_us, 3),
153
  "latency_source": "grounded_app_middleware",
154
  "throughput_m": round((40 * 1e6) / (latency_us * 1000), 2) if latency_us > 0 else 0.0,
155
- "active_count": env.engine.active_count,
156
- "total_ingested": env.engine.total_ingested,
157
- "ring_buffer_size": env.engine.ring_buffer_size,
158
- "buffer_saturation": (env.engine.ring_buffer_size / env.engine.pool_capacity) * 100,
159
- "step_count": env.state.step_count,
160
  "metrics": {"tp": tp, "tn": tn, "fp": fp, "fn": fn},
161
- "difficulty": getattr(env, 'difficulty', "EASY")
162
  }
163
 
164
  @app.post("/dashboard/get_action")
@@ -167,12 +175,10 @@ async def get_dashboard_action(req: ActionRequest):
167
  if req.action_type == "perfect":
168
  decisions = [1] * batch_size
169
  elif req.action_type == "llm":
170
- # FIX: Read directly from llm_session memory to bypass OS environment sync issues
171
  api_key = llm_session.get("api_key")
172
  base_url = llm_session.get("base_url")
173
  model_name = llm_session.get("model_name")
174
 
175
- # Fallback to the first available model if none explicitly selected
176
  if not model_name and llm_session.get("available_models"):
177
  model_name = llm_session["available_models"][0]
178
 
@@ -187,14 +193,12 @@ async def get_dashboard_action(req: ActionRequest):
187
  @app.post("/config/llm")
188
  async def config_llm(cfg: LLMConfig):
189
  api_key = cfg.api_key
190
- # Fallback to session key if UI sends blank string and default token is active
191
  if not api_key:
192
  api_key = llm_session.get("api_key")
193
  if not api_key:
194
  raise HTTPException(status_code=400, detail="API Key cannot be blank")
195
 
196
  base_url = "https://router.huggingface.co/v1"
197
-
198
  if api_key.startswith("AIza"):
199
  base_url = "https://generativelanguage.googleapis.com/v1beta/openai/"
200
  elif api_key.startswith("sk-ant"):
@@ -202,7 +206,6 @@ async def config_llm(cfg: LLMConfig):
202
 
203
  try:
204
  client = AsyncOpenAI(base_url=base_url, api_key=api_key, max_retries=2)
205
-
206
  try:
207
  response = await client.models.list()
208
  model_list = [m.id for m in response.data]
@@ -212,23 +215,17 @@ async def config_llm(cfg: LLMConfig):
212
  else:
213
  raise e
214
 
215
- # Save baseline session variables
216
  llm_session["api_key"] = api_key
217
  llm_session["base_url"] = base_url
218
  llm_session["available_models"] = model_list
219
-
220
  system_health["key_validated"] = True
221
  system_health["model_detected"] = len(model_list) > 0
222
 
223
  if cfg.model_name and cfg.model_name in model_list:
224
- # FIX: Explicitly save model_name into memory
225
  llm_session["model_name"] = cfg.model_name
226
-
227
- # (Optional) Keep OS environ for other scripts, but app.py won't rely on it
228
  os.environ["MODEL_NAME"] = cfg.model_name
229
  os.environ["LLM_API_KEY"] = api_key
230
  os.environ["API_BASE_URL"] = base_url
231
-
232
  system_health["connected"] = True
233
  msg = f"Injected {cfg.model_name} credentials."
234
  else:
@@ -251,14 +248,14 @@ async def config_default():
251
 
252
  @app.post("/config/difficulty")
253
  async def set_difficulty(cfg: DifficultyConfig):
254
- if env and NATIVE_VERIFIED:
255
  os.environ["TASK_ID"] = cfg.level.lower()
256
  if "easy" in cfg.level.lower():
257
- env.difficulty = hft_mod.Difficulty.EASY
258
  elif "medium" in cfg.level.lower():
259
- env.difficulty = hft_mod.Difficulty.MEDIUM
260
  else:
261
- env.difficulty = hft_mod.Difficulty.HARD
262
  return {"status": "success", "difficulty": cfg.level}
263
  return {"status": "error", "message": "Engine not loaded"}
264
 
@@ -267,12 +264,12 @@ async def websocket_telemetry(websocket: WebSocket):
267
  await websocket.accept()
268
  try:
269
  while True:
270
- if env and NATIVE_VERIFIED:
271
  data = {
272
- "active_count": env.engine.active_count,
273
- "total_ingested": env.engine.total_ingested,
274
- "ring_buffer_size": env.engine.ring_buffer_size,
275
- "pool_capacity": env.engine.pool_capacity,
276
  "latency_us": round(app_metrics["last_step_latency_us"], 3),
277
  "status": "NATIVE_ACTIVE"
278
  }
@@ -521,8 +518,6 @@ async def root_dashboard():
521
  <script>
522
  const consoleOut = document.getElementById('console-out');
523
  const ledgerBody = document.getElementById('ledger-body');
524
-
525
- // TRACKING STATE FOR ZERO-CONFIG
526
  let usingDefaultToken = false;
527
 
528
  function logMsg(msg, type='info') {
@@ -593,10 +588,7 @@ async def root_dashboard():
593
  async function discoverModels() {
594
  const key = document.getElementById('api-key').value;
595
  if(!key) return;
596
-
597
- // If user manually types a key, they are no longer using the default
598
  usingDefaultToken = false;
599
-
600
  logMsg("Validating key and mapping models...", "info");
601
  const res = await fetch('/config/llm', {
602
  method: 'POST',
@@ -604,7 +596,6 @@ async def root_dashboard():
604
  body: JSON.stringify({api_key: key})
605
  });
606
  const data = await res.json();
607
-
608
  if(data.status === 'success') {
609
  const select = document.getElementById('model-select');
610
  select.innerHTML = '';
@@ -623,13 +614,9 @@ async def root_dashboard():
623
  async function saveConfig() {
624
  const key = document.getElementById('api-key').value;
625
  const model = document.getElementById('model-select').value;
626
-
627
- // Check if model is missing, OR if both the input is empty AND the default token isn't active
628
  if (!model || (!key && !usingDefaultToken)) {
629
- logMsg("Key/Model missing.", "err");
630
- return;
631
  }
632
-
633
  const res = await fetch('/config/llm', {
634
  method: 'POST',
635
  headers: {'Content-Type': 'application/json'},
@@ -637,8 +624,7 @@ async def root_dashboard():
637
  });
638
  const data = await res.json();
639
  if(data.status === 'success') {
640
- logMsg(data.message, "success");
641
- updateState();
642
  } else {
643
  logMsg(data.message, "err");
644
  }
@@ -657,8 +643,7 @@ async def root_dashboard():
657
  opt.value = m; opt.innerText = m;
658
  select.appendChild(opt);
659
  });
660
- logMsg("Zero-config active.", "success");
661
- updateState();
662
  } else {
663
  logMsg(data.message, "err");
664
  }
@@ -695,7 +680,6 @@ async def root_dashboard():
695
  body: JSON.stringify({action_type: actionType})
696
  });
697
 
698
- // ADDED: Handle the 400 error cleanly if the API key is missing
699
  if (!actionRes.ok) {
700
  const errData = await actionRes.json();
701
  logMsg("LLM Error: " + (errData.detail || "Failed to generate decisions"), "err");
@@ -703,14 +687,12 @@ async def root_dashboard():
703
  }
704
 
705
  const actionData = await actionRes.json();
706
-
707
  if(!actionData.decisions) {
708
  logMsg("Decision matrix generation failed.", "err"); return;
709
  }
710
 
711
  logMsg(`Executing Step with ${actionData.decisions.length} decisions...`, "info");
712
 
713
- // FIX: Wrap the payload in the 'action' key required by OpenEnv
714
  const res = await fetch('/step', {
715
  method: 'POST',
716
  headers: {'Content-Type': 'application/json'},
@@ -726,9 +708,10 @@ async def root_dashboard():
726
 
727
  const data = await res.json();
728
 
729
- const reward = data.reward ?? data.observation?.reward ?? 0.0;
730
- const done = data.done ?? data.observation?.done ?? false;
731
- const step = data.step_count ?? data.observation?.metadata?.step_count ?? 'N/A';
 
732
 
733
  logMsg(`[RECON] Reward: ${reward.toFixed(4)} | Success`, reward >= 0.8 ? 'success' : 'warn');
734
 
@@ -749,8 +732,14 @@ async def root_dashboard():
749
  }
750
  }
751
 
752
- setInterval(updateState, 1000);
753
- updateState();
 
 
 
 
 
 
754
  </script>
755
  </body>
756
  </html>
@@ -758,7 +747,6 @@ async def root_dashboard():
758
  return HTMLResponse(content=html_content)
759
 
760
  def main():
761
- # Hugging Face Spaces expects traffic on port 7860
762
  port = int(os.getenv("PORT", 7860))
763
  uvicorn.run(app, host="0.0.0.0", port=port)
764
 
 
26
  sys.path.insert(0, _CURRENT_DIR)
27
 
28
  try:
 
29
  from fin_auditor_environment import FinAuditorEnvironment, hft_auditor
30
  from models import AuditorAction, AuditorObservation
31
 
32
  HAS_ENV = True
33
  NATIVE_VERIFIED = hft_auditor is not None
34
+ hft_mod = hft_auditor
35
 
36
  except ImportError as e:
37
  HAS_ENV = False
 
43
  # ==============================================================================
44
  # PHASE 2: SYSTEM STATE & AUTHORITY TRACKING
45
  # ==============================================================================
 
46
 
47
+ # FIX: Global pointer to capture the OpenEnv-managed instance
48
+ active_env_instance = None
49
+
50
+ if HAS_ENV and NATIVE_VERIFIED:
51
+ class TrackedFinAuditorEnvironment(FinAuditorEnvironment):
52
+ """Wrapper class to capture the environment instance created by OpenEnv"""
53
+ def __init__(self, *args, **kwargs):
54
+ super().__init__(*args, **kwargs)
55
+ global active_env_instance
56
+ active_env_instance = self
57
+
58
+ # OpenEnv creates the FastAPI app and instantiates TrackedFinAuditorEnvironment internally
59
+ app = create_app(TrackedFinAuditorEnvironment, AuditorAction, AuditorObservation)
60
  else:
61
  app = FastAPI(title="PayGorn (MOCK MODE)")
62
  @app.post("/reset")
 
68
 
69
  @app.middleware("http")
70
  async def capture_step_latency(request: Request, call_next):
 
71
  if request.url.path == "/step":
72
  start_ns = time.perf_counter_ns()
73
  response = await call_next(request)
 
94
  }
95
 
96
  class LLMConfig(BaseModel):
97
+ api_key: str = ""
98
  model_name: Optional[str] = None
99
  base_url: Optional[str] = None
100
 
 
141
  # ==============================================================================
142
  @app.get("/state")
143
  async def get_state():
144
+ # FIX: Check the dynamically tracked instance instead of the old static one
145
+ if not active_env_instance:
146
  return {"status": "FALLBACK_MOCK_MODE", "health": system_health, "accuracy": 0.0, "latency_us": 0.0, "throughput_m": 0.0, "buffer_saturation": 0.0, "active_count": 0, "total_ingested": 0, "step_count": 0, "difficulty": "EASY", "metrics": {"tp": 0, "tn": 0, "fp": 0, "fn": 0}}
147
 
148
+ tp = getattr(active_env_instance.state, 'last_tp', 0)
149
+ fp = getattr(active_env_instance.state, 'last_fp', 0)
150
+ tn = getattr(active_env_instance.state, 'last_tn', 0)
151
+ fn = getattr(active_env_instance.state, 'last_fn', 0)
152
 
153
  accuracy = tp / (tp + fp) if (tp + fp) > 0 else 0.0
154
  latency_us = app_metrics["last_step_latency_us"]
 
160
  "latency_us": round(latency_us, 3),
161
  "latency_source": "grounded_app_middleware",
162
  "throughput_m": round((40 * 1e6) / (latency_us * 1000), 2) if latency_us > 0 else 0.0,
163
+ "active_count": active_env_instance.engine.active_count,
164
+ "total_ingested": active_env_instance.engine.total_ingested,
165
+ "ring_buffer_size": active_env_instance.engine.ring_buffer_size,
166
+ "buffer_saturation": (active_env_instance.engine.ring_buffer_size / active_env_instance.engine.pool_capacity) * 100,
167
+ "step_count": active_env_instance.state.step_count,
168
  "metrics": {"tp": tp, "tn": tn, "fp": fp, "fn": fn},
169
+ "difficulty": getattr(active_env_instance, 'difficulty', "EASY")
170
  }
171
 
172
  @app.post("/dashboard/get_action")
 
175
  if req.action_type == "perfect":
176
  decisions = [1] * batch_size
177
  elif req.action_type == "llm":
 
178
  api_key = llm_session.get("api_key")
179
  base_url = llm_session.get("base_url")
180
  model_name = llm_session.get("model_name")
181
 
 
182
  if not model_name and llm_session.get("available_models"):
183
  model_name = llm_session["available_models"][0]
184
 
 
193
  @app.post("/config/llm")
194
  async def config_llm(cfg: LLMConfig):
195
  api_key = cfg.api_key
 
196
  if not api_key:
197
  api_key = llm_session.get("api_key")
198
  if not api_key:
199
  raise HTTPException(status_code=400, detail="API Key cannot be blank")
200
 
201
  base_url = "https://router.huggingface.co/v1"
 
202
  if api_key.startswith("AIza"):
203
  base_url = "https://generativelanguage.googleapis.com/v1beta/openai/"
204
  elif api_key.startswith("sk-ant"):
 
206
 
207
  try:
208
  client = AsyncOpenAI(base_url=base_url, api_key=api_key, max_retries=2)
 
209
  try:
210
  response = await client.models.list()
211
  model_list = [m.id for m in response.data]
 
215
  else:
216
  raise e
217
 
 
218
  llm_session["api_key"] = api_key
219
  llm_session["base_url"] = base_url
220
  llm_session["available_models"] = model_list
 
221
  system_health["key_validated"] = True
222
  system_health["model_detected"] = len(model_list) > 0
223
 
224
  if cfg.model_name and cfg.model_name in model_list:
 
225
  llm_session["model_name"] = cfg.model_name
 
 
226
  os.environ["MODEL_NAME"] = cfg.model_name
227
  os.environ["LLM_API_KEY"] = api_key
228
  os.environ["API_BASE_URL"] = base_url
 
229
  system_health["connected"] = True
230
  msg = f"Injected {cfg.model_name} credentials."
231
  else:
 
248
 
249
  @app.post("/config/difficulty")
250
  async def set_difficulty(cfg: DifficultyConfig):
251
+ if active_env_instance and NATIVE_VERIFIED:
252
  os.environ["TASK_ID"] = cfg.level.lower()
253
  if "easy" in cfg.level.lower():
254
+ active_env_instance.difficulty = hft_mod.Difficulty.EASY
255
  elif "medium" in cfg.level.lower():
256
+ active_env_instance.difficulty = hft_mod.Difficulty.MEDIUM
257
  else:
258
+ active_env_instance.difficulty = hft_mod.Difficulty.HARD
259
  return {"status": "success", "difficulty": cfg.level}
260
  return {"status": "error", "message": "Engine not loaded"}
261
 
 
264
  await websocket.accept()
265
  try:
266
  while True:
267
+ if active_env_instance and NATIVE_VERIFIED:
268
  data = {
269
+ "active_count": active_env_instance.engine.active_count,
270
+ "total_ingested": active_env_instance.engine.total_ingested,
271
+ "ring_buffer_size": active_env_instance.engine.ring_buffer_size,
272
+ "pool_capacity": active_env_instance.engine.pool_capacity,
273
  "latency_us": round(app_metrics["last_step_latency_us"], 3),
274
  "status": "NATIVE_ACTIVE"
275
  }
 
518
  <script>
519
  const consoleOut = document.getElementById('console-out');
520
  const ledgerBody = document.getElementById('ledger-body');
 
 
521
  let usingDefaultToken = false;
522
 
523
  function logMsg(msg, type='info') {
 
588
  async function discoverModels() {
589
  const key = document.getElementById('api-key').value;
590
  if(!key) return;
 
 
591
  usingDefaultToken = false;
 
592
  logMsg("Validating key and mapping models...", "info");
593
  const res = await fetch('/config/llm', {
594
  method: 'POST',
 
596
  body: JSON.stringify({api_key: key})
597
  });
598
  const data = await res.json();
 
599
  if(data.status === 'success') {
600
  const select = document.getElementById('model-select');
601
  select.innerHTML = '';
 
614
  async function saveConfig() {
615
  const key = document.getElementById('api-key').value;
616
  const model = document.getElementById('model-select').value;
 
 
617
  if (!model || (!key && !usingDefaultToken)) {
618
+ logMsg("Key/Model missing.", "err"); return;
 
619
  }
 
620
  const res = await fetch('/config/llm', {
621
  method: 'POST',
622
  headers: {'Content-Type': 'application/json'},
 
624
  });
625
  const data = await res.json();
626
  if(data.status === 'success') {
627
+ logMsg(data.message, "success"); updateState();
 
628
  } else {
629
  logMsg(data.message, "err");
630
  }
 
643
  opt.value = m; opt.innerText = m;
644
  select.appendChild(opt);
645
  });
646
+ logMsg("Zero-config active.", "success"); updateState();
 
647
  } else {
648
  logMsg(data.message, "err");
649
  }
 
680
  body: JSON.stringify({action_type: actionType})
681
  });
682
 
 
683
  if (!actionRes.ok) {
684
  const errData = await actionRes.json();
685
  logMsg("LLM Error: " + (errData.detail || "Failed to generate decisions"), "err");
 
687
  }
688
 
689
  const actionData = await actionRes.json();
 
690
  if(!actionData.decisions) {
691
  logMsg("Decision matrix generation failed.", "err"); return;
692
  }
693
 
694
  logMsg(`Executing Step with ${actionData.decisions.length} decisions...`, "info");
695
 
 
696
  const res = await fetch('/step', {
697
  method: 'POST',
698
  headers: {'Content-Type': 'application/json'},
 
708
 
709
  const data = await res.json();
710
 
711
+ // FIX: Robust payload extraction handling regardless of OpenEnv wrapper depth
712
+ const reward = data.reward ?? data.observation?.reward ?? data.info?.reward ?? 0.0;
713
+ const done = data.done ?? data.observation?.done ?? data.info?.done ?? false;
714
+ const step = data.step_count ?? data.observation?.step_count ?? data.info?.step_count ?? data.observation?.metadata?.step_count ?? 'N/A';
715
 
716
  logMsg(`[RECON] Reward: ${reward.toFixed(4)} | Success`, reward >= 0.8 ? 'success' : 'warn');
717
 
 
732
  }
733
  }
734
 
735
+ // FIX: Auto-Reset the environment on boot so it actually has data to process
736
+ window.addEventListener('DOMContentLoaded', async () => {
737
+ logMsg("Auto-initializing environment engine...", "info");
738
+ await executeReset();
739
+ setInterval(updateState, 1000);
740
+ updateState();
741
+ });
742
+
743
  </script>
744
  </body>
745
  </html>
 
747
  return HTMLResponse(content=html_content)
748
 
749
  def main():
 
750
  port = int(os.getenv("PORT", 7860))
751
  uvicorn.run(app, host="0.0.0.0", port=port)
752