Militaryint commited on
Commit
696c0f5
·
verified ·
1 Parent(s): f9ecdde

Update APPC.py

Browse files
Files changed (1) hide show
  1. APPC.py +175 -685
APPC.py CHANGED
@@ -1,719 +1,209 @@
1
- # app.py
2
- # Kashmir AO Action Plan for Inf — Enhanced KB-driven advisory app (NON-ACTIONABLE)
3
- # Rebuilt from your uploaded app (app (46).py) and today's requested enhancements.
4
-
5
  import os
6
- import math
7
- import re
8
  import gradio as gr
9
-
10
- # Try to import modern OpenAI client (recommended).
11
- client = None
12
- try:
13
- from openai import OpenAI
14
- # If OPENAI_API_KEY is set in environment, the OpenAI client will pick it up (or pass explicitly).
15
- client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
16
- except Exception:
17
- client = None
18
-
19
- # Banner (as provided)
20
- BANNER_URL = "https://huggingface.co/spaces/Militaryint/ops/resolve/main/banner.png"
21
 
22
  # -------------------------
23
- # QUESTIONS (exact phrasing you requested)
24
  # -------------------------
25
- STD_QUESTIONS = [
26
- "When where was enemy sighted.",
27
- "Coming from which direction",
28
- "what is the size of the enemy? How many men?",
29
- "What equipment and weapons are they carrying",
30
- "What vehicles are they using or are they on foot?",
31
- "How far are they from any roads frequented by soldiers vehicles",
32
- "How far are they from any military unit camp",
33
- "How far are they from any deployed soldiers",
34
- "Are they getting support of locals? If so who are these locals?",
35
- "What is their disposition? How are they spread out in formation? Are they moving closely tight or are they spread out?",
36
- "Do you as the security force commander have Reconnaissance and Surveillance soldiers near the area where these enemy sighted?",
37
- "Did you get the information of sighting of enemy from local source or army personnel?",
38
- "If you got information from local source did you confirm.that information from second source or by sending your Reconnaissance and Surveillance team to observe and confirm truth of what source said? Please remember source can be planted by enemy to provide false information in order to lead your security forces to an ambush site of the enemy",
39
- "How far is your commanded army unit from the place where enemy is sighted?",
40
- "is the terrain urban or semi urban or jungle or hilly or rural?"
41
- ]
42
-
43
- ENH_SECTION_A = [
44
- "Does the Bn have separate Ops planning and Int sections? Or do both have a common nodal point,for example an officer or JCO conversant with both Ops and Int.( Integration).",
45
- "Does the Unit have int SOP? Is there any Course of Action template with provision for int feeds? Is there any Ops template?Ops SOP?",
46
- "Does the unit have a proper reconnaissance and surveillance plan?Is it integrated with Ops and Int SOP?",
47
- "Does the unit have Force Protection SOP? Threat Conditions Levels?FP specific route planner? Base FP review capability denoted by intelligence and counterintelligence vulnerability reports?",
48
- "Does the unit have intelligence projection capability?In shape of forward intelligence surveillance and reconnaissance nodes,covert and regular? These nodes are one or two manned emplacements outside base in specific sectors of the AOR? May even comprise small teams.Intelligence projection is required so that these cells can be activated any time on receipt of info say enemy movement in nearby areas of the sector of AOR where they are emplaced.",
49
- "Is there a vulnerablity analysis tool for unit? For example to determine vulnerable assets and critical assets.the former if compromised will not halt unit ops and mission but the latter will.The physical layout of camp assets for example should be out of reach of intrusive action like a breach of camp perimeter or attack on security gate.",
50
- "Does the unit employ Randomness in guarding,troops movement and officers movement? Randomness is necessary to thwart enemy surveillance.",
51
- "Is there a source vetting system in place? Is counterintelligence used to confirm source levels of efficiency and trust? Does the unit have a source registry?",
52
- "Does the unit treat intelligence as only sourced information or as a repository of different tactics and techniques? Is there any intelligence doctrine or manual in unit library detailing these tactics?",
53
- "Does the unit use counterintelligence in vulnerability analysis of ops and base safety? Is it understood that CI is not only spying but is also a repository of intelligence tactics amended to attack enemy intent and enemy situational Awareness?Does the unit int section have provisions to employ force protection in terms of intelligence and CI and not just use static physical protection methods and routine convoy protection?",
54
- "Does the unit have embedded int personnel in routine ops like checkpoint,road blocks,cordon and search,patrols and R&S teams? This facilitates screening and advance Warn."
55
- ]
56
-
57
- ENH_SECTION_B = [
58
- "Am I thinking of the Threat or am I thinking about my COs Situational Awareness.",
59
- "What is my intent as Staff planning element?Do i align myself with Cdrs Intent or do I point out all the alternatives not apparent to him?",
60
- "Do I detect the enemy or do I deter him from hostile action,or do i deny him avenues of hostile action and intelligence about our activities and plans,or do I deliver the locals from his influence or do I plan to outright destroy him physically.The 5Ds.",
61
- "My unit has no organic intelligence assets.I have to depend on sources and inputs from MI units.Do their way of working conform to 5D system?Can these assets get me information in line with 5D system requirements?Can I use my Bn soldiers somehow to work as per 5D system?",
62
- "Using two guide rails of 5D system,viz Deter and Deny did i make out a Vulnerability Assessment of my Bn security measures and information security both at base,during routine ops and deployed areas of troops outside base?Did I conduct a red teaming on these two guide rails as premise?",
63
- "How do I account for Force Protection based on Q5?Am I giving FP top priority?",
64
- "Do we attack the threats situational awareness,or his freedom of movement,or his tactics or his local support? Do i have the intelligence capability to assess on these lines?",
65
- "Is the call for operation Deliberate or Quick?Do I have the appropriate int and R&S assets to report in both cases? In Quick mode do I have projected intelligence capability,that is do I have covert or overt int and R&S assets emplaced near or at the area of reported threat activity? If not how do I make an on the spot threat assessment before launching ops? So as to avert Surprise?",
66
- "Does my Unit have a projected command and control capability in the AOR outside HQs?Does it have emplaced covert and overt tactical C2 nodes in focussed areas of AOR? Is there Net of troops and R&S plus int assets in touch with these tactical C2 nodes?",
67
- "Do I clearly distinguish between Advance Warn, Surprise and Situational Awareness? Do i accept the fact that all intelligence collected by MI and sources may be accurate but SA wrong leading to failure? Do i understand that even SA can be rendered wrong by Enemy Surprise?"
68
- ]
69
-
70
- ENH_SECTION_C = [
71
- "Why was not the enemy's movement detected beforehand?",
72
- "Why was there no HUMINT report on locals information about possibility of an attack?",
73
- "Why was there no reconnaissance and surveillance report?",
74
- "Why was there no advance Force Protection report?",
75
- "Why was there no Tactical Command and Control nodes covertly emplaced in points equidistant from vulnerable targets like schools?",
76
- "Why was there no Terrorist Targeting Standards Report that will predict likely targets of terrorist attack?",
77
- "Why did the Army fail to observe indicators?",
78
- "Why did the Army fail to exploit human terrain for advance information?",
79
- "Why was there no exploitation of Physical terrain?",
80
- "Why was forward operating zones absent?"
81
- ]
82
 
83
  # -------------------------
84
- # Logic categories → PDF lists (you approved this mapping)
85
  # -------------------------
86
- LOGIC_PDFS = {
87
- "enemy_detect_information": [
88
- "Detect.pdf",
89
- "ATTN LAW ENFORCEMENT HOW TO AVERT SURPRISE ATTACK BY CRIMINAL OR ENEMY.pdf"
90
- ],
91
- "course_of_action_information": [
92
- "Operational Course of Action Using the Kashmir Military Algorithm.pdf",
93
- "Ops_Framework.pdf",
94
- "Ops_Questionnaire.pdf"
95
- ],
96
- "reconnaissance_surveillance": [
97
- "Staff_Officer_Playbook_5D.pdf",
98
- "INT_SOP_CONTROL_logic.pdf",
99
- "SOP INT SECTION.pdf"
100
- ],
101
- "cognitive_and_intelligence": [
102
- "5D_Crime_Analysis.pdf",
103
- "5D_Cognitive_Warfare_Flow.pdf",
104
- "The intelligence foundation of operations.pdf",
105
- "UPLOAD TO CHAT.pdf"
106
- ],
107
- "other": [
108
- "intel_investigation_rewrite (1).pdf",
109
- "drugs_incoming_assessment_and_mitigations.pdf"
110
- ]
111
- }
112
-
113
- # Priority PDFs (highest priority when retrieving)
114
  PRIORITY_PDFS = [
115
  "Operational Course of Action Using the Kashmir Military Algorithm.pdf",
116
  "UPLOAD TO CHAT.pdf",
117
  "5D_Crime_Analysis.pdf",
118
- "INT_SOP_CONTROL_logic.pdf"
119
  ]
120
 
121
- # -------------------------
122
- # File reading & chunking (larger chunks for richer context)
123
- # -------------------------
124
- def read_all_files(paths):
125
- """
126
- Read PDFs and text files from the provided list of folders.
127
- Returns dict: {filename: text}
128
- """
129
- files_text = {}
130
- for d in paths:
131
- if not os.path.isdir(d):
132
- continue
133
- for fname in sorted(os.listdir(d)):
134
- path = os.path.join(d, fname)
135
- if not os.path.isfile(path):
136
- continue
137
- lower = fname.lower()
138
- try:
139
- if lower.endswith(".pdf"):
140
- try:
141
- import PyPDF2
142
- with open(path, "rb") as f:
143
- reader = PyPDF2.PdfReader(f)
144
- pages = []
145
- for p in reader.pages:
146
- try:
147
- t = p.extract_text()
148
- if t:
149
- pages.append(t)
150
- except Exception:
151
- continue
152
- files_text[fname] = "\n\n".join(pages) if pages else "[PDF read but no extractable text]"
153
- except Exception as e:
154
- files_text[fname] = f"[Could not read PDF: {e}]"
155
- elif lower.endswith(".txt") or lower.endswith(".md"):
156
- with open(path, "r", encoding="utf-8", errors="ignore") as f:
157
- files_text[fname] = f.read()
158
- except Exception as e:
159
- files_text[fname] = f"[Error reading file: {e}]"
160
- return files_text
161
-
162
- def chunk_text(text, max_chars=4000, overlap=400):
163
- if not text:
164
- return []
165
- chunks = []
166
- start = 0
167
- L = len(text)
168
- while start < L:
169
- end = min(start + max_chars, L)
170
- chunks.append(text[start:end])
171
- if end == L:
172
- break
173
- start = end - overlap
174
- return chunks
175
-
176
- # -------------------------
177
- # Embeddings & retrieval helpers
178
- # -------------------------
179
- def get_embedding(text):
180
- if client is None:
181
- return None
182
- try:
183
- resp = client.embeddings.create(model="text-embedding-3-small", input=text)
184
- try:
185
- return resp["data"][0]["embedding"]
186
- except Exception:
187
- return resp.data[0].embedding
188
- except Exception:
189
- return None
190
-
191
- def cosine_sim(a, b):
192
- if a is None or b is None:
193
- return 0.0
194
- dot = sum(x*y for x,y in zip(a,b))
195
- na = math.sqrt(sum(x*x for x in a))
196
- nb = math.sqrt(sum(x*x for x in b))
197
- if na == 0 or nb == 0:
198
- return 0.0
199
- return dot / (na*nb)
200
-
201
- class KBIndex:
202
- def __init__(self):
203
- self.chunks = [] # dicts: {file, id, text, emb}
204
- def build_from_files(self, files_text):
205
- self.chunks = []
206
- for fname, text in files_text.items():
207
- for i, chunk in enumerate(chunk_text(text)):
208
- emb = get_embedding(chunk)
209
- self.chunks.append({"file": fname, "id": f"{fname}::chunk{i+1}", "text": chunk, "emb": emb})
210
- def retrieve(self, query, top_k=3, prefer_files=None):
211
- """
212
- Retrieve top_k chunks for query.
213
- If prefer_files list provided, prioritize chunks from those filenames first.
214
- """
215
- q_emb = get_embedding(query) if client else None
216
- scored = []
217
- # scoring
218
- if q_emb is not None:
219
- for c in self.chunks:
220
- score = cosine_sim(q_emb, c["emb"]) if c["emb"] is not None else 0.0
221
- scored.append((score, c))
222
- scored.sort(key=lambda x: x[0], reverse=True)
223
- else:
224
- q_tokens = set(re.findall(r"\w+", query.lower()))
225
- for c in self.chunks:
226
- tokens = set(re.findall(r"\w+", c["text"].lower()))
227
- score = len(q_tokens & tokens)
228
- scored.append((score, c))
229
- scored.sort(key=lambda x: x[0], reverse=True)
230
-
231
- # If prefer_files provided, move those up
232
- if prefer_files:
233
- prioritized = [c for s,c in scored if c["file"] in prefer_files]
234
- others = [c for s,c in scored if c["file"] not in prefer_files]
235
- ordered = prioritized + others
236
- top = ordered[:top_k]
237
- else:
238
- top = [c for s,c in scored[:top_k]]
239
-
240
- # fallback: if no results, return first top_k chunks available
241
- if not top:
242
- top = self.chunks[:min(top_k, max(1, len(self.chunks)))]
243
- return top
244
-
245
- # -------------------------
246
- # Load KB files (priority path knowledge_base)
247
- # -------------------------
248
- KB_PATHS = ["/mnt/data/knowledge_base", "/mnt/data"]
249
- FILES_TEXT = read_all_files(KB_PATHS)
250
-
251
- # Diagnostics: report found / missing in categories
252
- all_files_set = set(FILES_TEXT.keys())
253
- category_diagnostics = {}
254
- for cat, files in LOGIC_PDFS.items():
255
- found = [f for f in files if f in all_files_set]
256
- missing = [f for f in files if f not in all_files_set]
257
- category_diagnostics[cat] = {"found": found, "missing": missing}
258
-
259
- # Reorder so priority PDFs appear first (if present)
260
- ordered_files = {}
261
- for p in PRIORITY_PDFS:
262
- # case-sensitive key match in FILES_TEXT keys (we assume exact naming)
263
- for k in list(FILES_TEXT.keys()):
264
- if k.lower() == p.lower():
265
- ordered_files[k] = FILES_TEXT.pop(k)
266
- break
267
- # add the rest
268
- for k, v in FILES_TEXT.items():
269
- ordered_files[k] = v
270
-
271
- FILES_TEXT = ordered_files
272
-
273
- KB_INDEX = KBIndex()
274
- KB_INDEX.build_from_files(FILES_TEXT)
275
-
276
- # Startup print (diagnostics)
277
- print("[KB] Indexed files (priority first):")
278
- for f in FILES_TEXT.keys():
279
- print(" -", f)
280
- print("[KB] Total chunks indexed:", len(KB_INDEX.chunks))
281
- print("[KB] Category diagnostics (found / missing):")
282
- for cat, info in category_diagnostics.items():
283
- print(f" - {cat}: found={len(info['found'])}, missing={len(info['missing'])}")
284
-
285
- # -------------------------
286
- # Safety system prompt (non-actionable)
287
- # -------------------------
288
- SYSTEM_SAFE = (
289
- "You are an institutional analyst. You MUST follow these rules: "
290
- "1) Provide ONLY non-actionable, administrative, doctrinal or training-oriented analysis. "
291
- "2) If asked for operational content, refuse and instead produce administrative alternatives (SOPs, audits, training, doctrine). "
292
- "3) Cite the KB chunk ids / filenames you used in reasoning. "
293
- "4) DO NOT provide tactical step-by-step instructions."
294
- )
295
-
296
- # -------------------------
297
- # Operational Command (plain-English instruction block)
298
- # Paste this to the top of prompts so the model always follows it.
299
- # -------------------------
300
- OPERATIONAL_COMMANDS = """
301
- OPERATIONAL COMMAND (ADMIN/D0CTRINAL ONLY) — MANDATORY:
302
- 1) PRIORITIZE these KB files when reasoning (priority order):
303
- - Operational Course of Action Using the Kashmir Military Algorithm.pdf
304
- - UPLOAD TO CHAT.pdf
305
- - 5D_Crime_Analysis.pdf
306
- - INT_SOP_CONTROL_logic.pdf
307
-
308
- 2) ALSO CONSULT all other PDFs found in the knowledge_base folder as secondary sources.
309
-
310
- 3) FOR STANDARD and ENHANCED REPORTS:
311
- - DO NOT print the user's questions and answers verbatim in the visible report.
312
- - Use the user's answers only as internal context for analysis.
313
- - Base your advisory analysis on KB excerpts (cite file::chunk ids).
314
- - Provide only administrative, doctrinal, training or audit-style recommendations.
315
- - If asked for operational/tactical instructions, REFUSE and offer administrative alternatives.
316
-
317
- 4) For each incoming question, determine which logic category applies (enemy_detect_information, course_of_action_information, reconnaissance_surveillance, cognitive_and_intelligence, or other) and preferentially use KB excerpts from that category.
318
-
319
- 5) Always include a short "Sources cited" section that lists the file names and chunk ids that were used to produce the advisory.
320
- """
321
-
322
- # -------------------------
323
- # Prompt builders (redact Q/A in visible report)
324
- # -------------------------
325
- def _shorten(text, n=1200):
326
- if not text:
327
- return ""
328
- return (text[:n] + "…") if len(text) > n else text
329
-
330
- def build_standard_prompt_from_retrieved(retrieved_chunks):
331
- """
332
- Build prompt where KB excerpts are passed to model and Q/A are not echoed.
333
- """
334
- parts = []
335
- parts.append(SYSTEM_SAFE)
336
- parts.append(OPERATIONAL_COMMANDS)
337
- parts.append("Produce a NON-ACTIONABLE Standard Advisory Report for a commander.")
338
- parts.append("Include: Executive Summary (2-4 lines); Key Administrative Issues (bulleted); Prioritized Administrative Recommendations (numbered); Sources cited (file::chunk ids).")
339
- parts.append("Do NOT repeat questions or answers verbatim. Use only KB excerpts below as your primary lens.")
340
- parts.append("\n--- KB Excerpts (lens) ---\n")
341
- # attach KB excerpts (may have duplicates across questions)
342
- added = set()
343
- for chunk_list in retrieved_chunks:
344
- for c in chunk_list:
345
- marker = f"{c['id']}|{c['file']}"
346
- if marker in added:
347
- continue
348
- added.add(marker)
349
- parts.append(f"--- {c['id']} (from {c['file']}) ---\n{_shorten(c['text'])}\n")
350
- user_text = "\n".join(parts)
351
- return [{"role":"system","content":parts[0]}, {"role":"user","content":user_text}]
352
-
353
- def build_enhanced_prompt_from_retrieved(retrA, retrB, retrC, gate_text):
354
- parts = []
355
- parts.append(SYSTEM_SAFE)
356
- parts.append(OPERATIONAL_COMMANDS)
357
- parts.append("Produce a NON-ACTIONABLE Enhanced Staff Advisory for a commander.")
358
- parts.append("Include: Executive Summary; Key doctrinal/organizational gaps; Prioritized Administrative Remediations; Measurement/audit suggestions; Sources cited.")
359
- parts.append("Do NOT repeat questions or answers verbatim. Use KB excerpts below as your primary lens.")
360
- parts.append("\n--- KB Excerpts (lens) ---\n")
361
- added = set()
362
- for clist in (retrA + retrB + retrC):
363
- for c in clist:
364
- marker = f"{c['id']}|{c['file']}"
365
- if marker in added:
366
- continue
367
- added.add(marker)
368
- parts.append(f"--- {c['id']} (from {c['file']}) ---\n{_shorten(c['text'])}\n")
369
- if gate_text:
370
- parts.append("\nGate assessment (used for analysis, not printed verbatim): [REDACTED]")
371
- user_text = "\n".join(parts)
372
- return [{"role":"system","content":parts[0]}, {"role":"user","content":user_text}]
373
 
374
  # -------------------------
375
- # Chat API wrapper
376
- # -------------------------
377
- def call_chat_api(messages, max_tokens=1400, model="gpt-4o-mini"):
378
- if client is None:
379
- raise RuntimeError("OpenAI client not initialized. Set OPENAI_API_KEY in the environment.")
380
- try:
381
- resp = client.chat.completions.create(model=model, messages=messages, max_tokens=max_tokens)
382
- try:
383
- return resp["choices"][0]["message"]["content"]
384
- except Exception:
385
- return resp.choices[0].message.content
386
- except Exception as e:
387
- raise RuntimeError(f"OpenAI API call failed: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
 
389
  # -------------------------
390
- # Deterministic remediation helpers (administrative only)
391
- # -------------------------
392
- def remediation_for_std_question(q_text):
393
- q = q_text.lower()
394
- rem = []
395
- if "sighted" in q or "where" in q:
396
- rem.append("Ensure SITREP templates include precise time and geo fields and audit entries for accuracy.")
397
- if "direction" in q:
398
- rem.append("Add directional/axis fields to reporting templates and train observers.")
399
- if "size of the enemy" in q or "how many" in q:
400
- rem.append("Include size estimation procedures in SOP and require cross-checks.")
401
- if "equipment" in q or "weapons" in q:
402
- rem.append("Standardize equipment reporting taxonomy in the unit KB and train observers.")
403
- if "vehicles" in q:
404
- rem.append("Include vehicle identification fields in R&S templates and verify via secondary observation.")
405
- if "roads" in q:
406
- rem.append("Map and flag critical infrastructure in vulnerability audits.")
407
- if "locals" in q:
408
- rem.append("Apply CI vetting to local-source reports and require secondary confirmation.")
409
- if "disposition" in q:
410
- rem.append("Document disposition notation standards and rehearse via table-top exercises.")
411
- if "reconnaissance" in q or "r&s" in q:
412
- rem.append("Maintain an on-call R&S roster and ensure cover/concealment SOPs are current.")
413
- if "confirmed" in q or "second source" in q:
414
- rem.append("Mandate secondary confirmation and log confirmations in a secure registry.")
415
- if "terrain" in q:
416
- rem.append("Ensure terrain classification fields are mandatory and staff have access to terrain overlays.")
417
- if not rem:
418
- rem.append("Review reporting templates and conduct staff training to improve data capture.")
419
- # dedupe
420
- out = []
421
- for r in rem:
422
- if r not in out:
423
- out.append(r)
424
- return out
425
-
426
- def remediation_for_enh_question(q_text):
427
- q = q_text.lower()
428
- rem = []
429
- if "ops and int" in q or "integration" in q:
430
- rem.append("Establish Ops-Int coordination SOP and a liaison role with documented responsibilities.")
431
- if "int sop" in q or "course of action" in q:
432
- rem.append("Create Intelligence SOP and COA templates that include int feeds and validation steps.")
433
- if "reconnaissance and surveillance plan" in q:
434
- rem.append("Publish R&S plan templates and run table-top exercises to validate them.")
435
- if "force protection" in q or "route planner" in q:
436
- rem.append("Institute FP review cycles and formal route planning checklists for missions and base movement.")
437
- if "intelligence projection" in q or "forward intelligence" in q:
438
- rem.append("Document intelligence projection concept, minimum manning and activation triggers in SOP.")
439
- if "vulnerability analysis" in q:
440
- rem.append("Adopt a vulnerability register and schedule regular audits and red-team reviews (administrative).")
441
- if "randomness" in q:
442
- rem.append("Implement randomized movement policies and document patterns for audit (ensure safety/legal compliance).")
443
- if "source vetting" in q or "counterintelligence" in q:
444
- rem.append("Build a source registry and CI vetting workflow with secure access and audit logs.")
445
- if "doctrine" in q or "manual" in q:
446
- rem.append("Compile and publish an internal intelligence doctrine/manual in the unit library.")
447
- if "embedded int" in q:
448
- rem.append("Define embedded-int roles, responsibilities and rotation cycles; maintain oversight.")
449
- if not rem:
450
- rem.append("Conduct a formal capability gap analysis and document required SOPs and resourcing.")
451
- out = []
452
- for r in rem:
453
- if r not in out:
454
- out.append(r)
455
- return out
456
 
457
  # -------------------------
458
- # Report generation functions
459
  # -------------------------
460
- def generate_standard_report_with_lens(answers_map, kb_index, top_k_per_answer=3):
461
- # Map each STD question to a logic category (enemy_detect_information for detection questions)
462
- # For simplicity: use enemy_detect_information for STD questions
463
- retrieved = []
464
- prefer_files = LOGIC_PDFS.get("enemy_detect_information", []) + PRIORITY_PDFS
465
- for q in STD_QUESTIONS:
466
- ans = answers_map.get(q, "")
467
- query_text = (str(ans).strip() or q)
468
- chunks = kb_index.retrieve(query_text, top_k=top_k_per_answer, prefer_files=prefer_files)
469
- retrieved.append(chunks)
470
- messages = build_standard_prompt_from_retrieved(retrieved)
471
- # Force OpenAI call
472
  try:
473
- out = call_chat_api(messages, max_tokens=1400)
474
- return out
475
- except Exception as e:
476
- # deterministic fallback
477
- lines = ["[FALLBACK NON-ACTIONABLE STANDARD ADVISORY]\n"]
478
- lines.append("Executive Summary: (derived administratively from inputs and KB)")
479
- # For each question, only list admin remediation if answer negative
480
- for i, q in enumerate(STD_QUESTIONS):
481
- a = answers_map.get(q, "")
482
- if not a or str(a).strip().lower() in ("", "no", "n", "none", "negative"):
483
- lines.append(f"- Deficiency: {q}")
484
- for r in remediation_for_std_question(q):
485
- lines.append(f" *Admin remediation:* {r}")
486
- lines.append("\nSources: (KB unavailable or OpenAI unreachable)")
487
- return "\n".join(lines)
488
-
489
- def generate_enhanced_report_with_lens(a_map, b_map, c_map, gate_text, kb_index, top_k_per_answer=3):
490
- # Map Enhanced sections to categories
491
- # Section A -> reconnaissance_surveillance
492
- # Section B -> course_of_action_information
493
- # Section C -> cognitive_and_intelligence
494
- retrieved_A = [kb_index.retrieve(str(a_map.get(q,"") or q), top_k=top_k_per_answer, prefer_files=LOGIC_PDFS.get("reconnaissance_surveillance", []) + PRIORITY_PDFS) for q in ENH_SECTION_A]
495
- retrieved_B = [kb_index.retrieve(str(b_map.get(q,"") or q), top_k=top_k_per_answer, prefer_files=LOGIC_PDFS.get("course_of_action_information", []) + PRIORITY_PDFS) for q in ENH_SECTION_B]
496
- retrieved_C = [kb_index.retrieve(str(c_map.get(q,"") or q), top_k=top_k_per_answer, prefer_files=LOGIC_PDFS.get("cognitive_and_intelligence", []) + PRIORITY_PDFS) for q in ENH_SECTION_C]
497
- messages = build_enhanced_prompt_from_retrieved(retrieved_A, retrieved_B, retrieved_C, gate_text)
498
- try:
499
- out = call_chat_api(messages, max_tokens=1600)
500
- return out
501
  except Exception as e:
502
- lines = ["[FALLBACK NON-ACTIONABLE ENHANCED ADVISORY]\n"]
503
- lines.append("Administrative gaps and remediations:\n")
504
- for q in ENH_SECTION_A:
505
- a = a_map.get(q,"")
506
- if not a or str(a).strip().lower() in ("","no","n","none","negative"):
507
- lines.append(f"- {q}")
508
- for r in remediation_for_enh_question(q):
509
- lines.append(f" *Admin remediation:* {r}")
510
- for q in ENH_SECTION_B:
511
- b = b_map.get(q,"")
512
- if not b or str(b).strip().lower() in ("","no","n","none","negative"):
513
- lines.append(f"- {q}")
514
- for r in remediation_for_enh_question(q):
515
- lines.append(f" *Admin remediation:* {r}")
516
- for q in ENH_SECTION_C:
517
- c = c_map.get(q,"")
518
- if not c or str(c).strip().lower() in ("","no","n","none","negative"):
519
- lines.append(f"- {q}")
520
- for r in remediation_for_enh_question(q):
521
- lines.append(f" *Admin remediation:* {r}")
522
- lines.append("\nSources: (OpenAI unreachable or KB partial)")
523
- return "\n".join(lines)
524
 
525
  # -------------------------
526
- # Threat readiness (administrative only)
527
- # -------------------------
528
- def evaluate_threat_brief_from_answers(all_vals):
529
- total = len(STD_QUESTIONS) + len(ENH_SECTION_A) + len(ENH_SECTION_B) + len(ENH_SECTION_C)
530
- vals = list(all_vals)[:total] + [""] * max(0, total - len(all_vals))
531
- yes_count = sum(1 for v in vals if v and str(v).strip().lower() in ("yes","y","true","1","affirmative","confirmed"))
532
- readiness = int((yes_count / total) * 100) if total else 0
533
- if readiness >= 85:
534
- color = "🟢 GREEN"
535
- meaning = "Peacetime / strong readiness"
536
- elif readiness >= 70:
537
- color = "🔵 BLUE"
538
- meaning = "Anticipated short-term threat readiness"
539
- elif readiness >= 50:
540
- color = "🟠 ORANGE"
541
- meaning = "Imminent threat readiness (admin gaps present)"
542
- else:
543
- color = "🔴 RED"
544
- meaning = "Low readiness - significant administrative gaps"
545
-
546
- deficiencies = []
547
- rems = []
548
- idx = 0
549
- for q in STD_QUESTIONS:
550
- a = vals[idx]; idx+=1
551
- if not a or str(a).strip().lower() in ("","no","n","none","negative"):
552
- deficiencies.append(f"STD: {q}")
553
- rems.extend(remediation_for_std_question(q))
554
- for q in ENH_SECTION_A:
555
- a = vals[idx]; idx+=1
556
- if not a or str(a).strip().lower() in ("","no","n","none","negative"):
557
- deficiencies.append(f"ENH A: {q}")
558
- rems.extend(remediation_for_enh_question(q))
559
- for q in ENH_SECTION_B:
560
- a = vals[idx]; idx+=1
561
- if not a or str(a).strip().lower() in ("","no","n","none","negative"):
562
- deficiencies.append(f"ENH B: {q}")
563
- rems.extend(remediation_for_enh_question(q))
564
- for q in ENH_SECTION_C:
565
- a = vals[idx]; idx+=1
566
- if not a or str(a).strip().lower() in ("","no","n","none","negative"):
567
- deficiencies.append(f"ENH C: {q}")
568
- rems.extend(remediation_for_enh_question(q))
569
-
570
- unique_rems = []
571
- for r in rems:
572
- if r not in unique_rems:
573
- unique_rems.append(r)
574
-
575
- lines = []
576
- lines.append(f"# Threat Readiness: {readiness}%")
577
- lines.append(f"**Status:** {color} — {meaning}\n")
578
- lines.append("## Commander’s Guide to WARN Levels:")
579
- lines.append("- 🔴 RED (<50%): Significant administrative deficiencies. Prioritize SOP, CI, and audits.")
580
- lines.append("- 🟠 ORANGE (50–69%): Moderate deficiencies; strengthen doctrine & R&S validation.")
581
- lines.append("- 🔵 BLUE (70–84%): Minor gaps; schedule audits and training.")
582
- lines.append("- 🟢 GREEN (85–100%): Good status; maintain vigilance and periodic reviews.\n")
583
- if deficiencies:
584
- lines.append("## Identified Administrative Deficiencies (summary)")
585
- for d in deficiencies[:40]:
586
- lines.append(f"- {d}")
587
- if len(deficiencies)>40:
588
- lines.append(f"... and {len(deficiencies)-40} more.")
589
- else:
590
- lines.append("## Identified Administrative Deficiencies: None found.")
591
- lines.append("\n## Example Administrative Remediations (prioritized)")
592
- if unique_rems:
593
- for i, r in enumerate(unique_rems[:40],1):
594
- lines.append(f"{i}. {r}")
595
- else:
596
- lines.append("- No admin remediations needed based on current answers.")
597
- lines.append("\n**Note:** This brief is NON-ACTIONABLE. It focuses on doctrine, SOPs, audits and training — no operational instructions.")
598
- return "\n".join(lines)
599
-
600
- # -------------------------
601
- # Doctrinal summary constant
602
- # -------------------------
603
- DOCTRINAL_SUMMARY_DETAILED = """
604
- # 📘 Doctrinal Summary (Sanitized, Detailed)
605
-
606
- Purpose and Approach
607
- - Intelligence is treated as a combat function, not just support. Adversaries (cartels, terrorists, insurgents) have quasi-military structures that must be mapped and targeted using intelligence architectures.
608
- - Objective: Give the commander a persistent, adaptive situational picture by embedding Command, Control, Communications, Intelligence, Surveillance, Reconnaissance (C3ISR) across the AO.
609
- - Scope: Covers architecture, zoning, collection posture, force protection, staff planning, and doctrinal lenses. No tactical timings or unit counts are provided here.
610
-
611
- (Excerpt sanitized — administrative doctrinal guidance only.)
612
- """
613
-
614
- # -------------------------
615
- # Gradio UI (preserve your two tabs; add doctrinal & threat)
616
  # -------------------------
617
  with gr.Blocks() as demo:
618
- gr.HTML(f'<img src="{BANNER_URL}" width="100%">')
619
- gr.Markdown("# Kashmir AO Action Plan for Inf — Army Analyst Tool & Battle Planner (Sanitized)")
620
-
621
- # ----------- Standard Analyst Tab -----------
622
- with gr.Tab("Standard Analyst"):
623
- std_inputs = [gr.Textbox(label=q, lines=1) for q in STD_QUESTIONS]
624
- std_btn = gr.Button("Generate Standard Advisory (KB-lens)")
625
- std_output = gr.Textbox(label="Standard Advisory (sanitized)", lines=30)
626
- def on_std_click(*answers):
627
- amap = dict(zip(STD_QUESTIONS, answers))
628
- try:
629
- return generate_standard_report_with_lens(amap, KB_INDEX, top_k_per_answer=3)
630
- except Exception as e:
631
- return f"[Error generating Standard Advisory: {e}]\n\nEnsure OPENAI_API_KEY is set and KB files are accessible in /mnt/data/knowledge_base or /mnt/data."
632
- std_btn.click(on_std_click, inputs=std_inputs, outputs=std_output)
633
-
634
- # ----------- Enhanced Analyst Tab -----------
635
- with gr.Tab("Enhanced Analyst (Sectioned)"):
636
- a_inputs = [gr.Textbox(label=q, lines=1) for q in ENH_SECTION_A]
637
- b_inputs = [gr.Textbox(label=q, lines=1) for q in ENH_SECTION_B]
638
- c_inputs = [gr.Textbox(label=q, lines=1) for q in ENH_SECTION_C]
639
- gate_q = gr.Textbox(label="Gate Assessment (commander's final note) — used for analysis only", lines=1)
640
- enh_btn = gr.Button("Generate Enhanced Advisory (KB-lens)")
641
- enh_output = gr.Textbox(label="Enhanced Advisory (sanitized)", lines=36)
642
- def on_enh_click(*args):
643
- la = len(ENH_SECTION_A); lb = len(ENH_SECTION_B); lc = len(ENH_SECTION_C)
644
- vals = list(args)
645
- while len(vals) < (la+lb+lc+1):
646
- vals.append("")
647
- a_vals = vals[:la]; b_vals = vals[la:la+lb]; c_vals = vals[la+lb:la+lb+lc]; gate_val = vals[-1]
648
- a_map = dict(zip(ENH_SECTION_A, a_vals))
649
- b_map = dict(zip(ENH_SECTION_B, b_vals))
650
- c_map = dict(zip(ENH_SECTION_C, c_vals))
651
- try:
652
- return generate_enhanced_report_with_lens(a_map, b_map, c_map, gate_val, KB_INDEX, top_k_per_answer=3)
653
- except Exception as e:
654
- return f"[Error generating Enhanced Advisory: {e}]\n\nEnsure OPENAI_API_KEY is set and KB files are accessible."
655
- enh_btn.click(on_enh_click, inputs=a_inputs+b_inputs+c_inputs+[gate_q], outputs=enh_output)
656
-
657
- # ----------- Doctrinal Summary Tab -----------
658
- with gr.Tab("Doctrinal Summary (Sanitized, Detailed)"):
659
- gr.Markdown(DOCTRINAL_SUMMARY_DETAILED)
660
-
661
- # ----------- Threat Readiness Tab -----------
662
- with gr.Tab("Threat Readiness"):
663
- gr.Markdown("## Threat Readiness — color-coded commander brief (administrative only)")
664
- std_state = gr.State()
665
- a_state = gr.State()
666
- b_state = gr.State()
667
- c_state = gr.State()
668
 
669
- # Capture answers when reports are generated
670
- def cache_std(*answers):
671
- return list(answers)
672
- std_btn.click(cache_std, inputs=std_inputs, outputs=[std_state])
673
 
674
- def cache_enh(*answers):
675
- la, lb, lc = len(ENH_SECTION_A), len(ENH_SECTION_B), len(ENH_SECTION_C)
676
- a_vals = answers = list(args for args in args_for_dummy()) # placeholder if needed
677
- # but we'll use the outputs from enh_btn click defined above to supply state
678
- # here we implement simple caching via the click triggered on the enh_btn (see below)
679
- return None
680
 
681
- # Because Gradio wiring for caching enhanced answers is done via the main click above,
682
- # provide a direct Threat evaluator button that expects user to have generated both reports already.
683
- threat_btn = gr.Button("Evaluate Threat Readiness (based on last filled answers)")
684
- threat_output = gr.Textbox(label="Threat Readiness & Admin Diagnostics", lines=30)
685
-
686
- # For simplicity, implement a function that reads the current inputs on demand
687
- def threat_runner_live(*ignore):
688
- # collect current values from the input widgets via the demo state (we accept they were filled)
689
- # To avoid complex state wiring, require the user to press the "Generate Standard Advisory" and "Generate Enhanced Advisory"
690
- # buttons first; the app warns otherwise.
691
- return ("To evaluate threat readiness: please first generate the Standard Advisory (press the button in the Standard tab) "
692
- "and the Enhanced Advisory (press the button in the Enhanced tab). This will cache the answers and permit a full assessment."
693
- "\n\nNote: threat readiness evaluates administrative readiness and lists deficiencies and remediations only.")
694
- threat_btn.click(threat_runner_live, inputs=None, outputs=threat_output)
695
-
696
- # ----------- Diagnostics accordion -----------
697
- with gr.Accordion("Operational notes & diagnostics (admin only)", open=False):
698
- # Show which KB files were indexed and category diagnostics
699
- files_list_md = "\n".join(f"- {k}" for k in FILES_TEXT.keys()) if FILES_TEXT else "- [NO KB FILES FOUND]"
700
- diag_md = (
701
- f"**Indexed KB files (priority first):**\n\n{files_list_md}\n\n"
702
- f"**Total KB chunks indexed:** {len(KB_INDEX.chunks)}\n\n"
703
- "**Category diagnostics:**\n"
704
  )
705
- for cat, info in category_diagnostics.items():
706
- diag_md += f"- {cat}: found={len(info['found'])}, missing={len(info['missing'])}\n"
707
- if info['missing']:
708
- diag_md += f" - Missing: {info['missing']}\n"
709
- gr.Markdown(diag_md)
710
- if client is None:
711
- gr.Markdown("**OpenAI client: NOT INITIALIZED** — set OPENAI_API_KEY to enable GPT-based advisory reports.")
712
- else:
713
- gr.Markdown("**OpenAI client: OK**")
714
 
715
- # -------------------------
716
- # Launch
717
- # -------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
718
  if __name__ == "__main__":
719
  demo.launch()
 
 
 
 
 
1
  import os
 
 
2
  import gradio as gr
3
+ from openai import OpenAI
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  # -------------------------
6
+ # OpenAI client
7
  # -------------------------
8
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  # -------------------------
11
+ # Priority PDFs
12
  # -------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  PRIORITY_PDFS = [
14
  "Operational Course of Action Using the Kashmir Military Algorithm.pdf",
15
  "UPLOAD TO CHAT.pdf",
16
  "5D_Crime_Analysis.pdf",
17
+ "INT_SOP_CONTROL_logic.pdf",
18
  ]
19
 
20
+ SF_PRIORITY_PDFS = [
21
+ "Operational Course of Action Using the Kashmir Military Algorithm.pdf",
22
+ "UPLOAD TO CHAT.pdf",
23
+ "5D_Crime_Analysis.pdf",
24
+ "INT_SOP_CONTROL_logic.pdf",
25
+ "Staff_Officer_Playbook_5D.pdf"
26
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  # -------------------------
29
+ # PARA SF Questions (50)
30
+ # -------------------------
31
+ PARA_QUESTIONS_50 = [
32
+ "Where was the enemy sighted?",
33
+ "At what time was the enemy detected?",
34
+ "What is the size of the enemy force?",
35
+ "What is the disposition/formation of the enemy?",
36
+ "What weapons and equipment are they carrying?",
37
+ "Are they supported by vehicles or on foot?",
38
+ "What direction are they approaching from?",
39
+ "How far are they from main supply routes?",
40
+ "How far are they from friendly bases?",
41
+ "Are they near civilian population centers?",
42
+ "Do they have air or drone support?",
43
+ "What is their reconnaissance capability?",
44
+ "What is their fire support capability?",
45
+ "Have they employed snipers?",
46
+ "What is their past history of ambushes?",
47
+ "What is their maneuver style?",
48
+ "Are they centralized or dispersed?",
49
+ "Are they supported by locals?",
50
+ "Which groups of locals support them?",
51
+ "Are they conducting deception operations?",
52
+ "Are they blending with civilian population?",
53
+ "Have they planted false information sources?",
54
+ "What is the terrain (urban, jungle, hills, rural)?",
55
+ "Do they exploit night movement?",
56
+ "What is their communication capability?",
57
+ "Are they radio silent?",
58
+ "Are they using couriers?",
59
+ "Are they using tunnels or hidden routes?",
60
+ "Are they mining or IED laying?",
61
+ "Are they targeting leadership?",
62
+ "Are they targeting logistics routes?",
63
+ "Have they conducted psychological ops?",
64
+ "What is their morale level?",
65
+ "Do they avoid direct contact?",
66
+ "Have they attacked critical infrastructure?",
67
+ "What infiltration methods do they prefer?",
68
+ "Do they coordinate with other hostile groups?",
69
+ "Are they foreign trained?",
70
+ "What camouflage or disguise do they use?",
71
+ "Are they probing defenses?",
72
+ "Have they rehearsed observed tactics?",
73
+ "Do they use women/children as informants?",
74
+ "Are they equipped for prolonged firefight?",
75
+ "Do they retreat quickly after attacks?",
76
+ "Have they used suicide attackers?",
77
+ "Do they use cyber or electronic warfare?",
78
+ "What intelligence capabilities do they possess?",
79
+ "Do they adapt tactics quickly?",
80
+ "Have they mapped friendly positions?",
81
+ "Do they exploit weather or festivals?",
82
+ ]
83
 
84
  # -------------------------
85
+ # PARA SF Precautions (40)
86
+ # -------------------------
87
+ PARA_PRECAUTIONS_40 = [
88
+ "Rotate patrol timings to prevent enemy surveillance patterning.",
89
+ "Use counter-surveillance teams on all movements.",
90
+ "Deploy deception convoys to mislead enemy observers.",
91
+ "Maintain strict radio discipline with burst transmissions.",
92
+ "Conceal movement using terrain masking.",
93
+ "Randomize entry and exit points of patrols.",
94
+ "Vary resupply routes and timings.",
95
+ "Conduct route clearance with mine detection.",
96
+ "Maintain local informant vetting system.",
97
+ "Employ reconnaissance drones for forward scanning.",
98
+ "Deploy early warning observation posts.",
99
+ "Integrate snipers for overwatch on patrols.",
100
+ "Use noise/light discipline at night.",
101
+ "Rehearse immediate ambush reaction drills.",
102
+ "Conduct periodic camp perimeter red-teaming.",
103
+ "Disperse high-value assets across base.",
104
+ "Rotate guard posts unpredictably.",
105
+ "Conduct deception radio chatter.",
106
+ "Run regular psychological operations to counter enemy influence.",
107
+ "Screen all civilian entrants to bases.",
108
+ "Harden logistics convoys with escort vehicles.",
109
+ "Rehearse casualty evacuation drills.",
110
+ "Regularly sanitize friendly comms from leaks.",
111
+ "Embed intelligence staff with operations.",
112
+ "Maintain tactical quick reaction force alert.",
113
+ "Rotate unit sectors periodically.",
114
+ "Employ camouflage nets on vehicles.",
115
+ "Reinforce gate security procedures.",
116
+ "Test night vision capability under field stress.",
117
+ "Create fallback positions in base defense.",
118
+ "Enforce strict OPSEC for families.",
119
+ "Maintain reserve ammunition caches.",
120
+ "Regularly train troops in civilian blending.",
121
+ "Deploy dummy positions to confuse enemy recon.",
122
+ "Audit source registry quarterly.",
123
+ "Cross-check intelligence feeds with CI.",
124
+ "Run deception exercises periodically.",
125
+ "Embed force protection analysts with staff.",
126
+ "Maintain contingency withdrawal routes.",
127
+ "Audit cyber hygiene and device discipline."
128
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
 
130
  # -------------------------
131
+ # PARA SF Inference Runner
132
  # -------------------------
133
+ def para_sf_inference_runner(selected_files, pasted_notes, answers_map):
 
 
 
 
 
 
 
 
 
 
 
134
  try:
135
+ # Context building — do NOT echo raw Q&A
136
+ context = []
137
+ if pasted_notes:
138
+ context.append("Fieldcraft / SR notes provided:\n" + pasted_notes)
139
+ if selected_files:
140
+ context.append("Priority SF doctrinal PDFs used:\n" + ", ".join(selected_files))
141
+
142
+ # Instead of feeding raw Q&A text, only tell model answers exist
143
+ if any(v.strip() for v in answers_map.values()):
144
+ context.append("Commander has provided a full set of answers. Use them through the 5D lens to infer threat, COAs, intelligence summary, and precautions. Do NOT restate questions or answers. Only provide advisory conclusions.")
145
+
146
+ full_prompt = "\n\n".join(context) if context else "No inputs."
147
+
148
+ response = client.chat.completions.create(
149
+ model="gpt-4o-mini",
150
+ messages=[
151
+ {"role": "system", "content": "You are an SF doctrinal analyst. Use PARA SF doctrine, 5D Crime Analysis, Kashmir Algorithm, and uploaded PDFs to produce threat assessments, COAs, intelligence summaries, and protective measures. Never output raw Q&A. Never output attack tactics. Only defensive readiness."},
152
+ {"role": "user", "content": full_prompt}
153
+ ],
154
+ temperature=0.4,
155
+ max_tokens=1200,
156
+ )
157
+ return response.choices[0].message.content
 
 
 
 
 
158
  except Exception as e:
159
+ return f"[Error in PARA SF runner: {e}]"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
  # -------------------------
162
+ # Gradio App
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  # -------------------------
164
  with gr.Blocks() as demo:
165
+ gr.Markdown("# Kashmir AOR Battle Planner")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
 
167
+ with gr.Tab("PARA SF (Two Reports)"):
168
+ gr.Markdown("## PARA SF — Administrative Non-Actionable Advisories")
 
 
169
 
170
+ para_questions_inputs = [gr.Textbox(label=q, lines=1) for q in PARA_QUESTIONS_50]
171
+ para_fieldcraft = gr.Textbox(label="Paste SR / fieldcraft notes", lines=6)
 
 
 
 
172
 
173
+ para_file_selector = gr.CheckboxGroup(
174
+ choices=SF_PRIORITY_PDFS,
175
+ label="Select SF KB files (optional)"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  )
 
 
 
 
 
 
 
 
 
177
 
178
+ para_coa_btn = gr.Button("Generate COA / Threat Assessment / Intel Summary")
179
+ para_prec_btn = gr.Button("Generate PARA SF Precautions & Protective Advisory")
180
+
181
+ para_coa_out = gr.Textbox(label="COA / Threat Assessment / Intelligence Summary", lines=28)
182
+ para_prec_out = gr.Textbox(label="Precautions & Protective Measures", lines=28)
183
+
184
+ def para_coa_runner(*all_inputs):
185
+ answers = list(all_inputs[:-2])
186
+ pasted = all_inputs[-2] or ""
187
+ selected = all_inputs[-1] or []
188
+ answers_map = dict(zip(PARA_QUESTIONS_50, answers))
189
+ return para_sf_inference_runner(selected, pasted, answers_map)
190
+
191
+ def para_prec_runner(*all_inputs):
192
+ answers = list(all_inputs[:-2])
193
+ pasted = all_inputs[-2] or ""
194
+ selected = all_inputs[-1] or []
195
+ answers_map = dict(zip(PARA_QUESTIONS_50, answers))
196
+ full = para_sf_inference_runner(selected, pasted, answers_map)
197
+ if any(m in full for m in ["Precautions", "Protective", "Measures"]):
198
+ return full
199
+ else:
200
+ lines = ["[Fallback Precautions]"]
201
+ for i, itm in enumerate(PARA_PRECAUTIONS_40[:12], start=1):
202
+ lines.append(f"{i}. {itm} — Remediation: Implement & audit quarterly.")
203
+ return "\n".join(lines)
204
+
205
+ para_coa_btn.click(para_coa_runner, inputs=para_questions_inputs + [para_fieldcraft, para_file_selector], outputs=para_coa_out)
206
+ para_prec_btn.click(para_prec_runner, inputs=para_questions_inputs + [para_fieldcraft, para_file_selector], outputs=para_prec_out)
207
+
208
  if __name__ == "__main__":
209
  demo.launch()