noahlee1234 commited on
Commit
ec6bf64
·
1 Parent(s): e071228

sec: default to error-only logging across worker and UI

Browse files
apps/cad-worker/README.md CHANGED
@@ -39,6 +39,7 @@ NATURALCAD_LOG_CODE=false # optional, default false
39
  NATURALCAD_INCLUDE_CODE_IN_RESPONSE=false # optional, default false
40
  NATURALCAD_STORE_CODE=true # optional, default true (stores generated code in DB)
41
  NATURALCAD_STORE_GLB=false # optional, default false (skip GLB upload to storage)
 
42
  ```
43
 
44
  Also required for uploads/logging:
 
39
  NATURALCAD_INCLUDE_CODE_IN_RESPONSE=false # optional, default false
40
  NATURALCAD_STORE_CODE=true # optional, default true (stores generated code in DB)
41
  NATURALCAD_STORE_GLB=false # optional, default false (skip GLB upload to storage)
42
+ NATURALCAD_VERBOSE_LOGS=false # optional, default false (only error logging)
43
  ```
44
 
45
  Also required for uploads/logging:
apps/cad-worker/main.py CHANGED
@@ -115,6 +115,17 @@ _SAFE_BUILTINS = {
115
  "ValueError": ValueError,
116
  }
117
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
  def _client_ip(request: Request) -> str:
120
  xff = request.headers.get("x-forwarded-for", "").strip()
@@ -291,7 +302,7 @@ def _log_job_to_supabase(
291
  key = os.environ.get("SUPABASE_SERVICE_ROLE_KEY", "")
292
 
293
  if not url or not key:
294
- print("Skipping DB logging: SUPABASE_URL or key not set")
295
  return
296
 
297
  endpoint = f"{url}/rest/v1/jobs"
@@ -322,11 +333,11 @@ def _log_job_to_supabase(
322
  payload.pop("generated_code", None)
323
  resp = client.post(endpoint, json=payload, headers=headers)
324
  if resp.status_code >= 400:
325
- print(f"DB log failed for job {job_id}: {resp.text}")
326
  else:
327
- print(f"DB log OK for job {job_id} (status={status})")
328
  except Exception as e:
329
- print(f"DB log error for job {job_id}: {e}")
330
 
331
 
332
  # ---------------------------------------------------------------------------
@@ -586,7 +597,7 @@ def generate_cad(prompt: str, mode: str = "part", output_type: str = "3d_solid")
586
  ]
587
 
588
  for attempt in range(max_attempts):
589
- print(f"LLM call {attempt + 1}/{max_attempts} | mode={mode} output_type={output_type}")
590
  try:
591
  headers = {
592
  "Authorization": f"Bearer {openrouter_api_key}",
@@ -610,13 +621,13 @@ def generate_cad(prompt: str, mode: str = "part", output_type: str = "3d_solid")
610
  response = client.post(openrouter_api_url, headers=headers, json=payload)
611
 
612
  if response.status_code >= 400:
613
- print(f"OpenRouter error {response.status_code}: {response.text[:500]}")
614
  return {"error": f"LLM provider unavailable ({response.status_code}). Please retry."}
615
 
616
  data = response.json()
617
  generated_code = (data.get("choices", [{}])[0].get("message", {}).get("content") or "").strip()
618
  if not generated_code:
619
- print(f"OpenRouter empty content response: {str(data)[:500]}")
620
  return {"error": "LLM returned empty output. Please retry."}
621
 
622
  # Strip markdown fences (model sometimes ignores rule 1)
@@ -628,11 +639,11 @@ def generate_cad(prompt: str, mode: str = "part", output_type: str = "3d_solid")
628
  generated_code = generated_code[:-3]
629
  generated_code = generated_code.strip()
630
  except Exception as e:
631
- print(f"LLM call failed: {e}")
632
  return {"error": "LLM call failed. Please retry."}
633
 
634
  if log_generated_code:
635
- print(f"Generated code:\n{generated_code}")
636
 
637
  from build123d import export_stl, export_step
638
 
@@ -644,7 +655,7 @@ def generate_cad(prompt: str, mode: str = "part", output_type: str = "3d_solid")
644
  is_safe, safety_error = _validate_generated_code(sanitized_code)
645
  if not is_safe:
646
  err_short = f"Rejected by AST guard: {safety_error}"
647
- print(err_short)
648
  if attempt < max_attempts - 1:
649
  messages.append({"role": "assistant", "content": generated_code})
650
  messages.append({
@@ -681,7 +692,7 @@ def generate_cad(prompt: str, mode: str = "part", output_type: str = "3d_solid")
681
  import traceback as _tb
682
  err_short = f"{type(e).__name__}: {e}"
683
  err_trace = _tb.format_exc()
684
- print(f"Execution failed: {err_short}")
685
  finally:
686
  os.environ.clear()
687
  os.environ.update(original_env)
@@ -695,7 +706,7 @@ def generate_cad(prompt: str, mode: str = "part", output_type: str = "3d_solid")
695
 
696
  if not exec_success:
697
  if attempt < max_attempts - 1:
698
- print("Retrying with error context...")
699
  # Cap traceback to avoid blowing the context window
700
  trace_snippet = err_trace[-2000:] if len(err_trace) > 2000 else err_trace
701
  messages.append({"role": "assistant", "content": generated_code})
@@ -725,16 +736,16 @@ def generate_cad(prompt: str, mode: str = "part", output_type: str = "3d_solid")
725
 
726
  try:
727
  export_stl(shape, str(stl_path))
728
- print(f"STL exported: {stl_path.stat().st_size} bytes")
729
  except Exception as e:
730
- print(f"STL export failed: {e}")
731
  stl_path = None
732
 
733
  try:
734
  export_step(shape, str(step_path))
735
- print(f"STEP exported: {step_path.exists()}")
736
  except Exception as e:
737
- print(f"STEP export failed: {e}")
738
  step_path = None
739
 
740
  try:
@@ -747,11 +758,11 @@ def generate_cad(prompt: str, mode: str = "part", output_type: str = "3d_solid")
747
  # Rotate to glTF Y-up convention
748
  mesh.apply_transform(tf.rotation_matrix(-math.pi / 2, [1, 0, 0]))
749
  mesh.export(str(glb_path))
750
- print(f"GLB exported: {glb_path.exists()}")
751
  else:
752
- print("Skipping GLB: no STL file")
753
  except Exception as e:
754
- print(f"GLB export failed: {e}")
755
 
756
  # ----------------------------------------------------------------
757
  # Upload to Supabase storage
@@ -767,11 +778,11 @@ def generate_cad(prompt: str, mode: str = "part", output_type: str = "3d_solid")
767
  continue
768
  storage_key = f"runs/{run_id}/model.{fmt}"
769
  file_bytes = file_path.read_bytes()
770
- print(f"Uploading {fmt}: {len(file_bytes)} bytes")
771
  try:
772
  urls[fmt] = _upload_to_supabase(storage_key, file_bytes, content_type)
773
  except Exception as e:
774
- print(f"Upload error for {fmt}: {e}")
775
 
776
  _log_job_to_supabase(run_id, prompt, mode, output_type, generated_code, "completed")
777
  return {
 
115
  "ValueError": ValueError,
116
  }
117
 
118
+ _VERBOSE_LOGS = os.environ.get("NATURALCAD_VERBOSE_LOGS", "false").strip().lower() in {"1", "true", "yes", "on"}
119
+
120
+
121
+ def _log_info(message: str) -> None:
122
+ if _VERBOSE_LOGS:
123
+ print(message)
124
+
125
+
126
+ def _log_error(message: str) -> None:
127
+ print(message)
128
+
129
 
130
  def _client_ip(request: Request) -> str:
131
  xff = request.headers.get("x-forwarded-for", "").strip()
 
302
  key = os.environ.get("SUPABASE_SERVICE_ROLE_KEY", "")
303
 
304
  if not url or not key:
305
+ _log_info("Skipping DB logging: SUPABASE_URL or key not set")
306
  return
307
 
308
  endpoint = f"{url}/rest/v1/jobs"
 
333
  payload.pop("generated_code", None)
334
  resp = client.post(endpoint, json=payload, headers=headers)
335
  if resp.status_code >= 400:
336
+ _log_error(f"DB log failed for job {job_id}: {resp.text}")
337
  else:
338
+ _log_info(f"DB log OK for job {job_id} (status={status})")
339
  except Exception as e:
340
+ _log_error(f"DB log error for job {job_id}: {e}")
341
 
342
 
343
  # ---------------------------------------------------------------------------
 
597
  ]
598
 
599
  for attempt in range(max_attempts):
600
+ _log_info(f"LLM call {attempt + 1}/{max_attempts} | mode={mode} output_type={output_type}")
601
  try:
602
  headers = {
603
  "Authorization": f"Bearer {openrouter_api_key}",
 
621
  response = client.post(openrouter_api_url, headers=headers, json=payload)
622
 
623
  if response.status_code >= 400:
624
+ _log_error(f"OpenRouter error {response.status_code}: {response.text[:500]}")
625
  return {"error": f"LLM provider unavailable ({response.status_code}). Please retry."}
626
 
627
  data = response.json()
628
  generated_code = (data.get("choices", [{}])[0].get("message", {}).get("content") or "").strip()
629
  if not generated_code:
630
+ _log_error(f"OpenRouter empty content response: {str(data)[:500]}")
631
  return {"error": "LLM returned empty output. Please retry."}
632
 
633
  # Strip markdown fences (model sometimes ignores rule 1)
 
639
  generated_code = generated_code[:-3]
640
  generated_code = generated_code.strip()
641
  except Exception as e:
642
+ _log_error(f"LLM call failed: {e}")
643
  return {"error": "LLM call failed. Please retry."}
644
 
645
  if log_generated_code:
646
+ _log_info(f"Generated code:\n{generated_code}")
647
 
648
  from build123d import export_stl, export_step
649
 
 
655
  is_safe, safety_error = _validate_generated_code(sanitized_code)
656
  if not is_safe:
657
  err_short = f"Rejected by AST guard: {safety_error}"
658
+ _log_error(err_short)
659
  if attempt < max_attempts - 1:
660
  messages.append({"role": "assistant", "content": generated_code})
661
  messages.append({
 
692
  import traceback as _tb
693
  err_short = f"{type(e).__name__}: {e}"
694
  err_trace = _tb.format_exc()
695
+ _log_error(f"Execution failed: {err_short}")
696
  finally:
697
  os.environ.clear()
698
  os.environ.update(original_env)
 
706
 
707
  if not exec_success:
708
  if attempt < max_attempts - 1:
709
+ _log_info("Retrying with error context...")
710
  # Cap traceback to avoid blowing the context window
711
  trace_snippet = err_trace[-2000:] if len(err_trace) > 2000 else err_trace
712
  messages.append({"role": "assistant", "content": generated_code})
 
736
 
737
  try:
738
  export_stl(shape, str(stl_path))
739
+ _log_info(f"STL exported: {stl_path.stat().st_size} bytes")
740
  except Exception as e:
741
+ _log_error(f"STL export failed: {e}")
742
  stl_path = None
743
 
744
  try:
745
  export_step(shape, str(step_path))
746
+ _log_info(f"STEP exported: {step_path.exists()}")
747
  except Exception as e:
748
+ _log_error(f"STEP export failed: {e}")
749
  step_path = None
750
 
751
  try:
 
758
  # Rotate to glTF Y-up convention
759
  mesh.apply_transform(tf.rotation_matrix(-math.pi / 2, [1, 0, 0]))
760
  mesh.export(str(glb_path))
761
+ _log_info(f"GLB exported: {glb_path.exists()}")
762
  else:
763
+ _log_info("Skipping GLB: no STL file")
764
  except Exception as e:
765
+ _log_error(f"GLB export failed: {e}")
766
 
767
  # ----------------------------------------------------------------
768
  # Upload to Supabase storage
 
778
  continue
779
  storage_key = f"runs/{run_id}/model.{fmt}"
780
  file_bytes = file_path.read_bytes()
781
+ _log_info(f"Uploading {fmt}: {len(file_bytes)} bytes")
782
  try:
783
  urls[fmt] = _upload_to_supabase(storage_key, file_bytes, content_type)
784
  except Exception as e:
785
+ _log_error(f"Upload error for {fmt}: {e}")
786
 
787
  _log_job_to_supabase(run_id, prompt, mode, output_type, generated_code, "completed")
788
  return {
apps/gradio-demo/README.md CHANGED
@@ -67,6 +67,7 @@ Optional environment variables:
67
  - `NATURALCAD_API_KEY`
68
  - `NATURALCAD_BACKEND_TIMEOUT` (default `4` seconds)
69
  - `NATURALCAD_SHOW_CODE` (default `false`; set `true` to show generated build123d code in UI logs)
 
70
  - `BUILD123D_PYTHON` (defaults to the current Python runtime, which is better for Hugging Face Space deployment)
71
 
72
  When backend is enabled and returns a `job.id`, the app will POST STL/STEP files to:
 
67
  - `NATURALCAD_API_KEY`
68
  - `NATURALCAD_BACKEND_TIMEOUT` (default `4` seconds)
69
  - `NATURALCAD_SHOW_CODE` (default `false`; set `true` to show generated build123d code in UI logs)
70
+ - `NATURALCAD_VERBOSE_LOGS` (default `false`; logs errors only unless enabled)
71
  - `BUILD123D_PYTHON` (defaults to the current Python runtime, which is better for Hugging Face Space deployment)
72
 
73
  When backend is enabled and returns a `job.id`, the app will POST STL/STEP files to:
apps/gradio-demo/app/main.py CHANGED
@@ -25,6 +25,7 @@ BACKEND_URL = os.getenv("NATURALCAD_BACKEND_URL", os.getenv("NL_CAD_BACKEND_URL"
25
  BACKEND_API_KEY = os.getenv("NATURALCAD_API_KEY", os.getenv("NL_CAD_API_KEY", ""))
26
  BACKEND_TIMEOUT_SECONDS = float(os.getenv("NATURALCAD_BACKEND_TIMEOUT", "60"))
27
  SHOW_GENERATED_CODE = os.getenv("NATURALCAD_SHOW_CODE", "false").strip().lower() in {"1", "true", "yes", "on"}
 
28
  ARTIFACTS_DIR = Path(__file__).parent.parent / "artifacts"
29
  RUNS_DIR = ARTIFACTS_DIR / "runs"
30
  LOGS_DIR = ARTIFACTS_DIR / "logs"
@@ -59,6 +60,15 @@ result = bp.part
59
  '''
60
 
61
 
 
 
 
 
 
 
 
 
 
62
  def _legacy_spec_from_semantic(spec: dict) -> dict:
63
  if "geometry_family" in spec and "parameters" in spec:
64
  return spec
@@ -504,53 +514,53 @@ def generate_from_prompt(prompt: str, mode: str, output_type: str):
504
 
505
  if glb_url:
506
  glb_path = run_dir / f"{run_id}.glb"
507
- print(f"DEBUG: Downloading GLB from {glb_url}")
508
  try:
509
  with request.urlopen(glb_url) as r:
510
  data = r.read()
511
- print(f"DEBUG: Downloaded {len(data)} bytes")
512
  if len(data) < 100:
513
- print(f"DEBUG: WARNING - GLB file too small: {data}")
514
  with open(glb_path, "wb") as f:
515
  f.write(data)
516
  glb_file = str(glb_path)
517
- print(f"DEBUG: GLB saved to {glb_file}, size {os.path.getsize(glb_file)}")
518
  except Exception as e:
519
- print(f"DEBUG: GLB download failed: {e}")
520
  glb_file = None
521
 
522
  if stl_url:
523
  stl_path = run_dir / f"{run_id}.stl"
524
- print(f"DEBUG: Downloading STL from {stl_url}")
525
  try:
526
  with request.urlopen(stl_url) as r:
527
  data = r.read()
528
- print(f"DEBUG: Downloaded {len(data)} bytes")
529
  if len(data) < 100:
530
- print(f"DEBUG: WARNING - STL file too small: {data}")
531
  with open(stl_path, "wb") as f:
532
  f.write(data)
533
  stl_file = str(stl_path)
534
- print(f"DEBUG: STL saved to {stl_file}, size {os.path.getsize(stl_file)}")
535
  except Exception as e:
536
- print(f"DEBUG: STL download failed: {e}")
537
  stl_file = None
538
 
539
  if step_url:
540
  step_path = run_dir / f"{run_id}.step"
541
- print(f"DEBUG: Downloading STEP from {step_url}")
542
  try:
543
  with request.urlopen(step_url) as r:
544
  data = r.read()
545
- print(f"DEBUG: Downloaded {len(data)} bytes")
546
  if len(data) < 100:
547
- print(f"DEBUG: WARNING - STEP file too small: {data}")
548
  with open(step_path, "wb") as f:
549
  f.write(data)
550
  step_file = str(step_path)
551
- print(f"DEBUG: STEP saved to {step_file}, size {os.path.getsize(step_file)}")
552
  except Exception as e:
553
- print(f"DEBUG: STEP download failed: {e}")
554
  step_file = None
555
 
556
  if not glb_file and stl_file:
@@ -565,9 +575,9 @@ def generate_from_prompt(prompt: str, mode: str, output_type: str):
565
  glb_path = run_dir / f"{run_id}.glb"
566
  mesh.export(str(glb_path))
567
  glb_file = str(glb_path)
568
- print(f"DEBUG: Generated local GLB from STL at {glb_file}")
569
  except Exception as e:
570
- print(f"DEBUG: Local GLB generation failed: {e}")
571
 
572
  if SHOW_GENERATED_CODE:
573
  combined_logs = f"Generated build123d code:\n\n{code}\n\n"
 
25
  BACKEND_API_KEY = os.getenv("NATURALCAD_API_KEY", os.getenv("NL_CAD_API_KEY", ""))
26
  BACKEND_TIMEOUT_SECONDS = float(os.getenv("NATURALCAD_BACKEND_TIMEOUT", "60"))
27
  SHOW_GENERATED_CODE = os.getenv("NATURALCAD_SHOW_CODE", "false").strip().lower() in {"1", "true", "yes", "on"}
28
+ VERBOSE_LOGS = os.getenv("NATURALCAD_VERBOSE_LOGS", "false").strip().lower() in {"1", "true", "yes", "on"}
29
  ARTIFACTS_DIR = Path(__file__).parent.parent / "artifacts"
30
  RUNS_DIR = ARTIFACTS_DIR / "runs"
31
  LOGS_DIR = ARTIFACTS_DIR / "logs"
 
60
  '''
61
 
62
 
63
+ def _log_info(message: str) -> None:
64
+ if VERBOSE_LOGS:
65
+ print(message)
66
+
67
+
68
+ def _log_error(message: str) -> None:
69
+ print(message)
70
+
71
+
72
  def _legacy_spec_from_semantic(spec: dict) -> dict:
73
  if "geometry_family" in spec and "parameters" in spec:
74
  return spec
 
514
 
515
  if glb_url:
516
  glb_path = run_dir / f"{run_id}.glb"
517
+ _log_info(f"Downloading GLB from {glb_url}")
518
  try:
519
  with request.urlopen(glb_url) as r:
520
  data = r.read()
521
+ _log_info(f"Downloaded GLB bytes: {len(data)}")
522
  if len(data) < 100:
523
+ _log_error("GLB file too small")
524
  with open(glb_path, "wb") as f:
525
  f.write(data)
526
  glb_file = str(glb_path)
527
+ _log_info(f"GLB saved to {glb_file}, size {os.path.getsize(glb_file)}")
528
  except Exception as e:
529
+ _log_error(f"GLB download failed: {e}")
530
  glb_file = None
531
 
532
  if stl_url:
533
  stl_path = run_dir / f"{run_id}.stl"
534
+ _log_info(f"Downloading STL from {stl_url}")
535
  try:
536
  with request.urlopen(stl_url) as r:
537
  data = r.read()
538
+ _log_info(f"Downloaded STL bytes: {len(data)}")
539
  if len(data) < 100:
540
+ _log_error("STL file too small")
541
  with open(stl_path, "wb") as f:
542
  f.write(data)
543
  stl_file = str(stl_path)
544
+ _log_info(f"STL saved to {stl_file}, size {os.path.getsize(stl_file)}")
545
  except Exception as e:
546
+ _log_error(f"STL download failed: {e}")
547
  stl_file = None
548
 
549
  if step_url:
550
  step_path = run_dir / f"{run_id}.step"
551
+ _log_info(f"Downloading STEP from {step_url}")
552
  try:
553
  with request.urlopen(step_url) as r:
554
  data = r.read()
555
+ _log_info(f"Downloaded STEP bytes: {len(data)}")
556
  if len(data) < 100:
557
+ _log_error("STEP file too small")
558
  with open(step_path, "wb") as f:
559
  f.write(data)
560
  step_file = str(step_path)
561
+ _log_info(f"STEP saved to {step_file}, size {os.path.getsize(step_file)}")
562
  except Exception as e:
563
+ _log_error(f"STEP download failed: {e}")
564
  step_file = None
565
 
566
  if not glb_file and stl_file:
 
575
  glb_path = run_dir / f"{run_id}.glb"
576
  mesh.export(str(glb_path))
577
  glb_file = str(glb_path)
578
+ _log_info(f"Generated local GLB from STL at {glb_file}")
579
  except Exception as e:
580
+ _log_error(f"Local GLB generation failed: {e}")
581
 
582
  if SHOW_GENERATED_CODE:
583
  combined_logs = f"Generated build123d code:\n\n{code}\n\n"