mtornani Claude Sonnet 4.6 commited on
Commit
ebc5116
·
1 Parent(s): b0b0ea0

Fix 3 root cause bugs blocking all 3 export docs

Browse files

1. export_core.py: fix broken regex r'|\s+|' → r'\|\s+\|'
- unescaped pipes in regex = OR operator → matched every char position
- ALL content rendered as vertical pipe-table (|E|r|r|o|r|e|...)
- one-char fix eliminates the vertical text bug across all docs

2. export_html.py: sidebar-header + cover use accent not primary
- when club primary = white (#FFFFFF), sidebar was white-on-white invisible
- sidebar-header: var(--primary) → var(--accent) (already dark)
- cover gradient: start from accent (dark) not primary (white)
- _process_section_content: validate content before rendering
shows orange warning box instead of broken content on API error

3. agents.py: on API failure return empty content not error message
- was: return {'content': wrapped.user_message}
- error text 'Errore di connessione.' stored as plan section content
- when exported it caused the vertical text bug (+ fix 1 above)
- now: return {'content': ''} with error info in metadata only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (3) hide show
  1. agents.py +5 -5
  2. export_core.py +2 -1
  3. export_html.py +15 -5
agents.py CHANGED
@@ -1034,7 +1034,7 @@ e soggette a revisione post-allineamento.
1034
  except Exception as e:
1035
  wrapped = handle_exception(e, context=f"agent_{self.spec.name}_ollama")
1036
  log_exception(wrapped, context=f"agent_{self.spec.name}")
1037
- return {'content': wrapped.user_message, 'sources': [], 'unverified_claims': [], 'metadata': {'error_id': wrapped.error_id}}
1038
 
1039
  # === OPENROUTER PATH ===
1040
  elif self._provider == "openrouter" and self._openrouter_client:
@@ -1050,7 +1050,7 @@ e soggette a revisione post-allineamento.
1050
  except Exception as e:
1051
  wrapped = handle_exception(e, context=f"agent_{self.spec.name}_openrouter")
1052
  log_exception(wrapped, context=f"agent_{self.spec.name}")
1053
- return {'content': wrapped.user_message, 'sources': [], 'unverified_claims': [], 'metadata': {'error_id': wrapped.error_id}}
1054
 
1055
  # === GEMINI PATH (default) ===
1056
  else:
@@ -1094,20 +1094,20 @@ e soggette a revisione post-allineamento.
1094
  details={"agent": self.spec.name, "fallback_error": str(fallback_e)}
1095
  )
1096
  log_exception(err, context=f"agent_{self.spec.name}")
1097
- return {'content': err.user_message, 'sources': [], 'unverified_claims': [], 'metadata': {'error_id': err.error_id}}
1098
  else:
1099
  err = AgentError(
1100
  message=f"Agent {self.spec.name} InvalidArgument: {e}",
1101
  details={"agent": self.spec.name, "error_type": "InvalidArgument"}
1102
  )
1103
  log_exception(err, context=f"agent_{self.spec.name}")
1104
- return {'content': err.user_message, 'sources': [], 'unverified_claims': [], 'metadata': {'error_id': err.error_id}}
1105
 
1106
  except Exception as e:
1107
  # Map to appropriate error type
1108
  wrapped = handle_exception(e, context=f"agent_{self.spec.name}")
1109
  log_exception(wrapped, context=f"agent_{self.spec.name}")
1110
- return {'content': wrapped.user_message, 'sources': [], 'unverified_claims': [], 'metadata': {'error_id': wrapped.error_id}}
1111
 
1112
  cleaned = self._post_process(raw_content)
1113
  content, sources, unverified = self.sourcer.process_content(cleaned, context=club_data.get('club_name', ''))
 
1034
  except Exception as e:
1035
  wrapped = handle_exception(e, context=f"agent_{self.spec.name}_ollama")
1036
  log_exception(wrapped, context=f"agent_{self.spec.name}")
1037
+ return {'content': '', 'sources': [], 'unverified_claims': [], 'metadata': {'error_id': wrapped.error_id, 'error_msg': wrapped.user_message}}
1038
 
1039
  # === OPENROUTER PATH ===
1040
  elif self._provider == "openrouter" and self._openrouter_client:
 
1050
  except Exception as e:
1051
  wrapped = handle_exception(e, context=f"agent_{self.spec.name}_openrouter")
1052
  log_exception(wrapped, context=f"agent_{self.spec.name}")
1053
+ return {'content': '', 'sources': [], 'unverified_claims': [], 'metadata': {'error_id': wrapped.error_id, 'error_msg': wrapped.user_message}}
1054
 
1055
  # === GEMINI PATH (default) ===
1056
  else:
 
1094
  details={"agent": self.spec.name, "fallback_error": str(fallback_e)}
1095
  )
1096
  log_exception(err, context=f"agent_{self.spec.name}")
1097
+ return {'content': '', 'sources': [], 'unverified_claims': [], 'metadata': {'error_id': err.error_id, 'error_msg': err.user_message}}
1098
  else:
1099
  err = AgentError(
1100
  message=f"Agent {self.spec.name} InvalidArgument: {e}",
1101
  details={"agent": self.spec.name, "error_type": "InvalidArgument"}
1102
  )
1103
  log_exception(err, context=f"agent_{self.spec.name}")
1104
+ return {'content': '', 'sources': [], 'unverified_claims': [], 'metadata': {'error_id': err.error_id, 'error_msg': err.user_message}}
1105
 
1106
  except Exception as e:
1107
  # Map to appropriate error type
1108
  wrapped = handle_exception(e, context=f"agent_{self.spec.name}")
1109
  log_exception(wrapped, context=f"agent_{self.spec.name}")
1110
+ return {'content': '', 'sources': [], 'unverified_claims': [], 'metadata': {'error_id': wrapped.error_id, 'error_msg': wrapped.user_message}}
1111
 
1112
  cleaned = self._post_process(raw_content)
1113
  content, sources, unverified = self.sourcer.process_content(cleaned, context=club_data.get('club_name', ''))
export_core.py CHANGED
@@ -171,7 +171,8 @@ class BaseExporter(ABC):
171
  content = content.replace('\r\n', '\n')
172
 
173
  # TABLES: Separate markdown table rows that are on a single line
174
- content = re.sub(r'|\s+|', '|\n|', content)
 
175
 
176
  # Pattern: ### 1. TITLE -> converts to header h3 (removes the number)
177
  content = re.sub(r'###\s+\d+[\.\)]\s*', '\n\n### ', content)
 
171
  content = content.replace('\r\n', '\n')
172
 
173
  # TABLES: Separate markdown table rows that are on a single line
174
+ # NOTE: pipes must be escaped with \| — unescaped | in regex means OR
175
+ content = re.sub(r'\|\s+\|', '|\n|', content)
176
 
177
  # Pattern: ### 1. TITLE -> converts to header h3 (removes the number)
178
  content = re.sub(r'###\s+\d+[\.\)]\s*', '\n\n### ', content)
export_html.py CHANGED
@@ -127,9 +127,19 @@ class ChunkedHTMLExporter(BaseExporter):
127
 
128
  def _process_section_content(self, content: str) -> str:
129
  """Converte contenuto markdown-like in HTML usando BaseExporter logic"""
130
- if content is None:
131
- return "<p><em>Contenuto non disponibile</em></p>"
132
-
 
 
 
 
 
 
 
 
 
 
133
  normalized = self._normalize_markdown(content)
134
  return self._markdown_to_html(normalized)
135
 
@@ -326,7 +336,7 @@ class ChunkedHTMLExporter(BaseExporter):
326
  display: flex; flex-direction: column; z-index: 1000;
327
  }}
328
 
329
- .sidebar-header {{ padding: 24px 20px; background: var(--primary); color: white; text-align: center; border-bottom: 3px solid var(--accent); }}
330
  .nav {{ flex: 1; overflow-y: auto; padding: 16px 10px; }}
331
  .nav-item {{ display: block; padding: 10px 14px; margin-bottom: 3px; color: #4a5568; text-decoration: none; border-radius: 6px; font-weight: 600; font-size: 0.85rem; border-left: 3px solid transparent; }}
332
  .nav-item:hover {{ background: var(--bg); color: var(--accent); border-left-color: var(--accent); }}
@@ -335,7 +345,7 @@ class ChunkedHTMLExporter(BaseExporter):
335
  .container {{ max-width: 900px; margin: 0 auto; padding: 60px 40px; }}
336
 
337
  .cover {{
338
- height: 55vh; background: linear-gradient(150deg, var(--primary) 0%, var(--accent) 100%);
339
  color: white; display: flex; flex-direction: column;
340
  justify-content: center; align-items: center; text-align: center;
341
  position: relative; overflow: hidden;
 
127
 
128
  def _process_section_content(self, content: str) -> str:
129
  """Converte contenuto markdown-like in HTML usando BaseExporter logic"""
130
+ if not content:
131
+ return "<p><em>Contenuto non disponibile per questa sezione.</em></p>"
132
+
133
+ # Guard: content troppo corto o messaggio d'errore → non renderizzare
134
+ _error_markers = ["errore di connessione", "verifica la connessione", "si è verificato un errore"]
135
+ if len(content.strip()) < 80 or any(m in content.lower() for m in _error_markers):
136
+ return (
137
+ '<div style="border-left:4px solid #F57C00; background:#FFF8E1; padding:14px 18px; '
138
+ 'border-radius:0 6px 6px 0; color:#555; font-style:italic;">'
139
+ '⚠ Sezione non generata correttamente. Rigenera il piano per ottenere i contenuti completi.'
140
+ '</div>'
141
+ )
142
+
143
  normalized = self._normalize_markdown(content)
144
  return self._markdown_to_html(normalized)
145
 
 
336
  display: flex; flex-direction: column; z-index: 1000;
337
  }}
338
 
339
+ .sidebar-header {{ padding: 24px 20px; background: var(--accent); color: var(--text-on-primary); text-align: center; border-bottom: 3px solid var(--primary); }}
340
  .nav {{ flex: 1; overflow-y: auto; padding: 16px 10px; }}
341
  .nav-item {{ display: block; padding: 10px 14px; margin-bottom: 3px; color: #4a5568; text-decoration: none; border-radius: 6px; font-weight: 600; font-size: 0.85rem; border-left: 3px solid transparent; }}
342
  .nav-item:hover {{ background: var(--bg); color: var(--accent); border-left-color: var(--accent); }}
 
345
  .container {{ max-width: 900px; margin: 0 auto; padding: 60px 40px; }}
346
 
347
  .cover {{
348
+ height: 55vh; background: linear-gradient(150deg, var(--accent) 0%, var(--primary) 100%);
349
  color: white; display: flex; flex-direction: column;
350
  justify-content: center; align-items: center; text-align: center;
351
  position: relative; overflow: hidden;