mfirat007 commited on
Commit
d74f9c1
·
verified ·
1 Parent(s): ae5fe34

Upload sources.py

Browse files
Files changed (1) hide show
  1. sources.py +98 -75
sources.py CHANGED
@@ -117,81 +117,105 @@ def format_expandable_sources(source_nodes, question: str = "", answer: str = ""
117
  Fail-closed behavior: if the full article cannot be resolved from ARTICLE_SOURCES or
118
  SEMANTIC_UNITS, do not display node/evidence text as if it were the official source.
119
  """
120
- seen = set()
121
- blocks = []
122
- for node_with_score in source_nodes:
123
- meta = getattr(node_with_score.node, "metadata", {}) or {}
124
- document_id = _metadata_document_id(meta)
125
- article = meta.get("article", "") or meta.get("article_id", "")
126
- title = meta.get("title", "") or meta.get("article_title", "")
127
- key = _dedupe_key(document_id, article, title)
128
- if key in seen:
129
- continue
130
- seen.add(key)
131
-
132
- article_source = _lookup_article_source(document_id, article)
133
- if article_source:
134
- title = article_source.get("title") or title
135
- body_text = article_source.get("source_text", "")
136
- else:
137
- unit = _lookup_semantic_unit(document_id, article)
138
- title = unit.get("title") or title
139
- body_text = unit.get("source_text", "")
140
-
141
- if not body_text:
142
- body_text = _missing_source_message(document_id, article)
143
-
144
- label = _source_label(document_id, article, title, article_source.get("document_title", "") if article_source else "")
145
- blocks.append(_render_source_block(label, body_text))
146
-
147
- official = _official_source_link()
148
- return "\n\n".join([_SOURCE_DISCLOSURE_STYLE] + blocks + [official])
149
-
150
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  def format_clause_sources(clauses: list[dict], question: str = "", answer: str = "") -> str:
152
- """Render full article sources for clause/evidence-based answers.
153
-
154
- The answer engine selects small evidence spans for grounding, but the UI source panel
155
- must show the full related article. Therefore this function never uses
156
- `clause['source_text']` as a fallback. That field may contain only a clause, evidence
157
- span, or generated/derived text. If the full article is missing, the UI displays an
158
- explicit diagnostic message instead of pretending the evidence span is the source.
159
- """
160
- blocks = []
161
- seen_articles = set()
162
- for clause in clauses or []:
163
- document_id = _clause_document_id(clause)
164
- article = str(clause.get("article_id", "") or clause.get("article", "")).strip()
165
- if not article:
166
- continue
167
-
168
- key = _dedupe_key(document_id, article)
169
- if key in seen_articles:
170
- continue
171
- seen_articles.add(key)
172
-
173
- article_source = _lookup_article_source(document_id, article)
174
- title = ""
175
- body_text = ""
176
- if article_source:
177
- title = str(article_source.get("title", "") or "").strip()
178
- body_text = str(article_source.get("source_text", "") or "").strip()
179
-
180
- if not body_text:
181
- # Accept explicit full article text only. Do not fall back to clause['source_text'].
182
- explicit_full_article = str(clause.get("article_source_text", "") or "").strip()
183
- if explicit_full_article:
184
- body_text = explicit_full_article
185
- title = title or _source_title_from_clause(clause)
186
-
187
- if not body_text:
188
- body_text = _missing_source_message(document_id, article)
189
-
190
- title = title or _source_title_from_clause(clause)
191
- label = _source_label(document_id, article, title, article_source.get("document_title", "") if article_source else clause.get("document_title", ""))
192
- blocks.append(_render_source_block(label, body_text))
193
-
194
- official = _official_source_link()
 
 
 
 
195
  return "\n\n".join([_SOURCE_DISCLOSURE_STYLE] + blocks + [official])
196
 
197
 
@@ -199,7 +223,6 @@ def get_article_source(document_id: str, article_id: str) -> dict[str, Any]:
199
  """Return the canonical full-article source already loaded by the runtime."""
200
  return dict(_lookup_article_source(document_id, article_id) or {})
201
 
202
-
203
  def _normalize_article_sources(article_sources: dict[str, dict]) -> tuple[dict[str, dict[str, Any]], set[str]]:
204
  normalized: dict[str, dict[str, Any]] = {}
205
  plain_key_documents: dict[str, set[str]] = {}
 
117
  Fail-closed behavior: if the full article cannot be resolved from ARTICLE_SOURCES or
118
  SEMANTIC_UNITS, do not display node/evidence text as if it were the official source.
119
  """
120
+ seen = set()
121
+ blocks = []
122
+ for node_with_score in source_nodes:
123
+ meta = getattr(node_with_score.node, "metadata", {}) or {}
124
+ document_id = _metadata_document_id(meta)
125
+ article = meta.get("article", "") or meta.get("article_id", "")
126
+ title = meta.get("title", "") or meta.get("article_title", "")
127
+ key = _dedupe_key(document_id, article, title)
128
+ if key in seen:
129
+ continue
130
+ seen.add(key)
131
+
132
+ article_source = _lookup_article_source(document_id, article)
133
+ if article_source:
134
+ title = article_source.get("title") or title
135
+ body_text = article_source.get("source_text", "")
136
+ else:
137
+ unit = _lookup_semantic_unit(document_id, article)
138
+ title = unit.get("title") or title
139
+ body_text = unit.get("source_text", "")
140
+
141
+ if not body_text:
142
+ body_text = _missing_source_message(document_id, article)
143
+
144
+ label = _source_label(document_id, article, title, article_source.get("document_title", "") if article_source else "")
145
+ blocks.append(_render_source_block(label, body_text))
146
+
147
+ official = _official_source_link()
148
+ return "\n\n".join([_SOURCE_DISCLOSURE_STYLE] + blocks + [official])
149
+
150
+
151
+
152
+ def _extract_text_article_sources(text: str) -> list[dict[str, str]]:
153
+ import re
154
+ sources = []
155
+ pattern = r"\b(2547|2914|2809)(?:\s+say[ıi]l[ıi]\s+kanun)?\s+(?:say[ıi]l[ıi]\s+)?(?:kanunun\s+)?(Ge[cç]ici\s+Madde\s+\d+|Ek\s+Madde\s+\d+|Madde\s+\d+(?:/[A-Za-zÇĞİÖŞÜçğıöşü])?)\b"
156
+ for match in re.finditer(pattern, text or "", flags=re.IGNORECASE):
157
+ code = match.group(1)
158
+ doc_id = f"TR-KANUN-{code}"
159
+ raw_art = match.group(2).strip()
160
+ art_norm = re.sub(r"\s+", " ", raw_art).title()
161
+ if art_norm.lower().startswith("madde "):
162
+ art_norm = f"Madde {art_norm[6:].upper()}"
163
+ elif art_norm.lower().startswith("ek madde "):
164
+ art_norm = f"Ek Madde {art_norm[9:]}"
165
+ elif art_norm.lower().startswith("geçici madde ") or art_norm.lower().startswith("gecici madde "):
166
+ art_norm = f"Geçici Madde {art_norm[13:].upper()}"
167
+ sources.append({"document_id": doc_id, "article_id": art_norm})
168
+ return sources
169
+
170
+
171
  def format_clause_sources(clauses: list[dict], question: str = "", answer: str = "") -> str:
172
+ """Render full article sources for clause/evidence-based answers.
173
+
174
+ The answer engine selects small evidence spans for grounding, but the UI source panel
175
+ must show the full related article. Therefore this function never uses
176
+ `clause['source_text']` as a fallback. That field may contain only a clause, evidence
177
+ span, or generated/derived text. If the full article is missing, the UI displays an
178
+ explicit diagnostic message instead of pretending the evidence span is the source.
179
+ """
180
+ blocks = []
181
+ seen_articles = set()
182
+ all_clauses = list(clauses or [])
183
+ if answer:
184
+ all_clauses.extend(_extract_text_article_sources(answer))
185
+
186
+ for clause in all_clauses:
187
+ document_id = _clause_document_id(clause)
188
+ article = str(clause.get("article_id", "") or clause.get("article", "")).strip()
189
+ if not article:
190
+ continue
191
+
192
+ key = _dedupe_key(document_id, article)
193
+ if key in seen_articles:
194
+ continue
195
+ seen_articles.add(key)
196
+
197
+ article_source = _lookup_article_source(document_id, article)
198
+ title = ""
199
+ body_text = ""
200
+ if article_source:
201
+ title = str(article_source.get("title", "") or "").strip()
202
+ body_text = str(article_source.get("source_text", "") or "").strip()
203
+
204
+ if not body_text:
205
+ # Accept explicit full article text only. Do not fall back to clause['source_text'].
206
+ explicit_full_article = str(clause.get("article_source_text", "") or "").strip()
207
+ if explicit_full_article:
208
+ body_text = explicit_full_article
209
+ title = title or _source_title_from_clause(clause)
210
+
211
+ if not body_text:
212
+ body_text = _missing_source_message(document_id, article)
213
+
214
+ title = title or _source_title_from_clause(clause)
215
+ label = _source_label(document_id, article, title, article_source.get("document_title", "") if article_source else clause.get("document_title", ""))
216
+ blocks.append(_render_source_block(label, body_text))
217
+
218
+ official = _official_source_link()
219
  return "\n\n".join([_SOURCE_DISCLOSURE_STYLE] + blocks + [official])
220
 
221
 
 
223
  """Return the canonical full-article source already loaded by the runtime."""
224
  return dict(_lookup_article_source(document_id, article_id) or {})
225
 
 
226
  def _normalize_article_sources(article_sources: dict[str, dict]) -> tuple[dict[str, dict[str, Any]], set[str]]:
227
  normalized: dict[str, dict[str, Any]] = {}
228
  plain_key_documents: dict[str, set[str]] = {}