KayO Codex commited on
Commit
ca94ced
·
1 Parent(s): 8a6fd8e

feat: add legislation explainer app

Browse files

Co-authored-by: Codex <codex@openai.com>

app.py ADDED
@@ -0,0 +1,933 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Gradio entrypoint for the Legislation Explainer app."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import html
6
+ from pathlib import Path
7
+ from uuid import uuid4
8
+ from typing import Any
9
+
10
+ import gradio as gr
11
+
12
+ from config import APP_DESCRIPTION, APP_TITLE, DEFAULT_PROVIDER
13
+ from services.example_bills import example_bill_titles, example_bill_urls_by_title
14
+ from services import ingest
15
+ from services.providers import get_default_api_key, instantiate_client, list_providers, validate_api_key
16
+ from services.rag_pipeline import (
17
+ AnalysisResult,
18
+ answer_query,
19
+ answer_query_from_full_document,
20
+ build_cached_document_artifacts,
21
+ generate_analysis_progress,
22
+ get_precomputed_example_artifacts,
23
+ prepare_document_artifacts,
24
+ warm_embedding_stack,
25
+ )
26
+
27
+
28
+ def _provider_options() -> list[tuple[str, str, str]]:
29
+ return [(config.name, config.display_name, config.instructions) for config in list_providers()]
30
+
31
+
32
+ PROVIDER_OPTIONS = _provider_options()
33
+ PROVIDER_LABELS = [display for _, display, _ in PROVIDER_OPTIONS]
34
+ PROVIDER_BY_LABEL = {display: name for name, display, _ in PROVIDER_OPTIONS}
35
+ PROVIDER_DETAILS = {name: description for name, _, description in PROVIDER_OPTIONS}
36
+ PROVIDER_LABEL_BY_NAME = {name: display for name, display, _ in PROVIDER_OPTIONS}
37
+ ANALYSIS_PLACEHOLDER = "Run an analysis to populate this section."
38
+ EXAMPLE_BILL_LABELS = example_bill_titles()
39
+ EXAMPLE_BILL_URLS = example_bill_urls_by_title()
40
+ APP_THEME = gr.themes.Soft(
41
+ primary_hue="sky",
42
+ secondary_hue="slate",
43
+ neutral_hue="stone",
44
+ ).set(
45
+ body_background_fill="linear-gradient(180deg, #f7fafc 0%, #edf4f7 100%)",
46
+ block_background_fill="rgba(255, 255, 255, 0.88)",
47
+ block_border_color="#d7e3ea",
48
+ block_shadow="0 18px 48px rgba(15, 23, 42, 0.06)",
49
+ button_primary_background_fill="linear-gradient(135deg, #7dd3fc 0%, #67e8f9 100%)",
50
+ button_primary_background_fill_hover="linear-gradient(135deg, #38bdf8 0%, #22d3ee 100%)",
51
+ button_primary_border_color="#7dd3fc",
52
+ button_secondary_background_fill="#f8fafc",
53
+ button_secondary_background_fill_hover="#eef6f8",
54
+ )
55
+ APP_HEAD = """
56
+ <script>
57
+ (() => {
58
+ function toggleSidebar() {
59
+ const sidebar = document.getElementById("sidebar-panel");
60
+ if (!sidebar) {
61
+ return;
62
+ }
63
+ const isHidden = sidebar.style.display === "none";
64
+ sidebar.style.display = isHidden ? "" : "none";
65
+ }
66
+
67
+ window.__toggleSidebar = toggleSidebar;
68
+ })();
69
+ </script>
70
+ """
71
+ APP_CSS = """
72
+ #app-shell {
73
+ background: transparent;
74
+ }
75
+ #layout-shell {
76
+ gap: 1.25rem;
77
+ align-items: flex-start;
78
+ }
79
+ #topbar-row {
80
+ justify-content: flex-start;
81
+ align-items: center;
82
+ margin-bottom: 0.75rem;
83
+ }
84
+ #sidebar-toggle-shell {
85
+ width: auto;
86
+ }
87
+ #sidebar-toggle-shell > div {
88
+ width: auto !important;
89
+ }
90
+ #sidebar-toggle-icon {
91
+ display: inline-flex;
92
+ align-items: center;
93
+ justify-content: center;
94
+ width: 2.75rem;
95
+ height: 1.75rem;
96
+ border: 1px solid var(--button-secondary-border-color);
97
+ border-radius: 5px;
98
+ background: var(--button-secondary-background-fill);
99
+ cursor: pointer;
100
+ transition: background-color 0.2s ease, border-color 0.2s ease;
101
+ }
102
+ #sidebar-toggle-icon:hover {
103
+ background: var(--button-secondary-background-fill-hover);
104
+ }
105
+ #sidebar-toggle-icon svg {
106
+ width: 1.35rem;
107
+ height: 1.35rem;
108
+ stroke: #6b7280;
109
+ }
110
+ #sidebar-toggle {
111
+ display: none;
112
+ }
113
+ #sidebar-panel {
114
+ max-width: 360px;
115
+ position: sticky;
116
+ top: 1rem;
117
+ align-self: flex-start;
118
+ border-radius: 16px;
119
+ overflow: hidden;
120
+ }
121
+ #main-panel {
122
+ min-width: 0;
123
+ }
124
+ #analysis-panel,
125
+ #chat-panel {
126
+ border: 1px solid var(--block-border-color);
127
+ border-radius: 16px;
128
+ padding: 1rem;
129
+ backdrop-filter: blur(12px);
130
+ }
131
+ #analysis-output {
132
+ min-height: 100px;
133
+ }
134
+ #status-output,
135
+ #chat-status {
136
+ min-height: 1.5rem;
137
+ }
138
+ #view-document-shell {
139
+ margin-top: 0.5rem;
140
+ }
141
+ #view-document-shell .view-document-link,
142
+ #view-document-shell .view-document-link-disabled {
143
+ display: flex;
144
+ align-items: center;
145
+ justify-content: center;
146
+ gap: 0.45rem;
147
+ width: 100%;
148
+ min-height: 2.75rem;
149
+ padding: 0.7rem 1rem;
150
+ border-radius: 0.6rem;
151
+ border: 1px solid var(--button-secondary-border-color);
152
+ font-weight: 600;
153
+ text-decoration: none;
154
+ box-sizing: border-box;
155
+ }
156
+ #view-document-shell .view-document-link {
157
+ color: inherit;
158
+ background: var(--button-secondary-background-fill);
159
+ }
160
+ #view-document-shell .view-document-link:hover {
161
+ background: var(--button-secondary-background-fill-hover);
162
+ }
163
+ #view-document-shell .view-document-link-disabled {
164
+ color: inherit;
165
+ background: var(--button-secondary-background-fill);
166
+ cursor: not-allowed;
167
+ pointer-events: none;
168
+ }
169
+ #view-document-shell .view-document-link-disabled svg {
170
+ opacity: 0.9;
171
+ }
172
+ #view-document-shell svg {
173
+ width: 0.95rem;
174
+ height: 0.95rem;
175
+ flex: 0 0 auto;
176
+ }
177
+ #example-bills-heading {
178
+ display: flex;
179
+ align-items: center;
180
+ gap: 0.35rem;
181
+ margin-bottom: 0.35rem;
182
+ }
183
+ #example-bills-heading p {
184
+ margin: 0;
185
+ }
186
+ #example-bills-info {
187
+ position: relative;
188
+ display: inline-flex;
189
+ align-items: center;
190
+ justify-content: center;
191
+ width: 1rem;
192
+ height: 1rem;
193
+ border: 1px solid #94a3b8;
194
+ border-radius: 999px;
195
+ color: #64748b;
196
+ font-size: 0.72rem;
197
+ font-weight: 700;
198
+ line-height: 1;
199
+ cursor: help;
200
+ }
201
+ #example-bills-info::after {
202
+ content: attr(data-tooltip);
203
+ position: absolute;
204
+ left: calc(100% + 0.45rem);
205
+ top: 50%;
206
+ transform: translateY(-50%);
207
+ width: 240px;
208
+ padding: 0.6rem 0.7rem;
209
+ border-radius: 0.6rem;
210
+ background: rgba(15, 23, 42, 0.96);
211
+ color: #f8fafc;
212
+ font-size: 0.78rem;
213
+ font-weight: 500;
214
+ line-height: 1.35;
215
+ opacity: 0;
216
+ visibility: hidden;
217
+ pointer-events: none;
218
+ box-shadow: 0 12px 28px rgba(15, 23, 42, 0.22);
219
+ transition: opacity 0.18s ease;
220
+ z-index: 10;
221
+ }
222
+ #example-bills-info:hover::after,
223
+ #example-bills-info:focus-visible::after {
224
+ opacity: 1;
225
+ visibility: visible;
226
+ }
227
+ #app-shell .label-wrap,
228
+ #app-shell .label-wrap > *,
229
+ #app-shell label,
230
+ #app-shell legend,
231
+ #app-shell [data-testid="block-label"] {
232
+ background: transparent !important;
233
+ box-shadow: none !important;
234
+ }
235
+ """
236
+
237
+ _GRADIO_SESSION_CACHE: dict[str, dict[str, Any]] = {}
238
+
239
+
240
+ def _empty_session() -> dict[str, Any]:
241
+ return {"session_id": uuid4().hex}
242
+
243
+
244
+ def _session_record(session_state: dict[str, Any] | None) -> dict[str, Any]:
245
+ session_state = session_state or _empty_session()
246
+ session_id = session_state.get("session_id")
247
+ if not session_id:
248
+ session_id = uuid4().hex
249
+ session_state["session_id"] = session_id
250
+ return _GRADIO_SESSION_CACHE.setdefault(
251
+ session_id,
252
+ {
253
+ "api_config": None,
254
+ "analysis": None,
255
+ "vector_store": None,
256
+ "doc_text": None,
257
+ "chat_history": [],
258
+ "pending_deeper_question": None,
259
+ "source_url": None,
260
+ },
261
+ )
262
+
263
+
264
+ def _resolve_provider(use_advanced: bool, provider_label: str | None) -> str:
265
+ return DEFAULT_PROVIDER
266
+ # Proposed future expansion: restore bring-your-own provider selection.
267
+ # if not use_advanced:
268
+ # return DEFAULT_PROVIDER
269
+ # if not provider_label:
270
+ # return DEFAULT_PROVIDER
271
+ # return PROVIDER_BY_LABEL.get(provider_label, DEFAULT_PROVIDER)
272
+
273
+
274
+ def _resolve_api_key(provider: str, use_advanced: bool, qwen_key: str | None, custom_key: str | None) -> str | None:
275
+ return qwen_key or get_default_api_key(provider)
276
+ # Proposed future expansion: restore bring-your-own provider API keys.
277
+ # if not use_advanced and provider == DEFAULT_PROVIDER:
278
+ # return qwen_key or get_default_api_key(provider)
279
+ # return custom_key or get_default_api_key(provider)
280
+
281
+
282
+ def _ingest_sources(file_path: str | None, url: str | None) -> str:
283
+ chunks = []
284
+ if file_path:
285
+ path = Path(file_path)
286
+ chunks.append(ingest.ingest_file(path.name, path.read_bytes()))
287
+ if url and url.strip():
288
+ chunks.append(ingest.fetch_url_content(url.strip()))
289
+ if not chunks:
290
+ raise ingest.IngestionError("Provide a file or URL to analyze.")
291
+ return ingest.combine_sources(chunks)
292
+
293
+
294
+ def _escape_md_cell(value: str) -> str:
295
+ return str(value).replace("|", "\\|").replace("\n", " ").strip()
296
+
297
+
298
+ def _markdown_bullets(items: list[str]) -> str:
299
+ if not items:
300
+ return "-"
301
+ return "\n".join(f"- {_escape_md_cell(item) or '—'}" for item in items)
302
+
303
+
304
+ def _analysis_attr(analysis: AnalysisResult, field: str, default: Any) -> Any:
305
+ return getattr(analysis, field, default)
306
+
307
+
308
+ def _swot_attr(analysis: AnalysisResult, field: str) -> list[str]:
309
+ swot = _analysis_attr(analysis, "swot", None)
310
+ return getattr(swot, field, []) if swot is not None else []
311
+
312
+
313
+ def _format_table_rows(rows: list[list[str]], column_count: int) -> str:
314
+ if not rows:
315
+ return "| " + " | ".join(["—"] * column_count) + " |"
316
+ return "\n".join("| " + " | ".join(_escape_md_cell(cell) or "—" for cell in row) + " |" for row in rows)
317
+
318
+
319
+ def _format_analysis(analysis: AnalysisResult | None) -> str:
320
+ if analysis is None:
321
+ return ANALYSIS_PLACEHOLDER
322
+
323
+ executive_summary = _escape_md_cell(_analysis_attr(analysis, "executive_summary", "")) or "No executive summary returned."
324
+ bill_summary = _markdown_bullets(_analysis_attr(analysis, "bill_summary", []))
325
+ implementation_rows = [
326
+ [
327
+ getattr(item, "stakeholder", ""),
328
+ getattr(item, "obligation", ""),
329
+ getattr(item, "implementation_burden", ""),
330
+ getattr(item, "risk_or_note", ""),
331
+ ]
332
+ for item in _analysis_attr(analysis, "implementation", [])
333
+ ]
334
+ critique_rows = [
335
+ [
336
+ getattr(item, "issue", ""),
337
+ getattr(item, "why_it_matters", ""),
338
+ getattr(item, "recommendation", ""),
339
+ ]
340
+ for item in _analysis_attr(analysis, "critique", [])
341
+ ]
342
+ strengths = _swot_attr(analysis, "strengths")
343
+ weaknesses = _swot_attr(analysis, "weaknesses")
344
+ opportunities = _swot_attr(analysis, "opportunities")
345
+ threats = _swot_attr(analysis, "threats")
346
+ swot_pair_rows = [
347
+ [strengths[idx] if idx < len(strengths) else "", weaknesses[idx] if idx < len(weaknesses) else ""]
348
+ for idx in range(max(len(strengths), len(weaknesses), 1))
349
+ ]
350
+ swot_risk_rows = [
351
+ [opportunities[idx] if idx < len(opportunities) else "", threats[idx] if idx < len(threats) else ""]
352
+ for idx in range(max(len(opportunities), len(threats), 1))
353
+ ]
354
+
355
+ return "\n\n".join(
356
+ [
357
+ "## Executive Summary\n" + executive_summary,
358
+ "## Summary of the Bill\n" + bill_summary,
359
+ "## Implementation Implications\n"
360
+ "| Stakeholder | Obligation | Burden | Risk / Note |\n"
361
+ "|---|---|---|---|\n"
362
+ + _format_table_rows(implementation_rows, 4),
363
+ "## Critique\n"
364
+ "| Issue | Why it matters | Recommendation |\n"
365
+ "|---|---|---|\n"
366
+ + _format_table_rows(critique_rows, 3),
367
+ "## SWOT Analysis\n"
368
+ "| Strengths | Weaknesses |\n"
369
+ "|---|---|\n"
370
+ + _format_table_rows(swot_pair_rows, 2)
371
+ + "\n\n"
372
+ + "| Opportunities | Threats |\n"
373
+ + "|---|---|\n"
374
+ + _format_table_rows(swot_risk_rows, 2),
375
+ ]
376
+ )
377
+
378
+
379
+ def _format_chat_entry(answer_text: str, citations: list[dict[str, Any]], *, provenance: str = "analysis_based") -> str:
380
+ prefix = ""
381
+ if provenance == "analysis_based":
382
+ prefix = "_Based on the summary and analysis._\n\n"
383
+ elif provenance == "full_document":
384
+ prefix = "_Deeper full-document answer._\n\n"
385
+
386
+ if not citations:
387
+ return prefix + answer_text
388
+ citations_text = "\n".join(f"- [ref{item['ref_id']}] {item['snippet']}" for item in citations)
389
+ return f"{prefix}{answer_text}\n\nSupporting snippets:\n{citations_text}"
390
+
391
+
392
+ def _deeper_answer_updates(visible: bool, label: str = "Run deeper full-document answer") -> gr.update:
393
+ return gr.update(visible=visible, value=label)
394
+
395
+
396
+ # Proposed future expansion: restore bring-your-own provider help text and
397
+ # controls when the app supports non-hackathon model/provider comparison again.
398
+ # def _provider_help_text(provider_label: str | None, use_advanced: bool) -> str:
399
+ # provider = _resolve_provider(use_advanced, provider_label)
400
+ # return PROVIDER_DETAILS[provider]
401
+ #
402
+ #
403
+ # def _toggle_provider_mode(use_advanced: bool) -> tuple[gr.update, gr.update, gr.update, gr.update]:
404
+ # provider_value = PROVIDER_LABEL_BY_NAME[DEFAULT_PROVIDER]
405
+ # return (
406
+ # gr.update(visible=not use_advanced),
407
+ # gr.update(visible=use_advanced),
408
+ # gr.update(visible=use_advanced),
409
+ # gr.update(value=_provider_help_text(provider_value, use_advanced)),
410
+ # )
411
+
412
+
413
+ def _set_example_url(example_label: str | None) -> str:
414
+ if not example_label:
415
+ return ""
416
+ return EXAMPLE_BILL_URLS.get(example_label, "")
417
+
418
+
419
+ def _render_document_link(url_value: str | None) -> str:
420
+ icon = (
421
+ '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" '
422
+ 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">'
423
+ '<path d="M14 5h5v5"></path>'
424
+ '<path d="M10 14 19 5"></path>'
425
+ '<path d="M19 14v5h-14v-14h5"></path>'
426
+ "</svg>"
427
+ )
428
+ if not url_value or not url_value.strip():
429
+ return (
430
+ '<span class="view-document-link-disabled">'
431
+ f'<span>View source</span>{icon}'
432
+ "</span>"
433
+ )
434
+ safe_url = html.escape(url_value.strip(), quote=True)
435
+ return (
436
+ f'<a class="view-document-link" href="{safe_url}" target="_blank" rel="noopener noreferrer">'
437
+ f'<span>View source</span>{icon}'
438
+ "</a>"
439
+ )
440
+
441
+
442
+ def _analysis_stage_message(stage: str) -> str:
443
+ return f"_Processing... {stage}_"
444
+
445
+
446
+ def _show_warning(message: str) -> str:
447
+ gr.Warning(message)
448
+ return message
449
+
450
+
451
+ def _show_error(message: str) -> str:
452
+ gr.Error(message)
453
+ return message
454
+
455
+
456
+ def analyze_document(
457
+ uploaded_file: str | None,
458
+ url_value: str | None,
459
+ use_advanced: bool,
460
+ provider_label: str | None,
461
+ qwen_key: str | None,
462
+ custom_key: str | None,
463
+ session_state: dict[str, Any] | None,
464
+ ):
465
+ session_state = session_state or _empty_session()
466
+ record = _session_record(session_state)
467
+ provider = _resolve_provider(use_advanced, provider_label)
468
+ api_key = _resolve_api_key(provider, use_advanced, qwen_key, custom_key)
469
+ is_valid, error = validate_api_key(provider, api_key)
470
+ if not is_valid:
471
+ message = "Check the selected provider and API key, then try again."
472
+ _show_warning(message)
473
+ yield (
474
+ session_state,
475
+ message,
476
+ ANALYSIS_PLACEHOLDER,
477
+ [],
478
+ _deeper_answer_updates(False),
479
+ "",
480
+ )
481
+ return
482
+
483
+ precomputed = get_precomputed_example_artifacts(url_value)
484
+ if precomputed is not None:
485
+ record.clear()
486
+ record.update(
487
+ {
488
+ "api_config": {"provider": provider, "api_key": api_key},
489
+ "analysis": precomputed.analysis.model_dump(),
490
+ "vector_store": precomputed.vector_store,
491
+ "doc_text": precomputed.document_text,
492
+ "chat_history": [],
493
+ "pending_deeper_question": None,
494
+ "source_url": url_value,
495
+ }
496
+ )
497
+ yield (
498
+ session_state,
499
+ "Loaded precomputed analysis for this example bill.",
500
+ _format_analysis(precomputed.analysis),
501
+ [],
502
+ _deeper_answer_updates(False),
503
+ "",
504
+ )
505
+ return
506
+
507
+ yield (
508
+ session_state,
509
+ "Loading and parsing document...",
510
+ _analysis_stage_message("Loading and parsing document..."),
511
+ [],
512
+ _deeper_answer_updates(False),
513
+ "",
514
+ )
515
+
516
+ try:
517
+ document_text = _ingest_sources(uploaded_file, url_value)
518
+ except ingest.IngestionError as exc:
519
+ message = str(exc)
520
+ _show_warning(message)
521
+ yield (
522
+ session_state,
523
+ message,
524
+ ANALYSIS_PLACEHOLDER,
525
+ [],
526
+ _deeper_answer_updates(False),
527
+ "",
528
+ )
529
+ return
530
+ except Exception: # noqa: BLE001
531
+ message = "We couldn't load that document. Check the link or upload the file directly and try again."
532
+ _show_error(message)
533
+ yield (
534
+ session_state,
535
+ message,
536
+ ANALYSIS_PLACEHOLDER,
537
+ [],
538
+ _deeper_answer_updates(False),
539
+ "",
540
+ )
541
+ return
542
+
543
+ yield (
544
+ session_state,
545
+ "Preparing chunks and provider client...",
546
+ _analysis_stage_message("Preparing chunks and provider client..."),
547
+ [],
548
+ _deeper_answer_updates(False),
549
+ "",
550
+ )
551
+
552
+ try:
553
+ provider_client = instantiate_client(provider, api_key or "")
554
+ except Exception: # noqa: BLE001
555
+ message = "We couldn't connect to the selected model provider. Check your API key and settings, then try again."
556
+ _show_error(message)
557
+ yield (
558
+ session_state,
559
+ message,
560
+ ANALYSIS_PLACEHOLDER,
561
+ [],
562
+ _deeper_answer_updates(False),
563
+ "",
564
+ )
565
+ return
566
+
567
+ yield (
568
+ session_state,
569
+ "Building retrieval index...",
570
+ _analysis_stage_message("Building retrieval index..."),
571
+ [],
572
+ _deeper_answer_updates(False),
573
+ "",
574
+ )
575
+
576
+ try:
577
+ _, chunks, vector_store = prepare_document_artifacts(document_text)
578
+ except Exception: # noqa: BLE001
579
+ message = "We couldn't prepare the document for analysis. Please try again."
580
+ _show_error(message)
581
+ yield (
582
+ session_state,
583
+ message,
584
+ ANALYSIS_PLACEHOLDER,
585
+ [],
586
+ _deeper_answer_updates(False),
587
+ "",
588
+ )
589
+ return
590
+
591
+ try:
592
+ analysis = AnalysisResult()
593
+ for stage_message, partial in generate_analysis_progress(provider_client, document_text):
594
+ analysis = partial
595
+ yield (
596
+ session_state,
597
+ stage_message,
598
+ _format_analysis(partial),
599
+ [],
600
+ _deeper_answer_updates(False),
601
+ "",
602
+ )
603
+ except Exception: # noqa: BLE001
604
+ message = "We couldn't generate the bill analysis right now. Please try again."
605
+ _show_error(message)
606
+ yield (
607
+ session_state,
608
+ message,
609
+ ANALYSIS_PLACEHOLDER,
610
+ [],
611
+ _deeper_answer_updates(False),
612
+ "",
613
+ )
614
+ return
615
+
616
+ build_cached_document_artifacts(
617
+ document_text=document_text,
618
+ chunks=chunks,
619
+ analysis=analysis,
620
+ vector_store=vector_store,
621
+ source_url=url_value,
622
+ )
623
+ record.clear()
624
+ record.update(
625
+ {
626
+ "api_config": {"provider": provider, "api_key": api_key},
627
+ "analysis": analysis.model_dump(),
628
+ "vector_store": vector_store,
629
+ "doc_text": document_text,
630
+ "chat_history": [],
631
+ "pending_deeper_question": None,
632
+ "source_url": url_value,
633
+ }
634
+ )
635
+ analysis_output = _format_analysis(analysis)
636
+ yield session_state, "", analysis_output, [], _deeper_answer_updates(False), ""
637
+
638
+
639
+ def ask_question(
640
+ question: str | None,
641
+ session_state: dict[str, Any] | None,
642
+ chat_history: list[dict[str, str]] | None,
643
+ ):
644
+ session_state = session_state or _empty_session()
645
+ record = _session_record(session_state)
646
+ chat_history = chat_history or []
647
+ if (not record.get("doc_text") and not record.get("vector_store")) or not record.get("api_config"):
648
+ yield chat_history, session_state, "Analyze a document before asking questions.", _deeper_answer_updates(False), ""
649
+ return
650
+ if not question or not question.strip():
651
+ yield chat_history, session_state, "", _deeper_answer_updates(False), ""
652
+ return
653
+
654
+ question_text = question.strip()
655
+ yield chat_history, session_state, "Searching analysis for an answer...", _deeper_answer_updates(False), ""
656
+
657
+ try:
658
+ provider_client = instantiate_client(
659
+ record["api_config"]["provider"],
660
+ record["api_config"]["api_key"],
661
+ )
662
+ yield chat_history, session_state, "Searching analysis for an answer...", _deeper_answer_updates(False), ""
663
+ answer = answer_query(
664
+ provider_client,
665
+ AnalysisResult.model_validate(record["analysis"]) if record.get("analysis") else None,
666
+ record.get("vector_store"),
667
+ question_text,
668
+ doc_text=record.get("doc_text"),
669
+ )
670
+ except Exception: # noqa: BLE001
671
+ message = "We couldn't answer that question right now. Please try again."
672
+ _show_error(message)
673
+ yield chat_history, session_state, message, _deeper_answer_updates(False), ""
674
+ return
675
+
676
+ citations = [citation.model_dump() for citation in answer.citations]
677
+ chat_history = chat_history + [
678
+ {"role": "user", "content": question_text},
679
+ {"role": "assistant", "content": _format_chat_entry(answer.answer, citations, provenance=answer.provenance)},
680
+ ]
681
+ record["chat_history"] = chat_history
682
+ record["pending_deeper_question"] = question_text if answer.deeper_answer_available else None
683
+ deeper_hint = answer.consent_prompt if answer.deeper_answer_available else ""
684
+ yield chat_history, session_state, "", _deeper_answer_updates(answer.deeper_answer_available), deeper_hint
685
+
686
+
687
+ def run_deeper_answer(
688
+ session_state: dict[str, Any] | None,
689
+ chat_history: list[dict[str, str]] | None,
690
+ ):
691
+ session_state = session_state or _empty_session()
692
+ record = _session_record(session_state)
693
+ chat_history = chat_history or []
694
+ question_text = record.get("pending_deeper_question")
695
+ if not question_text:
696
+ yield chat_history, session_state, "No pending question for deeper analysis.", _deeper_answer_updates(False), ""
697
+ return
698
+
699
+ yield chat_history, session_state, "Reading the full document for a deeper answer...", _deeper_answer_updates(False), ""
700
+
701
+ try:
702
+ provider_client = instantiate_client(
703
+ record["api_config"]["provider"],
704
+ record["api_config"]["api_key"],
705
+ )
706
+ answer = answer_query_from_full_document(
707
+ provider_client,
708
+ record.get("vector_store"),
709
+ question_text,
710
+ doc_text=record.get("doc_text"),
711
+ )
712
+ except Exception: # noqa: BLE001
713
+ message = "We couldn't generate the deeper full-document answer right now. Please try again."
714
+ _show_error(message)
715
+ yield chat_history, session_state, message, _deeper_answer_updates(True), "A deeper full-document answer is still available."
716
+ return
717
+
718
+ citations = [citation.model_dump() for citation in answer.citations]
719
+ chat_history = chat_history + [
720
+ {"role": "assistant", "content": _format_chat_entry(answer.answer, citations, provenance=answer.provenance)},
721
+ ]
722
+ record["chat_history"] = chat_history
723
+ record["pending_deeper_question"] = None
724
+ yield chat_history, session_state, "", _deeper_answer_updates(False), ""
725
+
726
+
727
+ def reset_session(session_state: dict[str, Any] | None):
728
+ if session_state and session_state.get("session_id"):
729
+ _GRADIO_SESSION_CACHE.pop(session_state["session_id"], None)
730
+ empty = _empty_session()
731
+ return empty, None, None, "", _render_document_link(""), ANALYSIS_PLACEHOLDER, [], "", "", _deeper_answer_updates(False), ""
732
+
733
+
734
+ def build_app() -> gr.Blocks:
735
+ with gr.Blocks(title=APP_TITLE, css=APP_CSS, theme=APP_THEME, elem_id="app-shell", head=APP_HEAD) as demo:
736
+ session_state = gr.State(_empty_session())
737
+
738
+ gr.Markdown(f"# {APP_TITLE}")
739
+ gr.Markdown(APP_DESCRIPTION)
740
+ with gr.Row(elem_id="topbar-row"):
741
+ gr.HTML(
742
+ """
743
+ <div id="sidebar-toggle-shell">
744
+ <div
745
+ id="sidebar-toggle-icon"
746
+ role="button"
747
+ aria-label="Toggle sidebar"
748
+ tabindex="0"
749
+ onclick="window.__toggleSidebar && window.__toggleSidebar()"
750
+ onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); window.__toggleSidebar && window.__toggleSidebar(); }"
751
+ >
752
+ <svg viewBox="0 0 24 24" fill="none" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
753
+ <rect x="3" y="4" width="18" height="16" rx="3"></rect>
754
+ <path d="M9 4v16"></path>
755
+ <path d="M6.5 9h.01"></path>
756
+ <path d="M6.5 12h.01"></path>
757
+ <path d="M6.5 15h.01"></path>
758
+ </svg>
759
+ </div>
760
+ </div>
761
+ """
762
+ )
763
+
764
+ with gr.Row(elem_id="layout-shell"):
765
+ with gr.Column(scale=1, elem_id="sidebar-panel"):
766
+ gr.Markdown("## Document Source")
767
+ gr.Markdown("Upload PDF, DOCX, TXT, or MD")
768
+ uploaded_file = gr.File(label="Upload PDF, DOCX, TXT, or MD", type="filepath", show_label=False)
769
+ gr.HTML(
770
+ """
771
+ <div id="example-bills-heading">
772
+ <p>Example bills</p>
773
+ <span
774
+ id="example-bills-info"
775
+ tabindex="0"
776
+ aria-label="Example bills information"
777
+ data-tooltip="The Ghana Innovation and Start-Up Bill, 2025 was omitted because a verifiable source could not be found."
778
+ >
779
+ i
780
+ </span>
781
+ </div>
782
+ """
783
+ )
784
+ example_selector = gr.Dropdown(
785
+ label="Example bills",
786
+ choices=EXAMPLE_BILL_LABELS,
787
+ value=None,
788
+ show_label=False,
789
+ )
790
+ gr.Markdown("Document URL")
791
+ url_value = gr.Textbox(label="Document URL", placeholder="https://example.com/bill", show_label=False)
792
+ analyze_button = gr.Button("Run analysis", variant="primary")
793
+ view_document_link = gr.HTML(_render_document_link(""), elem_id="view-document-shell")
794
+ reset_button = gr.Button("Reset session")
795
+ status_output = gr.Markdown(elem_id="status-output")
796
+ use_advanced = gr.State(False)
797
+ provider_label = gr.State(PROVIDER_LABEL_BY_NAME[DEFAULT_PROVIDER])
798
+ qwen_key = gr.State(None)
799
+ custom_key = gr.State(None)
800
+
801
+ # Proposed future expansion: restore the sidebar Model Settings
802
+ # section after the hackathon build is no longer pinned to Qwen.
803
+ # with gr.Accordion("Model Settings", open=False):
804
+ # provider_help = gr.Markdown(PROVIDER_DETAILS[DEFAULT_PROVIDER])
805
+ # gr.Markdown("Hugging Face Token")
806
+ # qwen_key = gr.Textbox(
807
+ # label="Hugging Face Token",
808
+ # type="password",
809
+ # placeholder="Leave blank to use HF_TOKEN",
810
+ # visible=True,
811
+ # show_label=False,
812
+ # )
813
+ # use_advanced = gr.Checkbox(label="Bring your own provider", value=False)
814
+ # gr.Markdown("Model Provider")
815
+ # provider_label = gr.Dropdown(
816
+ # label="Model Provider",
817
+ # choices=PROVIDER_LABELS,
818
+ # value=PROVIDER_LABEL_BY_NAME[DEFAULT_PROVIDER],
819
+ # visible=False,
820
+ # show_label=False,
821
+ # )
822
+ # gr.Markdown("API Key")
823
+ # custom_key = gr.Textbox(
824
+ # label="API Key",
825
+ # type="password",
826
+ # placeholder="Leave blank to use provider env var",
827
+ # visible=False,
828
+ # show_label=False,
829
+ # )
830
+
831
+ with gr.Column(scale=2, elem_id="main-panel"):
832
+ with gr.Group(elem_id="analysis-panel"):
833
+ gr.Markdown("# Summary Highlights")
834
+ analysis_output = gr.Markdown(ANALYSIS_PLACEHOLDER, elem_id="analysis-output")
835
+
836
+ with gr.Group(elem_id="chat-panel"):
837
+ gr.Markdown("## Ask Further Questions")
838
+ chatbot = gr.Chatbot(type="messages", show_label=False)
839
+ with gr.Row():
840
+ with gr.Column(scale=6, min_width=0):
841
+ gr.Markdown("Question")
842
+ question_input = gr.Textbox(
843
+ label="Question",
844
+ placeholder="What would you like to know?",
845
+ show_label=False,
846
+ )
847
+ ask_button = gr.Button("Ask", scale=1, variant="primary")
848
+ chat_status = gr.Markdown(elem_id="chat-status")
849
+ deeper_answer_button = gr.Button("Run deeper full-document answer", visible=False)
850
+ deeper_answer_hint = gr.Markdown("")
851
+
852
+ # Proposed future expansion: restore bring-your-own provider event wiring
853
+ # when alternate provider controls are re-enabled.
854
+ # use_advanced.change(
855
+ # _toggle_provider_mode,
856
+ # inputs=[use_advanced],
857
+ # outputs=[qwen_key, provider_label, custom_key, provider_help],
858
+ # )
859
+ # provider_label.change(
860
+ # _provider_help_text,
861
+ # inputs=[provider_label, use_advanced],
862
+ # outputs=[provider_help],
863
+ # )
864
+ example_selector.change(
865
+ lambda example_label: (
866
+ _set_example_url(example_label),
867
+ _render_document_link(_set_example_url(example_label)),
868
+ ),
869
+ inputs=[example_selector],
870
+ outputs=[url_value, view_document_link],
871
+ )
872
+ url_value.input(_render_document_link, inputs=[url_value], outputs=[view_document_link])
873
+ url_value.change(_render_document_link, inputs=[url_value], outputs=[view_document_link])
874
+ analyze_button.click(
875
+ analyze_document,
876
+ inputs=[uploaded_file, url_value, use_advanced, provider_label, qwen_key, custom_key, session_state],
877
+ outputs=[
878
+ session_state,
879
+ status_output,
880
+ analysis_output,
881
+ chatbot,
882
+ deeper_answer_button,
883
+ deeper_answer_hint,
884
+ ],
885
+ )
886
+ ask_button.click(
887
+ ask_question,
888
+ inputs=[question_input, session_state, chatbot],
889
+ outputs=[chatbot, session_state, chat_status, deeper_answer_button, deeper_answer_hint],
890
+ ).then(lambda: "", outputs=[question_input])
891
+ question_input.submit(
892
+ ask_question,
893
+ inputs=[question_input, session_state, chatbot],
894
+ outputs=[chatbot, session_state, chat_status, deeper_answer_button, deeper_answer_hint],
895
+ ).then(lambda: "", outputs=[question_input])
896
+ deeper_answer_button.click(
897
+ run_deeper_answer,
898
+ inputs=[session_state, chatbot],
899
+ outputs=[chatbot, session_state, chat_status, deeper_answer_button, deeper_answer_hint],
900
+ )
901
+ reset_button.click(
902
+ reset_session,
903
+ inputs=[session_state],
904
+ outputs=[
905
+ session_state,
906
+ uploaded_file,
907
+ example_selector,
908
+ url_value,
909
+ view_document_link,
910
+ analysis_output,
911
+ chatbot,
912
+ status_output,
913
+ chat_status,
914
+ deeper_answer_button,
915
+ deeper_answer_hint,
916
+ ],
917
+ )
918
+
919
+ demo.queue()
920
+ return demo
921
+
922
+
923
+ if gr.NO_RELOAD:
924
+ warm_embedding_stack()
925
+
926
+
927
+ # Expose the default `demo` symbol so Hugging Face Spaces can launch app.py.
928
+ demo = build_app()
929
+ app = demo
930
+
931
+
932
+ if __name__ == "__main__":
933
+ app.launch()
assets/example_bills/cybersecurity-amendment-bill-2025/analysis.json ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "executive_summary": "The Cybersecurity (Amendment) Bill, 2025 expands the powers and responsibilities of Ghana's Cyber Security Authority (CSA) to investigate and prosecute cybercrime, regulate emerging technologies, and strengthen cybersecurity governance. Key changes include granting the CSA prosecutorial authority under the Attorney-General's oversight, establishing standards for AI, IoT, and blockchain, and enhancing enforcement mechanisms. The bill also introduces stricter penalties for non-compliance, mandates registration of critical infrastructure, and creates a cybersecurity fund. These amendments aim to bolster national digital security, protect vulnerable groups, and align Ghana with global cybersecurity practices.",
3
+ "bill_summary": [
4
+ "Amends Section 3 to expand the CSA's objects to include investigating/prosecuting cybercrime, regulating emerging technologies, and protecting vulnerable populations.",
5
+ "Inserts Section 4A to grant the CSA authority to investigate/prosecute cybercrime under the Attorney-General's direction and set security standards for AI, IoT, and blockchain.",
6
+ "Amends Section 13 to include representatives from the Data Protection Commission, National Intelligence Bureau, and National Signals Bureau in the Joint Cybersecurity Committee.",
7
+ "Amends Section 35 to clarify criteria for designating critical information infrastructure (CII) and mandates sector-specific regulations.",
8
+ "Amends Section 36 to require CII owners to register with the CSA, nominate a point of contact, and pay annual fees, with penalties for non-compliance.",
9
+ "Inserts Section 59A to empower the CSA with criminal and administrative enforcement tools, including fines, injunctions, and asset confiscation.",
10
+ "Inserts Section 59B to grant the CSA jurisdiction to prosecute cybercrimes under the Electronic Transactions Act, 2008 (Act 772) and recover proceeds.",
11
+ "Amends Section 31 to establish a cybersecurity fund financed by fines, taxes, and fees, with 30% of certification revenue allocated to the fund.",
12
+ "Inserts Section 57A-57C to create a cyber hygiene certification scheme for professionals and service providers, with standardized fees and audits.",
13
+ "Amends Section 49 to require licenses/accreditations for cybersecurity service providers and practitioners, with penalties for unlicensed activity.",
14
+ "Inserts Section 59C to authorize the CSA to request information from CII owners and third parties during investigations, with court-ordered disclosure if necessary.",
15
+ "Amends Section 47 to mandate Sectoral Computer Emergency Response Teams to report cybersecurity incidents to the National CERT within 24 hours.",
16
+ "Amends Section 20A to ensure CSA staff receive terms/conditions comparable to security/intelligence agencies and legal officers of the Attorney-General.",
17
+ "Inserts Section 20B to grant CSA officers police-like powers (arrest, search, seizure) under specified laws.",
18
+ "Amends Section 57 to require accreditation for cybersecurity professionals/practitioners, with penalties for unaccredited practice."
19
+ ],
20
+ "implementation": [
21
+ {
22
+ "stakeholder": "Critical Information Infrastructure (CII) Owners",
23
+ "obligation": "Register designated CII with the Cyber Security Authority (CSA) and submit verified details, nominate a point of contact, and notify the CSA of ownership changes within seven days.",
24
+ "implementation_burden": "Administrative and compliance costs for registration, documentation, and ongoing reporting requirements.",
25
+ "risk_or_note": "Non-compliance risks administrative penalties (Second Schedule) and potential legal exposure for unregistered infrastructure."
26
+ },
27
+ {
28
+ "stakeholder": "Cybersecurity Service Providers",
29
+ "obligation": "Obtain a license from the CSA to provide cybersecurity services for profit or accreditation for not-for-profit services.",
30
+ "implementation_burden": "Costs and time associated with applying for licenses/accreditations and meeting CSA standards.",
31
+ "risk_or_note": "Unlicensed or unaccredited providers face penalties (up to 100,000 penalty units) and potential criminal liability."
32
+ },
33
+ {
34
+ "stakeholder": "Cybersecurity Professionals/Practitioners",
35
+ "obligation": "Obtain CSA accreditation to practice, and adhere to cyber hygiene certification schemes.",
36
+ "implementation_burden": "Training, certification costs, and compliance with periodic audits.",
37
+ "risk_or_note": "Unaccredited individuals face penalties (250\u201320,000 penalty units) and criminal liability for non-compliance."
38
+ },
39
+ {
40
+ "stakeholder": "Cyber Security Authority (CSA)",
41
+ "obligation": "Establish mechanisms for investigating/prosecuting cybercrime, certifying emerging technologies, and enforcing compliance via audits, penalties, and legal actions.",
42
+ "implementation_burden": "Resource allocation for expanded investigative, prosecutorial, and regulatory functions.",
43
+ "risk_or_note": "Potential operational strain from increased responsibilities and reliance on inter-agency coordination."
44
+ },
45
+ {
46
+ "stakeholder": "Law Enforcement and Judicial Bodies",
47
+ "obligation": "Process production orders for computer data, confirm property freezes, and adjudicate cybercrime cases under CSA jurisdiction.",
48
+ "implementation_burden": "Training on new procedures and handling of technical evidence.",
49
+ "risk_or_note": "Increased caseloads and potential delays in resolving cybercrime cases."
50
+ },
51
+ {
52
+ "stakeholder": "Private Sector Entities (Non-CII)",
53
+ "obligation": "Comply with CSA directives on cybersecurity standards and cooperate in investigations (e.g., furnishing information).",
54
+ "implementation_burden": "Adapting internal policies and systems to meet CSA requirements.",
55
+ "risk_or_note": "Non-compliance risks administrative penalties or legal action under Section 59A."
56
+ },
57
+ {
58
+ "stakeholder": "Non-Profit Cybersecurity Institutions",
59
+ "obligation": "Seek CSA accreditation to operate legally.",
60
+ "implementation_burden": "Meeting CSA-defined criteria for accreditation.",
61
+ "risk_or_note": "Unaccredited institutions face penalties and operational restrictions."
62
+ }
63
+ ],
64
+ "critique": [
65
+ {
66
+ "issue": "Expanded investigative and prosecutorial powers of the Cyber Security Authority (CSA) without clear safeguards",
67
+ "why_it_matters": "Section 59B grants the CSA authority to investigate and prosecute cybercrime independently, including freezing property and confiscating proceeds. This risks overreach and potential misuse of power without explicit judicial oversight or procedural safeguards to protect individual rights.",
68
+ "recommendation": "Introduce mandatory judicial review for property freezes and asset confiscation orders, and establish an independent oversight body to monitor CSA investigations."
69
+ },
70
+ {
71
+ "issue": "Ambiguity in 'critical information infrastructure' designation criteria",
72
+ "why_it_matters": "Section 35 allows the Minister to designate systems as critical based on vague factors like 'national security' or 'economic well-being.' This could lead to arbitrary designations, imposing compliance burdens on businesses without clear justification.",
73
+ "recommendation": "Define specific, measurable criteria for designation and require public consultation or parliamentary approval for such designations."
74
+ },
75
+ {
76
+ "issue": "Mandatory accreditation and licensing for cybersecurity professionals and services",
77
+ "why_it_matters": "Sections 57 and 49 require accreditation/licenses for cybersecurity work, which may stifle innovation and exclude small providers or startups unable to meet bureaucratic hurdles, reducing competition and access to services.",
78
+ "recommendation": "Create tiered accreditation levels with exemptions for low-risk services and provide financial or technical support for SMEs to meet requirements."
79
+ },
80
+ {
81
+ "issue": "Broad administrative penalties without proportionality",
82
+ "why_it_matters": "Penalties in Sections 59A and 59C range from 250 to 100,000 penalty units without clear proportionality to the severity of violations. This risks punitive overreach, especially for minor infractions.",
83
+ "recommendation": "Establish a graduated penalty system tied to specific offense categories and provide a right to appeal administrative penalties."
84
+ },
85
+ {
86
+ "issue": "Mandatory cyber hygiene certification for emerging technologies",
87
+ "why_it_matters": "Section 57A requires certification for AI, blockchain, and other technologies, which could delay innovation and create compliance costs for developers. The CSA may lack expertise to evaluate rapidly evolving fields.",
88
+ "recommendation": "Adopt a risk-based approach to certification, with exemptions for low-risk or experimental technologies, and establish a dedicated technical advisory panel."
89
+ },
90
+ {
91
+ "issue": "Increased funding obligations for businesses and public services",
92
+ "why_it_matters": "Section 31 mandates 12% of communications taxes and 9% of corporate taxes to fund cybersecurity, potentially burdening SMEs and public institutions. The CSA's fee-setting power (Section 57C) could further strain budgets.",
93
+ "recommendation": "Cap mandatory contributions and require cost-benefit analyses for new fees to ensure affordability for small businesses and public services."
94
+ },
95
+ {
96
+ "issue": "Lack of clarity on cross-sector coordination in the Joint Cybersecurity Committee (JCC)",
97
+ "why_it_matters": "Section 14 mandates JCC collaboration but does not define decision-making processes or conflict resolution mechanisms, risking inefficiency and duplication of efforts.",
98
+ "recommendation": "Establish clear protocols for JCC operations, including timelines for consensus-building and a chairperson with binding authority in deadlocks."
99
+ },
100
+ {
101
+ "issue": "Potential privacy risks from production orders for computer data",
102
+ "why_it_matters": "Section 59D allows ex-parte court applications for data production orders, which could enable surveillance without prior notice or defense. The 'reasonable grounds' standard is subjectively interpreted.",
103
+ "recommendation": "Require dual judicial approval for production orders and mandate post-issuance review to assess proportionality and necessity."
104
+ }
105
+ ],
106
+ "swot": {
107
+ "strengths": [
108
+ "Expands the Cyber Security Authority's (CSA) powers to investigate and prosecute cybercrime independently, enhancing response speed and reducing reliance on external agencies.",
109
+ "Introduces mandatory accreditation and certification for cybersecurity professionals, service providers, and emerging technologies, raising industry standards and trust.",
110
+ "Establishes a dedicated cybersecurity fund with multiple revenue streams (fines, taxes, fees), ensuring sustainable financing for operations and infrastructure.",
111
+ "Mandates cross-sector collaboration through the Joint Cybersecurity Committee (JCC), improving coordination between public and private entities for threat mitigation.",
112
+ "Enhances protection for vulnerable groups (women, elderly, persons with disabilities) through explicit provisions for online safety and digital rights promotion."
113
+ ],
114
+ "weaknesses": [
115
+ "Broad investigative and enforcement powers for the CSA without explicit oversight mechanisms risk potential overreach or misuse, particularly in data access and asset freezes.",
116
+ "Mandatory registration and certification requirements for critical infrastructure and cybersecurity professionals could create compliance burdens for small and medium enterprises (SMEs).",
117
+ "High administrative penalties (e.g., 50,000\u2013100,000 penalty units for unlicensed services) may disproportionately impact startups and informal operators.",
118
+ "The CSA's expanded role in prosecuting cybercrime under the Electronic Transactions Act, 2008 (Act 772) may lead to jurisdictional conflicts with existing law enforcement agencies.",
119
+ "Lack of detailed timelines or performance metrics for the CSA's new functions (e.g., certification of AI/quantum tech) risks delays or inconsistent implementation."
120
+ ],
121
+ "opportunities": [
122
+ "Standardization of cybersecurity practices for emerging technologies (AI, IoT, blockchain) positions Ghana as a regional leader in tech governance and innovation.",
123
+ "The cyber hygiene certification scheme creates a formal pathway for local professionals to gain international recognition, boosting employment and exports.",
124
+ "Mandatory incident reporting (24-hour window for Sectoral CERTs) improves threat intelligence sharing, enabling proactive defense against cross-sector attacks.",
125
+ "Integration of cybersecurity into national critical infrastructure (e.g., banking, utilities) strengthens economic resilience against disruptive cyber incidents.",
126
+ "The 30% revenue-sharing model for the cybersecurity fund incentivizes certified professionals and institutions to maintain compliance and quality."
127
+ ],
128
+ "threats": [
129
+ "Overly prescriptive standards for emerging technologies (e.g., AI, quantum computing) could stifle innovation by imposing rigid compliance requirements on nascent industries.",
130
+ "The CSA's authority to freeze assets during investigations may deter foreign investment if perceived as a risk to property rights without judicial oversight.",
131
+ "Mandatory accreditation for non-profit cybersecurity institutions could exclude grassroots organizations, limiting public access to affordable security services.",
132
+ "High compliance costs for critical infrastructure owners (registration fees, audits) may force smaller operators out of the market, reducing competition.",
133
+ "The bill's focus on criminal enforcement (e.g., imprisonment for non-compliance) risks prioritizing punitive measures over education and capacity-building for vulnerable groups."
134
+ ]
135
+ }
136
+ }
assets/example_bills/cybersecurity-amendment-bill-2025/chunks.json ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ "CYBERSECURITY (AMENDMENT) BILL, 2025\n1 ARRANGEMENT OF SECTIONS\nSection\n1. Section 3 of Act 1038 Amended\n2. Section 4A inserted\n3. Section 5 of Act 1038 amended\n4. Section 13 of Act 1038 amended\n5. Section 14 of Act 1038 amended\n6. Section 15A inserted\n7. Section 20A inserted\n8. Section 20B inserted\n9. Section 31 of Act 1038 amended\n10. Section 35 of Act 1038 amended\n11. Section 36 of Act 1038 amended\n12. Section 37 of Act 1038 amended\n13. Section 40 of Act 1038 amended\n14. Section 47 of Act 1038 amended\n15. Section 49 of Act 1038 amended\n16. Section 57 of Act 1038 amended\n17. Section 57A inserted\n18. Section 57B inserted\n19. Section 57C inserted\n20. Section 58A inserted\n21. Section 58B inserted\n22. Section 59 of Act 1038 amended\n23. Section 59A inserted\n24. Section 59B inserted\n25. Section 59C inserted\n26. Section 59D inserted\n27. Section 59E inserted\n28. Section 59F inserted\n29. Section 59G inserted\n30. Section 59H inserted\n31. Section 59I inserted\n32. Section 59J inserted\n33. Section 59K inserted\n34. Section 67 of Act 1038 amended\n35. Section 67A inserted\n36. Section 67B inserted\n37. Section 68 of Act 1038 amended\n38. Section 83 of Act 1038 amended\n39. Section 90 of Act 1038 amended\n40. Section 91 of Act 1038 amended\n41. Section 92 of Act 1038 amended\n42. Section 94 of Act 1038 amended\n43. Section 94A inserted\nCYBERSECURITY (AMENDMENT) BILL, 2025",
3
+ "39. Section 90 of Act 1038 amended\n40. Section 91 of Act 1038 amended\n41. Section 92 of Act 1038 amended\n42. Section 94 of Act 1038 amended\n43. Section 94A inserted\nCYBERSECURITY (AMENDMENT) BILL, 2025\n2 44. Section 94B inserted\n45. Section 97 of Act 1038 amended\n46. Section 98 of Act 1058 amended\n47. Section 99 of Act 1038 amended\n48. First Schedule to Act 1038 amended\n49. Second schedule to Act 1038 amended\n50. Third schedule to Act 1038 amended\nCYBERSECURITY (AMENDMENT) BILL, 2025\n3 A\nBILL\nENTITLED\nCYBERSECURITY (AMENDMENT) BILL, 2025\nAN ACT to amend the Cybersecurity Act, 2020 (Act 1038) to confer powers on the Cyber\nSecurity Authority to investigate and prosecute cybercrime on the authority of the\nAttorney -General and recover proceeds of cyber crime ; to revise the object and\nfunctions of the Cyber Security Authority; to revise the governance and\nadministration of the Cyber Security Authority ; to revise the enforcement powers of\nthe Cyber Security Authority and to provide for related matters.\nSection 3 of Act 10 38 Amended\n1. The Cybersecurity Act, 2020 (Act 1038) referred to in the Act as the \u201cprincipal\nenactment\u201d is amended by substitution for section 3, of\n\u201cObjects of the Authority\n3. The objects of the Authority are to:\n(a) regulate cybersecurity activities in the country;\n(b) prevent, manage and respond to cybersecurity threats and\ncybersecurity incidents ;\n(c) regulate owners of critical information infrastructure in respect of",
4
+ "3. The objects of the Authority are to:\n(a) regulate cybersecurity activities in the country;\n(b) prevent, manage and respond to cybersecurity threats and\ncybersecurity incidents ;\n(c) regulate owners of critical information infrastructure in respect of\ncybersecurity activities, cybersecurity service providers , cybersecurity\nprofessionals and practitioners, and cybersecurity establishments in\nthe country;\n(d) promote the development of cybersecurity in the country to ensure a\nsecured and resilient digital ecosystem;\n(e) prevent and detect cybercrime;\n(f) to facilitate the confiscation of proceeds of cybercrime;\n(g) establish a platform for cross -sector engagement on matters of\ncybersecurity for effective co -ordination and co -operation between\nkey public institutions and the private sector;\n(h) create awareness of cybersecurity matters; an d\n(i) collaborate with States, inter -governmental bodies, international\norganisations and international agencies to promote the cybersecurity\nof the country .\u201d.\nSection 4A inserted\n2. The principal enactment is amended by the insertion after section 4, of\n\u201cFurther Functions of the Authority\nCYBERSECURITY (AMENDMENT) BILL, 2025\n4 4A. The Authority , shall in addition to the functions in section 4, carry out the\nfollowing functions:\n(a) subject to article 88 of the Constitution, to investigate and on the\nauthority of the Attorney -General , prosecute cybercrime under this\nAct;\n(b) establish standards for certifying the security of innovative products ,\nArtificial Intelligence, cloud technology, quantum computing, big",
5
+ "following functions:\n(a) subject to article 88 of the Constitution, to investigate and on the\nauthority of the Attorney -General , prosecute cybercrime under this\nAct;\n(b) establish standards for certifying the security of innovative products ,\nArtificial Intelligence, cloud technology, quantum computing, big\ndata, Internet of Things (IoT) , blockchain -based technology and any\nother emerging technologies ;\n(c) certify the security of innovative products Artificial Intelligence,\ncloud technology, quantum computing, big data, Internet of Things\n(IoT) , blockchain -based technology and any other emerging\ntechnologies , in accordance with the standards established pursuant\nto paragraph ( b);\n(d) accredit the cybersecurity establishments of critical information\ninfrastructure owners, cybersecurity service providers, cybersecurity\npractitioners and professionals and other relevant person s or\ninstitutions ;\n(e) accredit non -profit cybersecurity institutions and cybersecurity\nprofessional bodies ;\n(f) promote the online protection of women, elderly , persons with\ndisabilities and underserved populations ;\n(g) collaborate with relevant institutions to develop mechanisms\nincluding technical security solutions and guidelines on the usage of\nsmart technology and other emerging technologies ; and\n(h) promote the protection of digital right s as it relates to cyber security. \u201d.\nSection 5 of Act 10 38 amended\n3. The principal enactment is amended in section 5 by the addition after subparagraph\n(iv) of paragraph (a) of subsection (1), of\n\u201c(v) Foreign Affairs and Regional Integration ; and\n(vi) Gender, Children and Social Protection .\u201d.\nSection 13 of Act 10 38 amended\n4. The principal enactment is amended in section 13 by:",
6
+ "(iv) of paragraph (a) of subsection (1), of\n\u201c(v) Foreign Affairs and Regional Integration ; and\n(vi) Gender, Children and Social Protection .\u201d.\nSection 13 of Act 10 38 amended\n4. The principal enactment is amended in section 13 by:\n(a) the substitution for paragraph (d) of subsection (2), of\n\u201c(d) the Executive Director of the Data Protection Commission or any\nsuccessor official or entity by whatever name designated, or a\nrepresentative of the Executive Director with the requisite\nknowledge and skills in cybercrime and cybersecurity matters,\nnominated by the Executive Director ;\u201d;\nCYBERSECURITY (AMENDMENT) BILL, 2025\n5 (b) the substitution for paragraph (g) and (k) respectively of subsection (2), of\n\u201c(g) the Director of the National Intelligence Bureau or any successor\nofficial or entity by whatever name designated, or a representative of\nthe Director with the requisite knowledge and skills in cybercrime ,\nand cybersecurity matters, nominated by the Director;\n(k) the Director General of National Signals Bureau or any successor\nofficial or entity by whatever name designated, or a representative of\nthe Director with the requisite knowledge and skills in cybercrime and\ncybersecurity matters, nominated by the Director; \u201d;\n(c) the addition after paragraph (r) of subsection (2), o f\n\u201c(s) Any other person the Minister may nominate, on the advice of the\nAuthority. \u201d.\n(d) the addition after subsection (13), of\n\u201c(14) A member of the JCC who is absent from three consecutive meetings",
7
+ "\u201c(s) Any other person the Minister may nominate, on the advice of the\nAuthority. \u201d.\n(d) the addition after subsection (13), of\n\u201c(14) A member of the JCC who is absent from three consecutive meetings\nwithout sufficient cause ceases to be a member of the JCC .\n(15) Where there is a vacancy, the Minister shall notify the President of\nthe vacancy , and the President shall appoint a person to fill the v acancy for\nthe unexpired term. \u201d.\nSection 1 4 of Act 10 38 amended\n5. The principal enactment is amended in section 14 by the substitution for subsection\n(1), of\n\u201c14 (1). The Joint Cybersecurity Committee shall collaborate with the Authority and\nthe sectors or institutions represented on the Committee for the implementation of\nrelevant cybersecurity measures including but not limited to the following ways:\n(a) identification of cybersecurity risks that may affect critical information\ninfrastructure or the overall cybersecurity of the country ;\n(b) development of cybersecurity policies and guidelines based on identified\nrisks, best practices and international standards to mitigate cybersecurity\nrisks;\n(c) coordination of cybersecurity activities between sectors and entities to\nensure that all cybersecurity efforts are aligned to prevent overlap and gaps;\n(d) facilitate the sharing of information between sectors and entities in a secure\nmanner and in compliance with relevant data protection laws ;\n(e) collaboration with sectors or institutions to tackle cybercrime including the\nprovision of a platform for incidence sharing and incidence response\nmechanisms to facilitate ;\nCYBERSECURITY (AMENDMENT) BILL, 2025",
8
+ "manner and in compliance with relevant data protection laws ;\n(e) collaboration with sectors or institutions to tackle cybercrime including the\nprovision of a platform for incidence sharing and incidence response\nmechanisms to facilitate ;\nCYBERSECURITY (AMENDMENT) BILL, 2025\n6 (f) evaluation of the effectiveness of implemented cybersecurity measures based\non established metrics with a view to continuously improve the cybersecurity\nmeasures ; and\n(g) collaboration with the Authority to educate the public on matters related to\ncybercrime and cybersecurity. \u201d.\nSection 15 A inserted\n6. The principal enactment is amended by the insertion after section 15, of\n\u201cAppointment of Deputy -Director s General\n15A. (1) The President shall in accordance with article 195 of the Constitution\nappoint such Deputy Directors -General as are necessary for the performance of the\nfunctions of the Authority.\n(2) The Deputy Directors -General shall hold office on the terms and\nconditions specified in the letters of appointment.\n(3) The Deputy Directors -General shall act in the absence of the\nDirector -General and perform any other functions as determined by the Board.\n(4) The Deputy Directors -General shall, subject to the provisions of this\nAct:\n(a) assist the Director -General in the discharge of his functions and\nperform such other functions as the Director -General or the Board\nmay delegate to him;\n(b) perform functions related to the object and functions of the\nAuthority on the direction or authority of the Director -General or\nthe Board ; and\n(c) be responsible for the direction of the Authority when the Director -\nGeneral is absent from Ghana or is otherwise unable to perform his\nfunctions. \u201d.\nSection 20A inserted",
9
+ "Authority on the direction or authority of the Director -General or\nthe Board ; and\n(c) be responsible for the direction of the Authority when the Director -\nGeneral is absent from Ghana or is otherwise unable to perform his\nfunctions. \u201d.\nSection 20A inserted\n7. The principal enactment is amended by the insertion after section 20, of\n\u201cTerms and Conditions of Service of Staff of the Authority\n20A. (1) The terms and conditions of service of the staff of the Authority shall\nnot be less favourable than the staff of the security and intelligence agencies .\nCYBERSECURITY (AMENDMENT) BILL, 2025\n7 (2) The conditions of service attached to posts of legal officers of the\nAuthority shall not be less than that of the posts attached to that of legal officers of\nthe Attorney -General of the same rank.\n(3) Staff shall, in addition to monthly salaries, be eligible for the payment\nof any gratuity, allowance, pension, subsidy or benefit to members in respect of\ntheir service or resignation or retirement .\u201d.\nSection 20B inserted\n8. The principal enactment is amended by the insertion after section 20, of\n\u201cPowers of police\n20B. (1) The Director -General, Deputy Director -General and other\nauthorised officers shall exercise the powers of a Police Officer, including the\npowers of arrest, search and seizure and have the same rights, protections\nimmunities conferred on a Police officer in the Criminal and Other Offences\n(Procedure) Act, 1960 (Act 30), the Police Service Act, 1970 (Act 350) and any other\nlaw related to a Police officer in the performance of their functions under the Act. \u201d.\nSection 31 of Act 10 38 amended",
10
+ "(Procedure) Act, 1960 (Act 30), the Police Service Act, 1970 (Act 350) and any other\nlaw related to a Police officer in the performance of their functions under the Act. \u201d.\nSection 31 of Act 10 38 amended\n9. The principal enactment is amended by substitution for section 31, of :\n\u201cSources of money for the Fund\n31. The sources of moneys for the Fund include:\n(a) seed money approved by Parliament;\n(b) moneys which may become lawfully payable to the Authority for the\nFund;\n(c) 50% of all fines arising from criminal penalties under th e Act;\n(f) grants, gifts, donations and other voluntary contributions;\n(g) 12% of the communications service tax for the Fund per annum;\n(h) 9 % corporate tax for the Fund per annum;\n(i) a charge determined by the Authority in accordance with the Fees\nand Charges (Miscellaneous Provisions) Act, 2018 (Act 983) and\nlevied on persons licensed by the Bank of Ghana to carry on business;\n(j) a proportion of the fees charged on all government e lectronic\nservices determined by the Authority ; and\n(h) any other moneys approved by Parliament for the Fund. \u201d.\nSection 35 of Act 10 38 amended\n10. The principal enactment is amended by substitution for section 35, of\n\u201cDesignation of critical information infrastructure\nCYBERSECURITY (AMENDMENT) BILL, 2025\n8 35. (1) The Minister may, on the advice of the Authority, designate a\ncomputer system or computer network as a critical information infrastructure if the\nMinister considers that the computer system or computer network is essential for\n(a) national security,",
11
+ "CYBERSECURITY (AMENDMENT) BILL, 2025\n8 35. (1) The Minister may, on the advice of the Authority, designate a\ncomputer system or computer network as a critical information infrastructure if the\nMinister considers that the computer system or computer network is essential for\n(a) national security,\n(b) the economic and social well -being of citizens , or\n(c) public health and safety.\n(2) Where the Minister designates a computer system or computer\nnetwork as a critical information infrastructure, the Minister shall publish the sector of the\ndesignated critical information infrastructure in the Gazette and, the owner of the Critical\nInformation Infrastructure shall be notified of the designation.\n(3) The Minister shall, in making a determination under subsection (1),\nconsider if the computer system or computer network is necessary for :\n(a) the security, defence or international relations of the country;\n(b) the production, preservation or identity of a confidential source of\ninformation related to the enforcement of criminal law;\n(c) the provision of services directly related to\n(i) communications and telecommunications infrastructure;\n(ii) banking and financial services;\n(iii) public utilities\n(iv) public transportation; and\n(v) public key infrastructure;\n(d) the protection of public safety and public health, including\nsystems related to essential emergency services;\n(e) an international business or communication affecting a citizen of\nGhana or any other international business in which a citizen of\nGhana or the Government has an interest;\n(f) the Legislature, Executive, Judiciary, Public Services or security\nagencies ; or\n(g) digital services;\n(h) services related to the supply chain in the critical information\ninfrastructure ecosystem; and",
12
+ "Ghana or any other international business in which a citizen of\nGhana or the Government has an interest;\n(f) the Legislature, Executive, Judiciary, Public Services or security\nagencies ; or\n(g) digital services;\n(h) services related to the supply chain in the critical information\ninfrastructure ecosystem; and\n(i) any other services determined by the Minister, on the advice of the\nAuthority .\n(4) The Minister shall, by publication in the Gazette , estab lish the\nprocedure for the regulation of a critical information infrastructure. \u201d.\nSection 36 of Act 10 38 amended\n11. The principal enactment is amended by substitution for section 3 6, of\n\u201cRegistration of critical information in frastructure\nCYBERSECURITY (AMENDMENT) BILL, 2025\n9 36. (1) The Owner of the designated critical information infrastructure shall\nregister their critical information infrastructure with the Authority , and shall pay the\nprescribed annual registration and designation fee as determined by the Authority\nor stipulated in a legislative instrument .\n(2) For compliance purposes, the owner of an unregistered critical\ninformation infrastructure shall receive a notification of registration letter from the\nAuthority to comply with the Act.\n(3) During registration, the owner of a critical information infrastructure\nshall nominate a point of contact and submit details of the nominee to the\nAuthority.\n(4) The owner of a critical information infrastructure shall submit to the\nAuthority, verified details of critical information infrastructure, through the\nchannels and modalities determined by the Authority.\n(5) The Authority shall validate the information submitted by the owner\nof a critical information infrastructure, and the owner shall receive a Certificate of\nRegistration upon completion of registration requirements.\n(6) The Authority shall through the issuance of guidelines or directives\ndetermine :",
13
+ "(5) The Authority shall validate the information submitted by the owner\nof a critical information infrastructure, and the owner shall receive a Certificate of\nRegistration upon completion of registration requirements.\n(6) The Authority shall through the issuance of guidelines or directives\ndetermine :\n(a) further requirements for the registration of a critical information\ninfrastructure ;\n(b) the procedure for the registration of a critical information\ninfrastructure; and\n(c) any other matter relating to the registration of a critical information\ninfrastructure.\n(7) Where there is any change in the legal ownership of a designated\ncritical information infrastructure, the owner of the designated critical information\ninfrastructure shall , within seven days after the change, inform the Authority of the\nchange in ownership.\n(8) An owner of a designated critical information infrastructure who\ncontravenes subsectio n (1) and (3) is liable to pay to the Authority the administrative\npenalty specified in the Second Schedule. \u201d.\nSection 37 of Act 10 38 amended\n12. The principal enactment is amended by substitution for section 37 , of\n\u201cWithdrawal of designation of critical information infrastructure\n\u201c37. (1) The Minister may, on the advice of the Authority, withdraw the\ndesignation of a critical information infrastructure at any time if the Minister considers that\nCYBERSECURITY (AMENDMENT) BILL, 2025\n10 the computer or computer network no longer satisfies the criteria of a critical information\ninfrastructure .\n(2) The Minister may publish the withdrawal of critical information\ninfrastructure sector in the Gazette .\n(3) The owner of a designated critical information infrastructure shall\nreceive notification of the withdrawal of the designation by letter from the Minister. \u201d.\nSection 40 of Act 10 38 amended\n13. The principal enactment is amended in section 40 by substitution for subsection",
14
+ "infrastructure sector in the Gazette .\n(3) The owner of a designated critical information infrastructure shall\nreceive notification of the withdrawal of the designation by letter from the Minister. \u201d.\nSection 40 of Act 10 38 amended\n13. The principal enactment is amended in section 40 by substitution for subsection\n(2), of\n\u201c(2) A person who contravenes subsection (1) commits an offence and is liable\non summary conviction to a fine of not less than four thousand penalty units and not more\nthan twenty -five thousand penalty units , or both .\u201d.\nSection 47 of Act 10 38 Amended\n14. The principal enactment is amended in section 47 by:\n(a) the addition after subsection (6), of\n\u201c(7) A Sectoral Computer Emergency Response Team shall within twenty -four\nhours after receiving a report of a cybersecurity incident from the owner of a designated\ncritical information infrastructure, a service provider, a licensee or any relevant person\nreport the cybersecurity incident to the National Computer Emergency Response Team.\u201d.\n(b) the addition after subsection (7), of\n\u201c(8) A Sectoral Computer Emergency Response Team who contravenes\nsubsection (7) is liable to pay to the Authority the administrative penalty specified in the\nSecond Schedule.\u201d.\nSection 49 of Act 1038 Amended\n15. The principal enactment is amended in section 49 by\n(a) the substitution for subsection (1), of\n\u201c(1) A person shall not provide a cybersecurity service for a reward unless that\nperson obtains a licence issued by the Authority in accordance with the Act.\u201d.\n(b) the substitution for subsection (2), of",
15
+ "\u201c(1) A person shall not provide a cybersecurity service for a reward unless that\nperson obtains a licence issued by the Authority in accordance with the Act.\u201d.\n(b) the substitution for subsection (2), of\nCYBERSECURITY (AMENDMENT) BILL, 2025\n11 \u201c(2) A person shall not provide a cybersecurity service on a not -for-profit basis\nunless that person obtains an accreditation issued by the Authority in accordance with the\nAct.\u201d.\n(c) addition after subsection (2), of\n\u201c(3) A person who contravenes subsection (1) and (2) is liable to pay to the\nAuthority the administrative penalty the equivalent to the cost of damage caused and value\nof the financial gain made , or an administrative penalty of not less than fifty thousand\npenalty units and not more than one hundred thousand penalty units. \u201d.\nSection 57 of Act 1038 Amended\n16. The principal enactment is amended by the substitution for section 57, of\n(1) The Authority shall establish a mechanism for the accreditation of\ncybersecurity professionals and practitioners.\n(2) An individual shall not practise as a cybersecurity professional or\ncybersecurity practitioner unless that individual has been accredited by the Authority;\n(3) A person shall not knowingly engage the services of a cybersecurity\nprofessional or cybersecurity practitioner who has not been accredited by the Authorit y;\n(4) A person who contravenes subsection (2) or (3) is liable to pay to the\nAuthority an administrative penalty of not less than two hundred and fifty penalty units\nand not more than twenty thousand penalty units .\nSection 57A inserted",
16
+ "(4) A person who contravenes subsection (2) or (3) is liable to pay to the\nAuthority an administrative penalty of not less than two hundred and fifty penalty units\nand not more than twenty thousand penalty units .\nSection 57A inserted\n17. The principal enactment is amended by the insertion after section 57, of\n\u201cCyber hygiene certification scheme\n57B. (1) The Authority shall establish a scheme for the certification of cybersecurity\nprofessionals and practitioners, and cybersecurity service providers who will be entrusted\nto perform cyber hygiene certification service as an alternative to other international best\npractice security frameworks.\n(2) The Cyber Security Authority shall conduct periodic audits to ensure\ncompliance with the cyber hygiene certification scheme and associated prescribed fees.\n(3) Any complaints regarding non -compliance shall be investigated, and\nappropriate enforcement actions shall be taken. \u201d.\nSection 57B inserted\nCYBERSECURITY (AMENDMENT) BILL, 2025\n12 18. The principal enactment is amended by the insertion after section 58A, of\n\u201cApplication for certification to provide the Cyber hygiene certification services\n57B. (1) A licensed cybersecurity service provider or accredited CP who seeks to\nprovide the Authority\u2019s cyber hygiene certification service shall apply in writing to the\nAuthority.\n(2) The application shall be made in the prescribed form and accompanied by\nthe:\n(a) supporting documentation, and\n(b) prescribed fee that the Authority may determine .\u201d.\nSection 57C inserted\n19. The principal enactment is amended by the insertion after section 58B, of\n\u201cStandardised , Review and Administration of Fees for Cyber Hygiene Certification\nService\n57C. (1) A certified cybersecurity professional or practitioner or cybersecurity service",
17
+ "Section 57C inserted\n19. The principal enactment is amended by the insertion after section 58B, of\n\u201cStandardised , Review and Administration of Fees for Cyber Hygiene Certification\nService\n57C. (1) A certified cybersecurity professional or practitioner or cybersecurity service\nprovider shall charge a fee for cyber hygiene certification services as determined by the\nCyber Security Authority.\n(2) A certified cybersecurity professional or practitioner or cybersecurity service\nprovide r shall not charge fees exceeding the approved flat rate for cyber hygiene\ncertification services.\n(3) A certified cybersecurity professional or practitioner or cybersecurity service\nprovider who fails to comply wit h subsection (2) shall be liable to pay to the Authority an\nadministrative penalty specified in the Second Schedule (not less than two hundred and\nfifty penalty units and not more than five thousand penalty units)\n(4) 30% of the revenue generated by a certified cybersecurity professional or\npractitioner or cybersecurity service provider under the scheme shall be paid into the\ncybersecurity fund.\n(5) The Cyber Security Authority shall, by notice published in the Gazette,\ndetermine the applicable flat rate for cyber hygiene certification services.\n(6) The flat rate shall be reviewed periodically based on, industry trends, and the\naffordability needs of stakeholders. \u201d.\nSection 58A inserted\n20. The principal enactment is amended by the insertion after section 5 8, of\nCYBERSECURITY (AMENDMENT) BILL, 2025\n13 \u201cCertification of the security of innovative and emerging technologies.\n58A. The Authority shall establish a mechanism for the certification of the security of\ninnovative and emerging technologies. \u201d.\nSection 58B inserted",
18
+ "CYBERSECURITY (AMENDMENT) BILL, 2025\n13 \u201cCertification of the security of innovative and emerging technologies.\n58A. The Authority shall establish a mechanism for the certification of the security of\ninnovative and emerging technologies. \u201d.\nSection 58B inserted\n21. The principal enactment is amended by the insertion after section 58A, of\n\u201cAccreditation of non -profit cybersecurity institutions\n58B. The Authority shall establish a mechanism for the accreditation of non -profit\ncybersecurity institutions .\u201d.\nSection 59 of Act 10 38 Amended\n22. The principal enactment is amended in section 59 by the addition after paragraph\n(e) of subsection (1), of\n\u201c(g) certif ication of the security of innovative and emerging technologies including\nArtificial Intelligence, cloud technology, quantum computing, big data, and blockchain -\nbased technology. \u201d.\nSection 59A inserted\n23. The principal enactment is amended by the insertion after section 59, of\n\u201cEnforcement powers\n59A. (1) A person who:\n(a) knowingly fails to comply with or acts in contravention of this Act,\nregulations or directives issued under this Act ;\n(b) knowingly fails to comply with prescribed cybersecurity standards and\nrequirements ;\n(c) provides cybersecurity services without a licence or acts as a cybersecurity\nprofessional or practitioner without accreditation ;\n(d) willfully obstructs, hinders, molests or assaults personnel of the Authority\nduly engaged in the exercise of power conferred on the Authority under this\nAct.\ncommits an offence and is liable on summary conviction to a term of imprisonment\nof not more than five years or to a fine of not more than twenty thousand penalty",
19
+ "duly engaged in the exercise of power conferred on the Authority under this\nAct.\ncommits an offence and is liable on summary conviction to a term of imprisonment\nof not more than five years or to a fine of not more than twenty thousand penalty\nunits, or to both.\nCYBERSECURITY (AMENDMENT) BILL, 2025\n14 (1) Where an offence is committed by a corporate entity, that entity is liable to\na fine of not more than twenty thousand penalty units and each director of\nthat entity shall be deemed to have committed the offence.\n(2) Despite subsection (1), the Authority may, where a person has breached this\nAct, Regulations or directives or where a licensee, an accreditation holder\nhas breached a condition contained in its licence or accreditation :\n(a) warn the person, licensee, or accreditation holder,\n(b) issue a cease -and-desist order,\n(c) apply to the High Court for\n(i) an injunction to restrain the person, licensee, or accreditation\nholder from continuing the breach, or\n(ii) other appropriate order to enforce compliance with this Act,\n(d) propose amendments to the licence or accreditation in accordance\nwith this Act or Regulations\n(e) suspend or terminate the licence or accreditation in accordance with\nthis Act, or\n(f) impose administrative penalties in accordance with the second\nschedule of the Act\n(g) or take any other action that it considers appropriate and that is not\ncontrary to this Act.\n(4) Nothing shall prevent the Authority from pursuing only criminal\nenforcement actions under subsection (1) or only administration\nenforcement actions under subsection (2), or a combination of\nboth. \u201d.\nSection 59B inserted",
20
+ "contrary to this Act.\n(4) Nothing shall prevent the Authority from pursuing only criminal\nenforcement actions under subsection (1) or only administration\nenforcement actions under subsection (2), or a combination of\nboth. \u201d.\nSection 59B inserted\n24. The principal enactment is amended by the insertion after section 59A, of\n\u201cPower to conduct investigations and to prosecute cybercrime \u201d\n59B. (1) The Authority shall upon the occurrence of a cybersecurity incident or a\ncybercrime conduct criminal investigations and prosecute same .\u201d\n(2) The Authority shall have the jurisdiction to prosecute all offences under the\nElectronic Transactions Act, 2008 (Act 772).\n(3) Where a person is convicted of a cybercrime under this Act, the Authority\nmay apply to the Court for an order to confiscate moneys, proceeds, benefits, properties\nand assets purchased by a person with proceeds derived from or in the commission of the\ncybercrime.\nCYBERSECURITY (AMENDMENT) BILL, 2025\n15\n(4) Despite subsection (5), nothing shall prevent the Authority from instituting\na civil action independently or at the same time as the prosecution to recover moneys,\nproceeds, benefits, properties and assets purchased by a person with proceeds derived from\nor in the commission of the cybercrime.\n(5) In relation to subsection (6) if criminal prosecution fails, civil asset recovery\nshould still proceed, and confiscation orders should have the effect of a civil judgment\nappealable from the High Court to the Court of Appeal. \u201d.\n(6) Where the Authority considers that freezing of property is necessary to\nfacilitate an investigation or prosecution, the Authority shall in writing direct the freezing",
21
+ "should still proceed, and confiscation orders should have the effect of a civil judgment\nappealable from the High Court to the Court of Appeal. \u201d.\n(6) Where the Authority considers that freezing of property is necessary to\nfacilitate an investigation or prosecution, the Authority shall in writing direct the freezing\nof:\n(a) the property of a person or entity being investigated; or\n(b) specified property held by a person or entity other than the person\nor entity being investigated or prosecuted.\n(7) The Authority shall within 14 days after the freezing of the property apply\nto the Court for a confirmation of the freezing.\nSection 59 C inserted\n25. The principal enactment is amended by the insertion after section 59 B, of\n\u201cPower to request for information\n59C. (1) The Authority may, for the purposes of carrying out an investigation in\nrespect of a contravention of the Act, Regulations or any other relevant enactment by the\nowner of a critical information infrastructure, a licensee, a service provider or any other\nperson, by notice in writing, require a person to :\n(a) attend at a time and place specified in the notice; and\n(b) furnish the Authority with information related to a matter relevant to the\ninvestigation.\n(2) A notice under subsection (1) shall indicate the subject matter and purpose\nof the request. \u201d.\n(3) Where a person required to furnish the Authority with an information is\nunder an obligation not to disclose, or asserts a right not to disclose, the Authority shall\napply to the High Court for an order for the production of the information.\n(4) Where an information is furnished to the Authority, the Authority shall\nmake copie s or extract from the Information and request the person",
22
+ "apply to the High Court for an order for the production of the information.\n(4) Where an information is furnished to the Authority, the Authority shall\nmake copie s or extract from the Information and request the person\nCYBERSECURITY (AMENDMENT) BILL, 2025\n16 producing the information to provide an ex planation on the content of the\ninformation where necessary.\n(5) A person who appears before the Authority may be represented by a Counsel\nof the choice of that person at any stage of the process .\n(6) Except as provided in subsection (2) where a person contravenes subsection (1)\n(a) and (b), that person commits an offence and is liable on summary conviction to a fine\nof not less than two hundred and fifty penalty units and not more than ten thousand penalty\nunits or to a term of imprisonment of not less than one year and not more than two years\nor to both. \u201d.\nSection 59D inserted\n26. The principal enactment is amended by the insertion after section 59C, of\n\u201cApplication for Production Order to Collect Computer Data\n59D. (1) An investigative officer may apply ex-parte to the High Court for a\nproduction order to collect computer data.\n(2) A production order under section (1) shall require a person in possession or\ncontrol of a computer or computer system to submit specified computer data which is\nstored in the computer or computer system to the investigative officer.\n(3) An investigative officer who makes an application under section (1) shall\ndemonstrate to the satisfaction of the Court that there are reasonable grounds to believe\nthat the computer data is reasonably required for the purposes of a specific criminal\ninvest igation.",
23
+ "stored in the computer or computer system to the investigative officer.\n(3) An investigative officer who makes an application under section (1) shall\ndemonstrate to the satisfaction of the Court that there are reasonable grounds to believe\nthat the computer data is reasonably required for the purposes of a specific criminal\ninvest igation.\n(4) Where an investigative officer makes an application under section (1), that\ninvestigative officer shall :\n(a) explain why the investigative officer believes the computer data sought, will\nbe available to the person in control of the computer or computer system;\n(b) identify and explain with specificity the type of computer data suspected to\nbe found on the computer or computer system;\n(c) identify and explain with specificity the computer data that may be found on\na computer or computer system that is the subject of an investigation or\nprosecution;\n(d) identify and explain with specificity the offences in respect of which the\nproduction order is sought; and\n(e) indicate what measures shall be taken to ensure that the computer data will\nbe procured\nCYBERSECURITY (AMENDMENT) BILL, 2025\n17\n(i) whilst maintaining the privacy of other users, customers and third\nparties, and\n(ii) without the disclosure of the computer data of any party not part of\nthe investigation. \u201d.\nSection 59 E inserted\n27. The principal enactment is amended by the insertion after section 59 D, of\n\u201cGrant of production order\n59E. (1) The High Court may grant an application for a production order\nunder subsection (1) of section 59D, if the Court is satisfied that\n(a) the investigative officer has complied with subsection (3) and (4) of section\n59D;",
24
+ "59E. (1) The High Court may grant an application for a production order\nunder subsection (1) of section 59D, if the Court is satisfied that\n(a) the investigative officer has complied with subsection (3) and (4) of section\n59D;\n(b) the information requested is commensurate, proportionate and necessary for\nthe purposes of a specific criminal investigation or prosecution;\n(c) measures shall be taken to ensure that the computer data is produced whilst\nmaintaining the privacy of other users, customers and third parties and\nwithout the disclosure of information and data of any party not part of the\ninvestigation; and\n(d) the investigation may be frustrated or seriously prejudiced unless the\nproduction of the information is permitted.\n(2) The Court shall require the service provider to keep confidential the\nproduction order and the execution of the production order under this\nsection.\n(3) A production order granted under this Act shall be served on a domestic\nservice provider, foreign service provider or both, or any other relevant\nperson. \u201d.\nSection 59F inserted\n28. The principal enactment is amended by the insertion after section 59E, of\n\u201cApplication for Warrant to Access, Search and Seize Computer Data, Computer or\nComputer System\n59F. (1) An investigative officer may apply ex -parte to the High Court for a warrant to :\n(a) access data on a computer or computer system;\n(b) search and seize a computer or a computer system; or\nCYBERSECURITY (AMENDMENT) BILL, 2025\n18 (c) make or retain a copy of computer data on a computer or computer system;\n(d) maintain the integrity of computer data on a computer or computer system;",
25
+ "(b) search and seize a computer or a computer system; or\nCYBERSECURITY (AMENDMENT) BILL, 2025\n18 (c) make or retain a copy of computer data on a computer or computer system;\n(d) maintain the integrity of computer data on a computer or computer system;\n(e) remove or render inaccessible computer data on a computer or computer\nsystem\nthat is suspected to contain information relevant to an investigation.\n(2) A warrant under subse ction (1) shall require a person who has knowledge\nabout the functioning of the computer or computer system to provide the\nnecessary information to enable an investigative officer undertake the actions\nrequired under paragraphs (a) to (e) of subsection (1).\n(3) An investigative officer who makes an application under subsection (1) shall\ndemonstrate to the satisfaction of the court that there are reasonable grounds\nto believe that the warrant is reasonably required for the purposes of a specific\ncriminal investiga tion.\n(4) A warrant under subsection (1) shall permit a law enforcement officer to\nexpeditiously extend the search for data from a computer or computer system\nto another computer or computer system or part of it in Ghana where the law\nenforcement officer has reasonable grounds to believe that the data sought is\naccessible from or available to that other computer or computer system or\npart of it in Ghana .\u201d.\nSection 59G inserted\n29. The principal enactment is amended by the insertion after section 59F, of\n\u201cGrant of warrant\n59G. (1) The High Court may grant an application for a warrant under subsection 1\nof section 59F if the Court is satisfied that\n(a) the investigative officer has complied with subsection (3) of section 59F.",
26
+ "\u201cGrant of warrant\n59G. (1) The High Court may grant an application for a warrant under subsection 1\nof section 59F if the Court is satisfied that\n(a) the investigative officer has complied with subsection (3) of section 59F.\n(b) the information requested is commensurate, proportionate and necessary\nfor the purposes of a specific criminal investigation or prosecution;\n(c) measures shall be taken to ensure that the computer data is produced whilst\nmaintaining the privacy of other users, customers and third parties and\nwithout the disclosure of information and data of any party not part of the\ninvestigation; and\n(d) the investigation may be frustrated or seriously prejudiced unless the\nproduction of the information is permitted.\nCYBERSECURITY (AMENDMENT) BILL, 2025\n19\n(2) The Court shall require the service provider to keep confidential the\nwarrant and the execution of the warrant under this section.\n(3) A warrant granted under this section shall be served on a domestic service\nprovider, foreign service provider or both, or any other relevant person. \u201d.\nSection 59H in serted\n30. The principal enactment is amended by the insertion after section 59G, of\n\u201cApplication for preservation order to preserve computer data\n(1) A provider of wire or electronic communication services or a remote\ncomputing service on the written request of a law enforcement agency, shall take the\nnecessary steps to preserve records and other evidence in its possession pending the issue\nof a Court o rder and shall take steps to ensure that the request by the law enforcement\nagency is not disclosed to third parties during the period.\n(2) Where an order from the Court is not obtained and served for fourteen days",
27
+ "of a Court o rder and shall take steps to ensure that the request by the law enforcement\nagency is not disclosed to third parties during the period.\n(2) Where an order from the Court is not obtained and served for fourteen days\nafter the receipt of the written request, the wire or electronic communication services, or\nremote computing service provider is not under any obligation to preserve the evidence.\n(3) An investigative officer authorised by a designated officer may apply ex-parte\nto the Court for an order to require a service provider or any person in control of a\ncomputer or computer system to preserve specified computer data, including traffic data\nthat has been stored by a computer system.\n(4) An investigative officer who makes an application under subsection (1) shall\ndemonstrate to the satisfaction of the Court that the computer data is particularly\nvulnerable to loss or modification. \u201d.\nSection 59I\n31. The principal enactment is amended by the insertion after section 59H, of\n\u201cGrant of preservation order\n59I. (1) The High Court may grant an application for a preservation order under\nsubsection (1) of section 59F if the Court is satisfied that :\n(a) the designated officer has complied with subsection (2) of section 59H;\n(b) the extent of the preservation order is commensurate, proportionate and\nnecessary for the purposes of a specific criminal investigation or\nprosecution;\nCYBERSECURITY (AMENDMENT) BILL, 2025\n20 (c) measures shall be taken to ensure that the computer data is preserved whilst\nmaintaining the privacy of other users, customers and third parties and\nwithout the disclosure of information and data of any party not part of the\ninvestigation; and\n(d) the investigation may be frustrated or seriously prejudiced unless the",
28
+ "20 (c) measures shall be taken to ensure that the computer data is preserved whilst\nmaintaining the privacy of other users, customers and third parties and\nwithout the disclosure of information and data of any party not part of the\ninvestigation; and\n(d) the investigation may be frustrated or seriously prejudiced unless the\npreservation order is permitted.\n(2) The preservation order issued under subsection (1)\n(a) is valid for a period of ninety days, and\n(b) may be renewed for a further period of ninety days upon application to the\nCourt.\n(3) The Court shall require the service provider to keep confidential a\npreservation order issued under this regulation. \u201d.\nSection 59J\n32. The principal enactment is amended by the insertion after section 59I, of\n\u201cPower of entry, inspection and audit\n59J. (1) The Authority may designate a person as an inspector to inspect premises.\n(2) A person designated as an inspector by the Authority may\n(a) obtain a warrant to :\n(i) enter and inspect premises; and\n(ii) conduct an audit of a computer system\n(b) at any reasonable time, enter inspect premises and conduct and audit of a\ncomputer system without a warrant where the inspector\n(i) reasonably believes that an inspection of the premises is necessary to\nensure compliance by the owner of a critical information infrastructure,\na licensee or a service provider or any other relevant person with the\nAct or Regulations; and\n(ii) has given the occupier of the premises seven days' notice in writing of\nthe intention to enter , inspect the premises and conduct an audit of a\ncomputer system .\n(3) An inspector shall, before exercising a power of entry and inspection,",
29
+ "Act or Regulations; and\n(ii) has given the occupier of the premises seven days' notice in writing of\nthe intention to enter , inspect the premises and conduct an audit of a\ncomputer system .\n(3) An inspector shall, before exercising a power of entry and inspection,\nCYBERSECURITY (AMENDMENT) BILL, 2025\n21 (a) produce evidence of the identity of the inspector; and\n(b) indicate the purpose of entry and inspection.\n(4) An inspector may\n(a) enter a premises;\n(b) inspect the premises;\n(c) inspect a document or equipment found on the premises;\n(d) require a person on the premises to provide any information, or produce any\ndocument in the possession or control of that person that the inspector\nconsiders relevant to ensure compliance with the Act and Regulation and\n(e) require any person on the premises to provide information in respect of a\ndocument or the location of a document.\n(4) For the purpose of this regulation, \u201cpremises\u201d does not include domestic\npremises. \u201d.\nSection 59K inserted\n33. The principal enactment is amended by the insertion after section 59J, of\n\u201cWitness and Inform ant Protection\n59K. (1) Except with the written, informed, explicit and freely given consent of a\nwitness or inform ant, no witness or inform ant who discloses information relating to\ncybercrime under this Act or administrative infractions under this Act, shall be obliged to\ndisclose his name or address or other personal information , or state any matter which\nmight lead to the ir discovery.\n(2) Unless otherwise provided, t he information and the identity of the witness\nor inform ant shall be held to be secret between the Authority and the witness or informer;",
30
+ "disclose his name or address or other personal information , or state any matter which\nmight lead to the ir discovery.\n(2) Unless otherwise provided, t he information and the identity of the witness\nor inform ant shall be held to be secret between the Authority and the witness or informer;\nand all matters relating to such information should be privileged and sh all not be disclosed\nin any proceedings before any court, tribunal, commission, or authority.\n(3) The Authority take all necessary and reasonable steps to protect the safety\nand welfare of a witness or informant , and such t he protection shall extend to persons by\nvirtue of being related to a witness or info rmant, or on account of a testimony given by a\nwitness or informer , or for any other reason upon which the Authority may consider\nsufficient. \u201d.\nSection 67 of Act 10 38 amended\n34. The principal enactment is amended by :\nCYBERSECURITY (AMENDMENT) BILL, 2025\n22 (a) the substitution for the heading before section 67 \u201cOther Online Sexual\nOffences, of\n\u201cOther cybercrime\u201d.\n(b) the substitution for subsection (2) of section 67 , of\n\u201c(2) A person who contravenes subsection (1) commits an offence and is liable\non summary conviction to a term of imprisonment of not less than three years and not\nmore than ten years or a fine of not less than two hundred and fifty penalty units and not\nmore than twenty -five thousand penalty units or to both.\u201d.\nSection 67A inserted\n35. The principal enactment is amended by the insertion after section 67, of\n\u201cCyberbullying and online harassment\n67A. (1) A person shall not use the internet or any form of electronic medium",
31
+ "more than twenty -five thousand penalty units or to both.\u201d.\nSection 67A inserted\n35. The principal enactment is amended by the insertion after section 67, of\n\u201cCyberbullying and online harassment\n67A. (1) A person shall not use the internet or any form of electronic medium\nor technology to bully a child or an adult.\n(2) A person shall not use a computer or any other electronic medium to\n(a) send a threatening message or a lewd message to a child;\n(b) make sexual advances towards a child; or\n(c) persistently make contact with\n(i) a child, whether or not the contact is acceptable; or\n(ii) another person where the contact is unacceptable.\n(3) A person shall not create a false identity online or a fake social media\nprofile for the purposes of following\n(a) a child, whether or not the contact is acceptable; or\n(b) an adult, where the contact is unacceptable.\n(4) A person shall not use any online computer service or any other\nelectronic device to:\n(a) track the location of a child;\n(b) monitor the real -world activities of a child;\n(c) track the location of another person without the consent of that\nperson;\nCYBERSECURITY (AMENDMENT) BILL, 2025\n23 (d) monitor the real -world activities of another person without the\nconsent of that person; or\n(e) obsessively track the location of\n(i) a child; or\n(ii) another person without the consent of that person.\n(5) Despite subsection (4), a parent or legal guardian may for the",
32
+ "consent of that person; or\n(e) obsessively track the location of\n(i) a child; or\n(ii) another person without the consent of that person.\n(5) Despite subsection (4), a parent or legal guardian may for the\npurposes of ensuring the safety of a child, track the location of the child, or monitor\nthe real -world activities of the child.\n(6) A person shall not use an electronic device or medium to send\n(a) an unwanted,\n(b) an unsolicited,\n(c) a frightening,\n(d) an obscene,\n(e) a harassing, or\n(f) a threatening electronic mail, text message or instant message to a\nchild or an adult.\n(7) A service provider of a user -to-user service including\n(a) a chatroom,\n(b) a social network site,\n(c) an online gaming platform, or\n(d) a virtual, augmented and mixed reality shall protect a child from all\nforms of online violence and cyberbullying from users of the user -\nto-user service.\n(8) A person shall not use an electronic device or an electronic medium\nto deliberately spread false or misleading information with the intent\nto deceive or manipulate a person or a fact.\n(9) A person who contravenes subsections (1), (2), (3), (4) or (6) commits\nan offence and is liable on summary conviction to a fine of not less\nthan two thousand, five hundred penalty units and not more than\nfive thousand penalty units or to a term of impriso nment of not less",
33
+ "an offence and is liable on summary conviction to a fine of not less\nthan two thousand, five hundred penalty units and not more than\nfive thousand penalty units or to a term of impriso nment of not less\nthan one year and not more than three years or to both.\nCYBERSECURITY (AMENDMENT) BILL, 2025\n24 (10) A person who contravenes sub regulation (7) or (8) commits an\noffence and is liable on summary conviction to a fine of not less than\nfive thousand penalty units and not more than twenty -five thousand\npenalty units or to a term of imprisonment of not less than three\nyears and not more than five years or to both.\n(11) For the purpose of this Act, \u201ccyberbullying\u201d includes any digital\ncommunication and any other activity which strips the recipient of\nthe dignity of the recipient, or causes fear or physical or emotional\nharm such as\n(a) repeatedly sending offensive, rude and insulting messages to\na person;\n(b) distributing derogatory information about a child or any other\nperson;\n(c) posting or sending offensive photos of a child or any other\nperson, whether or not the photos\n(i) have been digitally altered, or\n(ii) were taken with the consent of the victim, with the\nintention to humiliate or embarrass the victim;\n(d) breaking into an electronic mail, social networking or any\nelectronic account and using the virtual identity of a victim to\nsend, upload or distribute embarrassing materials to or about\nothers;\n(e) sharing sensitive personal information or any embarrassing\ninformation, or tricking a child or any person to reveal\npersonal or embarrassing information about that child or any\nother person and sharing the information obtained with\nothers;",
34
+ "send, upload or distribute embarrassing materials to or about\nothers;\n(e) sharing sensitive personal information or any embarrassing\ninformation, or tricking a child or any person to reveal\npersonal or embarrassing information about that child or any\nother person and sharing the information obtained with\nothers;\n(f) repeatedly sending threatening or intimidating messages with\nthreats of harm, or engaging in online activities that cause fear\nin a child or puts any other person in fear;\n(g) sending messages to another person telling the person to\ncommit suicide;\n(h) sending grossly offensive, indecent or obscene\ncommunication with the intention of causing emotional\ndistress to a child or any other person; or\n(i) sending electronic messages that denigrate a child or any other\nperson on the basis of\n(i) colour;\nCYBERSECURITY (AMENDMENT) BILL, 2025\n25 (ii) race;\n(iii) ethnic or national origins;\n(iv) religion ;\n(v) gender; or\n(vi) disability. \u201d.\nSection 67B inserted\n36. The principal enactment is amended by the insertion after section 67A, of\n\u201cCyberstalking of a person\n\u201c67B. (1) In furtherance of section 65 of the Act, a person shall not use the\ninternet, chatroom , a social network site, an online gaming platform, or a virtual,\naugmented and mixed reality to\n(a) harass or stalk a person ;\n(b) assume a false identity to anonymously harass or stalk a person;\n(c) create false information about a person; or\n(d) post or disseminate false information about a person.\n(2) A person who contravenes subsection (1) commits an offence and is liable",
35
+ "(c) create false information about a person; or\n(d) post or disseminate false information about a person.\n(2) A person who contravenes subsection (1) commits an offence and is liable\non summary conviction to a term of imprisonment of not less than one year and not more\nthan ten years or a fine of less than one hundred penalty units and not more than ten\nthousand penalty units or to both.\u201d.\nSection 68 of Act 10 38 amended\n37. The principal enactment is amended in section 68 by substitution for subsection\n(2), of\n\u201c(2) A person who contravenes subsection (1) commits an offence and is liable\non summary conviction to a term of imprisonment of not less than three years and not\nmore than ten years or a fine of less than two hundred and fifty penalty units and not more\nthan twenty -five thousand penalty units or to both.\u201d.\nSection 83 of Act 1038 amended\n38. The principal enactment is amended in section 83 by:\n(a) the substitution for paragraph (e) of subsection (4), of\n\u201c(e) requesting for information regarding domain name registration ;\u201d.\n(b) the addition after paragraph (e) of subsection (4) , of\nCYBERSECURITY (AMENDMENT) BILL, 2025\n26 \u201c(f) expedited disclosure of specified stored computer data in a service provider's\npossession or control in an emergency situation without a request for mutual\nassistance ;\u201d.\n(c) the addition after paragraph (f) of subsection (4), of\n\u201c(g) any other matter related to paragraphs (a) to ( f).\u201d.",
36
+ "possession or control in an emergency situation without a request for mutual\nassistance ;\u201d.\n(c) the addition after paragraph (f) of subsection (4), of\n\u201c(g) any other matter related to paragraphs (a) to ( f).\u201d.\n(d) the addition after paragraph (g) of subsection (4), of\n\u201c(5) For the purpose of this section an emergency means, a situation in which\nthere is a significant and imminent risk to the life or safety of any natural person. \u201d.\nSection 90 of Act 10 38 amended\n39. The principal enactment is amended in section 90 by the addition after subsection\n(2), of\n\u201c(3) Despite section 90(1) and section 90A , the Circuit Court shall have the\njurisdiction to try an offence under sections 62 to 68 A of this Act. \u201d.\nSection 91 of Act 10 38 amended\n40. The principal enactment is amended in section 91 by the addition after paragraph\n(h), of\n\u201c(i) the designation of Sectoral Computer Emergency Response Team;\n(j) the accreditation of Sectoral Computer Emergency Response Team;\n(k) the accreditation of cybersecurity establishments ;\n(l) the accreditation of non -profit cybersecurity institution ;\n(m) the protection of children ;\n(n) safeguarding the development and deployment of innovative and emerging\ntechnologies or solutions ;\n(o) licensing of cyber security service providers ;\n(p) accreditation of cybersecurity professionals and practitioners;\n(p) any other matter required for ensuring the cybersecurity of the country .\u201d.\nSection 92 of Act 10 38 amended\n41. The principal enactment is amended in section 92 by :",
37
+ "(p) accreditation of cybersecurity professionals and practitioners;\n(p) any other matter required for ensuring the cybersecurity of the country .\u201d.\nSection 92 of Act 10 38 amended\n41. The principal enactment is amended in section 92 by :\n(a) the substitution for subsection (1), of\n\u201c(1) the Authority may issue directives to an owner of a critical information\ninfrastructure, a cybersecurity service provider or service provider , innovators, developers\nfor the purpose of ensuring the cybersecurity of the country.\n(b) the substitution for subsection (2), of\nCYBERSECURITY (AMENDMENT) BILL, 2025\n27 \u201c(2) An owner of a critical information infrastructure, cybersecurity service\nprovider or service provider, innovator, developer or any relevant person who fails to\ncomply with the directives issued under this section is liable to pay to the Authority the\nadmin istrative penalty specified in the Second Schedule .\u201d.\n(c) the insertion after subsection (2) , of\n\u201c(3) The Authority may issue directives to innovators, developers, service\nproviders and cybersecurity service providers for the purpose of securing the adoption and\ndeployment of innovative and emerging technologies or solutions.\u201d.\nSection 94 of Act 10 38 amended\n42. The principal enactment is amended by the substitution for section 94, of\n\u201c(1) A person who, without lawful authority accesses or retrieves subscriber\ninformation or intercepts traffic data or content data, commits an offence and is liable on\nsummary conviction to a fine of not less than two thousand five hundred penalty units and",
38
+ "\u201c(1) A person who, without lawful authority accesses or retrieves subscriber\ninformation or intercepts traffic data or content data, commits an offence and is liable on\nsummary conviction to a fine of not less than two thousand five hundred penalty units and\nnot more than fifteen thousand penalty units or to a term of imprisonment of not less than\ntwo years and not more than five years or, to both.\n(2) An attempt to gain access to a critical information infrastructure or its\ndependencies, successful or not, constitutes an unlawful access to the critical information\ninfrastructure.\n(3) Any person who attempts to secure access to a critical information\ninfrastructure or its dependencies, successful or not commits an offence and is liable on\nsummary conviction to a term of imprisonment of not less than two years and not more\nthan five years or a fine of not less than four thousand penalty units and not more than\ntwenty -five thousand penalty units or to both.\n(4) The tampering and or destruction of critical information infrastructure or its\ncomponents which affects the confidentiality, integrity or availability of the critical\ninformation infrastructure constitutes unlawful access to critical information infrastructure ,\nand a person shall be subject to the same criminal penalties specified in subsection (1). \u201d.\nSection 94A inserted\n43. The principal enactment is amended by the insertion after section 94, of\n\u201cComputer related Forgery\n95A. (1) A person shall not intentionally and without right, input, alter, delete\nor suppress computer data resulting in inauthentic data with the intent that they be\nconsidered or acted upon for legal purposes as if they were authentic, regardless of whether\nor no t the data are directly readable and intelligible.\u201d",
39
+ "or suppress computer data resulting in inauthentic data with the intent that they be\nconsidered or acted upon for legal purposes as if they were authentic, regardless of whether\nor no t the data are directly readable and intelligible.\u201d\n(2) A person who contravenes subsection (1) commits an offence and\nshall be deemed to have committed the offence of forger y under Chapter Two of the\nCYBERSECURITY (AMENDMENT) BILL, 2025\n28 Criminal Offences Act, 1960 (Act 29) , and shall liable on summary conviction to the same\npunishment for that particular kind of forgery under the law. \u201d.\nSection 94B inserted\n44. The principal enactment is amended by the inser tion after section 94A, of\n\u201cComputer related Fraud\n95B. (1) A person shall not intentionally and without right, cause the loss of\nproperty to another person by means of:\n(a) Any input, alteration, deletion or suppression of computer data;\n(b) Any interference with the functioning of a computer system;\n(c) Any deception as to factual circumstances made through a computer\nsystem that causes a person to do or omit to do anything which that person would not\notherwise do or omit to do; with the fraudulent or dishonest intent of procuring for oneself\nor for another person, without right, a gain in money or other property .\n(2) A person who contravenes subsection (1) commits an offence and\nshall be deemed to have committed the offence of defrauding by false pretence under\nsection 13 1 of the Criminal Offences Act, 1960 (Act 29) , and shall liable on summary",
40
+ "(2) A person who contravenes subsection (1) commits an offence and\nshall be deemed to have committed the offence of defrauding by false pretence under\nsection 13 1 of the Criminal Offences Act, 1960 (Act 29) , and shall liable on summary\nconviction to the same punishment for defrauding by false pretence under that law. \u201d.\nSection 97 of Act 10 38 amended\n45. The principal enactment is amended in section 97 by:\n(a) the insertion before \u201cAuthority \u201d, of\n\u201cArtificial Intelligence is technology that enables computers and machines to\nsimulate human learning, comprehension, problem solving, decision -making, creativity and\nautonomy ;\u201d;\n\u201cblock-chain based technology \u201d means a n advanced decentralised digital record or\ndigital ledger of transactions, that enables recording of transactions, tracking of assets, and\nsharing of records across computer networks in a transparent and immutable way; \u201d.\n\u201cbig data\u201d means a collection of extremely large organized, semi -structured, and\nunstructured information that grows exponentially over time;\u201d ;\n(b) the insertion after \u201cchild\u201d, of\n\u201ccloud technology\u201d means technology that delivers computing services and offers\non-demand access to computing resources such as physical or virtual services, data\nstorage, servers, databases, networking, intelligence, analytics, and software over the\ninternet ;\u201d;\n(c) the insertion after \u201c cloud technology\u201d, of\nCYBERSECURITY (AMENDMENT) BILL, 2025\n29 \u201cconfiscation\u201d, which includes forfeiture where applicable, shall mean the\npermanent deprivation of property by order of a court or other competent\nauthority;\u201d;\n(d) the insertion after \u201cdigital ecosystem\u201d of",
41
+ "CYBERSECURITY (AMENDMENT) BILL, 2025\n29 \u201cconfiscation\u201d, which includes forfeiture where applicable, shall mean the\npermanent deprivation of property by order of a court or other competent\nauthority;\u201d;\n(d) the insertion after \u201cdigital ecosystem\u201d of\n\u201cdigital services\u201d means services delivered electronically whether traditionally or\nby internet, with minimal physical interaction, and includes:\n(a) social media platforms;\n(b) e-commerce platforms;\n(c) video -on-demand or streaming platforms;\n(d) messaging applications,\n(e) fintech software platforms;\n(f) online banking platforms;\n(e) the insertion after \u201cDirector -General\u201d, of\n\u201cEmergency\u201d shall mean a situation in which there is a significant and imminent\nrisk to the life or safety of any natural person;\u201d;\n(f) the insertion after \u201ce -services\u201d, of\n\u201cFreezing\u201d or \u201cseizure\u201d mean temporarily prohibiting the transfer, conversion,\ndisposition or movement of property or temporarily assuming custody or control\nof property on the basis of an order issued by a court or other competent\nauthority;\u201d;\n(g) the insertion after \u201cinterception of warrant \u201d, of\n\u201cInternet of Things (IoT) means a network of interrelated physical devices, vehicles,\nappliances and other physical objects that connect and exchange data with other\ndevices and the cloud across wireless networks , and are typically embedded with\nsensors, processing ability and software and can include mechanical and digital\nmachines and consumer objects ;\u201d;\n(h) the substitution for \u201ccomputer system \u201d, of\n\u201ccomputer system\u201d means an arrangement of interconnected computers that is\ndesigned to perform one or more specific functions, and includes\n(a) an information processing system;",
42
+ "machines and consumer objects ;\u201d;\n(h) the substitution for \u201ccomputer system \u201d, of\n\u201ccomputer system\u201d means an arrangement of interconnected computers that is\ndesigned to perform one or more specific functions, and includes\n(a) an information processing system;\n(b) an operational technology system, a programmable logic controller, a\nsupervisory control and data acquisition system, or a distributed control\nsystem; and\nCYBERSECURITY (AMENDMENT) BILL, 2025\n30 (c) dependency systems that support the functionality of a computer or\ncomputer network ;\u201d;\n(i) the substitution for \u201claw enforcement agency\u201d, of\n\u201claw enforcement agency\u201d means\n(a) The Police\n(b) The Customs Division of the Ghana Revenue Authority and\n(c) Cyber Security Authority\n(d) Any other agency authorised by law to exercise the powers of the Police\n(j) the insertion after \u201cprohibited intimate image and visual recording\u201d, of\n\u201cproperty\u201d mean s assets of every kind, in the country or elsewhere whether\ncorporeal or incorporeal, movable or immovable, tangible or intangible, including\nvirtual assets, and legal documents or instruments evidencing title to, or interest in,\nsuch property assets;\u201d;\n(k) the insertion after \u201cproperty\u201d, of\n\u201cproperty of or in the possession or control of a person\u201d includes a gift made by\nthat person;\n(l) the insertion after \u201cpublish\u201d, of\n\u201cquantum computing \u201d means technology that solves complex problems based on\nprinciples of quantum mechanics;\u201d .\nSection 98 of Act 1058 amended\n46. The principal enactment is amended by substitution for section 98, of\n\u201cRepeals and savings",
43
+ "that person;\n(l) the insertion after \u201cpublish\u201d, of\n\u201cquantum computing \u201d means technology that solves complex problems based on\nprinciples of quantum mechanics;\u201d .\nSection 98 of Act 1058 amended\n46. The principal enactment is amended by substitution for section 98, of\n\u201cRepeals and savings\n(1) Sections 118 and 136 of the Electronic Transaction Act, 2008 (Act 772)\nare repealed.\n(2) Sections 35 to 40 this Act repeals sections 55 to 62 of the Electronic\nTransaction Act, 2008 (Act 772) .\n(3) Despite the repeal of sections, any Regulations, bye -laws, directives,\nnotices, orders, directions, appointments or other acts lawfully made or\ndone under the repealed enactment and in force immediately before the\nCYBERSECURITY (AMENDMENT) BILL, 2025\n31 coming into force of this Act shall be deemed to have been made or done\nunder this Act and shall continue to have effect until instruments issued\nor made until revoked, cancelled, withdrawn or terminated.\u201d.\nSection 99 of Act 10 38 amended\n47. The Extradition Act, 1960 (Act 22) is amended by:\n(a) the addition after subsection (3) of section , of\n\u201c(4) Despite subsection (1), for the purposes of extradition , Cybercrime and\nCyber Offences under the First Schedule to this Act, in the absence of a separate\narrangement, bilateral or multilateral agreement between Ghana and another signatory of\nthe Budapest Convention on Cybercrime or any other convention , extradition of fugitive\ncriminals may still take place between Ghana and another signatory of the Budapest\nConvention on Cybercrime or any other convention and shall be governed by the\nprovisions of the said convention. \u201d.",
44
+ "the Budapest Convention on Cybercrime or any other convention , extradition of fugitive\ncriminals may still take place between Ghana and another signatory of the Budapest\nConvention on Cybercrime or any other convention and shall be governed by the\nprovisions of the said convention. \u201d.\nFirst schedule to Act 1038 amended\n48. The principal enactment is amended by substitution for the preamble, of\n\u201cA service provided for reward or a not -for profit basis intended primarily for or aimed at\nensuring or safeguarding the cybersecurity of a computer or computer system belonging\nto a person and includes.\u201d.\nSecond schedule to Act 10 38 amended\n49. The principal enactment is amended by substitution for administrative penalties for\nsections 36(4), 39(2)(a), 39(2)(b), 39(2)(c) and 92(2) and addition of administrative penalties\nfor sections 36(1) and 39(3)(c) .\nNo. Sections of\nAct creating\ncontravention Contravention Administrative\nPenalty\n1. 36(1) The Owner of the designated critical\ninformation infrastructure shall register\ntheir critical information infrastructure\nwith the Authority, and shall pay the\nprescribed annual registration and\ndesignation fee as determined by the\nAuthority or stipulated i n a legislative\ninstrument. Not less than five\nthousand penalty\nunits, not more than\nfifty thousand\npenalty units .\n2. 36(4) Owner of a registered critical information\ninfrastructure failing to inform the\nAuthority within seven days of the change\nin legal ownership of the registered critical Not less than five\nthousand penalty\nunits, not more than\nCYBERSECURITY (AMENDMENT) BILL, 2025\n32 information infrastructure.\nfifty thousand\npenalty units.\n3. 39(2)(a) Owner of a critical information",
45
+ "Authority within seven days of the change\nin legal ownership of the registered critical Not less than five\nthousand penalty\nunits, not more than\nCYBERSECURITY (AMENDMENT) BILL, 2025\n32 information infrastructure.\nfifty thousand\npenalty units.\n3. 39(2)(a) Owner of a critical information\ninfrastructure failing to report a\ncybersecurity incident .\nNot less than ten\nthousand penalty\nunits, not more than\nfifty thousand\npenalty units.\n4. 39(2)(b) Owner of a critical information\ninfrastructure failing to cause an audit to be\nperformed on the critical information\ninfrastructure .\nNot less than five\nthousand penalty\nunits, not more than\ntwenty thousand\npenalty units.\n5. 39(3)(c) Owner of a critical information\ninfrastructure failing to submit a copy of\nthe audit report to the Authority.\nNot less than five\nthousand penalty\nunits, not more than\ntwenty thousand\npenalty units.\n6. 92(2) Owner of a critical information\ninfrastructure, a cybersecurity service\nprovider or a provider of a digital service\nfailing to comply with a directive issued by\nthe Authority . Not less than ten\nthousand penalty\nunits, not more than\nfifty thousand\npenalty units\nThird schedule to Act 1038 amended\n50. The principal enactment is amended by addition after the \u201cOath of Secrecy\u201d, of\nThe Official Oath\n(section 5)\nI,\n\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026..\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\u2026\ndo (in the name of the Almighty God swear) (solemnly affirm) that I will at all times well\nand truly serve the Republic of Ghana in the office of \u2026\u2026.and that I will uphold, preserve,",
46
+ "do (in the name of the Almighty God swear) (solemnly affirm) that I will at all times well\nand truly serve the Republic of Ghana in the office of \u2026\u2026.and that I will uphold, preserve,\nprotect and defend the constituti on of the Republic of Ghana as by law established (so\nhelp me God)."
47
+ ]
assets/example_bills/cybersecurity-amendment-bill-2025/document.txt ADDED
@@ -0,0 +1,1104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ CYBERSECURITY (AMENDMENT) BILL, 2025
2
+ 1 ARRANGEMENT OF SECTIONS
3
+ Section
4
+ 1. Section 3 of Act 1038 Amended
5
+ 2. Section 4A inserted
6
+ 3. Section 5 of Act 1038 amended
7
+ 4. Section 13 of Act 1038 amended
8
+ 5. Section 14 of Act 1038 amended
9
+ 6. Section 15A inserted
10
+ 7. Section 20A inserted
11
+ 8. Section 20B inserted
12
+ 9. Section 31 of Act 1038 amended
13
+ 10. Section 35 of Act 1038 amended
14
+ 11. Section 36 of Act 1038 amended
15
+ 12. Section 37 of Act 1038 amended
16
+ 13. Section 40 of Act 1038 amended
17
+ 14. Section 47 of Act 1038 amended
18
+ 15. Section 49 of Act 1038 amended
19
+ 16. Section 57 of Act 1038 amended
20
+ 17. Section 57A inserted
21
+ 18. Section 57B inserted
22
+ 19. Section 57C inserted
23
+ 20. Section 58A inserted
24
+ 21. Section 58B inserted
25
+ 22. Section 59 of Act 1038 amended
26
+ 23. Section 59A inserted
27
+ 24. Section 59B inserted
28
+ 25. Section 59C inserted
29
+ 26. Section 59D inserted
30
+ 27. Section 59E inserted
31
+ 28. Section 59F inserted
32
+ 29. Section 59G inserted
33
+ 30. Section 59H inserted
34
+ 31. Section 59I inserted
35
+ 32. Section 59J inserted
36
+ 33. Section 59K inserted
37
+ 34. Section 67 of Act 1038 amended
38
+ 35. Section 67A inserted
39
+ 36. Section 67B inserted
40
+ 37. Section 68 of Act 1038 amended
41
+ 38. Section 83 of Act 1038 amended
42
+ 39. Section 90 of Act 1038 amended
43
+ 40. Section 91 of Act 1038 amended
44
+ 41. Section 92 of Act 1038 amended
45
+ 42. Section 94 of Act 1038 amended
46
+ 43. Section 94A inserted
47
+ CYBERSECURITY (AMENDMENT) BILL, 2025
48
+ 2 44. Section 94B inserted
49
+ 45. Section 97 of Act 1038 amended
50
+ 46. Section 98 of Act 1058 amended
51
+ 47. Section 99 of Act 1038 amended
52
+ 48. First Schedule to Act 1038 amended
53
+ 49. Second schedule to Act 1038 amended
54
+ 50. Third schedule to Act 1038 amended
55
+ CYBERSECURITY (AMENDMENT) BILL, 2025
56
+ 3 A
57
+ BILL
58
+ ENTITLED
59
+ CYBERSECURITY (AMENDMENT) BILL, 2025
60
+ AN ACT to amend the Cybersecurity Act, 2020 (Act 1038) to confer powers on the Cyber
61
+ Security Authority to investigate and prosecute cybercrime on the authority of the
62
+ Attorney -General and recover proceeds of cyber crime ; to revise the object and
63
+ functions of the Cyber Security Authority; to revise the governance and
64
+ administration of the Cyber Security Authority ; to revise the enforcement powers of
65
+ the Cyber Security Authority and to provide for related matters.
66
+ Section 3 of Act 10 38 Amended
67
+ 1. The Cybersecurity Act, 2020 (Act 1038) referred to in the Act as the “principal
68
+ enactment” is amended by substitution for section 3, of
69
+ “Objects of the Authority
70
+ 3. The objects of the Authority are to:
71
+ (a) regulate cybersecurity activities in the country;
72
+ (b) prevent, manage and respond to cybersecurity threats and
73
+ cybersecurity incidents ;
74
+ (c) regulate owners of critical information infrastructure in respect of
75
+ cybersecurity activities, cybersecurity service providers , cybersecurity
76
+ professionals and practitioners, and cybersecurity establishments in
77
+ the country;
78
+ (d) promote the development of cybersecurity in the country to ensure a
79
+ secured and resilient digital ecosystem;
80
+ (e) prevent and detect cybercrime;
81
+ (f) to facilitate the confiscation of proceeds of cybercrime;
82
+ (g) establish a platform for cross -sector engagement on matters of
83
+ cybersecurity for effective co -ordination and co -operation between
84
+ key public institutions and the private sector;
85
+ (h) create awareness of cybersecurity matters; an d
86
+ (i) collaborate with States, inter -governmental bodies, international
87
+ organisations and international agencies to promote the cybersecurity
88
+ of the country .”.
89
+ Section 4A inserted
90
+ 2. The principal enactment is amended by the insertion after section 4, of
91
+ “Further Functions of the Authority
92
+ CYBERSECURITY (AMENDMENT) BILL, 2025
93
+ 4 4A. The Authority , shall in addition to the functions in section 4, carry out the
94
+ following functions:
95
+ (a) subject to article 88 of the Constitution, to investigate and on the
96
+ authority of the Attorney -General , prosecute cybercrime under this
97
+ Act;
98
+ (b) establish standards for certifying the security of innovative products ,
99
+ Artificial Intelligence, cloud technology, quantum computing, big
100
+ data, Internet of Things (IoT) , blockchain -based technology and any
101
+ other emerging technologies ;
102
+ (c) certify the security of innovative products Artificial Intelligence,
103
+ cloud technology, quantum computing, big data, Internet of Things
104
+ (IoT) , blockchain -based technology and any other emerging
105
+ technologies , in accordance with the standards established pursuant
106
+ to paragraph ( b);
107
+ (d) accredit the cybersecurity establishments of critical information
108
+ infrastructure owners, cybersecurity service providers, cybersecurity
109
+ practitioners and professionals and other relevant person s or
110
+ institutions ;
111
+ (e) accredit non -profit cybersecurity institutions and cybersecurity
112
+ professional bodies ;
113
+ (f) promote the online protection of women, elderly , persons with
114
+ disabilities and underserved populations ;
115
+ (g) collaborate with relevant institutions to develop mechanisms
116
+ including technical security solutions and guidelines on the usage of
117
+ smart technology and other emerging technologies ; and
118
+ (h) promote the protection of digital right s as it relates to cyber security. ”.
119
+ Section 5 of Act 10 38 amended
120
+ 3. The principal enactment is amended in section 5 by the addition after subparagraph
121
+ (iv) of paragraph (a) of subsection (1), of
122
+ “(v) Foreign Affairs and Regional Integration ; and
123
+ (vi) Gender, Children and Social Protection .”.
124
+ Section 13 of Act 10 38 amended
125
+ 4. The principal enactment is amended in section 13 by:
126
+ (a) the substitution for paragraph (d) of subsection (2), of
127
+ “(d) the Executive Director of the Data Protection Commission or any
128
+ successor official or entity by whatever name designated, or a
129
+ representative of the Executive Director with the requisite
130
+ knowledge and skills in cybercrime and cybersecurity matters,
131
+ nominated by the Executive Director ;”;
132
+ CYBERSECURITY (AMENDMENT) BILL, 2025
133
+ 5 (b) the substitution for paragraph (g) and (k) respectively of subsection (2), of
134
+ “(g) the Director of the National Intelligence Bureau or any successor
135
+ official or entity by whatever name designated, or a representative of
136
+ the Director with the requisite knowledge and skills in cybercrime ,
137
+ and cybersecurity matters, nominated by the Director;
138
+ (k) the Director General of National Signals Bureau or any successor
139
+ official or entity by whatever name designated, or a representative of
140
+ the Director with the requisite knowledge and skills in cybercrime and
141
+ cybersecurity matters, nominated by the Director; ”;
142
+ (c) the addition after paragraph (r) of subsection (2), o f
143
+ “(s) Any other person the Minister may nominate, on the advice of the
144
+ Authority. ”.
145
+ (d) the addition after subsection (13), of
146
+ “(14) A member of the JCC who is absent from three consecutive meetings
147
+ without sufficient cause ceases to be a member of the JCC .
148
+ (15) Where there is a vacancy, the Minister shall notify the President of
149
+ the vacancy , and the President shall appoint a person to fill the v acancy for
150
+ the unexpired term. ”.
151
+ Section 1 4 of Act 10 38 amended
152
+ 5. The principal enactment is amended in section 14 by the substitution for subsection
153
+ (1), of
154
+ “14 (1). The Joint Cybersecurity Committee shall collaborate with the Authority and
155
+ the sectors or institutions represented on the Committee for the implementation of
156
+ relevant cybersecurity measures including but not limited to the following ways:
157
+ (a) identification of cybersecurity risks that may affect critical information
158
+ infrastructure or the overall cybersecurity of the country ;
159
+ (b) development of cybersecurity policies and guidelines based on identified
160
+ risks, best practices and international standards to mitigate cybersecurity
161
+ risks;
162
+ (c) coordination of cybersecurity activities between sectors and entities to
163
+ ensure that all cybersecurity efforts are aligned to prevent overlap and gaps;
164
+ (d) facilitate the sharing of information between sectors and entities in a secure
165
+ manner and in compliance with relevant data protection laws ;
166
+ (e) collaboration with sectors or institutions to tackle cybercrime including the
167
+ provision of a platform for incidence sharing and incidence response
168
+ mechanisms to facilitate ;
169
+ CYBERSECURITY (AMENDMENT) BILL, 2025
170
+ 6 (f) evaluation of the effectiveness of implemented cybersecurity measures based
171
+ on established metrics with a view to continuously improve the cybersecurity
172
+ measures ; and
173
+ (g) collaboration with the Authority to educate the public on matters related to
174
+ cybercrime and cybersecurity. ”.
175
+ Section 15 A inserted
176
+ 6. The principal enactment is amended by the insertion after section 15, of
177
+ “Appointment of Deputy -Director s General
178
+ 15A. (1) The President shall in accordance with article 195 of the Constitution
179
+ appoint such Deputy Directors -General as are necessary for the performance of the
180
+ functions of the Authority.
181
+ (2) The Deputy Directors -General shall hold office on the terms and
182
+ conditions specified in the letters of appointment.
183
+ (3) The Deputy Directors -General shall act in the absence of the
184
+ Director -General and perform any other functions as determined by the Board.
185
+ (4) The Deputy Directors -General shall, subject to the provisions of this
186
+ Act:
187
+ (a) assist the Director -General in the discharge of his functions and
188
+ perform such other functions as the Director -General or the Board
189
+ may delegate to him;
190
+ (b) perform functions related to the object and functions of the
191
+ Authority on the direction or authority of the Director -General or
192
+ the Board ; and
193
+ (c) be responsible for the direction of the Authority when the Director -
194
+ General is absent from Ghana or is otherwise unable to perform his
195
+ functions. ”.
196
+ Section 20A inserted
197
+ 7. The principal enactment is amended by the insertion after section 20, of
198
+ “Terms and Conditions of Service of Staff of the Authority
199
+ 20A. (1) The terms and conditions of service of the staff of the Authority shall
200
+ not be less favourable than the staff of the security and intelligence agencies .
201
+ CYBERSECURITY (AMENDMENT) BILL, 2025
202
+ 7 (2) The conditions of service attached to posts of legal officers of the
203
+ Authority shall not be less than that of the posts attached to that of legal officers of
204
+ the Attorney -General of the same rank.
205
+ (3) Staff shall, in addition to monthly salaries, be eligible for the payment
206
+ of any gratuity, allowance, pension, subsidy or benefit to members in respect of
207
+ their service or resignation or retirement .”.
208
+ Section 20B inserted
209
+ 8. The principal enactment is amended by the insertion after section 20, of
210
+ “Powers of police
211
+ 20B. (1) The Director -General, Deputy Director -General and other
212
+ authorised officers shall exercise the powers of a Police Officer, including the
213
+ powers of arrest, search and seizure and have the same rights, protections
214
+ immunities conferred on a Police officer in the Criminal and Other Offences
215
+ (Procedure) Act, 1960 (Act 30), the Police Service Act, 1970 (Act 350) and any other
216
+ law related to a Police officer in the performance of their functions under the Act. ”.
217
+ Section 31 of Act 10 38 amended
218
+ 9. The principal enactment is amended by substitution for section 31, of :
219
+ “Sources of money for the Fund
220
+ 31. The sources of moneys for the Fund include:
221
+ (a) seed money approved by Parliament;
222
+ (b) moneys which may become lawfully payable to the Authority for the
223
+ Fund;
224
+ (c) 50% of all fines arising from criminal penalties under th e Act;
225
+ (f) grants, gifts, donations and other voluntary contributions;
226
+ (g) 12% of the communications service tax for the Fund per annum;
227
+ (h) 9 % corporate tax for the Fund per annum;
228
+ (i) a charge determined by the Authority in accordance with the Fees
229
+ and Charges (Miscellaneous Provisions) Act, 2018 (Act 983) and
230
+ levied on persons licensed by the Bank of Ghana to carry on business;
231
+ (j) a proportion of the fees charged on all government e lectronic
232
+ services determined by the Authority ; and
233
+ (h) any other moneys approved by Parliament for the Fund. ”.
234
+ Section 35 of Act 10 38 amended
235
+ 10. The principal enactment is amended by substitution for section 35, of
236
+ “Designation of critical information infrastructure
237
+ CYBERSECURITY (AMENDMENT) BILL, 2025
238
+ 8 35. (1) The Minister may, on the advice of the Authority, designate a
239
+ computer system or computer network as a critical information infrastructure if the
240
+ Minister considers that the computer system or computer network is essential for
241
+ (a) national security,
242
+ (b) the economic and social well -being of citizens , or
243
+ (c) public health and safety.
244
+ (2) Where the Minister designates a computer system or computer
245
+ network as a critical information infrastructure, the Minister shall publish the sector of the
246
+ designated critical information infrastructure in the Gazette and, the owner of the Critical
247
+ Information Infrastructure shall be notified of the designation.
248
+ (3) The Minister shall, in making a determination under subsection (1),
249
+ consider if the computer system or computer network is necessary for :
250
+ (a) the security, defence or international relations of the country;
251
+ (b) the production, preservation or identity of a confidential source of
252
+ information related to the enforcement of criminal law;
253
+ (c) the provision of services directly related to
254
+ (i) communications and telecommunications infrastructure;
255
+ (ii) banking and financial services;
256
+ (iii) public utilities
257
+ (iv) public transportation; and
258
+ (v) public key infrastructure;
259
+ (d) the protection of public safety and public health, including
260
+ systems related to essential emergency services;
261
+ (e) an international business or communication affecting a citizen of
262
+ Ghana or any other international business in which a citizen of
263
+ Ghana or the Government has an interest;
264
+ (f) the Legislature, Executive, Judiciary, Public Services or security
265
+ agencies ; or
266
+ (g) digital services;
267
+ (h) services related to the supply chain in the critical information
268
+ infrastructure ecosystem; and
269
+ (i) any other services determined by the Minister, on the advice of the
270
+ Authority .
271
+ (4) The Minister shall, by publication in the Gazette , estab lish the
272
+ procedure for the regulation of a critical information infrastructure. ”.
273
+ Section 36 of Act 10 38 amended
274
+ 11. The principal enactment is amended by substitution for section 3 6, of
275
+ “Registration of critical information in frastructure
276
+ CYBERSECURITY (AMENDMENT) BILL, 2025
277
+ 9 36. (1) The Owner of the designated critical information infrastructure shall
278
+ register their critical information infrastructure with the Authority , and shall pay the
279
+ prescribed annual registration and designation fee as determined by the Authority
280
+ or stipulated in a legislative instrument .
281
+ (2) For compliance purposes, the owner of an unregistered critical
282
+ information infrastructure shall receive a notification of registration letter from the
283
+ Authority to comply with the Act.
284
+ (3) During registration, the owner of a critical information infrastructure
285
+ shall nominate a point of contact and submit details of the nominee to the
286
+ Authority.
287
+ (4) The owner of a critical information infrastructure shall submit to the
288
+ Authority, verified details of critical information infrastructure, through the
289
+ channels and modalities determined by the Authority.
290
+ (5) The Authority shall validate the information submitted by the owner
291
+ of a critical information infrastructure, and the owner shall receive a Certificate of
292
+ Registration upon completion of registration requirements.
293
+ (6) The Authority shall through the issuance of guidelines or directives
294
+ determine :
295
+ (a) further requirements for the registration of a critical information
296
+ infrastructure ;
297
+ (b) the procedure for the registration of a critical information
298
+ infrastructure; and
299
+ (c) any other matter relating to the registration of a critical information
300
+ infrastructure.
301
+ (7) Where there is any change in the legal ownership of a designated
302
+ critical information infrastructure, the owner of the designated critical information
303
+ infrastructure shall , within seven days after the change, inform the Authority of the
304
+ change in ownership.
305
+ (8) An owner of a designated critical information infrastructure who
306
+ contravenes subsectio n (1) and (3) is liable to pay to the Authority the administrative
307
+ penalty specified in the Second Schedule. ”.
308
+ Section 37 of Act 10 38 amended
309
+ 12. The principal enactment is amended by substitution for section 37 , of
310
+ “Withdrawal of designation of critical information infrastructure
311
+ “37. (1) The Minister may, on the advice of the Authority, withdraw the
312
+ designation of a critical information infrastructure at any time if the Minister considers that
313
+ CYBERSECURITY (AMENDMENT) BILL, 2025
314
+ 10 the computer or computer network no longer satisfies the criteria of a critical information
315
+ infrastructure .
316
+ (2) The Minister may publish the withdrawal of critical information
317
+ infrastructure sector in the Gazette .
318
+ (3) The owner of a designated critical information infrastructure shall
319
+ receive notification of the withdrawal of the designation by letter from the Minister. ”.
320
+ Section 40 of Act 10 38 amended
321
+ 13. The principal enactment is amended in section 40 by substitution for subsection
322
+ (2), of
323
+ “(2) A person who contravenes subsection (1) commits an offence and is liable
324
+ on summary conviction to a fine of not less than four thousand penalty units and not more
325
+ than twenty -five thousand penalty units , or both .”.
326
+ Section 47 of Act 10 38 Amended
327
+ 14. The principal enactment is amended in section 47 by:
328
+ (a) the addition after subsection (6), of
329
+ “(7) A Sectoral Computer Emergency Response Team shall within twenty -four
330
+ hours after receiving a report of a cybersecurity incident from the owner of a designated
331
+ critical information infrastructure, a service provider, a licensee or any relevant person
332
+ report the cybersecurity incident to the National Computer Emergency Response Team.”.
333
+ (b) the addition after subsection (7), of
334
+ “(8) A Sectoral Computer Emergency Response Team who contravenes
335
+ subsection (7) is liable to pay to the Authority the administrative penalty specified in the
336
+ Second Schedule.”.
337
+ Section 49 of Act 1038 Amended
338
+ 15. The principal enactment is amended in section 49 by
339
+ (a) the substitution for subsection (1), of
340
+ “(1) A person shall not provide a cybersecurity service for a reward unless that
341
+ person obtains a licence issued by the Authority in accordance with the Act.”.
342
+ (b) the substitution for subsection (2), of
343
+ CYBERSECURITY (AMENDMENT) BILL, 2025
344
+ 11 “(2) A person shall not provide a cybersecurity service on a not -for-profit basis
345
+ unless that person obtains an accreditation issued by the Authority in accordance with the
346
+ Act.”.
347
+ (c) addition after subsection (2), of
348
+ “(3) A person who contravenes subsection (1) and (2) is liable to pay to the
349
+ Authority the administrative penalty the equivalent to the cost of damage caused and value
350
+ of the financial gain made , or an administrative penalty of not less than fifty thousand
351
+ penalty units and not more than one hundred thousand penalty units. ”.
352
+ Section 57 of Act 1038 Amended
353
+ 16. The principal enactment is amended by the substitution for section 57, of
354
+ (1) The Authority shall establish a mechanism for the accreditation of
355
+ cybersecurity professionals and practitioners.
356
+ (2) An individual shall not practise as a cybersecurity professional or
357
+ cybersecurity practitioner unless that individual has been accredited by the Authority;
358
+ (3) A person shall not knowingly engage the services of a cybersecurity
359
+ professional or cybersecurity practitioner who has not been accredited by the Authorit y;
360
+ (4) A person who contravenes subsection (2) or (3) is liable to pay to the
361
+ Authority an administrative penalty of not less than two hundred and fifty penalty units
362
+ and not more than twenty thousand penalty units .
363
+ Section 57A inserted
364
+ 17. The principal enactment is amended by the insertion after section 57, of
365
+ “Cyber hygiene certification scheme
366
+ 57B. (1) The Authority shall establish a scheme for the certification of cybersecurity
367
+ professionals and practitioners, and cybersecurity service providers who will be entrusted
368
+ to perform cyber hygiene certification service as an alternative to other international best
369
+ practice security frameworks.
370
+ (2) The Cyber Security Authority shall conduct periodic audits to ensure
371
+ compliance with the cyber hygiene certification scheme and associated prescribed fees.
372
+ (3) Any complaints regarding non -compliance shall be investigated, and
373
+ appropriate enforcement actions shall be taken. ”.
374
+ Section 57B inserted
375
+ CYBERSECURITY (AMENDMENT) BILL, 2025
376
+ 12 18. The principal enactment is amended by the insertion after section 58A, of
377
+ “Application for certification to provide the Cyber hygiene certification services
378
+ 57B. (1) A licensed cybersecurity service provider or accredited CP who seeks to
379
+ provide the Authority’s cyber hygiene certification service shall apply in writing to the
380
+ Authority.
381
+ (2) The application shall be made in the prescribed form and accompanied by
382
+ the:
383
+ (a) supporting documentation, and
384
+ (b) prescribed fee that the Authority may determine .”.
385
+ Section 57C inserted
386
+ 19. The principal enactment is amended by the insertion after section 58B, of
387
+ “Standardised , Review and Administration of Fees for Cyber Hygiene Certification
388
+ Service
389
+ 57C. (1) A certified cybersecurity professional or practitioner or cybersecurity service
390
+ provider shall charge a fee for cyber hygiene certification services as determined by the
391
+ Cyber Security Authority.
392
+ (2) A certified cybersecurity professional or practitioner or cybersecurity service
393
+ provide r shall not charge fees exceeding the approved flat rate for cyber hygiene
394
+ certification services.
395
+ (3) A certified cybersecurity professional or practitioner or cybersecurity service
396
+ provider who fails to comply wit h subsection (2) shall be liable to pay to the Authority an
397
+ administrative penalty specified in the Second Schedule (not less than two hundred and
398
+ fifty penalty units and not more than five thousand penalty units)
399
+ (4) 30% of the revenue generated by a certified cybersecurity professional or
400
+ practitioner or cybersecurity service provider under the scheme shall be paid into the
401
+ cybersecurity fund.
402
+ (5) The Cyber Security Authority shall, by notice published in the Gazette,
403
+ determine the applicable flat rate for cyber hygiene certification services.
404
+ (6) The flat rate shall be reviewed periodically based on, industry trends, and the
405
+ affordability needs of stakeholders. ”.
406
+ Section 58A inserted
407
+ 20. The principal enactment is amended by the insertion after section 5 8, of
408
+ CYBERSECURITY (AMENDMENT) BILL, 2025
409
+ 13 “Certification of the security of innovative and emerging technologies.
410
+ 58A. The Authority shall establish a mechanism for the certification of the security of
411
+ innovative and emerging technologies. ”.
412
+ Section 58B inserted
413
+ 21. The principal enactment is amended by the insertion after section 58A, of
414
+ “Accreditation of non -profit cybersecurity institutions
415
+ 58B. The Authority shall establish a mechanism for the accreditation of non -profit
416
+ cybersecurity institutions .”.
417
+ Section 59 of Act 10 38 Amended
418
+ 22. The principal enactment is amended in section 59 by the addition after paragraph
419
+ (e) of subsection (1), of
420
+ “(g) certif ication of the security of innovative and emerging technologies including
421
+ Artificial Intelligence, cloud technology, quantum computing, big data, and blockchain -
422
+ based technology. ”.
423
+ Section 59A inserted
424
+ 23. The principal enactment is amended by the insertion after section 59, of
425
+ “Enforcement powers
426
+ 59A. (1) A person who:
427
+ (a) knowingly fails to comply with or acts in contravention of this Act,
428
+ regulations or directives issued under this Act ;
429
+ (b) knowingly fails to comply with prescribed cybersecurity standards and
430
+ requirements ;
431
+ (c) provides cybersecurity services without a licence or acts as a cybersecurity
432
+ professional or practitioner without accreditation ;
433
+ (d) willfully obstructs, hinders, molests or assaults personnel of the Authority
434
+ duly engaged in the exercise of power conferred on the Authority under this
435
+ Act.
436
+ commits an offence and is liable on summary conviction to a term of imprisonment
437
+ of not more than five years or to a fine of not more than twenty thousand penalty
438
+ units, or to both.
439
+ CYBERSECURITY (AMENDMENT) BILL, 2025
440
+ 14 (1) Where an offence is committed by a corporate entity, that entity is liable to
441
+ a fine of not more than twenty thousand penalty units and each director of
442
+ that entity shall be deemed to have committed the offence.
443
+ (2) Despite subsection (1), the Authority may, where a person has breached this
444
+ Act, Regulations or directives or where a licensee, an accreditation holder
445
+ has breached a condition contained in its licence or accreditation :
446
+ (a) warn the person, licensee, or accreditation holder,
447
+ (b) issue a cease -and-desist order,
448
+ (c) apply to the High Court for
449
+ (i) an injunction to restrain the person, licensee, or accreditation
450
+ holder from continuing the breach, or
451
+ (ii) other appropriate order to enforce compliance with this Act,
452
+ (d) propose amendments to the licence or accreditation in accordance
453
+ with this Act or Regulations
454
+ (e) suspend or terminate the licence or accreditation in accordance with
455
+ this Act, or
456
+ (f) impose administrative penalties in accordance with the second
457
+ schedule of the Act
458
+ (g) or take any other action that it considers appropriate and that is not
459
+ contrary to this Act.
460
+ (4) Nothing shall prevent the Authority from pursuing only criminal
461
+ enforcement actions under subsection (1) or only administration
462
+ enforcement actions under subsection (2), or a combination of
463
+ both. ”.
464
+ Section 59B inserted
465
+ 24. The principal enactment is amended by the insertion after section 59A, of
466
+ “Power to conduct investigations and to prosecute cybercrime ”
467
+ 59B. (1) The Authority shall upon the occurrence of a cybersecurity incident or a
468
+ cybercrime conduct criminal investigations and prosecute same .”
469
+ (2) The Authority shall have the jurisdiction to prosecute all offences under the
470
+ Electronic Transactions Act, 2008 (Act 772).
471
+ (3) Where a person is convicted of a cybercrime under this Act, the Authority
472
+ may apply to the Court for an order to confiscate moneys, proceeds, benefits, properties
473
+ and assets purchased by a person with proceeds derived from or in the commission of the
474
+ cybercrime.
475
+ CYBERSECURITY (AMENDMENT) BILL, 2025
476
+ 15
477
+ (4) Despite subsection (5), nothing shall prevent the Authority from instituting
478
+ a civil action independently or at the same time as the prosecution to recover moneys,
479
+ proceeds, benefits, properties and assets purchased by a person with proceeds derived from
480
+ or in the commission of the cybercrime.
481
+ (5) In relation to subsection (6) if criminal prosecution fails, civil asset recovery
482
+ should still proceed, and confiscation orders should have the effect of a civil judgment
483
+ appealable from the High Court to the Court of Appeal. ”.
484
+ (6) Where the Authority considers that freezing of property is necessary to
485
+ facilitate an investigation or prosecution, the Authority shall in writing direct the freezing
486
+ of:
487
+ (a) the property of a person or entity being investigated; or
488
+ (b) specified property held by a person or entity other than the person
489
+ or entity being investigated or prosecuted.
490
+ (7) The Authority shall within 14 days after the freezing of the property apply
491
+ to the Court for a confirmation of the freezing.
492
+ Section 59 C inserted
493
+ 25. The principal enactment is amended by the insertion after section 59 B, of
494
+ “Power to request for information
495
+ 59C. (1) The Authority may, for the purposes of carrying out an investigation in
496
+ respect of a contravention of the Act, Regulations or any other relevant enactment by the
497
+ owner of a critical information infrastructure, a licensee, a service provider or any other
498
+ person, by notice in writing, require a person to :
499
+ (a) attend at a time and place specified in the notice; and
500
+ (b) furnish the Authority with information related to a matter relevant to the
501
+ investigation.
502
+ (2) A notice under subsection (1) shall indicate the subject matter and purpose
503
+ of the request. ”.
504
+ (3) Where a person required to furnish the Authority with an information is
505
+ under an obligation not to disclose, or asserts a right not to disclose, the Authority shall
506
+ apply to the High Court for an order for the production of the information.
507
+ (4) Where an information is furnished to the Authority, the Authority shall
508
+ make copie s or extract from the Information and request the person
509
+ CYBERSECURITY (AMENDMENT) BILL, 2025
510
+ 16 producing the information to provide an ex planation on the content of the
511
+ information where necessary.
512
+ (5) A person who appears before the Authority may be represented by a Counsel
513
+ of the choice of that person at any stage of the process .
514
+ (6) Except as provided in subsection (2) where a person contravenes subsection (1)
515
+ (a) and (b), that person commits an offence and is liable on summary conviction to a fine
516
+ of not less than two hundred and fifty penalty units and not more than ten thousand penalty
517
+ units or to a term of imprisonment of not less than one year and not more than two years
518
+ or to both. ”.
519
+ Section 59D inserted
520
+ 26. The principal enactment is amended by the insertion after section 59C, of
521
+ “Application for Production Order to Collect Computer Data
522
+ 59D. (1) An investigative officer may apply ex-parte to the High Court for a
523
+ production order to collect computer data.
524
+ (2) A production order under section (1) shall require a person in possession or
525
+ control of a computer or computer system to submit specified computer data which is
526
+ stored in the computer or computer system to the investigative officer.
527
+ (3) An investigative officer who makes an application under section (1) shall
528
+ demonstrate to the satisfaction of the Court that there are reasonable grounds to believe
529
+ that the computer data is reasonably required for the purposes of a specific criminal
530
+ invest igation.
531
+ (4) Where an investigative officer makes an application under section (1), that
532
+ investigative officer shall :
533
+ (a) explain why the investigative officer believes the computer data sought, will
534
+ be available to the person in control of the computer or computer system;
535
+ (b) identify and explain with specificity the type of computer data suspected to
536
+ be found on the computer or computer system;
537
+ (c) identify and explain with specificity the computer data that may be found on
538
+ a computer or computer system that is the subject of an investigation or
539
+ prosecution;
540
+ (d) identify and explain with specificity the offences in respect of which the
541
+ production order is sought; and
542
+ (e) indicate what measures shall be taken to ensure that the computer data will
543
+ be procured
544
+ CYBERSECURITY (AMENDMENT) BILL, 2025
545
+ 17
546
+ (i) whilst maintaining the privacy of other users, customers and third
547
+ parties, and
548
+ (ii) without the disclosure of the computer data of any party not part of
549
+ the investigation. ”.
550
+ Section 59 E inserted
551
+ 27. The principal enactment is amended by the insertion after section 59 D, of
552
+ “Grant of production order
553
+ 59E. (1) The High Court may grant an application for a production order
554
+ under subsection (1) of section 59D, if the Court is satisfied that
555
+ (a) the investigative officer has complied with subsection (3) and (4) of section
556
+ 59D;
557
+ (b) the information requested is commensurate, proportionate and necessary for
558
+ the purposes of a specific criminal investigation or prosecution;
559
+ (c) measures shall be taken to ensure that the computer data is produced whilst
560
+ maintaining the privacy of other users, customers and third parties and
561
+ without the disclosure of information and data of any party not part of the
562
+ investigation; and
563
+ (d) the investigation may be frustrated or seriously prejudiced unless the
564
+ production of the information is permitted.
565
+ (2) The Court shall require the service provider to keep confidential the
566
+ production order and the execution of the production order under this
567
+ section.
568
+ (3) A production order granted under this Act shall be served on a domestic
569
+ service provider, foreign service provider or both, or any other relevant
570
+ person. ”.
571
+ Section 59F inserted
572
+ 28. The principal enactment is amended by the insertion after section 59E, of
573
+ “Application for Warrant to Access, Search and Seize Computer Data, Computer or
574
+ Computer System
575
+ 59F. (1) An investigative officer may apply ex -parte to the High Court for a warrant to :
576
+ (a) access data on a computer or computer system;
577
+ (b) search and seize a computer or a computer system; or
578
+ CYBERSECURITY (AMENDMENT) BILL, 2025
579
+ 18 (c) make or retain a copy of computer data on a computer or computer system;
580
+ (d) maintain the integrity of computer data on a computer or computer system;
581
+ (e) remove or render inaccessible computer data on a computer or computer
582
+ system
583
+ that is suspected to contain information relevant to an investigation.
584
+ (2) A warrant under subse ction (1) shall require a person who has knowledge
585
+ about the functioning of the computer or computer system to provide the
586
+ necessary information to enable an investigative officer undertake the actions
587
+ required under paragraphs (a) to (e) of subsection (1).
588
+ (3) An investigative officer who makes an application under subsection (1) shall
589
+ demonstrate to the satisfaction of the court that there are reasonable grounds
590
+ to believe that the warrant is reasonably required for the purposes of a specific
591
+ criminal investiga tion.
592
+ (4) A warrant under subsection (1) shall permit a law enforcement officer to
593
+ expeditiously extend the search for data from a computer or computer system
594
+ to another computer or computer system or part of it in Ghana where the law
595
+ enforcement officer has reasonable grounds to believe that the data sought is
596
+ accessible from or available to that other computer or computer system or
597
+ part of it in Ghana .”.
598
+ Section 59G inserted
599
+ 29. The principal enactment is amended by the insertion after section 59F, of
600
+ “Grant of warrant
601
+ 59G. (1) The High Court may grant an application for a warrant under subsection 1
602
+ of section 59F if the Court is satisfied that
603
+ (a) the investigative officer has complied with subsection (3) of section 59F.
604
+ (b) the information requested is commensurate, proportionate and necessary
605
+ for the purposes of a specific criminal investigation or prosecution;
606
+ (c) measures shall be taken to ensure that the computer data is produced whilst
607
+ maintaining the privacy of other users, customers and third parties and
608
+ without the disclosure of information and data of any party not part of the
609
+ investigation; and
610
+ (d) the investigation may be frustrated or seriously prejudiced unless the
611
+ production of the information is permitted.
612
+ CYBERSECURITY (AMENDMENT) BILL, 2025
613
+ 19
614
+ (2) The Court shall require the service provider to keep confidential the
615
+ warrant and the execution of the warrant under this section.
616
+ (3) A warrant granted under this section shall be served on a domestic service
617
+ provider, foreign service provider or both, or any other relevant person. ”.
618
+ Section 59H in serted
619
+ 30. The principal enactment is amended by the insertion after section 59G, of
620
+ “Application for preservation order to preserve computer data
621
+ (1) A provider of wire or electronic communication services or a remote
622
+ computing service on the written request of a law enforcement agency, shall take the
623
+ necessary steps to preserve records and other evidence in its possession pending the issue
624
+ of a Court o rder and shall take steps to ensure that the request by the law enforcement
625
+ agency is not disclosed to third parties during the period.
626
+ (2) Where an order from the Court is not obtained and served for fourteen days
627
+ after the receipt of the written request, the wire or electronic communication services, or
628
+ remote computing service provider is not under any obligation to preserve the evidence.
629
+ (3) An investigative officer authorised by a designated officer may apply ex-parte
630
+ to the Court for an order to require a service provider or any person in control of a
631
+ computer or computer system to preserve specified computer data, including traffic data
632
+ that has been stored by a computer system.
633
+ (4) An investigative officer who makes an application under subsection (1) shall
634
+ demonstrate to the satisfaction of the Court that the computer data is particularly
635
+ vulnerable to loss or modification. ”.
636
+ Section 59I
637
+ 31. The principal enactment is amended by the insertion after section 59H, of
638
+ “Grant of preservation order
639
+ 59I. (1) The High Court may grant an application for a preservation order under
640
+ subsection (1) of section 59F if the Court is satisfied that :
641
+ (a) the designated officer has complied with subsection (2) of section 59H;
642
+ (b) the extent of the preservation order is commensurate, proportionate and
643
+ necessary for the purposes of a specific criminal investigation or
644
+ prosecution;
645
+ CYBERSECURITY (AMENDMENT) BILL, 2025
646
+ 20 (c) measures shall be taken to ensure that the computer data is preserved whilst
647
+ maintaining the privacy of other users, customers and third parties and
648
+ without the disclosure of information and data of any party not part of the
649
+ investigation; and
650
+ (d) the investigation may be frustrated or seriously prejudiced unless the
651
+ preservation order is permitted.
652
+ (2) The preservation order issued under subsection (1)
653
+ (a) is valid for a period of ninety days, and
654
+ (b) may be renewed for a further period of ninety days upon application to the
655
+ Court.
656
+ (3) The Court shall require the service provider to keep confidential a
657
+ preservation order issued under this regulation. ”.
658
+ Section 59J
659
+ 32. The principal enactment is amended by the insertion after section 59I, of
660
+ “Power of entry, inspection and audit
661
+ 59J. (1) The Authority may designate a person as an inspector to inspect premises.
662
+ (2) A person designated as an inspector by the Authority may
663
+ (a) obtain a warrant to :
664
+ (i) enter and inspect premises; and
665
+ (ii) conduct an audit of a computer system
666
+ (b) at any reasonable time, enter inspect premises and conduct and audit of a
667
+ computer system without a warrant where the inspector
668
+ (i) reasonably believes that an inspection of the premises is necessary to
669
+ ensure compliance by the owner of a critical information infrastructure,
670
+ a licensee or a service provider or any other relevant person with the
671
+ Act or Regulations; and
672
+ (ii) has given the occupier of the premises seven days' notice in writing of
673
+ the intention to enter , inspect the premises and conduct an audit of a
674
+ computer system .
675
+ (3) An inspector shall, before exercising a power of entry and inspection,
676
+ CYBERSECURITY (AMENDMENT) BILL, 2025
677
+ 21 (a) produce evidence of the identity of the inspector; and
678
+ (b) indicate the purpose of entry and inspection.
679
+ (4) An inspector may
680
+ (a) enter a premises;
681
+ (b) inspect the premises;
682
+ (c) inspect a document or equipment found on the premises;
683
+ (d) require a person on the premises to provide any information, or produce any
684
+ document in the possession or control of that person that the inspector
685
+ considers relevant to ensure compliance with the Act and Regulation and
686
+ (e) require any person on the premises to provide information in respect of a
687
+ document or the location of a document.
688
+ (4) For the purpose of this regulation, “premises” does not include domestic
689
+ premises. ”.
690
+ Section 59K inserted
691
+ 33. The principal enactment is amended by the insertion after section 59J, of
692
+ “Witness and Inform ant Protection
693
+ 59K. (1) Except with the written, informed, explicit and freely given consent of a
694
+ witness or inform ant, no witness or inform ant who discloses information relating to
695
+ cybercrime under this Act or administrative infractions under this Act, shall be obliged to
696
+ disclose his name or address or other personal information , or state any matter which
697
+ might lead to the ir discovery.
698
+ (2) Unless otherwise provided, t he information and the identity of the witness
699
+ or inform ant shall be held to be secret between the Authority and the witness or informer;
700
+ and all matters relating to such information should be privileged and sh all not be disclosed
701
+ in any proceedings before any court, tribunal, commission, or authority.
702
+ (3) The Authority take all necessary and reasonable steps to protect the safety
703
+ and welfare of a witness or informant , and such t he protection shall extend to persons by
704
+ virtue of being related to a witness or info rmant, or on account of a testimony given by a
705
+ witness or informer , or for any other reason upon which the Authority may consider
706
+ sufficient. ”.
707
+ Section 67 of Act 10 38 amended
708
+ 34. The principal enactment is amended by :
709
+ CYBERSECURITY (AMENDMENT) BILL, 2025
710
+ 22 (a) the substitution for the heading before section 67 “Other Online Sexual
711
+ Offences, of
712
+ “Other cybercrime”.
713
+ (b) the substitution for subsection (2) of section 67 , of
714
+ “(2) A person who contravenes subsection (1) commits an offence and is liable
715
+ on summary conviction to a term of imprisonment of not less than three years and not
716
+ more than ten years or a fine of not less than two hundred and fifty penalty units and not
717
+ more than twenty -five thousand penalty units or to both.”.
718
+ Section 67A inserted
719
+ 35. The principal enactment is amended by the insertion after section 67, of
720
+ “Cyberbullying and online harassment
721
+ 67A. (1) A person shall not use the internet or any form of electronic medium
722
+ or technology to bully a child or an adult.
723
+ (2) A person shall not use a computer or any other electronic medium to
724
+ (a) send a threatening message or a lewd message to a child;
725
+ (b) make sexual advances towards a child; or
726
+ (c) persistently make contact with
727
+ (i) a child, whether or not the contact is acceptable; or
728
+ (ii) another person where the contact is unacceptable.
729
+ (3) A person shall not create a false identity online or a fake social media
730
+ profile for the purposes of following
731
+ (a) a child, whether or not the contact is acceptable; or
732
+ (b) an adult, where the contact is unacceptable.
733
+ (4) A person shall not use any online computer service or any other
734
+ electronic device to:
735
+ (a) track the location of a child;
736
+ (b) monitor the real -world activities of a child;
737
+ (c) track the location of another person without the consent of that
738
+ person;
739
+ CYBERSECURITY (AMENDMENT) BILL, 2025
740
+ 23 (d) monitor the real -world activities of another person without the
741
+ consent of that person; or
742
+ (e) obsessively track the location of
743
+ (i) a child; or
744
+ (ii) another person without the consent of that person.
745
+ (5) Despite subsection (4), a parent or legal guardian may for the
746
+ purposes of ensuring the safety of a child, track the location of the child, or monitor
747
+ the real -world activities of the child.
748
+ (6) A person shall not use an electronic device or medium to send
749
+ (a) an unwanted,
750
+ (b) an unsolicited,
751
+ (c) a frightening,
752
+ (d) an obscene,
753
+ (e) a harassing, or
754
+ (f) a threatening electronic mail, text message or instant message to a
755
+ child or an adult.
756
+ (7) A service provider of a user -to-user service including
757
+ (a) a chatroom,
758
+ (b) a social network site,
759
+ (c) an online gaming platform, or
760
+ (d) a virtual, augmented and mixed reality shall protect a child from all
761
+ forms of online violence and cyberbullying from users of the user -
762
+ to-user service.
763
+ (8) A person shall not use an electronic device or an electronic medium
764
+ to deliberately spread false or misleading information with the intent
765
+ to deceive or manipulate a person or a fact.
766
+ (9) A person who contravenes subsections (1), (2), (3), (4) or (6) commits
767
+ an offence and is liable on summary conviction to a fine of not less
768
+ than two thousand, five hundred penalty units and not more than
769
+ five thousand penalty units or to a term of impriso nment of not less
770
+ than one year and not more than three years or to both.
771
+ CYBERSECURITY (AMENDMENT) BILL, 2025
772
+ 24 (10) A person who contravenes sub regulation (7) or (8) commits an
773
+ offence and is liable on summary conviction to a fine of not less than
774
+ five thousand penalty units and not more than twenty -five thousand
775
+ penalty units or to a term of imprisonment of not less than three
776
+ years and not more than five years or to both.
777
+ (11) For the purpose of this Act, “cyberbullying” includes any digital
778
+ communication and any other activity which strips the recipient of
779
+ the dignity of the recipient, or causes fear or physical or emotional
780
+ harm such as
781
+ (a) repeatedly sending offensive, rude and insulting messages to
782
+ a person;
783
+ (b) distributing derogatory information about a child or any other
784
+ person;
785
+ (c) posting or sending offensive photos of a child or any other
786
+ person, whether or not the photos
787
+ (i) have been digitally altered, or
788
+ (ii) were taken with the consent of the victim, with the
789
+ intention to humiliate or embarrass the victim;
790
+ (d) breaking into an electronic mail, social networking or any
791
+ electronic account and using the virtual identity of a victim to
792
+ send, upload or distribute embarrassing materials to or about
793
+ others;
794
+ (e) sharing sensitive personal information or any embarrassing
795
+ information, or tricking a child or any person to reveal
796
+ personal or embarrassing information about that child or any
797
+ other person and sharing the information obtained with
798
+ others;
799
+ (f) repeatedly sending threatening or intimidating messages with
800
+ threats of harm, or engaging in online activities that cause fear
801
+ in a child or puts any other person in fear;
802
+ (g) sending messages to another person telling the person to
803
+ commit suicide;
804
+ (h) sending grossly offensive, indecent or obscene
805
+ communication with the intention of causing emotional
806
+ distress to a child or any other person; or
807
+ (i) sending electronic messages that denigrate a child or any other
808
+ person on the basis of
809
+ (i) colour;
810
+ CYBERSECURITY (AMENDMENT) BILL, 2025
811
+ 25 (ii) race;
812
+ (iii) ethnic or national origins;
813
+ (iv) religion ;
814
+ (v) gender; or
815
+ (vi) disability. ”.
816
+ Section 67B inserted
817
+ 36. The principal enactment is amended by the insertion after section 67A, of
818
+ “Cyberstalking of a person
819
+ “67B. (1) In furtherance of section 65 of the Act, a person shall not use the
820
+ internet, chatroom , a social network site, an online gaming platform, or a virtual,
821
+ augmented and mixed reality to
822
+ (a) harass or stalk a person ;
823
+ (b) assume a false identity to anonymously harass or stalk a person;
824
+ (c) create false information about a person; or
825
+ (d) post or disseminate false information about a person.
826
+ (2) A person who contravenes subsection (1) commits an offence and is liable
827
+ on summary conviction to a term of imprisonment of not less than one year and not more
828
+ than ten years or a fine of less than one hundred penalty units and not more than ten
829
+ thousand penalty units or to both.”.
830
+ Section 68 of Act 10 38 amended
831
+ 37. The principal enactment is amended in section 68 by substitution for subsection
832
+ (2), of
833
+ “(2) A person who contravenes subsection (1) commits an offence and is liable
834
+ on summary conviction to a term of imprisonment of not less than three years and not
835
+ more than ten years or a fine of less than two hundred and fifty penalty units and not more
836
+ than twenty -five thousand penalty units or to both.”.
837
+ Section 83 of Act 1038 amended
838
+ 38. The principal enactment is amended in section 83 by:
839
+ (a) the substitution for paragraph (e) of subsection (4), of
840
+ “(e) requesting for information regarding domain name registration ;”.
841
+ (b) the addition after paragraph (e) of subsection (4) , of
842
+ CYBERSECURITY (AMENDMENT) BILL, 2025
843
+ 26 “(f) expedited disclosure of specified stored computer data in a service provider's
844
+ possession or control in an emergency situation without a request for mutual
845
+ assistance ;”.
846
+ (c) the addition after paragraph (f) of subsection (4), of
847
+ “(g) any other matter related to paragraphs (a) to ( f).”.
848
+ (d) the addition after paragraph (g) of subsection (4), of
849
+ “(5) For the purpose of this section an emergency means, a situation in which
850
+ there is a significant and imminent risk to the life or safety of any natural person. ”.
851
+ Section 90 of Act 10 38 amended
852
+ 39. The principal enactment is amended in section 90 by the addition after subsection
853
+ (2), of
854
+ “(3) Despite section 90(1) and section 90A , the Circuit Court shall have the
855
+ jurisdiction to try an offence under sections 62 to 68 A of this Act. ”.
856
+ Section 91 of Act 10 38 amended
857
+ 40. The principal enactment is amended in section 91 by the addition after paragraph
858
+ (h), of
859
+ “(i) the designation of Sectoral Computer Emergency Response Team;
860
+ (j) the accreditation of Sectoral Computer Emergency Response Team;
861
+ (k) the accreditation of cybersecurity establishments ;
862
+ (l) the accreditation of non -profit cybersecurity institution ;
863
+ (m) the protection of children ;
864
+ (n) safeguarding the development and deployment of innovative and emerging
865
+ technologies or solutions ;
866
+ (o) licensing of cyber security service providers ;
867
+ (p) accreditation of cybersecurity professionals and practitioners;
868
+ (p) any other matter required for ensuring the cybersecurity of the country .”.
869
+ Section 92 of Act 10 38 amended
870
+ 41. The principal enactment is amended in section 92 by :
871
+ (a) the substitution for subsection (1), of
872
+ “(1) the Authority may issue directives to an owner of a critical information
873
+ infrastructure, a cybersecurity service provider or service provider , innovators, developers
874
+ for the purpose of ensuring the cybersecurity of the country.
875
+ (b) the substitution for subsection (2), of
876
+ CYBERSECURITY (AMENDMENT) BILL, 2025
877
+ 27 “(2) An owner of a critical information infrastructure, cybersecurity service
878
+ provider or service provider, innovator, developer or any relevant person who fails to
879
+ comply with the directives issued under this section is liable to pay to the Authority the
880
+ admin istrative penalty specified in the Second Schedule .”.
881
+ (c) the insertion after subsection (2) , of
882
+ “(3) The Authority may issue directives to innovators, developers, service
883
+ providers and cybersecurity service providers for the purpose of securing the adoption and
884
+ deployment of innovative and emerging technologies or solutions.”.
885
+ Section 94 of Act 10 38 amended
886
+ 42. The principal enactment is amended by the substitution for section 94, of
887
+ “(1) A person who, without lawful authority accesses or retrieves subscriber
888
+ information or intercepts traffic data or content data, commits an offence and is liable on
889
+ summary conviction to a fine of not less than two thousand five hundred penalty units and
890
+ not more than fifteen thousand penalty units or to a term of imprisonment of not less than
891
+ two years and not more than five years or, to both.
892
+ (2) An attempt to gain access to a critical information infrastructure or its
893
+ dependencies, successful or not, constitutes an unlawful access to the critical information
894
+ infrastructure.
895
+ (3) Any person who attempts to secure access to a critical information
896
+ infrastructure or its dependencies, successful or not commits an offence and is liable on
897
+ summary conviction to a term of imprisonment of not less than two years and not more
898
+ than five years or a fine of not less than four thousand penalty units and not more than
899
+ twenty -five thousand penalty units or to both.
900
+ (4) The tampering and or destruction of critical information infrastructure or its
901
+ components which affects the confidentiality, integrity or availability of the critical
902
+ information infrastructure constitutes unlawful access to critical information infrastructure ,
903
+ and a person shall be subject to the same criminal penalties specified in subsection (1). ”.
904
+ Section 94A inserted
905
+ 43. The principal enactment is amended by the insertion after section 94, of
906
+ “Computer related Forgery
907
+ 95A. (1) A person shall not intentionally and without right, input, alter, delete
908
+ or suppress computer data resulting in inauthentic data with the intent that they be
909
+ considered or acted upon for legal purposes as if they were authentic, regardless of whether
910
+ or no t the data are directly readable and intelligible.”
911
+ (2) A person who contravenes subsection (1) commits an offence and
912
+ shall be deemed to have committed the offence of forger y under Chapter Two of the
913
+ CYBERSECURITY (AMENDMENT) BILL, 2025
914
+ 28 Criminal Offences Act, 1960 (Act 29) , and shall liable on summary conviction to the same
915
+ punishment for that particular kind of forgery under the law. ”.
916
+ Section 94B inserted
917
+ 44. The principal enactment is amended by the inser tion after section 94A, of
918
+ “Computer related Fraud
919
+ 95B. (1) A person shall not intentionally and without right, cause the loss of
920
+ property to another person by means of:
921
+ (a) Any input, alteration, deletion or suppression of computer data;
922
+ (b) Any interference with the functioning of a computer system;
923
+ (c) Any deception as to factual circumstances made through a computer
924
+ system that causes a person to do or omit to do anything which that person would not
925
+ otherwise do or omit to do; with the fraudulent or dishonest intent of procuring for oneself
926
+ or for another person, without right, a gain in money or other property .
927
+ (2) A person who contravenes subsection (1) commits an offence and
928
+ shall be deemed to have committed the offence of defrauding by false pretence under
929
+ section 13 1 of the Criminal Offences Act, 1960 (Act 29) , and shall liable on summary
930
+ conviction to the same punishment for defrauding by false pretence under that law. ”.
931
+ Section 97 of Act 10 38 amended
932
+ 45. The principal enactment is amended in section 97 by:
933
+ (a) the insertion before “Authority ”, of
934
+ “Artificial Intelligence is technology that enables computers and machines to
935
+ simulate human learning, comprehension, problem solving, decision -making, creativity and
936
+ autonomy ;”;
937
+ “block-chain based technology ” means a n advanced decentralised digital record or
938
+ digital ledger of transactions, that enables recording of transactions, tracking of assets, and
939
+ sharing of records across computer networks in a transparent and immutable way; ”.
940
+ “big data” means a collection of extremely large organized, semi -structured, and
941
+ unstructured information that grows exponentially over time;” ;
942
+ (b) the insertion after “child”, of
943
+ “cloud technology” means technology that delivers computing services and offers
944
+ on-demand access to computing resources such as physical or virtual services, data
945
+ storage, servers, databases, networking, intelligence, analytics, and software over the
946
+ internet ;”;
947
+ (c) the insertion after “ cloud technology”, of
948
+ CYBERSECURITY (AMENDMENT) BILL, 2025
949
+ 29 “confiscation”, which includes forfeiture where applicable, shall mean the
950
+ permanent deprivation of property by order of a court or other competent
951
+ authority;”;
952
+ (d) the insertion after “digital ecosystem” of
953
+ “digital services” means services delivered electronically whether traditionally or
954
+ by internet, with minimal physical interaction, and includes:
955
+ (a) social media platforms;
956
+ (b) e-commerce platforms;
957
+ (c) video -on-demand or streaming platforms;
958
+ (d) messaging applications,
959
+ (e) fintech software platforms;
960
+ (f) online banking platforms;
961
+ (e) the insertion after “Director -General”, of
962
+ “Emergency” shall mean a situation in which there is a significant and imminent
963
+ risk to the life or safety of any natural person;”;
964
+ (f) the insertion after “e -services”, of
965
+ “Freezing” or “seizure” mean temporarily prohibiting the transfer, conversion,
966
+ disposition or movement of property or temporarily assuming custody or control
967
+ of property on the basis of an order issued by a court or other competent
968
+ authority;”;
969
+ (g) the insertion after “interception of warrant ”, of
970
+ “Internet of Things (IoT) means a network of interrelated physical devices, vehicles,
971
+ appliances and other physical objects that connect and exchange data with other
972
+ devices and the cloud across wireless networks , and are typically embedded with
973
+ sensors, processing ability and software and can include mechanical and digital
974
+ machines and consumer objects ;”;
975
+ (h) the substitution for “computer system ”, of
976
+ “computer system” means an arrangement of interconnected computers that is
977
+ designed to perform one or more specific functions, and includes
978
+ (a) an information processing system;
979
+ (b) an operational technology system, a programmable logic controller, a
980
+ supervisory control and data acquisition system, or a distributed control
981
+ system; and
982
+ CYBERSECURITY (AMENDMENT) BILL, 2025
983
+ 30 (c) dependency systems that support the functionality of a computer or
984
+ computer network ;”;
985
+ (i) the substitution for “law enforcement agency”, of
986
+ “law enforcement agency” means
987
+ (a) The Police
988
+ (b) The Customs Division of the Ghana Revenue Authority and
989
+ (c) Cyber Security Authority
990
+ (d) Any other agency authorised by law to exercise the powers of the Police
991
+ (j) the insertion after “prohibited intimate image and visual recording”, of
992
+ “property” mean s assets of every kind, in the country or elsewhere whether
993
+ corporeal or incorporeal, movable or immovable, tangible or intangible, including
994
+ virtual assets, and legal documents or instruments evidencing title to, or interest in,
995
+ such property assets;”;
996
+ (k) the insertion after “property”, of
997
+ “property of or in the possession or control of a person” includes a gift made by
998
+ that person;
999
+ (l) the insertion after “publish”, of
1000
+ “quantum computing ” means technology that solves complex problems based on
1001
+ principles of quantum mechanics;” .
1002
+ Section 98 of Act 1058 amended
1003
+ 46. The principal enactment is amended by substitution for section 98, of
1004
+ “Repeals and savings
1005
+ (1) Sections 118 and 136 of the Electronic Transaction Act, 2008 (Act 772)
1006
+ are repealed.
1007
+ (2) Sections 35 to 40 this Act repeals sections 55 to 62 of the Electronic
1008
+ Transaction Act, 2008 (Act 772) .
1009
+ (3) Despite the repeal of sections, any Regulations, bye -laws, directives,
1010
+ notices, orders, directions, appointments or other acts lawfully made or
1011
+ done under the repealed enactment and in force immediately before the
1012
+ CYBERSECURITY (AMENDMENT) BILL, 2025
1013
+ 31 coming into force of this Act shall be deemed to have been made or done
1014
+ under this Act and shall continue to have effect until instruments issued
1015
+ or made until revoked, cancelled, withdrawn or terminated.”.
1016
+ Section 99 of Act 10 38 amended
1017
+ 47. The Extradition Act, 1960 (Act 22) is amended by:
1018
+ (a) the addition after subsection (3) of section , of
1019
+ “(4) Despite subsection (1), for the purposes of extradition , Cybercrime and
1020
+ Cyber Offences under the First Schedule to this Act, in the absence of a separate
1021
+ arrangement, bilateral or multilateral agreement between Ghana and another signatory of
1022
+ the Budapest Convention on Cybercrime or any other convention , extradition of fugitive
1023
+ criminals may still take place between Ghana and another signatory of the Budapest
1024
+ Convention on Cybercrime or any other convention and shall be governed by the
1025
+ provisions of the said convention. ”.
1026
+ First schedule to Act 1038 amended
1027
+ 48. The principal enactment is amended by substitution for the preamble, of
1028
+ “A service provided for reward or a not -for profit basis intended primarily for or aimed at
1029
+ ensuring or safeguarding the cybersecurity of a computer or computer system belonging
1030
+ to a person and includes.”.
1031
+ Second schedule to Act 10 38 amended
1032
+ 49. The principal enactment is amended by substitution for administrative penalties for
1033
+ sections 36(4), 39(2)(a), 39(2)(b), 39(2)(c) and 92(2) and addition of administrative penalties
1034
+ for sections 36(1) and 39(3)(c) .
1035
+ No. Sections of
1036
+ Act creating
1037
+ contravention Contravention Administrative
1038
+ Penalty
1039
+ 1. 36(1) The Owner of the designated critical
1040
+ information infrastructure shall register
1041
+ their critical information infrastructure
1042
+ with the Authority, and shall pay the
1043
+ prescribed annual registration and
1044
+ designation fee as determined by the
1045
+ Authority or stipulated i n a legislative
1046
+ instrument. Not less than five
1047
+ thousand penalty
1048
+ units, not more than
1049
+ fifty thousand
1050
+ penalty units .
1051
+ 2. 36(4) Owner of a registered critical information
1052
+ infrastructure failing to inform the
1053
+ Authority within seven days of the change
1054
+ in legal ownership of the registered critical Not less than five
1055
+ thousand penalty
1056
+ units, not more than
1057
+ CYBERSECURITY (AMENDMENT) BILL, 2025
1058
+ 32 information infrastructure.
1059
+ fifty thousand
1060
+ penalty units.
1061
+ 3. 39(2)(a) Owner of a critical information
1062
+ infrastructure failing to report a
1063
+ cybersecurity incident .
1064
+ Not less than ten
1065
+ thousand penalty
1066
+ units, not more than
1067
+ fifty thousand
1068
+ penalty units.
1069
+ 4. 39(2)(b) Owner of a critical information
1070
+ infrastructure failing to cause an audit to be
1071
+ performed on the critical information
1072
+ infrastructure .
1073
+ Not less than five
1074
+ thousand penalty
1075
+ units, not more than
1076
+ twenty thousand
1077
+ penalty units.
1078
+ 5. 39(3)(c) Owner of a critical information
1079
+ infrastructure failing to submit a copy of
1080
+ the audit report to the Authority.
1081
+ Not less than five
1082
+ thousand penalty
1083
+ units, not more than
1084
+ twenty thousand
1085
+ penalty units.
1086
+ 6. 92(2) Owner of a critical information
1087
+ infrastructure, a cybersecurity service
1088
+ provider or a provider of a digital service
1089
+ failing to comply with a directive issued by
1090
+ the Authority . Not less than ten
1091
+ thousand penalty
1092
+ units, not more than
1093
+ fifty thousand
1094
+ penalty units
1095
+ Third schedule to Act 1038 amended
1096
+ 50. The principal enactment is amended by addition after the “Oath of Secrecy”, of
1097
+ The Official Oath
1098
+ (section 5)
1099
+ I,
1100
+ ……………………………………………………………..……………………………
1101
+ do (in the name of the Almighty God swear) (solemnly affirm) that I will at all times well
1102
+ and truly serve the Republic of Ghana in the office of …….and that I will uphold, preserve,
1103
+ protect and defend the constituti on of the Republic of Ghana as by law established (so
1104
+ help me God).
assets/example_bills/cybersecurity-amendment-bill-2025/metadata.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "generated_at": "2026-06-01T11:48:29.371678+00:00",
3
+ "provider": "qwen",
4
+ "model": "Qwen/Qwen3-32B:cheapest",
5
+ "source_url": "https://www.csa.gov.gh/resources/Cybersecurity%20%28Amendment%29%20Draft%20Bill%202025%20final%2015102025.pdf",
6
+ "document_hash": "d879a4473b3631906b7c4e965195d53f4693a722ce7644e187ab76fca33ef763",
7
+ "chunk_size": 350,
8
+ "chunk_overlap": 60,
9
+ "embedding_model": "sentence-transformers/all-MiniLM-L6-v2"
10
+ }
assets/example_bills/manifest.json ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "cybersecurity-amendment-bill-2025",
4
+ "title": "Cybersecurity (Amendment) Bill, 2025",
5
+ "source_url": "https://www.csa.gov.gh/resources/Cybersecurity%20%28Amendment%29%20Draft%20Bill%202025%20final%2015102025.pdf",
6
+ "document_hash": "d879a4473b3631906b7c4e965195d53f4693a722ce7644e187ab76fca33ef763",
7
+ "asset_dir": "cybersecurity-amendment-bill-2025"
8
+ },
9
+ {
10
+ "id": "data-harmonisation-bill",
11
+ "title": "Data Harmonisation Bill",
12
+ "source_url": "https://moc.gov.gh/wp-content/uploads/2023/03/Data-Harmonisation-Bill.pdf",
13
+ "document_hash": "",
14
+ "asset_dir": "data-harmonisation-bill"
15
+ },
16
+ {
17
+ "id": "data-protection-bill-2025",
18
+ "title": "Data Protection Bill, 2025",
19
+ "source_url": "https://moc.gov.gh/wp-content/uploads/2023/03/DATA-PROTECTION-BILL.pdf",
20
+ "document_hash": "",
21
+ "asset_dir": "data-protection-bill-2025"
22
+ },
23
+ {
24
+ "id": "digital-economy-and-innovation-development-fund-bill-2025",
25
+ "title": "Digital Economy and Innovation Development Fund Bill, 2025",
26
+ "source_url": "https://moc.gov.gh/wp-content/uploads/2023/03/DIGITAL-ECONOMY-AND-INNOVATION-DEVELOPMENT-FUND-BILL.pdf",
27
+ "document_hash": "",
28
+ "asset_dir": "digital-economy-and-innovation-development-fund-bill-2025"
29
+ },
30
+ {
31
+ "id": "electronic-communications-bill-2025",
32
+ "title": "Electronic Communications Bill, 2025",
33
+ "source_url": "https://moc.gov.gh/wp-content/uploads/2023/03/NCA-Electronic-Communications-Bill-2025.pdf",
34
+ "document_hash": "",
35
+ "asset_dir": "electronic-communications-bill-2025"
36
+ },
37
+ {
38
+ "id": "electronic-transactions-bill-2025",
39
+ "title": "Electronic Transactions Bill, 2025",
40
+ "source_url": "https://nita.gov.gh/wp-content/uploads/2025/NITA-ETA-public-consultation-2025.pdf",
41
+ "document_hash": "",
42
+ "asset_dir": "electronic-transactions-bill-2025"
43
+ },
44
+ {
45
+ "id": "emerging-technologies-bill-2025",
46
+ "title": "Emerging Technologies Bill, 2025",
47
+ "source_url": "https://moc.gov.gh/wp-content/uploads/2023/03/MoCDTI-EMERGING-TECHNOLOGIES-DRAFT-BILL-2025-2.pdf",
48
+ "document_hash": "",
49
+ "asset_dir": "emerging-technologies-bill-2025"
50
+ },
51
+ {
52
+ "id": "ghana-domain-name-registry-bill-2025",
53
+ "title": "Ghana Domain Name Registry Bill, 2025",
54
+ "source_url": "https://moc.gov.gh/wp-content/uploads/2023/03/Ghana-Domain-Name-Registry-Bill.pdf",
55
+ "document_hash": "",
56
+ "asset_dir": "ghana-domain-name-registry-bill-2025"
57
+ },
58
+ {
59
+ "id": "ghana-meteorological-authority-bill-2025",
60
+ "title": "Ghana Meteorological Authority Bill, 2025",
61
+ "source_url": "https://moc.gov.gh/wp-content/uploads/2023/03/GHANA-METEOROLOGICAL-AUTHORITY-BILL.pdf",
62
+ "document_hash": "",
63
+ "asset_dir": "ghana-meteorological-authority-bill-2025"
64
+ },
65
+ {
66
+ "id": "kofi-annan-centre-of-excellence-in-ict-bill-2025",
67
+ "title": "Kofi Annan Centre of Excellence in ICT Bill, 2025",
68
+ "source_url": "https://moc.gov.gh/wp-content/uploads/2023/03/Kofi-Annan-Centre-of-Excellence-Bill.pdf",
69
+ "document_hash": "",
70
+ "asset_dir": "kofi-annan-centre-of-excellence-in-ict-bill-2025"
71
+ },
72
+ {
73
+ "id": "mdhi-bill-2025",
74
+ "title": "MDHI Bill, 2025",
75
+ "source_url": "https://moc.gov.gh/wp-content/uploads/2023/03/MISINFORMATION-DISINFORMATION-HATE-SPEECH-1.pdf",
76
+ "document_hash": "",
77
+ "asset_dir": "mdhi-bill-2025"
78
+ },
79
+ {
80
+ "id": "national-communications-authority-bill-2025",
81
+ "title": "National Communications Authority Bill, 2025",
82
+ "source_url": "https://moc.gov.gh/wp-content/uploads/2023/03/NCA-National-Communications-Authority-Bill-2025.pdf",
83
+ "document_hash": "",
84
+ "asset_dir": "national-communications-authority-bill-2025"
85
+ },
86
+ {
87
+ "id": "national-information-technology-authority-bill-2025",
88
+ "title": "National Information Technology Authority Bill, 2025",
89
+ "source_url": "https://moc.gov.gh/wp-content/uploads/2023/03/NITA-NATIONAL-INFORMATION-TECHNOLOGY-AUTHORITY-BILL_-10-07-25.pdf",
90
+ "document_hash": "",
91
+ "asset_dir": "national-information-technology-authority-bill-2025"
92
+ },
93
+ {
94
+ "id": "postal-courier-and-logistics-commission-bill-2025",
95
+ "title": "Postal, Courier and Logistics Commission Bill, 2025",
96
+ "source_url": "https://moc.gov.gh/wp-content/uploads/2023/03/POSTAL-COURIER-LOGISTICS-COMMISSION-BILL.pdf",
97
+ "document_hash": "",
98
+ "asset_dir": "postal-courier-and-logistics-commission-bill-2025"
99
+ }
100
+ ]
services/__init__.py ADDED
File without changes
services/example_bills.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Shared example bill manifest helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from dataclasses import dataclass
7
+ from functools import lru_cache
8
+ from pathlib import Path
9
+ from urllib.parse import urlsplit, urlunsplit
10
+
11
+
12
+ PROJECT_ROOT = Path(__file__).resolve().parent.parent
13
+ EXAMPLE_BILL_ASSETS_ROOT = PROJECT_ROOT / "assets" / "example_bills"
14
+ EXAMPLE_BILL_MANIFEST_PATH = EXAMPLE_BILL_ASSETS_ROOT / "manifest.json"
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class ExampleBill:
19
+ id: str
20
+ title: str
21
+ source_url: str
22
+ document_hash: str
23
+ asset_dir: str
24
+
25
+ @property
26
+ def root_dir(self) -> Path:
27
+ return EXAMPLE_BILL_ASSETS_ROOT / self.asset_dir
28
+
29
+ @property
30
+ def analysis_path(self) -> Path:
31
+ return self.root_dir / "analysis.json"
32
+
33
+ @property
34
+ def document_path(self) -> Path:
35
+ return self.root_dir / "document.txt"
36
+
37
+ @property
38
+ def chunks_path(self) -> Path:
39
+ return self.root_dir / "chunks.json"
40
+
41
+ @property
42
+ def metadata_path(self) -> Path:
43
+ return self.root_dir / "metadata.json"
44
+
45
+ @property
46
+ def vector_store_dir(self) -> Path:
47
+ return self.root_dir / "vector_store"
48
+
49
+
50
+ def normalize_example_url(url: str) -> str:
51
+ stripped = url.strip()
52
+ parsed = urlsplit(stripped)
53
+ normalized_path = parsed.path.rstrip("/") or parsed.path
54
+ return urlunsplit((parsed.scheme.lower(), parsed.netloc.lower(), normalized_path, parsed.query, ""))
55
+
56
+
57
+ @lru_cache(maxsize=1)
58
+ def load_example_bills() -> tuple[ExampleBill, ...]:
59
+ payload = json.loads(EXAMPLE_BILL_MANIFEST_PATH.read_text(encoding="utf-8"))
60
+ return tuple(ExampleBill(**item) for item in payload)
61
+
62
+
63
+ def reload_example_bills() -> tuple[ExampleBill, ...]:
64
+ load_example_bills.cache_clear()
65
+ return load_example_bills()
66
+
67
+
68
+ def example_bill_titles() -> list[str]:
69
+ return [bill.title for bill in load_example_bills()]
70
+
71
+
72
+ def example_bill_urls_by_title() -> dict[str, str]:
73
+ return {bill.title: bill.source_url for bill in load_example_bills()}
74
+
75
+
76
+ def example_bill_by_title(title: str) -> ExampleBill | None:
77
+ for bill in load_example_bills():
78
+ if bill.title == title:
79
+ return bill
80
+ return None
81
+
82
+
83
+ def example_bill_by_url(url: str | None) -> ExampleBill | None:
84
+ if not url:
85
+ return None
86
+ normalized = normalize_example_url(url)
87
+ for bill in load_example_bills():
88
+ if normalize_example_url(bill.source_url) == normalized:
89
+ return bill
90
+ return None
services/ingest.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Utilities for ingesting and normalizing documents."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import io
6
+ import os
7
+ import re
8
+ import tempfile
9
+ from urllib.parse import urlparse
10
+ from typing import Iterable
11
+
12
+ import requests
13
+ from docx import Document as DocxDocument
14
+ from lxml import html
15
+ from readability import Document as ReadabilityDocument
16
+ from pypdf import PdfReader
17
+
18
+ from config import MAX_UPLOAD_SIZE_MB, TIMEOUT_SECONDS
19
+
20
+ SUPPORTED_EXTENSIONS = {
21
+ ".pdf": "application/pdf",
22
+ ".txt": "text/plain",
23
+ ".md": "text/markdown",
24
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
25
+ }
26
+
27
+ _CONTROL_CHARS_RE = re.compile(r"[\x00-\x08\x0b\x0c\x0e-\x1f]")
28
+
29
+
30
+ class IngestionError(RuntimeError):
31
+ pass
32
+
33
+
34
+ def _ensure_size_limit(file_size: int) -> None:
35
+ max_bytes = MAX_UPLOAD_SIZE_MB * 1024 * 1024
36
+ if file_size > max_bytes:
37
+ raise IngestionError(
38
+ f"File exceeds maximum size of {MAX_UPLOAD_SIZE_MB} MB."
39
+ )
40
+
41
+
42
+ def extract_text_from_pdf(data: bytes) -> str:
43
+ reader = PdfReader(io.BytesIO(data))
44
+ text_chunks: list[str] = []
45
+ for page in reader.pages:
46
+ chunk = page.extract_text() or ""
47
+ text_chunks.append(chunk.strip())
48
+ return _normalize_text(_repair_known_collapsed_terms("\n".join(text_chunks)))
49
+
50
+
51
+ def extract_text_from_docx(data: bytes) -> str:
52
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".docx") as tmp:
53
+ tmp.write(data)
54
+ tmp_path = tmp.name
55
+ try:
56
+ document = DocxDocument(tmp_path)
57
+ paragraphs = [p.text.strip() for p in document.paragraphs if p.text.strip()]
58
+ return _normalize_text("\n".join(paragraphs))
59
+ finally:
60
+ os.unlink(tmp_path)
61
+
62
+
63
+ def extract_text_from_text(data: bytes) -> str:
64
+ return _normalize_text(data.decode("utf-8", errors="ignore"))
65
+
66
+
67
+ def ingest_file(filename: str, file_bytes: bytes) -> str:
68
+ _ensure_size_limit(len(file_bytes))
69
+ ext = os.path.splitext(filename)[1].lower()
70
+ if ext not in SUPPORTED_EXTENSIONS:
71
+ raise IngestionError(f"Unsupported file type: {ext}")
72
+
73
+ if ext == ".pdf":
74
+ return extract_text_from_pdf(file_bytes)
75
+ if ext == ".docx":
76
+ return extract_text_from_docx(file_bytes)
77
+ return extract_text_from_text(file_bytes)
78
+
79
+
80
+ def fetch_url_content(url: str) -> str:
81
+ response = requests.get(url, timeout=TIMEOUT_SECONDS)
82
+ response.raise_for_status()
83
+ content_type = (response.headers.get("content-type") or "").lower()
84
+ path = urlparse(url).path.lower()
85
+
86
+ if "application/pdf" in content_type or path.endswith(".pdf"):
87
+ return extract_text_from_pdf(response.content)
88
+ if (
89
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document" in content_type
90
+ or path.endswith(".docx")
91
+ ):
92
+ return extract_text_from_docx(response.content)
93
+ if any(text_type in content_type for text_type in ("text/plain", "text/markdown")) or any(
94
+ path.endswith(ext) for ext in (".txt", ".md")
95
+ ):
96
+ return extract_text_from_text(response.content)
97
+
98
+ readable = ReadabilityDocument(_strip_xml_control_chars(response.text))
99
+ summary_html = _strip_xml_control_chars(readable.summary(html_partial=True))
100
+ parsed = html.fromstring(summary_html)
101
+ title = readable.short_title() or readable.title() or ""
102
+ text_content = parsed.text_content()
103
+ return _normalize_text("\n".join([title, text_content]))
104
+
105
+
106
+ def combine_sources(sources: Iterable[str]) -> str:
107
+ return _normalize_text("\n\n".join(filter(None, sources)))
108
+
109
+
110
+ def _normalize_text(value: str) -> str:
111
+ value = _strip_xml_control_chars(value)
112
+ clean_lines = [line.strip() for line in value.splitlines()]
113
+ return "\n".join(line for line in clean_lines if line)
114
+
115
+
116
+ def _strip_xml_control_chars(value: str) -> str:
117
+ return _CONTROL_CHARS_RE.sub("", value)
118
+
119
+
120
+ def _repair_known_collapsed_terms(value: str) -> str:
121
+ replacements = {
122
+ "NATIONALINFORMATIONTECHNOLOGYAUTHORITY": "National Information Technology Authority",
123
+ "NationalInformationTechnologyAuthority": "National Information Technology Authority",
124
+ }
125
+ for collapsed, expanded in replacements.items():
126
+ value = value.replace(collapsed, expanded)
127
+ return value
services/precomputed_assets.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Helpers for bundled example-bill analysis assets."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from services.example_bills import ExampleBill, example_bill_by_url
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class PrecomputedExampleAsset:
15
+ bill: ExampleBill
16
+ analysis_payload: dict[str, Any]
17
+ document_text: str
18
+ chunks: list[str]
19
+ metadata: dict[str, Any]
20
+
21
+
22
+ def load_precomputed_asset_for_url(url: str | None) -> PrecomputedExampleAsset | None:
23
+ bill = example_bill_by_url(url)
24
+ if bill is None:
25
+ return None
26
+ return load_precomputed_asset(bill)
27
+
28
+
29
+ def load_precomputed_asset(bill: ExampleBill) -> PrecomputedExampleAsset | None:
30
+ if not bill.analysis_path.exists():
31
+ return None
32
+ if not bill.document_path.exists():
33
+ return None
34
+ if not bill.chunks_path.exists():
35
+ return None
36
+ if not bill.vector_store_dir.exists():
37
+ return None
38
+
39
+ analysis_payload = json.loads(bill.analysis_path.read_text(encoding="utf-8"))
40
+ document_text = bill.document_path.read_text(encoding="utf-8")
41
+ chunks = json.loads(bill.chunks_path.read_text(encoding="utf-8"))
42
+ metadata = {}
43
+ if bill.metadata_path.exists():
44
+ metadata = json.loads(bill.metadata_path.read_text(encoding="utf-8"))
45
+
46
+ return PrecomputedExampleAsset(
47
+ bill=bill,
48
+ analysis_payload=analysis_payload,
49
+ document_text=document_text,
50
+ chunks=chunks,
51
+ metadata=metadata,
52
+ )
53
+
54
+
55
+ def write_precomputed_asset(
56
+ bill: ExampleBill,
57
+ *,
58
+ analysis_payload: dict[str, Any],
59
+ document_text: str,
60
+ chunks: list[str],
61
+ metadata: dict[str, Any],
62
+ ) -> None:
63
+ bill.root_dir.mkdir(parents=True, exist_ok=True)
64
+ bill.analysis_path.write_text(json.dumps(analysis_payload, indent=2), encoding="utf-8")
65
+ bill.document_path.write_text(document_text, encoding="utf-8")
66
+ bill.chunks_path.write_text(json.dumps(chunks, indent=2), encoding="utf-8")
67
+ bill.metadata_path.write_text(json.dumps(metadata, indent=2), encoding="utf-8")
services/providers.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Provider utilities for handling user-supplied API keys."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Any, Dict, Optional
7
+
8
+ from anthropic import Anthropic
9
+ from openai import OpenAI
10
+
11
+ from config import (
12
+ ANTHROPIC_API_KEY,
13
+ COHERE_API_KEY,
14
+ DEFAULT_PROVIDER,
15
+ DEFAULT_COHERE_KEY,
16
+ GEMINI_API_KEY,
17
+ DEFAULT_QWEN_MODEL,
18
+ HF_TOKEN,
19
+ OPENAI_API_KEY,
20
+ PROVIDER_METADATA,
21
+ ProviderConfig,
22
+ ProviderLiteral,
23
+ )
24
+
25
+
26
+ @dataclass
27
+ class ProviderClient:
28
+ """Container for a provider client and default model."""
29
+
30
+ name: ProviderLiteral
31
+ client: Any
32
+ default_model: str
33
+ api_key: str
34
+
35
+
36
+ _PROVIDER_MODEL_MAP: Dict[ProviderLiteral, str] = {
37
+ "qwen": DEFAULT_QWEN_MODEL,
38
+ "openai": "gpt-5.5",
39
+ "anthropic": "claude-sonnet-4-6",
40
+ "gemini": "gemini-2.5-flash",
41
+ "cohere": "command-a-reasoning-08-2025",
42
+ }
43
+
44
+
45
+ _DEFAULT_PROVIDER_KEYS: Dict[ProviderLiteral, Optional[str]] = {
46
+ "qwen": HF_TOKEN,
47
+ "openai": OPENAI_API_KEY,
48
+ "anthropic": ANTHROPIC_API_KEY,
49
+ "gemini": GEMINI_API_KEY,
50
+ "cohere": COHERE_API_KEY or DEFAULT_COHERE_KEY,
51
+ }
52
+
53
+
54
+ def get_default_api_key(provider: ProviderLiteral) -> Optional[str]:
55
+ return _DEFAULT_PROVIDER_KEYS.get(provider)
56
+
57
+
58
+ def get_provider_config(name: ProviderLiteral) -> ProviderConfig:
59
+ for config in PROVIDER_METADATA:
60
+ if config.name == name:
61
+ return config
62
+ raise ValueError(f"Unsupported provider: {name}")
63
+
64
+
65
+ def validate_api_key(provider: ProviderLiteral, api_key: str | None) -> tuple[bool, Optional[str]]:
66
+ resolved_key = api_key or get_default_api_key(provider)
67
+ if not resolved_key:
68
+ return False, "API key is required."
69
+
70
+ config = get_provider_config(provider)
71
+ if config.key_prefix:
72
+ if not resolved_key.startswith(config.key_prefix):
73
+ return False, f"Expected key to start with '{config.key_prefix}'."
74
+
75
+ if len(resolved_key.strip()) < 10:
76
+ return False, "API key looks too short."
77
+
78
+ return True, None
79
+
80
+
81
+ def instantiate_client(provider: ProviderLiteral, api_key: str) -> ProviderClient:
82
+ if provider == "qwen":
83
+ client = OpenAI(api_key=api_key, base_url="https://router.huggingface.co/v1")
84
+ elif provider == "openai":
85
+ client = OpenAI(api_key=api_key)
86
+ elif provider == "anthropic":
87
+ client = Anthropic(api_key=api_key)
88
+ elif provider == "gemini":
89
+ try:
90
+ import google.generativeai as genai
91
+ except ImportError as exc: # pragma: no cover - defensive path
92
+ raise RuntimeError(
93
+ "google-generativeai must be installed to use the Gemini provider"
94
+ ) from exc
95
+
96
+ genai.configure(api_key=api_key)
97
+ client = genai
98
+ elif provider == "cohere":
99
+ try:
100
+ import cohere # type: ignore[import-not-found]
101
+ except ImportError as exc: # pragma: no cover - defensive path
102
+ raise RuntimeError("cohere must be installed to use the Cohere provider") from exc
103
+
104
+ client = cohere.Client(api_key=api_key)
105
+ else:
106
+ raise ValueError(f"Unsupported provider: {provider}")
107
+
108
+ default_model = _PROVIDER_MODEL_MAP[provider]
109
+ return ProviderClient(name=provider, client=client, default_model=default_model, api_key=api_key)
110
+
111
+
112
+ def list_providers() -> list[ProviderConfig]:
113
+ return sorted(PROVIDER_METADATA, key=lambda config: config.name != DEFAULT_PROVIDER)
services/rag_pipeline.py ADDED
@@ -0,0 +1,1276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """RAG pipeline utilities for embeddings, summaries, and Q&A."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import inspect
7
+ import json
8
+ import re
9
+ from concurrent.futures import ThreadPoolExecutor
10
+ from dataclasses import dataclass, field
11
+ from functools import lru_cache
12
+ from pathlib import Path
13
+ from typing import Any, Callable, Dict, Iterable, List, ForwardRef, Optional, Protocol, cast
14
+
15
+ try:
16
+ from pydantic.v1 import typing as _pydantic_typing # type: ignore[attr-defined]
17
+ except ImportError: # pragma: no cover - optional dependency path
18
+ _pydantic_typing = None
19
+ else:
20
+ _forward_sig = inspect.signature(ForwardRef._evaluate)
21
+ _recursive_guard_param = _forward_sig.parameters.get("recursive_guard")
22
+ if _recursive_guard_param is not None:
23
+ def _evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any:
24
+ evaluator = cast(Any, type_)._evaluate
25
+ if "type_params" in evaluator.__code__.co_varnames:
26
+ return evaluator(globalns, localns, None, recursive_guard=set())
27
+ return evaluator(globalns, localns, recursive_guard=set())
28
+
29
+ _pydantic_typing.evaluate_forwardref = _evaluate_forwardref # type: ignore[attr-defined]
30
+
31
+ from langchain_community.embeddings import SentenceTransformerEmbeddings
32
+ from langchain_community.vectorstores import FAISS
33
+ from langchain_core.documents import Document
34
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
35
+ from pydantic import BaseModel, Field
36
+ from transformers import AutoTokenizer
37
+
38
+ from config import (
39
+ ANTHROPIC_THINKING_BUDGET,
40
+ DEFAULT_CHUNK_OVERLAP,
41
+ DEFAULT_CHUNK_SIZE,
42
+ DEFAULT_EMBEDDING_MODEL,
43
+ OPENAI_REASONING_EFFORT,
44
+ PROVIDER_FULL_DOCUMENT_QA_TOKEN_BUDGETS,
45
+ SCAN_BATCH_SIZE,
46
+ SCAN_CHUNK_OVERLAP,
47
+ SCAN_CHUNK_SIZE,
48
+ SCAN_MAX_WINDOWS,
49
+ SCAN_TOP_K,
50
+ TOP_K_RETRIEVAL,
51
+ )
52
+ from services.precomputed_assets import PrecomputedExampleAsset, load_precomputed_asset_for_url
53
+ from services.providers import ProviderClient
54
+
55
+ OBJECTIVITY_INSTRUCTION = (
56
+ "Be objective. Reason things out. Based on the available context, and wherever the question "
57
+ "lends itself, consider multiple relevant perspectives, including opposing perspectives, "
58
+ "before responding."
59
+ )
60
+
61
+
62
+ class SWOTBlock(BaseModel):
63
+ strengths: List[str] = Field(default_factory=list)
64
+ weaknesses: List[str] = Field(default_factory=list)
65
+ opportunities: List[str] = Field(default_factory=list)
66
+ threats: List[str] = Field(default_factory=list)
67
+
68
+
69
+ class ImplementationItem(BaseModel):
70
+ stakeholder: str = ""
71
+ obligation: str = ""
72
+ implementation_burden: str = ""
73
+ risk_or_note: str = ""
74
+
75
+
76
+ class CritiqueItem(BaseModel):
77
+ issue: str = ""
78
+ why_it_matters: str = ""
79
+ recommendation: str = ""
80
+
81
+
82
+ class AnalysisResult(BaseModel):
83
+ executive_summary: str = ""
84
+ bill_summary: List[str] = Field(default_factory=list)
85
+ implementation: List[ImplementationItem] = Field(default_factory=list)
86
+ critique: List[CritiqueItem] = Field(default_factory=list)
87
+ swot: SWOTBlock = Field(default_factory=SWOTBlock)
88
+
89
+
90
+ class Citation(BaseModel):
91
+ ref_id: int
92
+ snippet: str
93
+
94
+
95
+ class AnswerResult(BaseModel):
96
+ answer: str
97
+ citations: List[Citation]
98
+ provenance: str = "analysis_based"
99
+ needs_deeper_consent: bool = False
100
+ deeper_answer_available: bool = False
101
+ consent_prompt: str = ""
102
+
103
+
104
+ class ScanChunk(BaseModel):
105
+ chunk_id: int
106
+ text: str
107
+
108
+
109
+ class ScanMatch(BaseModel):
110
+ chunk_id: int
111
+ relevance_score: int = Field(ge=0, le=3)
112
+ evidence_snippet: str = ""
113
+
114
+
115
+ class ScanResult(BaseModel):
116
+ matches: List[ScanMatch] = Field(default_factory=list)
117
+
118
+
119
+ class TokenizerLike(Protocol):
120
+ def encode(self, text: str, *args: Any, **kwargs: Any) -> list[int]:
121
+ ...
122
+
123
+
124
+ @dataclass
125
+ class AnalysisSnippet:
126
+ ref_id: int
127
+ section: str
128
+ text: str
129
+
130
+
131
+ @dataclass
132
+ class CachedDocumentArtifacts:
133
+ document_hash: str
134
+ document_text: str
135
+ chunks: list[str]
136
+ analysis: AnalysisResult
137
+ vector_store: FAISS | None
138
+ analysis_snippets: list[AnalysisSnippet] = field(default_factory=list)
139
+ source_url: str | None = None
140
+ example_bill_id: str | None = None
141
+ is_precomputed: bool = False
142
+ metadata: dict[str, Any] = field(default_factory=dict)
143
+
144
+
145
+ _DOCUMENT_CACHE: dict[str, CachedDocumentArtifacts] = {}
146
+
147
+
148
+ @lru_cache(maxsize=1)
149
+ def _embedding_model(model_name: str = DEFAULT_EMBEDDING_MODEL) -> SentenceTransformerEmbeddings:
150
+ return SentenceTransformerEmbeddings(model_name=model_name)
151
+
152
+
153
+ @lru_cache(maxsize=1)
154
+ def _embedding_tokenizer(model_name: str = DEFAULT_EMBEDDING_MODEL) -> TokenizerLike:
155
+ return AutoTokenizer.from_pretrained(model_name)
156
+
157
+
158
+ def warm_embedding_stack(model_name: str = DEFAULT_EMBEDDING_MODEL) -> None:
159
+ _embedding_tokenizer(model_name)
160
+ _embedding_model(model_name)
161
+
162
+
163
+ def _split_with_tokenizer(text: str, chunk_size: int, chunk_overlap: int) -> List[str]:
164
+ tokenizer = _embedding_tokenizer()
165
+ splitter = RecursiveCharacterTextSplitter(
166
+ chunk_size=chunk_size,
167
+ chunk_overlap=chunk_overlap,
168
+ length_function=lambda value: len(tokenizer.encode(value, add_special_tokens=False)),
169
+ separators=["\n\n", "\n", ". ", "; ", ", ", " ", ""],
170
+ )
171
+ return splitter.split_text(text)
172
+
173
+
174
+ def _token_count(text: str) -> int:
175
+ return len(_embedding_tokenizer().encode(text, add_special_tokens=False))
176
+
177
+
178
+ def _full_document_budget(provider_client: ProviderClient) -> int:
179
+ return PROVIDER_FULL_DOCUMENT_QA_TOKEN_BUDGETS.get(provider_client.name, 0)
180
+
181
+
182
+ def _supports_openai_reasoning(model_name: str) -> bool:
183
+ return model_name.startswith(("gpt-5", "o1", "o3", "o4"))
184
+
185
+
186
+ def _supports_anthropic_extended_thinking(model_name: str) -> bool:
187
+ return model_name.startswith(
188
+ (
189
+ "claude-3-7-sonnet",
190
+ "claude-sonnet-4",
191
+ "claude-opus-4",
192
+ )
193
+ )
194
+
195
+
196
+ def _openai_reasoning_kwargs(provider_client: ProviderClient) -> dict[str, Any]:
197
+ if provider_client.name != "openai" or not _supports_openai_reasoning(provider_client.default_model):
198
+ return {}
199
+ return {"reasoning": {"effort": OPENAI_REASONING_EFFORT}}
200
+
201
+
202
+ def _anthropic_thinking_kwargs(provider_client: ProviderClient) -> dict[str, Any]:
203
+ if provider_client.name != "anthropic" or not _supports_anthropic_extended_thinking(provider_client.default_model):
204
+ return {}
205
+ return {
206
+ "extra_body": {
207
+ "thinking": {
208
+ "type": "enabled",
209
+ "budget_tokens": ANTHROPIC_THINKING_BUDGET,
210
+ }
211
+ }
212
+ }
213
+
214
+
215
+ def split_into_chunks(text: str, chunk_size: int = DEFAULT_CHUNK_SIZE, chunk_overlap: int = DEFAULT_CHUNK_OVERLAP) -> List[str]:
216
+ return _split_with_tokenizer(text, chunk_size=chunk_size, chunk_overlap=chunk_overlap)
217
+
218
+
219
+ def split_into_scan_chunks(
220
+ text: str,
221
+ chunk_size: int = SCAN_CHUNK_SIZE,
222
+ chunk_overlap: int = SCAN_CHUNK_OVERLAP,
223
+ ) -> List[ScanChunk]:
224
+ chunks = _split_with_tokenizer(text, chunk_size=chunk_size, chunk_overlap=chunk_overlap)
225
+ return [ScanChunk(chunk_id=idx, text=chunk) for idx, chunk in enumerate(chunks, start=1)]
226
+
227
+
228
+ def build_vector_store(chunks: Iterable[str]) -> FAISS:
229
+ docs = [Document(page_content=chunk, metadata={"chunk_id": idx}) for idx, chunk in enumerate(chunks, start=1)]
230
+ embeddings = _embedding_model()
231
+ return FAISS.from_documents(docs, embedding=embeddings)
232
+
233
+
234
+ def document_hash_for_text(text: str) -> str:
235
+ return hashlib.sha256(text.encode("utf-8")).hexdigest()
236
+
237
+
238
+ def document_cache_key(document_hash: str) -> str:
239
+ return f"{document_hash}:{DEFAULT_CHUNK_SIZE}:{DEFAULT_CHUNK_OVERLAP}:{DEFAULT_EMBEDDING_MODEL}"
240
+
241
+
242
+ def get_cached_document(document_hash: str) -> CachedDocumentArtifacts | None:
243
+ return _DOCUMENT_CACHE.get(document_cache_key(document_hash))
244
+
245
+
246
+ def cache_document(artifacts: CachedDocumentArtifacts) -> CachedDocumentArtifacts:
247
+ _DOCUMENT_CACHE[document_cache_key(artifacts.document_hash)] = artifacts
248
+ return artifacts
249
+
250
+
251
+ def save_vector_store(vector_store: FAISS, output_dir: Path) -> None:
252
+ output_dir.parent.mkdir(parents=True, exist_ok=True)
253
+ vector_store.save_local(str(output_dir))
254
+
255
+
256
+ def load_vector_store(vector_store_dir: Path) -> FAISS:
257
+ return FAISS.load_local(
258
+ str(vector_store_dir),
259
+ _embedding_model(),
260
+ allow_dangerous_deserialization=True,
261
+ )
262
+
263
+
264
+ def hydrate_precomputed_example(asset: PrecomputedExampleAsset) -> CachedDocumentArtifacts:
265
+ analysis = AnalysisResult.model_validate(asset.analysis_payload)
266
+ vector_store = load_vector_store(asset.bill.vector_store_dir)
267
+ artifacts = CachedDocumentArtifacts(
268
+ document_hash=asset.bill.document_hash or document_hash_for_text(asset.document_text),
269
+ document_text=asset.document_text,
270
+ chunks=asset.chunks,
271
+ analysis=analysis,
272
+ vector_store=vector_store,
273
+ analysis_snippets=build_analysis_snippets(analysis),
274
+ source_url=asset.bill.source_url,
275
+ example_bill_id=asset.bill.id,
276
+ is_precomputed=True,
277
+ metadata=asset.metadata,
278
+ )
279
+ return cache_document(artifacts)
280
+
281
+
282
+ def _analysis_schema() -> Dict[str, Any]:
283
+ return {
284
+ "type": "object",
285
+ "properties": {
286
+ "executive_summary": {"type": "string"},
287
+ "bill_summary": {"type": "array", "items": {"type": "string"}},
288
+ "implementation": {
289
+ "type": "array",
290
+ "items": {
291
+ "type": "object",
292
+ "properties": {
293
+ "stakeholder": {"type": "string"},
294
+ "obligation": {"type": "string"},
295
+ "implementation_burden": {"type": "string"},
296
+ "risk_or_note": {"type": "string"},
297
+ },
298
+ "required": [
299
+ "stakeholder",
300
+ "obligation",
301
+ "implementation_burden",
302
+ "risk_or_note",
303
+ ],
304
+ "additionalProperties": False,
305
+ },
306
+ },
307
+ "critique": {
308
+ "type": "array",
309
+ "items": {
310
+ "type": "object",
311
+ "properties": {
312
+ "issue": {"type": "string"},
313
+ "why_it_matters": {"type": "string"},
314
+ "recommendation": {"type": "string"},
315
+ },
316
+ "required": ["issue", "why_it_matters", "recommendation"],
317
+ "additionalProperties": False,
318
+ },
319
+ },
320
+ "swot": {
321
+ "type": "object",
322
+ "properties": {
323
+ "strengths": {"type": "array", "items": {"type": "string"}},
324
+ "weaknesses": {"type": "array", "items": {"type": "string"}},
325
+ "opportunities": {"type": "array", "items": {"type": "string"}},
326
+ "threats": {"type": "array", "items": {"type": "string"}},
327
+ },
328
+ "required": ["strengths", "weaknesses", "opportunities", "threats"],
329
+ "additionalProperties": False,
330
+ },
331
+ },
332
+ "required": [
333
+ "executive_summary",
334
+ "bill_summary",
335
+ "implementation",
336
+ "critique",
337
+ "swot",
338
+ ],
339
+ "additionalProperties": False,
340
+ }
341
+
342
+
343
+ def _analysis_summary_schema() -> Dict[str, Any]:
344
+ return {
345
+ "type": "object",
346
+ "properties": {
347
+ "executive_summary": {"type": "string"},
348
+ "bill_summary": {"type": "array", "items": {"type": "string"}},
349
+ },
350
+ "required": ["executive_summary", "bill_summary"],
351
+ "additionalProperties": False,
352
+ }
353
+
354
+
355
+ def _implementation_schema() -> Dict[str, Any]:
356
+ return {
357
+ "type": "object",
358
+ "properties": {
359
+ "implementation": _analysis_schema()["properties"]["implementation"],
360
+ },
361
+ "required": ["implementation"],
362
+ "additionalProperties": False,
363
+ }
364
+
365
+
366
+ def _critique_schema() -> Dict[str, Any]:
367
+ return {
368
+ "type": "object",
369
+ "properties": {
370
+ "critique": _analysis_schema()["properties"]["critique"],
371
+ },
372
+ "required": ["critique"],
373
+ "additionalProperties": False,
374
+ }
375
+
376
+
377
+ def _swot_schema() -> Dict[str, Any]:
378
+ return {
379
+ "type": "object",
380
+ "properties": {
381
+ "swot": _analysis_schema()["properties"]["swot"],
382
+ },
383
+ "required": ["swot"],
384
+ "additionalProperties": False,
385
+ }
386
+
387
+
388
+ def _analysis_answer_schema() -> Dict[str, Any]:
389
+ return {
390
+ "type": "object",
391
+ "properties": {
392
+ "answer": {"type": "string"},
393
+ "is_sufficient": {"type": "boolean"},
394
+ },
395
+ "required": ["answer", "is_sufficient"],
396
+ "additionalProperties": False,
397
+ }
398
+
399
+
400
+ def _generate_json_payload(
401
+ provider_client: ProviderClient,
402
+ *,
403
+ prompt: str,
404
+ user_text: str,
405
+ schema: dict[str, Any],
406
+ schema_name: str,
407
+ max_tokens: int = 1500,
408
+ ) -> str:
409
+ if provider_client.name == "openai":
410
+ response = provider_client.client.responses.create(
411
+ model=provider_client.default_model,
412
+ input=[
413
+ {"role": "system", "content": prompt},
414
+ {"role": "user", "content": user_text},
415
+ ],
416
+ response_format={
417
+ "type": "json_schema",
418
+ "json_schema": {"name": schema_name, "schema": schema},
419
+ },
420
+ **_openai_reasoning_kwargs(provider_client),
421
+ )
422
+ return _extract_openai_json(response)
423
+ if provider_client.name == "qwen":
424
+ response = provider_client.client.chat.completions.create(
425
+ model=provider_client.default_model,
426
+ messages=[
427
+ {"role": "system", "content": prompt},
428
+ {
429
+ "role": "user",
430
+ "content": (
431
+ f"{user_text}\n\n"
432
+ "Return only valid JSON matching this schema:\n"
433
+ f"{json.dumps(schema)}"
434
+ ),
435
+ },
436
+ ],
437
+ temperature=0,
438
+ )
439
+ return _strip_json_fences(_extract_chat_completion_text(response))
440
+ if provider_client.name == "anthropic":
441
+ response = provider_client.client.messages.create(
442
+ model=provider_client.default_model,
443
+ max_tokens=max_tokens,
444
+ system=prompt,
445
+ messages=[{"role": "user", "content": [{"type": "text", "text": user_text}]}],
446
+ response_format={
447
+ "type": "json_schema",
448
+ "json_schema": {"name": schema_name, "schema": schema},
449
+ },
450
+ **_anthropic_thinking_kwargs(provider_client),
451
+ )
452
+ return response.content[0].text
453
+ if provider_client.name == "cohere":
454
+ response = provider_client.client.chat(
455
+ message=user_text,
456
+ model=provider_client.default_model,
457
+ preamble=prompt,
458
+ max_tokens=max_tokens,
459
+ temperature=0,
460
+ response_format={
461
+ "type": "json_object",
462
+ "schema": schema,
463
+ },
464
+ )
465
+ return _strip_json_fences(_extract_cohere_text(response))
466
+
467
+ genai = provider_client.client
468
+ model = genai.GenerativeModel(
469
+ model_name=provider_client.default_model,
470
+ system_instruction=prompt,
471
+ generation_config=genai.GenerationConfig(
472
+ temperature=0,
473
+ response_mime_type="application/json",
474
+ ),
475
+ )
476
+ response = model.generate_content(user_text)
477
+ return _extract_gemini_text(response)
478
+
479
+
480
+ def generate_analysis_progress(provider_client: ProviderClient, document_text: str) -> Iterable[tuple[str, AnalysisResult]]:
481
+ limited_text = document_text[:30000]
482
+ base_prompt = (
483
+ "You are a senior policy analyst helping citizens, civil society, startups, workers, consumers, "
484
+ "operators, and affected industries understand a government bill. Produce a clear briefing-style analysis, "
485
+ "not a generic summary. Be objective, text-disciplined, and grounded in the document. Separate what the bill "
486
+ "expressly provides from what is a reasonable inference about likely effects. Do not attribute powers, "
487
+ "safeguards, funding mechanisms, oversight tools, or policy instruments to the bill unless they are plainly "
488
+ "supported by the text. If support for a point is weak, omit it rather than embellish it. Focus on what the "
489
+ "bill changes, what ordinary people or practitioners in the affected field should be excited or worried "
490
+ "about, how businesses or industry participants may need to adapt, and what this could mean for growth, "
491
+ "friction, competition, cost, access, compliance, innovation, investor confidence, public-service delivery, "
492
+ "or digital infrastructure quality in the affected sector and, where relevant, the national economy. "
493
+ "Pay particular attention to ground-level effects on workers, professionals, entrepreneurs, recent "
494
+ "graduates, students, consumers, startups, SMEs, public institutions, and vulnerable or underserved groups "
495
+ "when the bill plausibly affects them, but do not force demographic groups into the analysis where the fit is "
496
+ "weak. Do not center implementation implications on lawmakers, legislators, or sponsoring government "
497
+ "agencies unless a direct downstream burden on the public or private sector depends on them. "
498
+ "For implementation items, identify the affected stakeholder group, the practical obligation or change in "
499
+ "behavior they may face, the likely implementation burden, and any practical risk or note. If a stakeholder "
500
+ "has no formal legal obligation, describe the real-world adjustment, exposure, opportunity, or compliance "
501
+ "expectation in plain language. Prefer concrete operational, lived-experience, and economic consequences over "
502
+ "abstract commentary. For critique items, use the structure: issue, why it matters, recommendation, and "
503
+ "focus on ambiguity, overlap, overreach, implementation bottlenecks, concentration of discretion, compliance "
504
+ "burden, and risks to competition, innovation, affordability, or access where relevant. Keep recommendations "
505
+ "tightly connected to the specific weakness identified. For SWOT, keep each item concise, specific, and "
506
+ "grounded in the bill or a clear downstream effect that follows from the bill. Avoid vague statements. "
507
+ "Avoid duplicating the same point across sections. "
508
+ + OBJECTIVITY_INSTRUCTION
509
+ )
510
+
511
+ partial = AnalysisResult()
512
+ summary_payload = _generate_json_payload(
513
+ provider_client,
514
+ prompt=base_prompt + " Return JSON for executive_summary and bill_summary only.",
515
+ user_text=f"Document content:\n\n{limited_text}",
516
+ schema=_analysis_summary_schema(),
517
+ schema_name="BillAnalysisSummary",
518
+ )
519
+ summary_result = json.loads(_strip_json_fences(summary_payload))
520
+ partial.executive_summary = summary_result.get("executive_summary", "")
521
+ partial.bill_summary = summary_result.get("bill_summary", [])
522
+ yield "Generating executive summary...", partial.model_copy(deep=True)
523
+
524
+ implementation_payload = _generate_json_payload(
525
+ provider_client,
526
+ prompt=base_prompt + " Return JSON for implementation implications only.",
527
+ user_text=f"Document content:\n\n{limited_text}",
528
+ schema=_implementation_schema(),
529
+ schema_name="BillAnalysisImplementation",
530
+ )
531
+ implementation_result = json.loads(_strip_json_fences(implementation_payload))
532
+ partial.implementation = [ImplementationItem.model_validate(item) for item in implementation_result.get("implementation", [])]
533
+ yield "Generating implementation implications...", partial.model_copy(deep=True)
534
+
535
+ critique_payload = _generate_json_payload(
536
+ provider_client,
537
+ prompt=base_prompt + " Return JSON for critique only.",
538
+ user_text=f"Document content:\n\n{limited_text}",
539
+ schema=_critique_schema(),
540
+ schema_name="BillAnalysisCritique",
541
+ )
542
+ critique_result = json.loads(_strip_json_fences(critique_payload))
543
+ partial.critique = [CritiqueItem.model_validate(item) for item in critique_result.get("critique", [])]
544
+ yield "Generating critique...", partial.model_copy(deep=True)
545
+
546
+ swot_payload = _generate_json_payload(
547
+ provider_client,
548
+ prompt=base_prompt + " Return JSON for SWOT only.",
549
+ user_text=f"Document content:\n\n{limited_text}",
550
+ schema=_swot_schema(),
551
+ schema_name="BillAnalysisSwot",
552
+ )
553
+ swot_result = json.loads(_strip_json_fences(swot_payload))
554
+ partial.swot = SWOTBlock.model_validate(swot_result.get("swot", {}))
555
+ yield "Generating SWOT...", partial.model_copy(deep=True)
556
+
557
+
558
+ def generate_analysis_once(provider_client: ProviderClient, document_text: str) -> AnalysisResult:
559
+ limited_text = document_text[:30000]
560
+ prompt = (
561
+ "You are a senior policy analyst helping citizens, civil society, startups, workers, consumers, "
562
+ "operators, and affected industries understand a government bill. Produce a clear briefing-style analysis, "
563
+ "not a generic summary. Be objective, text-disciplined, and grounded in the document. Separate what the bill "
564
+ "expressly provides from what is a reasonable inference about likely effects. Do not attribute powers, "
565
+ "safeguards, funding mechanisms, oversight tools, or policy instruments to the bill unless they are plainly "
566
+ "supported by the text. If support for a point is weak, omit it rather than embellish it. Focus on what the "
567
+ "bill changes, what ordinary people or practitioners in the affected field should be excited or worried "
568
+ "about, how businesses or industry participants may need to adapt, and what this could mean for growth, "
569
+ "friction, competition, cost, access, compliance, innovation, investor confidence, public-service delivery, "
570
+ "or digital infrastructure quality in the affected sector and, where relevant, the national economy. "
571
+ "Pay particular attention to ground-level effects on workers, professionals, entrepreneurs, recent "
572
+ "graduates, students, consumers, startups, SMEs, public institutions, and vulnerable or underserved groups "
573
+ "when the bill plausibly affects them, but do not force demographic groups into the analysis where the fit is "
574
+ "weak. Do not center implementation implications on lawmakers, legislators, or sponsoring government "
575
+ "agencies unless a direct downstream burden on the public or private sector depends on them. "
576
+ "For implementation items, identify the affected stakeholder group, the practical obligation or change in "
577
+ "behavior they may face, the likely implementation burden, and any practical risk or note. If a stakeholder "
578
+ "has no formal legal obligation, describe the real-world adjustment, exposure, opportunity, or compliance "
579
+ "expectation in plain language. Prefer concrete operational, lived-experience, and economic consequences over "
580
+ "abstract commentary. For critique items, use the structure: issue, why it matters, recommendation, and "
581
+ "focus on ambiguity, overlap, overreach, implementation bottlenecks, concentration of discretion, compliance "
582
+ "burden, and risks to competition, innovation, affordability, or access where relevant. Keep recommendations "
583
+ "tightly connected to the specific weakness identified. For SWOT, keep each item concise, specific, and "
584
+ "grounded in the bill or a clear downstream effect that follows from the bill. "
585
+ "Avoid vague statements. Avoid duplicating the same point across sections. "
586
+ "Respond using JSON that matches the provided schema. "
587
+ + OBJECTIVITY_INSTRUCTION
588
+ )
589
+ payload = _generate_json_payload(
590
+ provider_client,
591
+ prompt=prompt,
592
+ user_text=f"Document content:\n\n{limited_text}",
593
+ schema=_analysis_schema(),
594
+ schema_name="BillAnalysis",
595
+ )
596
+ return AnalysisResult.model_validate_json(_strip_json_fences(payload))
597
+
598
+
599
+ def generate_analysis(provider_client: ProviderClient, document_text: str) -> AnalysisResult:
600
+ final = AnalysisResult()
601
+ for _, partial in generate_analysis_progress(provider_client, document_text):
602
+ final = partial
603
+ return final
604
+
605
+
606
+ def prepare_document_artifacts(
607
+ document_text: str,
608
+ *,
609
+ provider_factory: Callable[[], Any] | None = None,
610
+ ) -> tuple[str, list[str], FAISS]:
611
+ document_hash = document_hash_for_text(document_text)
612
+ cached = get_cached_document(document_hash)
613
+ if cached is not None and cached.vector_store is not None:
614
+ return cached.document_hash, cached.chunks, cached.vector_store
615
+
616
+ with ThreadPoolExecutor(max_workers=2) as executor:
617
+ chunks_future = executor.submit(split_into_chunks, document_text)
618
+ provider_future = executor.submit(provider_factory) if provider_factory is not None else None
619
+ chunks = chunks_future.result()
620
+ vector_store = build_vector_store(chunks)
621
+ if provider_future is not None:
622
+ provider_future.result()
623
+ return document_hash, chunks, vector_store
624
+
625
+
626
+ def get_precomputed_example_artifacts(url: str | None) -> CachedDocumentArtifacts | None:
627
+ asset = load_precomputed_asset_for_url(url)
628
+ if asset is None:
629
+ return None
630
+ if asset.bill.document_hash:
631
+ cached = get_cached_document(asset.bill.document_hash)
632
+ if cached is not None:
633
+ return cached
634
+ return hydrate_precomputed_example(asset)
635
+
636
+
637
+ def build_cached_document_artifacts(
638
+ *,
639
+ document_text: str,
640
+ chunks: list[str],
641
+ analysis: AnalysisResult,
642
+ vector_store: FAISS | None,
643
+ source_url: str | None = None,
644
+ ) -> CachedDocumentArtifacts:
645
+ return cache_document(
646
+ CachedDocumentArtifacts(
647
+ document_hash=document_hash_for_text(document_text),
648
+ document_text=document_text,
649
+ chunks=chunks,
650
+ analysis=analysis,
651
+ vector_store=vector_store,
652
+ analysis_snippets=build_analysis_snippets(analysis),
653
+ source_url=source_url,
654
+ )
655
+ )
656
+
657
+
658
+ def build_analysis_snippets(analysis: AnalysisResult) -> list[AnalysisSnippet]:
659
+ snippets: list[AnalysisSnippet] = []
660
+ next_ref = 1
661
+
662
+ def push(section: str, text: str) -> None:
663
+ nonlocal next_ref
664
+ cleaned = " ".join(text.split()).strip()
665
+ if not cleaned:
666
+ return
667
+ snippets.append(AnalysisSnippet(ref_id=next_ref, section=section, text=cleaned))
668
+ next_ref += 1
669
+
670
+ push("Executive Summary", analysis.executive_summary)
671
+ for item in analysis.bill_summary:
672
+ push("Bill Summary", item)
673
+ for item in analysis.implementation:
674
+ push(
675
+ "Implementation",
676
+ f"{item.stakeholder}: {item.obligation}. Burden: {item.implementation_burden}. Risk/Note: {item.risk_or_note}",
677
+ )
678
+ for item in analysis.critique:
679
+ push(
680
+ "Critique",
681
+ f"{item.issue}. Why it matters: {item.why_it_matters}. Recommendation: {item.recommendation}",
682
+ )
683
+ for label, values in (
684
+ ("Strength", analysis.swot.strengths),
685
+ ("Weakness", analysis.swot.weaknesses),
686
+ ("Opportunity", analysis.swot.opportunities),
687
+ ("Threat", analysis.swot.threats),
688
+ ):
689
+ for value in values:
690
+ push(f"SWOT {label}", value)
691
+ return snippets
692
+
693
+
694
+ def _question_terms(text: str) -> set[str]:
695
+ return {term for term in re.findall(r"[a-z0-9]{3,}", text.lower())}
696
+
697
+
698
+ def search_analysis_snippets(snippets: list[AnalysisSnippet], question: str, top_k: int = 5) -> tuple[str, list[Citation]] | None:
699
+ if not snippets:
700
+ return None
701
+ terms = _question_terms(question)
702
+ if not terms:
703
+ return None
704
+
705
+ ranked: list[tuple[int, AnalysisSnippet]] = []
706
+ for snippet in snippets:
707
+ snippet_terms = _question_terms(snippet.text)
708
+ score = len(terms & snippet_terms)
709
+ if score > 0:
710
+ ranked.append((score, snippet))
711
+ if not ranked:
712
+ return None
713
+
714
+ ranked.sort(key=lambda item: (-item[0], item[1].ref_id))
715
+ selected = [snippet for _, snippet in ranked[:top_k]]
716
+ context_blocks: list[str] = []
717
+ citations: list[Citation] = []
718
+ for idx, snippet in enumerate(selected, start=1):
719
+ citations.append(Citation(ref_id=idx, snippet=snippet.text))
720
+ context_blocks.append(f"[ref{idx}] {snippet.section}\n{snippet.text}")
721
+ return "\n\n".join(context_blocks), citations
722
+
723
+
724
+ def _answer_question_from_analysis_context(
725
+ provider_client: ProviderClient,
726
+ context_text: str,
727
+ question: str,
728
+ ) -> dict[str, Any]:
729
+ prompt = (
730
+ "You answer follow-up questions about a government bill using only the provided analysis summary snippets. "
731
+ "Answer directly and clearly. Set is_sufficient to true only if the analysis snippets materially answer the "
732
+ "question. If they do not fully answer it, say what is missing and set is_sufficient to false. "
733
+ "Do not pretend you have read the full bill if you only have analysis snippets. "
734
+ + OBJECTIVITY_INSTRUCTION
735
+ )
736
+ payload = _generate_json_payload(
737
+ provider_client,
738
+ prompt=prompt,
739
+ user_text="Analysis snippets:\n" + context_text + "\n\nQuestion:\n" + question,
740
+ schema=_analysis_answer_schema(),
741
+ schema_name="AnalysisAnswer",
742
+ max_tokens=900,
743
+ )
744
+ return json.loads(_strip_json_fences(payload))
745
+
746
+
747
+ def answer_query(
748
+ provider_client: ProviderClient,
749
+ analysis: AnalysisResult | None,
750
+ vector_store: FAISS | None,
751
+ question: str,
752
+ doc_text: str | None = None,
753
+ allow_full_document: bool = False,
754
+ ) -> AnswerResult:
755
+ if analysis is not None:
756
+ snippets = build_analysis_snippets(analysis)
757
+ analysis_context = search_analysis_snippets(snippets, question)
758
+ if analysis_context is not None:
759
+ context_text, citations = analysis_context
760
+ analysis_answer = _answer_question_from_analysis_context(provider_client, context_text, question)
761
+ return AnswerResult(
762
+ answer=analysis_answer["answer"].strip(),
763
+ citations=citations,
764
+ provenance="analysis_based",
765
+ needs_deeper_consent=not analysis_answer.get("is_sufficient", False),
766
+ deeper_answer_available=bool(doc_text or vector_store is not None),
767
+ consent_prompt=(
768
+ "This answer is based on the summary and analysis. "
769
+ "If you want, I can run a deeper full-document answer next."
770
+ ),
771
+ )
772
+
773
+ if allow_full_document:
774
+ return answer_query_from_full_document(provider_client, vector_store, question, doc_text=doc_text)
775
+
776
+ return AnswerResult(
777
+ answer=(
778
+ "I couldn't fully answer that from the summary and analysis alone. "
779
+ "If you want, I can run a deeper full-document answer."
780
+ ),
781
+ citations=[],
782
+ provenance="analysis_based",
783
+ needs_deeper_consent=True,
784
+ deeper_answer_available=bool(doc_text or vector_store is not None),
785
+ consent_prompt="A deeper full-document answer is available.",
786
+ )
787
+
788
+
789
+ def answer_query_from_full_document(
790
+ provider_client: ProviderClient,
791
+ vector_store: FAISS | None,
792
+ question: str,
793
+ *,
794
+ doc_text: str | None = None,
795
+ ) -> AnswerResult:
796
+ context_text = "No context available."
797
+ citations: list[Citation] = []
798
+
799
+ if doc_text:
800
+ full_document_answer = _answer_question_from_full_document(provider_client, doc_text, question)
801
+ if full_document_answer is not None:
802
+ return AnswerResult(
803
+ answer=full_document_answer.strip(),
804
+ citations=[],
805
+ provenance="full_document",
806
+ )
807
+
808
+ scan_context = _scan_document_for_context(provider_client, doc_text, question)
809
+ if scan_context:
810
+ context_text, citations = scan_context
811
+
812
+ if not citations and vector_store is not None:
813
+ context_text, citations = _retrieve_context_from_vector_store(vector_store, question)
814
+
815
+ answer = _generate_answer_from_context(provider_client, context_text, question)
816
+ return AnswerResult(answer=answer.strip(), citations=citations, provenance="full_document")
817
+
818
+
819
+ def _answer_question_from_full_document(
820
+ provider_client: ProviderClient,
821
+ doc_text: str,
822
+ question: str,
823
+ ) -> str | None:
824
+ if _token_count(doc_text) > _full_document_budget(provider_client):
825
+ return None
826
+
827
+ prompt = (
828
+ "You answer questions about government bills. Read the full document carefully, "
829
+ "reason through the relevant provisions, and answer the user's question logically. "
830
+ "Base your answer only on the provided document. If the answer is not supported by the document, say you are unsure. "
831
+ + OBJECTIVITY_INSTRUCTION
832
+ )
833
+ user_text = "Full document:\n" + doc_text + "\n\nQuestion:\n" + question
834
+
835
+ if provider_client.name == "openai":
836
+ response = provider_client.client.responses.create(
837
+ model=provider_client.default_model,
838
+ input=[
839
+ {"role": "system", "content": prompt},
840
+ {"role": "user", "content": user_text},
841
+ ],
842
+ **_openai_reasoning_kwargs(provider_client),
843
+ )
844
+ return _extract_openai_text(response)
845
+ if provider_client.name == "qwen":
846
+ response = provider_client.client.chat.completions.create(
847
+ model=provider_client.default_model,
848
+ messages=[
849
+ {"role": "system", "content": prompt},
850
+ {"role": "user", "content": user_text},
851
+ ],
852
+ temperature=0,
853
+ )
854
+ return _extract_chat_completion_text(response)
855
+ if provider_client.name == "anthropic":
856
+ response = provider_client.client.messages.create(
857
+ model=provider_client.default_model,
858
+ max_tokens=1000,
859
+ system=prompt,
860
+ messages=[{"role": "user", "content": [{"type": "text", "text": user_text}]}],
861
+ **_anthropic_thinking_kwargs(provider_client),
862
+ )
863
+ return response.content[0].text
864
+ if provider_client.name == "cohere":
865
+ response = provider_client.client.chat(
866
+ message=user_text,
867
+ model=provider_client.default_model,
868
+ preamble=prompt,
869
+ max_tokens=1000,
870
+ temperature=0,
871
+ )
872
+ return _extract_cohere_text(response)
873
+
874
+ genai = provider_client.client
875
+ model = genai.GenerativeModel(
876
+ model_name=provider_client.default_model,
877
+ system_instruction=prompt,
878
+ generation_config=genai.GenerationConfig(
879
+ temperature=0,
880
+ response_mime_type="text/plain",
881
+ ),
882
+ )
883
+ response = model.generate_content(user_text)
884
+ return _extract_gemini_text(response)
885
+
886
+
887
+ def _retrieve_context_from_vector_store(vector_store: FAISS, question: str) -> tuple[str, list[Citation]]:
888
+ retrieved_docs = vector_store.similarity_search(question, k=TOP_K_RETRIEVAL)
889
+ context_blocks: list[str] = []
890
+ citations: list[Citation] = []
891
+ for idx, doc in enumerate(retrieved_docs, start=1):
892
+ snippet = doc.page_content.strip()
893
+ context_blocks.append(f"[{idx}] {snippet}")
894
+ citations.append(Citation(ref_id=idx, snippet=snippet))
895
+
896
+ context_text = "\n\n".join(context_blocks) if context_blocks else "No context available."
897
+ return context_text, citations
898
+
899
+
900
+ def _generate_answer_from_context(provider_client: ProviderClient, context_text: str, question: str) -> str:
901
+ prompt = (
902
+ "You answer questions about government bills. Use the context snippets provided. "
903
+ "Cite supporting snippets using [ref#] references matching the snippet number. "
904
+ "If unsure, say you are unsure. "
905
+ + OBJECTIVITY_INSTRUCTION
906
+ )
907
+
908
+ if provider_client.name == "openai":
909
+ response = provider_client.client.responses.create(
910
+ model=provider_client.default_model,
911
+ input=[
912
+ {"role": "system", "content": prompt},
913
+ {
914
+ "role": "user",
915
+ "content": (
916
+ "Context:\n" + context_text + "\n\nQuestion:\n" + question
917
+ ),
918
+ },
919
+ ],
920
+ **_openai_reasoning_kwargs(provider_client),
921
+ )
922
+ answer = _extract_openai_text(response)
923
+ elif provider_client.name == "qwen":
924
+ response = provider_client.client.chat.completions.create(
925
+ model=provider_client.default_model,
926
+ messages=[
927
+ {"role": "system", "content": prompt},
928
+ {
929
+ "role": "user",
930
+ "content": (
931
+ "Context:\n" + context_text + "\n\nQuestion:\n" + question
932
+ ),
933
+ },
934
+ ],
935
+ temperature=0,
936
+ )
937
+ answer = _extract_chat_completion_text(response)
938
+ elif provider_client.name == "anthropic":
939
+ response = provider_client.client.messages.create(
940
+ model=provider_client.default_model,
941
+ max_tokens=800,
942
+ system=prompt,
943
+ messages=[
944
+ {
945
+ "role": "user",
946
+ "content": [
947
+ {
948
+ "type": "text",
949
+ "text": (
950
+ "Context:\n" + context_text + "\n\nQuestion:\n" + question
951
+ ),
952
+ }
953
+ ],
954
+ }
955
+ ],
956
+ **_anthropic_thinking_kwargs(provider_client),
957
+ )
958
+ answer = response.content[0].text
959
+ elif provider_client.name == "cohere":
960
+ response = provider_client.client.chat(
961
+ message=(
962
+ "Context:\n" + context_text + "\n\nQuestion:\n" + question
963
+ ),
964
+ model=provider_client.default_model,
965
+ preamble=prompt,
966
+ max_tokens=800,
967
+ temperature=0,
968
+ )
969
+ answer = _extract_cohere_text(response)
970
+ else: # gemini
971
+ genai = provider_client.client
972
+ model = genai.GenerativeModel(
973
+ model_name=provider_client.default_model,
974
+ system_instruction=prompt,
975
+ generation_config=genai.GenerationConfig(
976
+ temperature=0,
977
+ response_mime_type="text/plain",
978
+ ),
979
+ )
980
+ response = model.generate_content(
981
+ "Context:\n"
982
+ + context_text
983
+ + "\n\nQuestion:\n"
984
+ + question
985
+ )
986
+ answer = _extract_gemini_text(response)
987
+
988
+ return answer
989
+
990
+
991
+ def _scan_document_for_context(
992
+ provider_client: ProviderClient,
993
+ doc_text: str,
994
+ question: str,
995
+ ) -> Optional[tuple[str, list[Citation]]]:
996
+ scan_chunks = split_into_scan_chunks(doc_text)
997
+ if not scan_chunks or len(scan_chunks) > SCAN_MAX_WINDOWS:
998
+ return None
999
+
1000
+ ranked_matches: list[ScanMatch] = []
1001
+ for batch_start in range(0, len(scan_chunks), SCAN_BATCH_SIZE):
1002
+ batch = scan_chunks[batch_start : batch_start + SCAN_BATCH_SIZE]
1003
+ parsed_result = _run_scan_batch_with_retry(provider_client, batch, question)
1004
+ if parsed_result is None:
1005
+ return None
1006
+ ranked_matches.extend(parsed_result.matches)
1007
+
1008
+ merged_matches = _merge_scan_matches(ranked_matches)
1009
+ if not merged_matches or merged_matches[0].relevance_score <= 0:
1010
+ return None
1011
+
1012
+ chunk_by_id = {chunk.chunk_id: chunk for chunk in scan_chunks}
1013
+ selected_matches = merged_matches[:SCAN_TOP_K]
1014
+ context_blocks: list[str] = []
1015
+ citations: list[Citation] = []
1016
+ for ref_id, match in enumerate(selected_matches, start=1):
1017
+ chunk = chunk_by_id.get(match.chunk_id)
1018
+ if chunk is None:
1019
+ continue
1020
+ snippet = match.evidence_snippet.strip() or _truncate_snippet(chunk.text)
1021
+ citations.append(Citation(ref_id=ref_id, snippet=snippet))
1022
+ context_blocks.append(
1023
+ f"[ref{ref_id}] Chunk {match.chunk_id} (score {match.relevance_score})\n"
1024
+ f"Evidence: {snippet}\n"
1025
+ f"Full context:\n{chunk.text.strip()}"
1026
+ )
1027
+
1028
+ if not citations:
1029
+ return None
1030
+ return "\n\n".join(context_blocks), citations
1031
+
1032
+
1033
+ def _run_scan_batch_with_retry(
1034
+ provider_client: ProviderClient,
1035
+ batch: list[ScanChunk],
1036
+ question: str,
1037
+ ) -> ScanResult | None:
1038
+ for _ in range(2):
1039
+ payload = _scan_batch(provider_client, batch, question)
1040
+ try:
1041
+ return ScanResult.model_validate_json(_strip_json_fences(payload))
1042
+ except Exception: # noqa: BLE001
1043
+ continue
1044
+ return None
1045
+
1046
+
1047
+ def _scan_batch(provider_client: ProviderClient, batch: list[ScanChunk], question: str) -> str:
1048
+ schema = {
1049
+ "type": "object",
1050
+ "properties": {
1051
+ "matches": {
1052
+ "type": "array",
1053
+ "items": {
1054
+ "type": "object",
1055
+ "properties": {
1056
+ "chunk_id": {"type": "integer"},
1057
+ "relevance_score": {"type": "integer", "minimum": 0, "maximum": 3},
1058
+ "evidence_snippet": {"type": "string"},
1059
+ },
1060
+ "required": ["chunk_id", "relevance_score", "evidence_snippet"],
1061
+ "additionalProperties": False,
1062
+ },
1063
+ }
1064
+ },
1065
+ "required": ["matches"],
1066
+ "additionalProperties": False,
1067
+ }
1068
+ prompt = (
1069
+ "You are scanning chunks from a government bill to find context relevant to a user question. "
1070
+ "Return only JSON matching the schema. Include only chunks that are at least somewhat relevant. "
1071
+ "Use relevance_score from 0 to 3, where 3 is highly relevant and 0 is irrelevant. "
1072
+ "Keep evidence_snippet short and copied from the chunk. "
1073
+ + OBJECTIVITY_INSTRUCTION
1074
+ )
1075
+ chunk_text = "\n\n".join(f"Chunk {chunk.chunk_id}:\n{chunk.text}" for chunk in batch)
1076
+ user_text = (
1077
+ f"Question:\n{question}\n\n"
1078
+ f"Chunks:\n{chunk_text}\n\n"
1079
+ f"Return JSON matching this schema:\n{json.dumps(schema)}"
1080
+ )
1081
+
1082
+ if provider_client.name == "openai":
1083
+ response = provider_client.client.responses.create(
1084
+ model=provider_client.default_model,
1085
+ input=[
1086
+ {"role": "system", "content": prompt},
1087
+ {"role": "user", "content": user_text},
1088
+ ],
1089
+ response_format={
1090
+ "type": "json_schema",
1091
+ "json_schema": {"name": "ChunkScan", "schema": schema},
1092
+ },
1093
+ **_openai_reasoning_kwargs(provider_client),
1094
+ )
1095
+ return _extract_openai_json(response)
1096
+ if provider_client.name == "qwen":
1097
+ response = provider_client.client.chat.completions.create(
1098
+ model=provider_client.default_model,
1099
+ messages=[
1100
+ {"role": "system", "content": prompt},
1101
+ {"role": "user", "content": user_text},
1102
+ ],
1103
+ temperature=0,
1104
+ )
1105
+ return _extract_chat_completion_text(response)
1106
+ if provider_client.name == "anthropic":
1107
+ response = provider_client.client.messages.create(
1108
+ model=provider_client.default_model,
1109
+ max_tokens=1200,
1110
+ system=prompt,
1111
+ messages=[{"role": "user", "content": [{"type": "text", "text": user_text}]}],
1112
+ **_anthropic_thinking_kwargs(provider_client),
1113
+ )
1114
+ return response.content[0].text
1115
+ if provider_client.name == "cohere":
1116
+ response = provider_client.client.chat(
1117
+ message=user_text,
1118
+ model=provider_client.default_model,
1119
+ preamble=prompt,
1120
+ max_tokens=1200,
1121
+ temperature=0,
1122
+ response_format={
1123
+ "type": "json_object",
1124
+ "schema": schema,
1125
+ },
1126
+ )
1127
+ return _extract_cohere_text(response)
1128
+
1129
+ genai = provider_client.client
1130
+ model = genai.GenerativeModel(
1131
+ model_name=provider_client.default_model,
1132
+ system_instruction=prompt,
1133
+ generation_config=genai.GenerationConfig(
1134
+ temperature=0,
1135
+ response_mime_type="application/json",
1136
+ ),
1137
+ )
1138
+ response = model.generate_content(user_text)
1139
+ return _extract_gemini_text(response)
1140
+
1141
+
1142
+ def _merge_scan_matches(matches: list[ScanMatch]) -> list[ScanMatch]:
1143
+ by_chunk_id: dict[int, ScanMatch] = {}
1144
+ for match in matches:
1145
+ existing = by_chunk_id.get(match.chunk_id)
1146
+ candidate_snippet = match.evidence_snippet.strip()
1147
+ if existing is None or match.relevance_score > existing.relevance_score:
1148
+ by_chunk_id[match.chunk_id] = match
1149
+ elif (
1150
+ existing.relevance_score == match.relevance_score
1151
+ and candidate_snippet
1152
+ and len(candidate_snippet) > len(existing.evidence_snippet.strip())
1153
+ ):
1154
+ by_chunk_id[match.chunk_id] = match
1155
+ return sorted(
1156
+ by_chunk_id.values(),
1157
+ key=lambda item: (-item.relevance_score, item.chunk_id),
1158
+ )
1159
+
1160
+
1161
+ def _truncate_snippet(text: str, limit: int = 220) -> str:
1162
+ compact = " ".join(text.split())
1163
+ if len(compact) <= limit:
1164
+ return compact
1165
+ return compact[: limit - 3].rstrip() + "..."
1166
+
1167
+
1168
+ def _extract_openai_json(response: Any) -> str:
1169
+ buffer = []
1170
+ output_items = getattr(response, "output", None)
1171
+ if output_items:
1172
+ for item in output_items:
1173
+ for block in item.content:
1174
+ if getattr(block, "type", None) == "output_json_schema":
1175
+ buffer.append(block.text)
1176
+ elif getattr(block, "type", None) == "output_text":
1177
+ buffer.append(block.text)
1178
+ if buffer:
1179
+ return "".join(buffer)
1180
+ if hasattr(response, "output_text"):
1181
+ return response.output_text
1182
+ return ""
1183
+
1184
+
1185
+ def _extract_openai_text(response: Any) -> str:
1186
+ buffer = []
1187
+ output_items = getattr(response, "output", None)
1188
+ if output_items:
1189
+ for item in output_items:
1190
+ for block in item.content:
1191
+ if getattr(block, "type", None) in ("output_text", "output_message"):
1192
+ buffer.append(block.text)
1193
+ if buffer:
1194
+ return "".join(buffer)
1195
+ if hasattr(response, "output_text"):
1196
+ return response.output_text
1197
+ return ""
1198
+
1199
+
1200
+ def _extract_chat_completion_text(response: Any) -> str:
1201
+ choices = getattr(response, "choices", None) or []
1202
+ if not choices:
1203
+ return ""
1204
+ message = getattr(choices[0], "message", None)
1205
+ if message is None:
1206
+ return ""
1207
+ content = getattr(message, "content", None)
1208
+ if isinstance(content, str):
1209
+ return _strip_thinking_block(content)
1210
+ if isinstance(content, list):
1211
+ parts = []
1212
+ for item in content:
1213
+ text = getattr(item, "text", None)
1214
+ if text:
1215
+ parts.append(text)
1216
+ return _strip_thinking_block("".join(parts))
1217
+ return ""
1218
+
1219
+
1220
+ def _extract_gemini_text(response: Any) -> str:
1221
+ text = getattr(response, "text", None)
1222
+ if text:
1223
+ return text
1224
+ candidates = getattr(response, "candidates", None)
1225
+ if candidates:
1226
+ parts = []
1227
+ for candidate in candidates:
1228
+ content = getattr(candidate, "content", None)
1229
+ if not content:
1230
+ continue
1231
+ for part in getattr(content, "parts", []) or []:
1232
+ segment = getattr(part, "text", None)
1233
+ if segment:
1234
+ parts.append(segment)
1235
+ if parts:
1236
+ return "".join(parts)
1237
+ return ""
1238
+
1239
+
1240
+ def _extract_cohere_text(response: Any) -> str:
1241
+ message = getattr(response, "message", None)
1242
+ if message:
1243
+ content = getattr(message, "content", None) or []
1244
+ parts = []
1245
+ for item in content:
1246
+ text = getattr(item, "text", None)
1247
+ if text:
1248
+ parts.append(text)
1249
+ if parts:
1250
+ return "".join(parts)
1251
+ text = getattr(response, "text", None)
1252
+ if text:
1253
+ return text
1254
+ return ""
1255
+
1256
+
1257
+ def _strip_json_fences(value: str) -> str:
1258
+ stripped = value.strip()
1259
+ if stripped.startswith("```json"):
1260
+ stripped = stripped[7:]
1261
+ elif stripped.startswith("```"):
1262
+ stripped = stripped[3:]
1263
+ if stripped.endswith("```"):
1264
+ stripped = stripped[:-3]
1265
+ return stripped.strip()
1266
+
1267
+
1268
+ def _strip_thinking_block(value: str) -> str:
1269
+ stripped = value.strip()
1270
+ without_think = re.sub(r"(?is)<think>.*?</think>", "", stripped).strip()
1271
+ if without_think:
1272
+ return without_think
1273
+ if "</think>" in stripped:
1274
+ _, _, tail = stripped.rpartition("</think>")
1275
+ return tail.strip()
1276
+ return stripped