File size: 25,353 Bytes
0017280
 
 
 
 
ebd834c
0017280
3626208
 
ccc3c6d
0017280
 
 
 
 
 
 
 
 
a916013
 
 
 
 
 
2317f6c
 
 
 
 
0017280
 
5ccab87
 
 
 
 
0017280
 
 
 
 
a886455
 
 
0017280
 
a886455
 
0017280
 
 
 
31a8b1a
0017280
8529300
31a8b1a
 
 
 
 
 
 
 
 
 
0017280
 
31a8b1a
0017280
 
 
 
 
 
 
 
 
 
 
8f35c1b
0017280
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91410f8
0017280
ca19369
1eac8bc
 
 
 
 
 
 
 
0017280
 
 
106354e
0017280
 
 
1eac8bc
 
 
0017280
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ebd834c
 
 
f611b99
ebd834c
 
 
 
 
9815e49
ebd834c
 
9815e49
 
 
 
9093d8f
 
 
 
 
ebd834c
8529300
8f35c1b
ebd834c
 
 
 
 
9f34a66
 
 
ebd834c
 
 
 
8529300
f611b99
 
 
 
 
 
8529300
 
 
 
37ac222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1e0ca0c
 
37ac222
a8ea1a5
 
 
 
 
 
37ac222
acd5c08
a8ea1a5
b1b4bbc
acd5c08
37ac222
 
 
3e540cd
 
 
 
05035b2
66a7714
 
05035b2
3e540cd
 
 
 
 
 
 
 
 
b82e7da
 
 
 
 
 
 
 
585c3ec
 
 
 
 
 
 
b82e7da
 
 
 
 
 
585c3ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b82e7da
 
 
 
 
 
 
 
3626208
b82e7da
3626208
 
 
 
 
 
 
b82e7da
3626208
b82e7da
3626208
 
 
 
 
 
 
b82e7da
 
 
c99694c
b82e7da
3626208
b82e7da
 
 
3626208
 
 
 
b82e7da
3626208
 
b82e7da
3626208
 
b82e7da
 
3626208
 
 
 
 
 
 
 
 
 
 
 
 
 
b82e7da
3626208
b82e7da
3626208
b82e7da
3626208
b82e7da
3626208
b82e7da
3626208
b82e7da
3626208
b82e7da
3626208
b82e7da
3626208
b82e7da
3626208
b82e7da
 
3626208
b82e7da
 
3626208
b82e7da
 
3626208
 
b82e7da
 
3626208
b82e7da
 
3626208
b82e7da
 
3626208
b82e7da
3626208
b82e7da
3626208
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b82e7da
3626208
b82e7da
3626208
b82e7da
3626208
b82e7da
3626208
b82e7da
3626208
 
b82e7da
3626208
 
b82e7da
3626208
 
c99694c
b82e7da
 
 
3626208
b82e7da
c99694c
3626208
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c99694c
b82e7da
 
 
3626208
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c99694c
 
 
3626208
 
b82e7da
 
 
3626208
 
b82e7da
05035b2
 
5fbd91a
05035b2
 
179e045
4dd0039
179e045
05035b2
fb09be0
05035b2
 
 
37ac222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
05035b2
37ac222
8529300
bfcd904
56334ae
 
 
 
49dbd33
86a2852
56334ae
 
ebd834c
d95cd31
8529300
8f35c1b
ebd834c
 
 
 
 
 
 
8529300
8f35c1b
ebd834c
 
 
 
8529300
1330e5c
 
8529300
 
fbd18c8
8529300
8f35c1b
 
 
1330e5c
8f35c1b
 
 
 
 
ebd834c
1330e5c
 
8529300
8f35c1b
ebd834c
 
 
8f35c1b
 
 
0017280
 
 
 
 
 
8f35c1b
0017280
 
 
 
8f35c1b
 
 
 
 
 
1330e5c
8f35c1b
 
 
 
f95cdb7
 
 
0017280
1330e5c
 
f95cdb7
8f35c1b
0017280
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1330e5c
 
 
 
0017280
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
"""
Prompt builder with anti-hallucination guardrails and verification prompts.

Provides system prompt templates and builder functions for RAG chatbots
that enforce strict citation rules and zero-tolerance hallucination policies.
Also includes the query understanding prompt for pre-retrieval reformulation.
"""
import os
from typing import List, Dict
from src.peri import APPROVED_OPTIONS

SYSTEM_PROMPT_TEMPLATE = """\
You are {bot_name}, a research assistant specializing in {domain}.

=====================================================================
ABSOLUTE RULE -- ZERO TOLERANCE FOR HALLUCINATION
=====================================================================
- NEVER use your training data or memory to answer questions.
  Every factual claim MUST be grounded in the provided context.
- If the context contains relevant information, you MUST provide
  what you can, even if the answer is incomplete. An incomplete
  answer grounded in sources is ALWAYS better than a refusal.
- ONLY refuse if the context contains NO relevant information at
  all. Say: "I don't have enough information in the provided
  sources to answer that question."
- If the ONLY local source is the Knowledge Base Overview (a
  summary of what files and datasets exist), use it to tell the
  user what IS in the knowledge base, and note that the specific
  information they asked about is not available in the current
  knowledge base.
- Do NOT speculate, extrapolate, or fill in gaps with outside
  knowledge. If the context is ambiguous, say so explicitly.
- STRUCTURED DATA WITH MISSING VALUES: N/A or empty values in
  dataset rows are NORMAL β€” they mean data was not collected or
  not available for that year/source. Do NOT treat N/A values as
  a reason to refuse answering. Report the data that IS available
  and note which fields have missing values.
=====================================================================

---------------------------------------------------------------------
CITATION RULES
---------------------------------------------------------------------
1. Every factual claim MUST carry a numbered endnote like [1], [2],
   etc. that maps to a reference at the end of your response.
   Do NOT output the literal text "[N]" β€” always use actual numbers.
2. Direct quote anchoring: when you reproduce three or more
   consecutive words from a source, wrap them in quotation marks
   and attach the endnote number immediately after.
3. Paraphrased claims still require a numbered endnote.

---------------------------------------------------------------------
REFERENCE LIST FORMAT
---------------------------------------------------------------------
At the end of every response, include a "References" section.

IMPORTANT: Do NOT use chunk IDs (CHUNK-LOCAL-001, CHUNK-SQL-001, CHUNK-WEB-001, etc.)
in the reference list. Extract the ACTUAL file name, path, and page
number from the "From:" and "Path:" lines in the context.

For local sources, use the file name and path:
  [1] Chenoweth_Stephan_2011.pdf (p. 23)
      -- knowledge_base/NAVCO 2.0/Chenoweth_Stephan_2011.pdf

For web sources, use author, year, title, and URL/DOI:
  [2] Nepstad, S.E. (2011). "Nonviolent Revolutions"
      DOI: https://doi.org/10.xxxx/yyyy

Source priority:
  Local sources (user's documents) > Web sources (search results)

Organize references into two groups when both are present:
  **Local Sources (Primary)**
  **Web Sources (Supplementary)**

---------------------------------------------------------------------
RESPONSE LENGTH
---------------------------------------------------------------------
Keep your response length proportional to the available evidence.
Short context = short answer.  Do NOT pad responses.

{kb_overview_section}=====================================================================
CONTEXT (use ONLY this to answer)
=====================================================================
{context}
=====================================================================
"""

VERIFICATION_PROMPT_TEMPLATE = """\
--- RESPONSE TO VERIFY ---
{response}
--- END RESPONSE ---

--- CONTEXT ---
{context}
--- END CONTEXT ---

--- PHRASE-LEVEL FLAGS ---
{phrase_flags}
--- END PHRASE FLAGS ---

--- SIMILARITY FLAGS ---
{similarity_flags}
--- END SIMILARITY FLAGS ---

Run through this 10-point verification checklist and report results:

1. Does every factual claim have a numbered citation endnote (e.g. [1], [2])?
2. For each cited claim, can you find supporting evidence in the context?
   - For prose/document sources: look for the specific sentence or passage.
   - For SQL/tabular data ([CHUNK-SQL-NNN] rows): the response may
     legitimately SUMMARIZE across multiple rows (e.g., "scores ranged
     from 3 to 5"). Verify that the individual data points exist in the
     SQL rows, even if no single row contains the exact summary statement.
     Aggregation, averaging, and range descriptions across SQL rows are
     VALID as long as the underlying numbers are present in the data.
3. Are there any claims that appear fabricated or hallucinated?
4. Are direct quotes accurately reproduced from the context?
5. Does the References section exist and list all cited sources?
6. Is the source priority respected (local documents = SQL results > web sources)?
7. Are any phrase-level flags confirmed as hallucinations?
8. Do similarity flags indicate unsupported semantic drift?
9. Is the response length proportional to the available evidence?
10. Do the citation numbers match the sources? Each citation should refer to
    a source in the References section that actually supports the claim.
    For SQL data, a single citation may cover an entire dataset/table.

Return your analysis as JSON in the following format:

{{
  "errors": [
    {{"check": 1, "description": "...", "severity": "high|medium|low"}}
  ],
  "error_count": 0,
  "pass": true,
  "summary": "Brief overall assessment"
}}

If there are no errors, return an empty "errors" list,
"error_count": 0, and "pass": true.
"""


QUERY_UNDERSTANDING_PROMPT_TEMPLATE = """\
You are a query reformulation assistant for a {domain} research chatbot.

Your job is to analyze the user's question and if the question is clear enough, rewrite it as an optimized search query

RULES for SEARCH:
- Expand abbreviations and acronyms (e.g., "HR" -> "human rights")
- Resolve pronouns using conversation history (e.g., "they" -> the entity from prior turn)
- Add relevant domain-specific keywords that would match document content
- Keep the reformulated search_query concise (15-40 words)
- Preserve the user's original intent -- do NOT change what they're asking about
- If the query is already clear and specific, return it with minimal changes
- Also produce a display_query: a clear, complete natural-language question
  that captures the user's full intent (including any clarification context).
  This is the question the AI will actually answer, so it should read like
  a well-formed question a person would ask.
- USE THE KNOWLEDGE BASE OVERVIEW below to standardize your query: use the
  exact dataset names, column names, file names, and terminology that appear
  in the knowledge base. This ensures better retrieval matching.
  (e.g., if the KB has a column "pts_a" for Political Terror Scale, rewrite
  "terror scores" as "pts_a Political Terror Scale scores")

{sql_routing_block}
{kb_overview_block}

USER'S QUESTION: {query}

Respond in JSON:
{{
  "search_query": "keyword-optimized query for vector search",
  "sql_query": "SQLite SELECT query",
  "display_query": "clear natural-language question for the AI to answer",
  "reasoning": "one sentence explaining your choice"
}}
"""

_SQL_ROUTING_INSTRUCTIONS = """\
You have access to structured SQL tables in addition to vector search.Provide a valid SQLite SELECT query
in the "sql_query" field. When route is "vector", you SHOULD still include
a "sql_query" if the data tables might answer the question as a fallback.
Use exact equality (=) for text columns. The system will automatically
retry with fuzzy matching (LIKE) if the exact query returns no results.
Use only table/column names from the schema below.

{schema_text}
"""

STATA_PROMPT_TEMPLATE = {
"do": {"rewrite": "Rewrite the following Stata do-file for improved optimization and clarity:",
        "suggestfix": "Consider any lines in the following do-file that may result in an error. "
                        "Check that the syntax is correct."
                        "Return any corrections in the form of 'current line -> corrected line'."
                        "If there are no errors, return 'No errors found'."
                        "Do-file:",
        "explain": "Explain how the following do-file works:"
        },
"code": {"rewrite": f"Rewrite the following Stata code for improved optimization and clarity:",
            "suggestfix": f"Consider this Stata code. Check for any errors. "
                        f"Return any corrections in the form of 'current line -> corrected line'.",
            "explain": f"Explain how the following Stata code works:"},
"error": {"suggestfix": "Consider the following error message, and relevant code. What is the error? "
                        "How do I fix it?"
                        "Return any corrections in the form of 'current line -> corrected line'.",
            "explain": "Explain what the following stata error code or message means. If code is provided, "
                        "describe the error in context of the code."}
}
CATEGORIZER_PROMPT_TEMPLATE = """\
You are a query-routing classifier for a {domain} research chatbot.
Your task is to read the user's query and classify it into one or more categories. Include all applicable classes in a list 
even if it is a single category and return ONLY valid JSON.
Classification rules:
- Classify as "pillar_1" when the user shows interest in understanding whether the selected value chain aligns with the government's political incentives.
- Classify as "pillar_2" when the user shows interest in understanding to what degree decisions are impacted by the lobbying of particular groups or by elite influence
- Classify as "pillar_3" when the user shows interest in understanding if the value chain/or investment intervention can be feasibly implemented given the broader institutional and policy environment
- Classify as "complete" if the input query indicates interest in all three pillars
- Classify as "qual" when shows interest in understanding if there are any political risks that could undermine the selected value chain or investment intervention
- If user input indicates the issue is resolved output "proceed" as the action, otherwise output "unresolved".
  Output:
  {{
    "category": "pillar_1"or"pillar_2"or"pillar_3"or"complete"or"qual",
    "action":"proceed"or"unresolved"
  }}
USER'S QUESTION: {query}
Return only the JSON object. Do not provide explanations, markdown, or additional text.
"""
INIT_PROMPT_TEMPLATE = """\
You are a query-routing classifier for a {domain} research chatbot.
Your task is to read the user's query and extract the country, value chain, and investment area in which the PERI analysis is to be run. Return ONLY valid JSON.
Place all output values in a list and if not specified in the user query return None

Match labels included in the user queries with options included in this items list and add the item that is the closest match. Only output None if a label is not explicitly mentioned in the user query. 
If no match is found return the label in the input
{approved_options}
Output:
{{
  "country": ["Malawi"] or None,
  "value_chain":["coffee"] or None,
  "investment":["credit"] or None
}}
USER'S QUESTION: {query}
Return only the JSON object. Do not provide explanations, markdown, or additional text.
"""

VALUE_CHAIN_PROMPT_TEMPLATE = """\
You are an expert policy and public expenditure analyst for a {domain} research chatbot.

Your task is to review retrieved documents from a knowledge base and extract evidence related to government commitment to a specified agricultural value chain.

IMPORTANT RULES

1. NEVER use your training data or memory to answer questions.
   Every factual claim MUST be grounded in the provided context.
2. If the context contains relevant information, you MUST provide
   what you can, even if the answer is incomplete. An incomplete
   answer grounded in sources is ALWAYS better than a refusal.
3. ONLY refuse if the context contains NO relevant information at
   all. If evidence is not found, return "NOT_FOUND".
4. Every extracted value must be accompanied by:
   - supporting quotation
   - source document name
   - page/section reference when available
5. Multiple documents may be provided.
6. Consolidate information across all documents.
7. STRUCTURED DATA WITH MISSING VALUES: N/A or empty values in
   dataset rows are NORMAL β€” they mean data was not collected or
   not available for that year/source. Do NOT treat N/A values as
   a reason to refuse answering. Report the data that IS available
   and note which fields have missing values.
8. Return output strictly as valid JSON.

---------------------------------------------------------------------
CITATION RULES
---------------------------------------------------------------------
1. Every factual claim MUST carry a numbered endnote like [1], [2],
   etc. that maps to a reference at the end of your response.
   Do NOT output the literal text "[N]" β€” always use actual numbers.
2. Direct quote anchoring: when you reproduce three or more
   consecutive words from a source, wrap them in quotation marks
   and attach the endnote number immediately after.
3. Paraphrased claims still require a numbered endnote.

---------------------------------------------------------------------
REFERENCE LIST FORMAT
---------------------------------------------------------------------
At the end of every response, include a "References" section.

IMPORTANT: Do NOT use chunk IDs (CHUNK-LOCAL-001, CHUNK-SQL-001, CHUNK-WEB-001, etc.)
in the reference list. Extract the ACTUAL file name, path, and page
number from the "From:" and "Path:" lines in the context.

For local sources, use the file name and path:
  [1] Chenoweth_Stephan_2011.pdf (p. 23)
      -- knowledge_base/NAVCO 2.0/Chenoweth_Stephan_2011.pdf

For web sources, use author, year, title, and URL/DOI:
  [2] Nepstad, S.E. (2011). "Nonviolent Revolutions"
      DOI: https://doi.org/10.xxxx/yyyy

Source priority:
  Local sources (user's documents) > Web sources (search results)

Organize references into two groups when both are present:
  **Local Sources (Primary)**
  **Web Sources (Supplementary)**

---------------------------------------------------------------------
RESPONSE LENGTH
---------------------------------------------------------------------
Keep your response length proportional to the available evidence.
Short context = short answer.  Do NOT pad responses.

--------------------------------------------------------
TARGET VALUE CHAIN
--------------------------------------------------------

Value Chain: {value_chain}

--------------------------------------------------------
EXTRACTION TASK
--------------------------------------------------------
{extraction_block}
"""

FINANCIAL_COMMITMENT_BLOCK = """\
Use ONLY the information contained in:

{context}

TASK

Search for:

- budget allocations
- expenditure allocations
- public investment programs
- projects
- financing commitments

Identify:

A. Budget allocation at least five years prior to current date

B. Most recent budget allocation

Extract:

- budget_year (datetime year)
- budget_value (float or int)
- currency (float or int)
- responsible_institution (string)

If multiple values exist, report all candidate values.
store the extracted information as a list of dictionaries for each candidate set of values

--------------------------------------------------------
BUDGET INFORMATION OUTPUT FORMAT
--------------------------------------------------------

[{{
    "budget_year": "",
    "budget_value": "",
    "currency": "",
    "responsible_institution": "",
    "evidence": [
      {{
        "quote": "",
        "source_document": "",
        "page_or_section": ""
      }}
    ]
  }}]
"""

STRATEGIC_COMMITMENT_BLOCK = """\

Use ONLY the information contained in:

{context}

TASKS:

a) Return a list of all national development plans that contain any mention of the value chain

b) Return a list of all agricultural strategy documents that contain any mention of the value chain

c) Return a list of all standalone strategies for the value chain

d) Return a list of all documents containing donor funded flagship projects that have any mention of the value chain

Prioritize documents published in the last five years.


Definitions

National Development Plan:
Country's primary medium-term development plan.

Agricultural Strategy:
Most recent agricultural strategy, investment plan, transformation plan,
or equivalent.

Standalone Value Chain Strategy:
Strategy primarily dedicated to the target value chain.

Flagship Project:
Major donor-supported initiative focused on the value chain.

--------------------------------------------------------
STRATEGIC COMMITMENT OUTPUT
--------------------------------------------------------
include a quote with the exact mention along with the publication year in a tuple as illustrated below

{{
    "development_plan": [{{
      "quote": "",
      "document": "",
      "page_or_section": "",
      "year": "",
    }}],

    "agricultural_strategy": [{{
      "quote": "",
      "document": "",
      "page_or_section": "",
      "year": "",
    }}],

    "standalone_strategy": [{{
      "quote": "",
      "document": "",
      "page_or_section": "",
      "year": "",
    }}],

    "flagship_project": [{{
      "quote": "",
      "document": "",
      "page_or_section": "",
      "year": "",
    }}],
}}
"""

INSTITUTIONAL_COMMITMENT_BLOCK = """\

Use ONLY the information contained in:

{context}

TASKS:

A. Return a list of documents indicating whether the value chain has a 
dedicated government agency or parastatal

B. Return a list of documents indicating whether the value chain has a 
dedicated research institute or research center

C. Return a list of documents indicating whether the value chain has a 
dedicated ministry department, directorate, authority, unit, board, or council

--------------------------------------------------------
OUTPUT FORMAT
--------------------------------------------------------
include a quote with the exact mention  as illustrated below.

{{
    "dedicated_agency": [{{
      "agency_name": "",
      "document": "",
      "page_or_section": "",
      "quote": "",
    }}],

    "dedicated_research_institute": [{{
      "institution_name": "",
      "document": "",
      "page_or_section": "",
      "quote": "",
    }}],

    "dedicated_ministerial_department": [{{
      "department_name": "",
      "document": "",
      "page_or_section": "",
      "quote": "",
    }}],
}}

"""

FINANCIAL_COMMITMENT_PROMPT = """\
Search for budget allocations, expenditure allocations, projects, programs,
or public investments associated with {value_chain}.
"""

STRATEGIC_COMMITMENT_PROMPT = """\
Extract all agricultural strategies, development plans, donor strategies, 
and {value_chain} specific strategies in the last 10 years. 

"""
INSTITUTIONAL_COMMITMENT_PROMPT = """\
Search for government agencies or parastatals, research institutes or centers,
ministry departments, directorates, authorities, or units, related to {value_chain}
"""

VC_RAG = {
    "financial_commitment":FINANCIAL_COMMITMENT_PROMPT,
    "strategic_commitment":STRATEGIC_COMMITMENT_PROMPT,
    "institutional_commitment":INSTITUTIONAL_COMMITMENT_PROMPT,
}

VC_EXTRACT = {
    "financial_commitment":FINANCIAL_COMMITMENT_BLOCK,
    "strategic_commitment":STRATEGIC_COMMITMENT_BLOCK,
    "institutional_commitment":INSTITUTIONAL_COMMITMENT_BLOCK,
}
def build_vc_analyzer_prompt(cfg:Dict,
                             retrieval_result:Dict,
                             value_chain:str,
                             domain:str
                             ):
    
    extraction_block = VC_EXTRACT[retrieval_result['var']].format(context=retrieval_result["context"])
    return VALUE_CHAIN_PROMPT_TEMPLATE.format(
        domain=domain,
        value_chain=value_chain,
        extraction_block=extraction_block
    ) 

def dict_to_string(d, indent=0):
    result = ""
    spacing = "  " * indent
    for key, value in d.items():
        if isinstance(value, dict):
            result += f"{spacing*2}{key}:\n\n" + dict_to_string(value, indent + 1)
        elif isinstance(value, list):
            result += f"{spacing*2}{key}: " + ", ".join(value) + "\n\n" + spacing
        else:
            result += f"{spacing}{key}: {value}\n\n"
            
    return result

def build_stata_prompt(query:str, 
                       subcmd:str, 
                       dofx:str, 
                       opts:List
                       ) -> str:
    
    for i in ["rewrite", "suggestfix"]:
        if opts[i]:
            return f"{STATA_PROMPT_TEMPLATE[subcmd][i]}\n{query} {dofx}"
        
    return f"{STATA_PROMPT_TEMPLATE[subcmd]['explain']}\n{query} {dofx}"

def build_categorizer_prompt(query:str,
                             domain:str
                             ):
    return CATEGORIZER_PROMPT_TEMPLATE.format(
        domain=domain,
        query=query,
    )

def build_init_prompt(query:str,
                             domain:str
                             ):
    return INIT_PROMPT_TEMPLATE.format(
        domain=domain,
        query=query,
        approved_options=APPROVED_OPTIONS
    )

def build_query_understanding_prompt(
    query: str, domain: str,
    sql_schema_summary: str = "",
    kb_overview: str = "",
) -> str:
    """Build the query understanding prompt for pre-retrieval reformulation.

    Args:
        query: The user's raw question.
        domain: Knowledge domain from config.
        history: Recent conversation messages for context resolution.
        sql_schema_summary: Compact SQL schema summary (empty if no tables).
        kb_overview: High-level KB overview for routing awareness (optional).

    Returns:
        Formatted query understanding prompt string.
    """
    if sql_schema_summary:
        sql_routing_block = _SQL_ROUTING_INSTRUCTIONS.format(
            schema_text=sql_schema_summary
        )
    else:
        sql_routing_block = '(No SQL tables available β€” always use route "vector" and omit sql_query.)\n'

    if kb_overview:
        kb_overview_block = (
            "KNOWLEDGE BASE CONTENTS (use this to understand what is available):\n"
            + kb_overview
            + "\n"
        )
    else:
        kb_overview_block = ""

    return QUERY_UNDERSTANDING_PROMPT_TEMPLATE.format(
        domain=domain,
        query=query,
        sql_routing_block=sql_routing_block,
        kb_overview_block=kb_overview_block,
    )


def build_prompt(
    context: str, bot_name: str, domain: str, kb_overview: str = "",
) -> str:
    """Build the system prompt with anti-hallucination guardrails.

    Args:
        context: The retrieved context chunks to ground the response.
        bot_name: Display name of the chatbot.
        domain: Knowledge domain the bot specializes in.
        kb_overview: High-level KB overview for general awareness (optional).

    Returns:
        Formatted system prompt string.
    """
    if kb_overview:
        overview_section = (
            "---------------------------------------------------------------------\n"
            "KNOWLEDGE BASE OVERVIEW (general awareness β€” do NOT cite this\n"
            "section directly; use it to understand the broader context)\n"
            "---------------------------------------------------------------------\n"
            f"{kb_overview}\n\n"
        )
    else:
        overview_section = ""

    # Sanitize context to prevent prompt injection via delimiter mimicry
    safe_context = context.replace("=" * 69, "\u2261" * 69)

    return SYSTEM_PROMPT_TEMPLATE.format(
        bot_name=bot_name,
        domain=domain,
        context=safe_context,
        kb_overview_section=overview_section,
    )


def build_verification_prompt(
    response: str,
    context: str,
    phrase_flags: list[str],
    similarity_flags: list[str],
) -> str:
    """Build the verification prompt for auditing an AI response.

    Args:
        response: The AI-generated response to verify.
        context: The original context the response should be grounded in.
        phrase_flags: List of phrase-level flag descriptions.
        similarity_flags: List of similarity-based flag descriptions.

    Returns:
        Formatted verification prompt string.
    """
    phrase_str = (
        "\n".join(f"- {flag}" for flag in phrase_flags)
        if phrase_flags
        else "No phrase-level flags detected."
    )
    similarity_str = (
        "\n".join(f"- {flag}" for flag in similarity_flags)
        if similarity_flags
        else "No similarity flags detected."
    )
    return VERIFICATION_PROMPT_TEMPLATE.format(
        response=response,
        context=context,
        phrase_flags=phrase_str,
        similarity_flags=similarity_str,
    )