Militaryint commited on
Commit
c43df60
·
verified ·
1 Parent(s): ea7c1e3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +143 -527
app.py CHANGED
@@ -1,533 +1,149 @@
1
- # app.py
2
-
3
- import os, re
4
- import gradio as gr
5
- from openai import OpenAI
6
- from pathlib import Path
7
- from PyPDF2 import PdfReader
8
-
9
- # -------------------------
10
- # Banner URL
11
- # -------------------------
12
- BANNER_URL = "https://huggingface.co/spaces/Militaryint/ops/resolve/main/banner.png"
13
-
14
- # -------------------------
15
- # Safety Block
16
- # -------------------------
17
- SYSTEM_SAFE = """
18
- You are a military analyst assistant.
19
- All outputs must remain ACTIONABLE, and advisory tactical army -only.
20
-
21
  """
22
 
23
- # -------------------------
24
- # OpenAI Client
25
- # -------------------------
26
- client = None
27
- try:
28
- client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
29
- except Exception as e:
30
- print("[OpenAI] client init error:", e)
31
- client = None
32
-
33
- # -------------------------
34
- # Knowledge Base
35
- # -------------------------
36
- def read_all_files(folder="knowledge_base"):
37
- files_text = {}
38
- p = Path(folder)
39
- if not p.exists():
40
- print(f"[KB] folder {folder} missing")
41
- return files_text
42
- for f in sorted(p.glob("*.pdf")):
43
- try:
44
- reader = PdfReader(str(f))
45
- txt = "\n".join((page.extract_text() or "") for page in reader.pages)
46
- files_text[f.name] = txt
47
- except Exception as e:
48
- print("[KB] error reading", f, e)
49
- return files_text
50
-
51
- FILES_TEXT = read_all_files("knowledge_base")
52
-
53
- # -------------------------
54
- # Priority PDFs
55
- # -------------------------
56
- PRIORITY_PDFS = [
57
- "Operational Course of Action Using the Kashmir Military Algorithm.pdf",
58
- "UPLOAD TO CHAT.pdf",
59
- "5D_Crime_Analysis.pdf",
60
- "INT_SOP_CONTROL_logic.pdf"
61
- ]
62
-
63
- # -------------------------
64
- # PARA SF Priority PDFs
65
- # -------------------------
66
- SF_PRIORITY_PDFS = [
67
- "Operational Course of Action Using the Kashmir Military Algorithm.pdf",
68
- "UPLOAD TO CHAT.pdf",
69
- "5D_Crime_Analysis.pdf",
70
- "INT_SOP_CONTROL_logic.pdf",
71
- "Staff_Officer_Playbook_5D.pdf"
72
- ]
73
-
74
- # -------------------------
75
- # Reorder so priority PDFs appear first (if present)
76
- # -------------------------
77
- ordered_files = {}
78
- for p in PRIORITY_PDFS:
79
- for k in list(FILES_TEXT.keys()):
80
- if k.lower() == p.lower():
81
- ordered_files[k] = FILES_TEXT.pop(k)
82
- break
83
- for k, v in FILES_TEXT.items():
84
- ordered_files[k] = v
85
- FILES_TEXT = ordered_files
86
-
87
- # -------------------------
88
- # KB Index (naive chunking & query)
89
- # -------------------------
90
- class KBIndex:
91
- def __init__(self, chunk_size=1200):
92
- self.docs = {}
93
- self.chunk_size = chunk_size
94
-
95
- def build_from_files(self, files_text):
96
- self.docs = {}
97
- for fn, txt in files_text.items():
98
- if not txt:
99
- continue
100
- chunks = []
101
- for i in range(0, len(txt), self.chunk_size):
102
- chunks.append(txt[i:i+self.chunk_size])
103
- self.docs[fn] = chunks
104
-
105
- def query(self, q, top_k=3):
106
- ql = q.lower().strip()
107
- results = []
108
- if not ql:
109
- return results
110
- for fn, chunks in self.docs.items():
111
- best = []
112
- for ch in chunks:
113
- if ql in ch.lower():
114
- best.append((fn, ch[:800]))
115
- results.extend(best)
116
- # return first top_k unique filenames/chunks
117
- seen = set()
118
- out = []
119
- for fn, ch in results:
120
- if (fn, ch) not in seen:
121
- out.append((fn, ch))
122
- seen.add((fn,ch))
123
- if len(out) >= top_k:
124
- break
125
- return out
126
-
127
- KB_INDEX = KBIndex()
128
- KB_INDEX.build_from_files(FILES_TEXT)
129
-
130
- print("[KB] Indexed files (priority first):")
131
- for f in FILES_TEXT.keys():
132
- print(" -", f)
133
- print("[KB] Total chunks indexed:", sum(len(v) for v in KB_INDEX.docs.values()))
134
-
135
- # -------------------------
136
- # Operational Command — Template
137
- # -------------------------
138
- def operational_command_prompt(answers_map, category):
139
- user_text = "\n".join(f"{k}: {v}" for k, v in answers_map.items() if str(v).strip())
140
- return [
141
- {"role": "system", "content": SYSTEM_SAFE},
142
- {"role": "user", "content": f"""
143
- You are to prepare a structured, advisory-only report.
144
-
145
- Category: {category}
146
-
147
- Inputs:
148
- {user_text}
149
-
150
- Knowledge base excerpts (if any) must be considered.
151
-
152
- Report must include:
153
- - Executive Summary (2-4 lines)
154
- - Threat Assessment (administrative & doctrinal)
155
- - Course of Action (doctrinal, admin, advisory; non-actionable)
156
- - Intelligence Summary (sources & KB citations)
157
- - Administrative Remediations (prioritized)
158
- Please clearly cite KB filenames used (filename::chunk indicator).
159
- """}
160
- ]
161
-
162
- # -------------------------
163
- # Standard / Enhanced Questions
164
- # -------------------------
165
- STD_QUESTIONS = [
166
- "When and where was enemy sighted?",
167
- "Coming from which direction?",
168
- "What is the size of the enemy (how many men)?",
169
- "What equipment and weapons are they carrying?",
170
- "What vehicles are they using or are they on foot?",
171
- "How far are they from any roads frequented by soldiers vehicles?",
172
- "How far are they from any military unit camp?",
173
- "How far are they from any deployed soldiers?",
174
- "Are they getting support of locals? If so who are these locals?",
175
- "What is their disposition? How are they spread out?",
176
- "Do you have Reconnaissance and Surveillance soldiers near the area?",
177
- "Did you get the information from local source or army personnel?",
178
- "If from local source, did you confirm from a second source or R&S team?",
179
- "How far is your commanded army unit from the enemy sighting?",
180
- "What is the terrain (urban, semi-urban, jungle, hilly, rural)?"
181
- ]
182
-
183
- ENH_SECTION_A = [
184
- "Does the Bn have separate Ops planning and Int sections?",
185
- "Does the Unit have an intelligence SOP / COA template?",
186
- "Does the unit have a reconnaissance & surveillance plan?",
187
- "Does the unit have Force Protection SOP and Threat Levels?",
188
- "Does the unit have intelligence projection capability (forward nodes)?"
189
- ]
190
-
191
- ENH_SECTION_B = [
192
- "Is there a vulnerability analysis tool for the unit?",
193
- "Does the unit employ randomness in movement and tasks?",
194
- "Is there a source vetting / CI system in place?",
195
- "Does the unit treat intelligence as doctrine or just data?",
196
- "Does the unit use CI in vulnerability & operational reviews?"
197
- ]
198
-
199
- ENH_SECTION_C = [
200
- "Are intelligence personnel embedded in routine ops?",
201
- "Am I thinking of the Threat or the CO's Situational Awareness?",
202
- "What is my intent as a staff planning element?",
203
- "Do I detect, deter, deny, deliver, or destroy (5D options)?",
204
- "Do external MI assets conform to the 5D system?",
205
- "Have I made a vulnerability assessment (Deter/Deny)?",
206
- "How do I account for Force Protection based on gaps?",
207
- "Do we attack threat SA, freedom of movement, tactics, or local support?",
208
- "Is operation Deliberate or Quick and do I have projected int assets?",
209
- "Do I clearly distinguish Advance Warn, Surprise and Situational Awareness?"
210
- ]
211
-
212
- # -------------------------
213
- # PARA SF Questions (50 real concise Qs)
214
- # -------------------------
215
- PARA_QUESTIONS_50 = [
216
- "Exact location (grid / place) of sighting?",
217
- "Date and time of first observation?",
218
- "Direction of enemy approach?",
219
- "Estimated number of personnel?",
220
- "Observed leader(s) or commanders?",
221
- "Enemy weapons observed (small arms, crew-served)?",
222
- "Presence of vehicles (type / count)?",
223
- "Signs of explosives or IED activity?",
224
- "Observed rates of movement (stationary / moving)?",
225
- "Formation or dispersion (tight / spread)?",
226
- "Use of local population for support?",
227
- "Local sympathizers identified (names/roles)?",
228
- "Logistics / resupply indicators?",
229
- "Known routes used by enemy?",
230
- "Recent history of enemy attacks in area?",
231
- "Patterns of life detected (timings, routines)?",
232
- "Use of communications (radios, phones, signals)?",
233
- "Evidence of foreign or external support?",
234
- "Sanctuary / hideouts identified?",
235
- "Medical support observed (casualty handling)?",
236
- "Use of deception or camouflage?",
237
- "Counter-surveillance signs noted?",
238
- "Electronic signature / unusual transmissions?",
239
- "Use of snipers or precision shooters?",
240
- "Use of indirect fires or mortars observed?",
241
- "Known HVTs (leadership, infrastructure) in area?",
242
- "Enemy morale indicators (behavior, chatter)?",
243
- "Training level (disciplined / ad hoc)?",
244
- "Use of booby traps or delayed attacks?",
245
- "Any previous successful ambushes nearby?",
246
- "Civilian movement patterns near enemy locations?",
247
- "Sources of local intel for friendly forces?",
248
- "Credibility of available human sources?",
249
- "Any known double-agents or compromised sources?",
250
- "Physical terrain features exploited by enemy?",
251
- "Weather impacts on enemy movement?",
252
- "Recent arrests/detentions related to enemy?",
253
- "Any legal or jurisdictional constraints locally?",
254
- "Evidence of command-and-control nodes?",
255
- "Access to fuel/facility caches?",
256
- "Enemy ability to disperse quickly?",
257
- "Likelihood of reinforcement from nearby areas?",
258
- "Time-to-redeploy for friendly quick reaction forces?",
259
- "Observations on enemy sustainment posture?",
260
- "Any indicators of planned escalation?",
261
- "Local civilian sentiment (hostile/neutral/supportive)?",
262
- "Possible safe-exit routes for friendly forces?",
263
- "Any cultural or legal sensitivities to consider?",
264
- "Any open-source / social media indicators?",
265
- "Urgency rating (low / med / high) from observer field notes?"
266
- ]
267
-
268
- # -------------------------
269
- # PARA SF Precautions / Protective Measures (40 items)
270
- # -------------------------
271
- PARA_PRECAUTIONS_40 = [
272
- "Maintain strict radio burst discipline and short transmissions",
273
- "Use alternate communication paths and pre-planned authentication",
274
- "Document and register all human sources with CI vetting",
275
- "Establish secure, auditable intelligence logs",
276
- "Define and rehearse contingency exfiltration routes",
277
- "Maintain camouflage and concealment SOPs for observation posts",
278
- "Rotate observation posts and R&S teams to avoid predictability",
279
- "Implement randomized foot and vehicle movement schedules",
280
- "Limit use of identified local infrastructure to reduce signature",
281
- "Use layered reporting with secondary confirmation requirement",
282
- "Pre-authorize administrative response windows to reduce delay",
283
- "Audit base layout and relocate critical assets from perimeter",
284
- "Maintain medical evacuation planning and casualty drills",
285
- "Ensure secure caches for critical supplies and spares",
286
- "Institute source validation and cross-source corroboration",
287
- "Use non-attributable liaison methods with local police/DEA",
288
- "Formalize SOP for evidence handling and chain-of-custody",
289
- "Maintain a log of all civilian interactions and transactions",
290
- "Conduct red-team administrative audits quarterly",
291
- "Maintain a vulnerability register and prioritized fixes",
292
- "Limit exposure of leadership movements via need-to-know",
293
- "Implement force protection route checklists before movement",
294
- "Deploy observation posts with concealment and escape plans",
295
- "Mandate brief, formatted SITREPs with required fields",
296
- "Establish covert Forward Tactical C2 nodes (administrative only)",
297
- "Use document-based debrief templates to capture lessons",
298
- "Set up area role cards and single-point contacts per sector",
299
- "Institute secure storage for source identity and vetting info",
300
- "Limit public posting of unit schedules and training events",
301
- "Use liaison with local law enforcement for non-operational support",
302
- "Schedule regular doctrine & SOP training sessions",
303
- "Maintain an audit trail for all intelligence product changes",
304
- "Set thresholds for escalation to higher HQ (administrative)",
305
- "Maintain alternate rendezvous points and safe houses",
306
- "Ensure all unit members have basic fieldcraft refresher training",
307
- "Plan periodic concealment and movement drills (administrative)",
308
- "Maintain a simple, unclassified index of likely HVT indicators",
309
- "Ensure information security (passwords, devices) audits quarterly",
310
- "Establish a schedule for reviewing and updating SOPs"
311
  ]
312
 
313
- # -------------------------
314
- # Report Generators (KB-first, then fallback to OpenAI SF doctrine but definitely fetch SF from army sources compatible with inputs)
315
- # -------------------------
316
- def call_chat_api_system_user(messages, max_tokens=800, model="gpt-4o-mini"):
317
- if client is None:
318
- raise RuntimeError("OpenAI client not available.")
319
- resp = client.chat.completions.create(model=model, messages=messages, max_tokens=max_tokens)
320
- try:
321
- return resp.choices[0].message.content
322
- except Exception:
323
- return resp.choices[0].message.content
324
-
325
- def generate_report_with_kb(answers_map, category, top_k=3):
326
- # Build KB hits per question (lens)
327
- kb_hits = []
328
- for q, a in answers_map.items():
329
- query = (str(a).strip() or q)
330
- hits = KB_INDEX.query(query, top_k=top_k)
331
- kb_hits.extend(hits)
332
- # Compose prompt
333
- excerpt_text = ""
334
- if kb_hits:
335
- seen = set()
336
- for fn, txt in kb_hits:
337
- key = f"{fn}"
338
- if key not in seen:
339
- excerpt_text += f"\n--- {fn} ---\n{txt[:1200]}\n"
340
- seen.add(key)
341
- # Build messages
342
- messages = operational_command_prompt(answers_map, category)
343
- if excerpt_text:
344
- messages[1]["content"] += f"\nKnowledge Base excerpts (priority applied):\n{excerpt_text}\n"
345
- else:
346
- # Indicate we'll fallback to SF doctrine
347
- messages[1]["content"] += "\n[No KB excerpts found for these inputs; assistant may fallback to authoritative SF doctrine for doctrinal guidance.]\n"
348
- # Call model
349
- try:
350
- out = call_chat_api_system_user(messages, max_tokens=900)
351
- return out.strip()
352
- except Exception as e:
353
- # Fallback deterministic admin report
354
- lines = ["[FALLBACK ACTIONABLE REPORT — AI unavailable]\n"]
355
- lines.append("Executive Summary: Administrative findings based on inputs.\n")
356
- lines.append("Key Issues:")
357
- for q, a in answers_map.items():
358
- lines.append(f"- {q}: {'[no answer]' if not str(a).strip() else str(a)}")
359
- lines.append("\nAdministrative Recommendations (deterministic):")
360
- lines.append("- Ensure SITREP templates have mandatory fields (time, geo, observer).")
361
- lines.append("- Institute source vetting and require secondary confirmation of local reports.")
362
- lines.append("- Conduct a quarterly vulnerability audit and publish remediations.")
363
- lines.append(f"\nError: {e}")
364
- return "\n".join(lines)
365
-
366
- # PARA SF runner (KB first + SF doctrine fallback)
367
- def para_sf_inference_runner(selected_files, pasted_notes, answers_map):
368
- # Force priority list: if selected_files provided use them, else use SF_PRIORITY_PDFS present in KB
369
- selected = selected_files or [p for p in SF_PRIORITY_PDFS if p in FILES_TEXT]
370
- kb_hits = []
371
- # pull KB excerpts only from selected first, then general KB if needed
372
- for q, a in answers_map.items():
373
- query = (str(a).strip() or q)
374
- # search within selected files first
375
- for fn in selected:
376
- if fn in KB_INDEX.docs:
377
- for ch in KB_INDEX.docs[fn]:
378
- if query.lower() in ch.lower():
379
- kb_hits.append((fn, ch[:1200]))
380
- break
381
- # if not found in selected, do general query
382
- if not any(fn == k for k,_ in kb_hits):
383
- hits = KB_INDEX.query(query, top_k=1)
384
- if hits:
385
- kb_hits.extend(hits)
386
- excerpt_text = ""
387
- if kb_hits:
388
- seen = set()
389
- for fn, txt in kb_hits:
390
- if fn not in seen:
391
- excerpt_text += f"\n--- {fn} ---\n{txt[:1200]}\n"
392
- seen.add(fn)
393
- # Compose user text
394
- user_text = "\n".join(f"{k}: {v}" for k, v in answers_map.items() if str(v).strip())
395
- messages = [
396
- {"role":"system","content":SYSTEM_SAFE},
397
- {"role":"user","content":f"""
398
- Prepare a NON-ACTIONABLE PARA SF advisory using the 5D lens and doctrine.
399
- Inputs:
400
- {user_text}
401
-
402
- Fieldcraft notes:
403
- {pasted_notes}
404
-
405
- Selected SF KB files (priority): {selected}
406
-
407
- Knowledge Base excerpts (if any):
408
- {excerpt_text}
409
-
410
- Output required:
411
- - Executive Summary (2-4 lines)
412
- - Doctrinal Course of Action (administrative / doctrinal guidance only)
413
- - Threat Assessment (high-level, non-actionable)
414
- - Intelligence Summary (sources cited)
415
- - PARA SF Precautions & Protective Measures (administrative list)
416
- Cite KB filenames used.
417
- """}]
418
  try:
419
- out = call_chat_api_system_user(messages, max_tokens=1000)
420
- return out.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
421
  except Exception as e:
422
- # Fallback deterministic extraction of precautions
423
- lines = [f"[FALLBACK ACTIONABLE PARA SF REPORT — AI unavailable: {e}]\n"]
424
- lines.append("Executive Summary: See fieldcraft and KB for details.\n")
425
- lines.append("Top observed inputs (sample):")
426
- cnt = 0
427
- for k,v in answers_map.items():
428
- if v and cnt < 8:
429
- lines.append(f"- {k}: {v}")
430
- cnt += 1
431
- lines.append("\nPrecautions (sample deterministic):")
432
- for i, itm in enumerate(PARA_PRECAUTIONS_40[:12], start=1):
433
- lines.append(f"{i}. {itm} Admin remediation: document & audit.")
434
- return "\n".join(lines)
435
-
436
- # -------------------------
437
- # Gradio UI
438
- # -------------------------
439
- with gr.Blocks() as demo:
440
- gr.HTML(f'<img src="{BANNER_URL}" width="100%">')
441
- gr.Markdown("# Kashmir AOR Action Plan — Battle Planner")
442
- gr.Markdown("⚠️ **ACTIONABLE — Doctrinal / Administrative Tactical guidance only.**")
443
-
444
- # ---- Standard Tab ----
445
- with gr.Tab("Standard"):
446
- std_inputs = [gr.Textbox(label=q, lines=1) for q in STD_QUESTIONS]
447
- std_button = gr.Button("Generate Standard Advisory")
448
- std_output = gr.Textbox(label="Standard Advisory Report (sanitized)", lines=28)
449
- def std_runner(*answers):
450
- amap = dict(zip(STD_QUESTIONS, answers))
451
- return generate_report_with_kb(amap, "Standard Threat Advisory")
452
- std_button.click(std_runner, inputs=std_inputs, outputs=std_output)
453
-
454
- # ---- Enhanced Tab ----
455
- with gr.Tab("Enhanced"):
456
- a_inputs = [gr.Textbox(label=q, lines=1) for q in ENH_SECTION_A]
457
- b_inputs = [gr.Textbox(label=q, lines=1) for q in ENH_SECTION_B]
458
- c_inputs = [gr.Textbox(label=q, lines=1) for q in ENH_SECTION_C]
459
- gate_input = gr.Textbox(label="Gate Question / Final Note", lines=1)
460
- enh_button = gr.Button("Generate Enhanced Advisory")
461
- enh_output = gr.Textbox(label="Enhanced Advisory Report (sanitized)", lines=28)
462
- def enh_runner(*answers):
463
- la, lb, lc = len(ENH_SECTION_A), len(ENH_SECTION_B), len(ENH_SECTION_C)
464
- vals = list(answers)
465
- # map A/B/C into a single answers_map for generation
466
- amap = {}
467
- for i, q in enumerate(ENH_SECTION_A):
468
- amap[q] = vals[i] if i < len(vals) else ""
469
- for j, q in enumerate(ENH_SECTION_B):
470
- idx = la + j
471
- amap[q] = vals[idx] if idx < len(vals) else ""
472
- for k, q in enumerate(ENH_SECTION_C):
473
- idx = la + lb + k
474
- amap[q] = vals[idx] if idx < len(vals) else ""
475
- gate = vals[-1] if vals else ""
476
- # include gate as special entry
477
- if gate:
478
- amap["Gate Assessment"] = gate
479
- return generate_report_with_kb(amap, "Enhanced 5D Advisory")
480
- enh_button.click(enh_runner, inputs=a_inputs+b_inputs+c_inputs+[gate_input], outputs=enh_output)
481
-
482
- # ---- Threat Readiness Tab ----
483
- with gr.Tab("Threat Readiness"):
484
- gr.Markdown("## Threat Readiness Color-coded Commander Brief (administrative)")
485
- threat_button = gr.Button("Evaluate Threat Readiness")
486
- threat_output = gr.Textbox(label="Threat Readiness & Diagnostics (sanitized)", lines=28)
487
- def threat_runner():
488
- lines = []
489
- lines.append("### Threat Readiness Level (Color-coded) — Administrative Brief")
490
- lines.append("- 🔴 RED (<50%): Significant administrative vulnerabilities. Prioritize SOP, CI, audits.")
491
- lines.append("- 🟠 ORANGE (50–69%): Moderate gaps; schedule doctrinal reviews and R&S validation.")
492
- lines.append("- 🔵 BLUE (70–84%): Minor gaps; plan targeted training and audits.")
493
- lines.append("- 🟢 GREEN (85–100%): Strong readiness; maintain periodic reviews.\n")
494
- lines.append("Commander’s Guide: Use remedial actions focused on doctrine, SOP updates, source vetting and audits. This brief is non-actionable.")
495
- return "\n".join(lines)
496
- threat_button.click(threat_runner, inputs=[], outputs=threat_output)
497
-
498
- # ---- PARA SF Tab ----
499
- with gr.Tab("PARA SF (Two Reports)"):
500
- gr.Markdown("## PARA SF — Two Separate Administrative Advisories (Non-Actionable)")
501
- para_questions_inputs = [gr.Textbox(label=q, lines=1) for q in PARA_QUESTIONS_50]
502
- para_fieldcraft = gr.Textbox(label="Paste Fieldcraft / SR notes", lines=6)
503
- para_file_selector = gr.CheckboxGroup(choices=SF_PRIORITY_PDFS, label="Select SF KB files (optional)")
504
- para_coa_btn = gr.Button("Generate COA / Threat Assessment / Intelligence Summary")
505
- para_prec_btn = gr.Button("Generate PARA SF Precautions & Protective Advisory")
506
- para_coa_out = gr.Textbox(label="COA / Threat Assessment / Intelligence Summary (sanitized)", lines=28)
507
- para_prec_out = gr.Textbox(label="PARA SF Precautions & Protective Measures (sanitized)", lines=28)
508
-
509
- def para_coa_runner(*all_inputs):
510
- # last two inputs are pasted notes and file selector
511
- answers = list(all_inputs[:-2])
512
- pasted = all_inputs[-2] or ""
513
- selected = all_inputs[-1] or []
514
- amap = dict(zip(PARA_QUESTIONS_50, answers))
515
- return para_sf_inference_runner(selected, pasted, amap)
516
-
517
- def para_prec_runner(*all_inputs):
518
- answers = list(all_inputs[:-2])
519
- pasted = all_inputs[-2] or ""
520
- selected = all_inputs[-1] or []
521
- amap = dict(zip(PARA_QUESTIONS_50, answers))
522
- # We'll run the same inference but return the precautions section — model is asked to include it
523
- return para_sf_inference_runner(selected, pasted, amap)
524
-
525
- para_coa_btn.click(para_coa_runner, inputs=para_questions_inputs+[para_fieldcraft, para_file_selector], outputs=para_coa_out)
526
- para_prec_btn.click(para_prec_runner, inputs=para_questions_inputs+[para_fieldcraft, para_file_selector], outputs=para_prec_out)
527
-
528
- # -------------------------
529
- # Launch
530
- # -------------------------
531
- if __name__ == "__main__":
532
- demo.launch(auth=("ops", "opsint5820"))
533
-
 
1
+ """
2
+ ARMYINTEL 5D–MDMP Training Engine
3
+ Purpose: Non‑kinetic military‑intelligence analysis simulator for education and defence training.
4
+ Frameworks: 5D (Detect–Deter–Deny–Deliver–Defend) + MDMP
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  """
6
 
7
+ import os, json, glob, concurrent.futures
8
+ from datetime import datetime
9
+ from docx import Document
10
+ from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
11
+ import gradio as gr
12
+ import openai
13
+
14
+ # ---------- CONFIG ----------
15
+ OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
16
+ if not OPENAI_API_KEY:
17
+ raise ValueError("OPENAI_API_KEY missing in environment secrets.")
18
+ openai.api_key = OPENAI_API_KEY
19
+
20
+ INTEL_TECHNIQUES = [
21
+ "HUMINT","SIGINT","IMINT","OSINT","MASINT","COMINT","ELINT","TECHINT",
22
+ "FINT","SOCMINT","GEOINT","ALL‑SOURCE INTELLIGENCE","CRISIS INTELLIGENCE",
23
+ "COUNTERINTELLIGENCE","RED TEAMING","BLUE TEAMING",
24
+ "CTRIP Analysis","SALUT Analysis","Decision Trees",
25
+ "Bayesian Reasoning","Network Influence Mapping"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  ]
27
 
28
+ # ---------- KNOWLEDGE‑BASE LOADING ----------
29
+ def load_kb_snippets():
30
+ kb_text = ""
31
+ for f in glob.glob("knowledge_base/*"):
32
+ name = os.path.basename(f)
33
+ if any(k in name for k in ["5d","5D","mdmp","MDMP","jsonl","SOP","coa"]):
34
+ try:
35
+ with open(f, "r", encoding="utf‑8", errors="ignore") as fh:
36
+ kb_text += f"\n--- {name} ---\n{fh.read()}\n"
37
+ except Exception:
38
+ pass
39
+ return kb_text[:20000] # safety cap
40
+
41
+ # ---------- CORE ANALYSIS ----------
42
+ def analyze_with_technique(tech, problem, kb_text):
43
+ prompt = f"""
44
+ Training context: defensive / educational only.
45
+
46
+ Using the {tech} discipline, apply the 5D framework (Detect, Deter, Deny, Deliver, Defend)
47
+ and MDMP reasoning to analyse the following situation:
48
+
49
+ {problem}
50
+
51
+ Reference knowledge (for orientation only):
52
+ {kb_text}
53
+
54
+ Return JSON:
55
+ {{
56
+ "technique": "{tech}",
57
+ "findings": "...",
58
+ "5D": {{
59
+ "Detect": "...", "Deter": "...", "Deny": "...", "Deliver": "...", "Defend": "..."
60
+ }},
61
+ "recommended_actions": ["...", "..."],
62
+ "confidence": {{"level":"High|Medium|Low","reason":"..."}}
63
+ }}
64
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  try:
66
+ resp = openai.ChatCompletion.create(
67
+ model="gpt-4-turbo",
68
+ messages=[
69
+ {"role":"system","content":"You are a senior military intelligence instructor. Provide non‑kinetic, training‑safe analysis."},
70
+ {"role":"user","content":prompt}
71
+ ],
72
+ temperature=0.5,
73
+ max_tokens=600
74
+ )
75
+ txt = resp["choices"][0]["message"]["content"].strip()
76
+ try:
77
+ data = json.loads(txt)
78
+ except:
79
+ data = {"technique":tech,"findings":txt,"5D":{},"recommended_actions":[],
80
+ "confidence":{"level":"Unknown","reason":"Text only"}}
81
+ return data
82
  except Exception as e:
83
+ return {"technique":tech,"findings":f"Analysis failed: {e}","5D":{},
84
+ "recommended_actions":[],"confidence":{"level":"Error","reason":str(e)}}
85
+
86
+ # ---------- DOCX REPORT ----------
87
+ def build_docx(analyses):
88
+ os.makedirs("reports", exist_ok=True)
89
+ fname = f"reports/ArmyIntel_5D_MDMP_Report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.docx"
90
+ doc = Document()
91
+ h = doc.add_heading("INTELLIGENCE ANALYSIS – INTELLIGENCE WARFARE 5D MDMP TRAINING REPORT",0)
92
+ h.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
93
+ doc.add_paragraph(f"Generated on {datetime.now():%d %b %Y %H:%M:%S}", style="Caption")
94
+ doc.add_paragraph("—"*30)
95
+
96
+ for i,a in enumerate(analyses,1):
97
+ doc.add_heading(f"{i}. {a.get('technique','Unknown')}", level=1)
98
+ doc.add_paragraph(a.get("findings","No findings."))
99
+
100
+ doc.add_heading("5D Analysis", level=2)
101
+ for d in ["Detect","Deter","Deny","Deliver","Defend"]:
102
+ doc.add_paragraph(f"• {d}: {a.get('5D',{}).get(d,'')}", style="List Bullet")
103
+
104
+ doc.add_heading("Recommended Actions", level=2)
105
+ if a.get("recommended_actions"):
106
+ for r in a["recommended_actions"]:
107
+ doc.add_paragraph(f"• {r}", style="List Bullet")
108
+ else:
109
+ doc.add_paragraph("• None / pending analysis")
110
+
111
+ c = a.get("confidence",{})
112
+ doc.add_paragraph(f"Confidence: {c.get('level','Unknown')} – {c.get('reason','')}")
113
+ doc.add_paragraph(" ")
114
+
115
+ doc.add_heading("MDMP Summary", level=1)
116
+ doc.add_paragraph(
117
+ "1. Problem identification → 2. Intelligence gathering → 3. COA development → "
118
+ "4. COA analysis → 5. Decision & execution (Training Simulation only)."
119
+ )
120
+ doc.save(fname)
121
+ return fname
122
+
123
+ # ---------- MAIN ENGINE ----------
124
+ def run_full_analysis(problem):
125
+ kb_text = load_kb_snippets()
126
+ analyses=[]
127
+ with concurrent.futures.ThreadPoolExecutor(max_workers=6) as pool:
128
+ futures={pool.submit(analyze_with_technique,t,problem,kb_text):t for t in INTEL_TECHNIQUES}
129
+ for f in concurrent.futures.as_completed(futures):
130
+ analyses.append(f.result())
131
+ return build_docx(analyses)
132
+
133
+ # ---------- GRADIO UI ----------
134
+ def start_analysis(problem):
135
+ if not problem.strip():
136
+ problem="Training scenario: suspected hostile surveillance around forward base; assess defensive posture."
137
+ path=run_full_analysis(problem)
138
+ return path
139
+
140
+ demo=gr.Interface(
141
+ fn=start_analysis,
142
+ inputs=gr.Textbox(label="Enter Training / Defensive Problem", lines=8),
143
+ outputs=gr.File(label="Download Training Intelligence Report"),
144
+ title="ARMYINTEL 5D + MDMP Training Engine",
145
+ description="Educational simulation engine for non‑kinetic intelligence analysis using 21 techniques + 5D + MDMP."
146
+ )
147
+
148
+ if __name__=="__main__":
149
+ demo.launch()