nothingworry commited on
Commit
4dc66a3
Β·
1 Parent(s): 0073bf0

imporve admin page & backend

Browse files
README.md CHANGED
@@ -18,7 +18,7 @@ This Hugging Face Space provides a Gradio interface to interact with the Integra
18
 
19
  - πŸ€– **Autonomous MCP Agents** – Tool-aware FastAPI agent that plans across RAG, Web, Admin, and LLM actions
20
  - πŸ“š **Knowledge Base Ingestion** – Upload raw text, URLs, or documents (PDF/DOCX/TXT/MD) directly from the Gradio UI
21
- - πŸ›‘οΈ **Admin Rules Management** – Dedicated tab to add/delete governance rules; all rules are persisted in SQLite for demo purposes
22
  - πŸ“Š **Admin Analytics** – Snapshot of tenant activity, tool usage, red-flag triggers, and overall query volume
23
  - 🌐 **Live Web Search** – DuckDuckGo-based MCP server with English-biased results
24
  - 🏒 **Multi-Tenant Isolation** – Every request requires a tenant ID; backend enforces isolation for chat, ingestion, and admin ops
 
18
 
19
  - πŸ€– **Autonomous MCP Agents** – Tool-aware FastAPI agent that plans across RAG, Web, Admin, and LLM actions
20
  - πŸ“š **Knowledge Base Ingestion** – Upload raw text, URLs, or documents (PDF/DOCX/TXT/MD) directly from the Gradio UI
21
+ - πŸ›‘οΈ **Admin Rules Management** – Dedicated tab to add/delete governance rules; all rules are persisted in SQLite for demo purposes and enforced during every chat request
22
  - πŸ“Š **Admin Analytics** – Snapshot of tenant activity, tool usage, red-flag triggers, and overall query volume
23
  - 🌐 **Live Web Search** – DuckDuckGo-based MCP server with English-biased results
24
  - 🏒 **Multi-Tenant Isolation** – Every request requires a tenant ID; backend enforces isolation for chat, ingestion, and admin ops
backend/api/services/agent_orchestrator.py CHANGED
@@ -72,8 +72,12 @@ class AgentOrchestrator:
72
  tool_input={"violations": [m.__dict__ for m in matches]},
73
  reason="redflag_triggered"
74
  )
 
 
 
 
75
  return AgentResponse(
76
- text="Your request has been blocked due to policy.",
77
  decision=decision,
78
  tool_traces=[{"redflags": [m.__dict__ for m in matches]}],
79
  reasoning_trace=reasoning_trace
 
72
  tool_input={"violations": [m.__dict__ for m in matches]},
73
  reason="redflag_triggered"
74
  )
75
+ summary = "; ".join(
76
+ f"{m.description or m.pattern} [severity: {m.severity}]"
77
+ for m in matches
78
+ ) or "Policy violation detected"
79
  return AgentResponse(
80
+ text=f"⚠️ Request blocked by Admin Plan: {summary}. Please review your governance rules or contact an administrator.",
81
  decision=decision,
82
  tool_traces=[{"redflags": [m.__dict__ for m in matches]}],
83
  reasoning_trace=reasoning_trace
backend/api/services/redflag_detector.py CHANGED
@@ -11,6 +11,7 @@ Enterprise RedFlagDetector
11
  - Sends notifications to Admin MCP or a webhook
12
  """
13
 
 
14
  import os
15
  import re
16
  import time
@@ -19,16 +20,25 @@ from typing import List, Dict, Any, Optional
19
  import httpx
20
 
21
  from ..models.redflag import RedFlagRule, RedFlagMatch
 
22
  from .semantic_encoder import embed_text, cosine_similarity
23
 
24
 
25
  class RedFlagDetector:
26
 
27
- def __init__(self, supabase_url: Optional[str] = None, supabase_key: Optional[str] = None, admin_mcp_url: Optional[str] = None, cache_ttl: int = 300):
 
 
 
 
 
 
 
28
  self.supabase_url = supabase_url or os.getenv("SUPABASE_URL")
29
  self.supabase_key = supabase_key or os.getenv("SUPABASE_SERVICE_KEY")
30
  self.admin_mcp_url = admin_mcp_url or os.getenv("ADMIN_MCP_URL")
31
  self.cache_ttl = cache_ttl
 
32
  self._rules_cache: Dict[str, Dict[str, Any]] = {} # tenant_id -> {"fetched_at":ts, "rules":[...]}
33
  self._rule_embeddings: Dict[str, Dict[str, List[float]]] = {}
34
  self._client = httpx.AsyncClient(timeout=15)
@@ -84,7 +94,18 @@ class RedFlagDetector:
84
  if entry and now - entry["fetched_at"] < self.cache_ttl:
85
  return entry["rules"]
86
 
87
- rules = await self._fetch_rules_from_supabase(tenant_id)
 
 
 
 
 
 
 
 
 
 
 
88
  self._rules_cache[tenant_id] = {"fetched_at": now, "rules": rules}
89
  # Pre-compute embeddings for semantic scoring
90
  embed_map: Dict[str, List[float]] = {}
@@ -99,6 +120,30 @@ class RedFlagDetector:
99
  self._rule_embeddings[tenant_id] = embed_map
100
  return rules
101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  async def check(self, tenant_id: str, text: str) -> List[RedFlagMatch]:
103
  """Return structured matches for the given tenant and text."""
104
  if not text:
 
11
  - Sends notifications to Admin MCP or a webhook
12
  """
13
 
14
+ import hashlib
15
  import os
16
  import re
17
  import time
 
20
  import httpx
21
 
22
  from ..models.redflag import RedFlagRule, RedFlagMatch
23
+ from ..storage.rules_store import RulesStore
24
  from .semantic_encoder import embed_text, cosine_similarity
25
 
26
 
27
  class RedFlagDetector:
28
 
29
+ def __init__(
30
+ self,
31
+ supabase_url: Optional[str] = None,
32
+ supabase_key: Optional[str] = None,
33
+ admin_mcp_url: Optional[str] = None,
34
+ cache_ttl: int = 300,
35
+ rules_store: Optional[RulesStore] = None,
36
+ ):
37
  self.supabase_url = supabase_url or os.getenv("SUPABASE_URL")
38
  self.supabase_key = supabase_key or os.getenv("SUPABASE_SERVICE_KEY")
39
  self.admin_mcp_url = admin_mcp_url or os.getenv("ADMIN_MCP_URL")
40
  self.cache_ttl = cache_ttl
41
+ self.rules_store = rules_store or RulesStore()
42
  self._rules_cache: Dict[str, Dict[str, Any]] = {} # tenant_id -> {"fetched_at":ts, "rules":[...]}
43
  self._rule_embeddings: Dict[str, Dict[str, List[float]]] = {}
44
  self._client = httpx.AsyncClient(timeout=15)
 
94
  if entry and now - entry["fetched_at"] < self.cache_ttl:
95
  return entry["rules"]
96
 
97
+ rules: List[RedFlagRule] = []
98
+ remote_rules: List[RedFlagRule] = []
99
+ if self.supabase_url and self.supabase_key:
100
+ try:
101
+ remote_rules = await self._fetch_rules_from_supabase(tenant_id)
102
+ except Exception:
103
+ remote_rules = []
104
+
105
+ local_rules = self._fetch_local_rules(tenant_id)
106
+ rules.extend(remote_rules)
107
+ rules.extend(local_rules)
108
+
109
  self._rules_cache[tenant_id] = {"fetched_at": now, "rules": rules}
110
  # Pre-compute embeddings for semantic scoring
111
  embed_map: Dict[str, List[float]] = {}
 
120
  self._rule_embeddings[tenant_id] = embed_map
121
  return rules
122
 
123
+ def _fetch_local_rules(self, tenant_id: str) -> List[RedFlagRule]:
124
+ if not self.rules_store:
125
+ return []
126
+
127
+ rows = self.rules_store.get_rules(tenant_id)
128
+ rules: List[RedFlagRule] = []
129
+ for raw in rows:
130
+ text = (raw or "").strip()
131
+ if not text:
132
+ continue
133
+ rule_id = hashlib.sha1(f"{tenant_id}:{text}".encode()).hexdigest()
134
+ rules.append(
135
+ RedFlagRule(
136
+ id=rule_id,
137
+ pattern=text,
138
+ description=text,
139
+ severity="high",
140
+ source="admin_local",
141
+ enabled=True,
142
+ keywords=[text.lower()] if len(text.split()) <= 6 else [],
143
+ )
144
+ )
145
+ return rules
146
+
147
  async def check(self, tenant_id: str, text: str) -> List[RedFlagMatch]:
148
  """Return structured matches for the given tenant and text."""
149
  if not text:
frontend/app/admin-rules/page.tsx ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Link from "next/link";
2
+
3
+ import { AdminRulesPanel } from "@/components/admin-rules-panel";
4
+ import { Footer } from "@/components/footer";
5
+
6
+ export default function AdminRulesPage() {
7
+ return (
8
+ <main className="mx-auto flex min-h-screen max-w-5xl flex-col gap-10 px-4 pb-16 pt-12 sm:px-6 lg:px-8">
9
+ <header className="flex flex-col gap-4 rounded-2xl border border-white/10 bg-white/5 px-6 py-6 text-slate-100 shadow-lg shadow-slate-950/40">
10
+ <div className="flex items-center justify-between gap-3">
11
+ <div className="flex items-center gap-3 text-base font-semibold">
12
+ <span className="inline-flex h-10 w-10 items-center justify-center rounded-2xl bg-gradient-to-br from-sky-400 to-cyan-500 text-slate-950">
13
+ IC
14
+ </span>
15
+ IntegraChat Β· Admin Rule Ingestion
16
+ </div>
17
+ <Link href="/" className="text-xs font-semibold uppercase tracking-[0.3em] text-cyan-300 hover:text-white">
18
+ ← Back Home
19
+ </Link>
20
+ </div>
21
+ <p className="text-sm text-slate-300">
22
+ Push governance policies, compliance workflows, and red-flag patterns to the backend&apos;s persistent rules
23
+ store using the MCP admin services.
24
+ </p>
25
+ </header>
26
+
27
+ <AdminRulesPanel />
28
+ <Footer />
29
+ </main>
30
+ );
31
+ }
32
+
frontend/app/analytics/page.tsx ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Link from "next/link";
2
+
3
+ import { AnalyticsPanel } from "@/components/analytics-panel";
4
+ import { Footer } from "@/components/footer";
5
+
6
+ export default function AnalyticsPage() {
7
+ return (
8
+ <main className="mx-auto flex min-h-screen max-w-5xl flex-col gap-10 px-4 pb-16 pt-12 sm:px-6 lg:px-8">
9
+ <header className="flex flex-col gap-4 rounded-2xl border border-white/10 bg-white/5 px-6 py-6 text-slate-100 shadow-lg shadow-slate-950/40">
10
+ <div className="flex items-center justify-between gap-3">
11
+ <div className="flex items-center gap-3 text-base font-semibold">
12
+ <span className="inline-flex h-10 w-10 items-center justify-center rounded-2xl bg-gradient-to-br from-sky-400 to-cyan-500 text-slate-950">
13
+ IC
14
+ </span>
15
+ IntegraChat Β· Analytics
16
+ </div>
17
+ <Link href="/" className="text-xs font-semibold uppercase tracking-[0.3em] text-cyan-300 hover:text-white">
18
+ ← Back Home
19
+ </Link>
20
+ </div>
21
+ <p className="text-sm text-slate-300">
22
+ Inspect tenant-wide metrics including tool usage, red-flag violations, and overall activityβ€”all powered by the
23
+ FastAPI analytics endpoints.
24
+ </p>
25
+ </header>
26
+
27
+ <AnalyticsPanel />
28
+ <Footer />
29
+ </main>
30
+ );
31
+ }
32
+
frontend/app/chat/page.tsx ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Link from "next/link";
2
+
3
+ import { ChatPanel } from "@/components/chat-panel";
4
+ import { Footer } from "@/components/footer";
5
+
6
+ export default function ChatPage() {
7
+ return (
8
+ <main className="mx-auto flex min-h-screen max-w-5xl flex-col gap-10 px-4 pb-16 pt-12 sm:px-6 lg:px-8">
9
+ <header className="flex flex-col gap-4 rounded-2xl border border-white/10 bg-white/5 px-6 py-6 text-slate-100 shadow-lg shadow-slate-950/40">
10
+ <div className="flex items-center justify-between gap-3">
11
+ <div className="flex items-center gap-3 text-base font-semibold">
12
+ <span className="inline-flex h-10 w-10 items-center justify-center rounded-2xl bg-gradient-to-br from-sky-400 to-cyan-500 text-slate-950">
13
+ IC
14
+ </span>
15
+ IntegraChat Β· Chat Bot
16
+ </div>
17
+ <Link href="/" className="text-xs font-semibold uppercase tracking-[0.3em] text-cyan-300 hover:text-white">
18
+ ← Back Home
19
+ </Link>
20
+ </div>
21
+ <p className="text-sm text-slate-300">
22
+ Experience the MCP agent orchestration layer with multi-tool reasoning, tenant isolation, and red-flag aware
23
+ responses.
24
+ </p>
25
+ </header>
26
+
27
+ <ChatPanel />
28
+ <Footer />
29
+ </main>
30
+ );
31
+ }
32
+
frontend/app/ingestion/page.tsx ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Link from "next/link";
2
+
3
+ import { KnowledgeBasePanel } from "@/components/knowledge-base-panel";
4
+ import { Footer } from "@/components/footer";
5
+
6
+ export default function IngestionPage() {
7
+ return (
8
+ <main className="mx-auto flex min-h-screen max-w-5xl flex-col gap-10 px-4 pb-16 pt-12 sm:px-6 lg:px-8">
9
+ <header className="flex flex-col gap-4 rounded-2xl border border-white/10 bg-white/5 px-6 py-6 text-slate-100 shadow-lg shadow-slate-950/40">
10
+ <div className="flex items-center justify-between gap-3">
11
+ <div className="flex items-center gap-3 text-base font-semibold">
12
+ <span className="inline-flex h-10 w-10 items-center justify-center rounded-2xl bg-gradient-to-br from-sky-400 to-cyan-500 text-slate-950">
13
+ IC
14
+ </span>
15
+ IntegraChat Β· Data Ingestion
16
+ </div>
17
+ <Link href="/" className="text-xs font-semibold uppercase tracking-[0.3em] text-cyan-300 hover:text-white">
18
+ ← Back Home
19
+ </Link>
20
+ </div>
21
+ <p className="text-sm text-slate-300">
22
+ Upload raw text, URLs, or documents to feed the tenant-specific RAG index. All inputs flow into the FastAPI +
23
+ MCP ingestion pipeline.
24
+ </p>
25
+ </header>
26
+
27
+ <KnowledgeBasePanel />
28
+ <Footer />
29
+ </main>
30
+ );
31
+ }
32
+
frontend/app/page.tsx CHANGED
@@ -1,3 +1,6 @@
 
 
 
1
  import { AnalyticsPanel } from "@/components/analytics-panel";
2
  import { ChatPanel } from "@/components/chat-panel";
3
  import { FeatureGrid } from "@/components/feature-grid";
@@ -5,26 +8,60 @@ import { Footer } from "@/components/footer";
5
  import { Hero } from "@/components/hero";
6
  import { KnowledgeBasePanel } from "@/components/knowledge-base-panel";
7
 
 
 
 
 
 
 
 
8
  export default function Home() {
9
  return (
10
  <main className="mx-auto flex min-h-screen max-w-6xl flex-col gap-10 px-4 pb-16 pt-12 sm:px-6 lg:px-8">
11
- <header className="flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-white/10 bg-white/5 px-6 py-4 text-sm text-slate-100 shadow-lg shadow-slate-950/40">
12
- <div className="flex items-center gap-3 text-base font-semibold">
13
- <span className="inline-flex h-10 w-10 items-center justify-center rounded-2xl bg-gradient-to-br from-sky-400 to-cyan-500 text-slate-950">
14
- IC
15
- </span>
16
- IntegraChat Operator Console
17
- </div>
18
- <div className="flex flex-wrap items-center gap-3 text-xs uppercase tracking-[0.4em] text-slate-400">
19
- FastAPI Β· MCP Servers Β· Celery Β· Next.js
 
 
20
  </div>
 
 
 
 
 
 
 
 
 
 
 
21
  </header>
22
 
23
  <Hero />
24
  <FeatureGrid />
25
- <KnowledgeBasePanel />
26
- <ChatPanel />
27
- <AnalyticsPanel />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  <Footer />
29
  </main>
30
  );
 
1
+ import Link from "next/link";
2
+
3
+ import { AdminRulesPanel } from "@/components/admin-rules-panel";
4
  import { AnalyticsPanel } from "@/components/analytics-panel";
5
  import { ChatPanel } from "@/components/chat-panel";
6
  import { FeatureGrid } from "@/components/feature-grid";
 
8
  import { Hero } from "@/components/hero";
9
  import { KnowledgeBasePanel } from "@/components/knowledge-base-panel";
10
 
11
+ const navItems = [
12
+ { label: "Data Ingestion", href: "/ingestion" },
13
+ { label: "Chat Bot", href: "/chat" },
14
+ { label: "Analytics", href: "/analytics" },
15
+ { label: "Admin Rule Ingestion", href: "/admin-rules" },
16
+ ];
17
+
18
  export default function Home() {
19
  return (
20
  <main className="mx-auto flex min-h-screen max-w-6xl flex-col gap-10 px-4 pb-16 pt-12 sm:px-6 lg:px-8">
21
+ <header className="flex flex-col gap-6 rounded-2xl border border-white/10 bg-white/5 px-6 py-6 text-slate-100 shadow-lg shadow-slate-950/40">
22
+ <div className="flex flex-wrap items-center justify-between gap-3 text-sm">
23
+ <div className="flex items-center gap-3 text-base font-semibold">
24
+ <span className="inline-flex h-10 w-10 items-center justify-center rounded-2xl bg-gradient-to-br from-sky-400 to-cyan-500 text-slate-950">
25
+ IC
26
+ </span>
27
+ IntegraChat Operator Console
28
+ </div>
29
+ <div className="flex flex-wrap items-center gap-3 text-xs uppercase tracking-[0.4em] text-slate-400">
30
+ FastAPI Β· MCP Servers Β· Celery Β· Next.js
31
+ </div>
32
  </div>
33
+ <nav className="flex flex-wrap gap-2">
34
+ {navItems.map((item) => (
35
+ <Link
36
+ key={item.href}
37
+ href={item.href}
38
+ className="rounded-full border border-white/15 px-4 py-2 text-xs font-semibold uppercase tracking-[0.2em] text-slate-200 transition hover:border-cyan-400 hover:text-white"
39
+ >
40
+ {item.label}
41
+ </Link>
42
+ ))}
43
+ </nav>
44
  </header>
45
 
46
  <Hero />
47
  <FeatureGrid />
48
+
49
+ <section id="data-ingestion" className="scroll-mt-28">
50
+ <KnowledgeBasePanel />
51
+ </section>
52
+
53
+ <section id="chat-bot" className="scroll-mt-28">
54
+ <ChatPanel />
55
+ </section>
56
+
57
+ <section id="analytics" className="scroll-mt-28">
58
+ <AnalyticsPanel />
59
+ </section>
60
+
61
+ <section id="admin-rules" className="scroll-mt-28">
62
+ <AdminRulesPanel />
63
+ </section>
64
+
65
  <Footer />
66
  </main>
67
  );
frontend/components/admin-rules-panel.tsx ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export function AdminRulesPanel() {
2
+ const highlights = [
3
+ {
4
+ title: "Bulk Upload",
5
+ description: "Paste multiple compliance rules at once and push them to the MCP backend.",
6
+ },
7
+ {
8
+ title: "Governance Timeline",
9
+ description: "Track when each rule was last updated for audit-readiness.",
10
+ },
11
+ {
12
+ title: "Rule Severity",
13
+ description: "Tag entries as advisory, warning, or block-listed for automated enforcement.",
14
+ },
15
+ {
16
+ title: "Tenant Isolation",
17
+ description: "Rules are scoped per tenant, ensuring zero data leakage.",
18
+ },
19
+ ];
20
+
21
+ return (
22
+ <div className="rounded-3xl border border-slate-800/80 bg-slate-900/60 p-8 shadow-2xl shadow-slate-950/40">
23
+ <div className="flex flex-col gap-4">
24
+ <div>
25
+ <p className="text-sm font-semibold uppercase tracking-[0.3em] text-cyan-400">Admin Controls</p>
26
+ <h2 className="mt-3 text-3xl font-semibold text-white">Admin Rule Ingestion</h2>
27
+ <p className="mt-3 text-base text-slate-300">
28
+ Upload governance policies, red-flag keywords, and compliance workflows directly into the IntegraChat admin
29
+ service. Rules are stored in the backend&apos;s SQLite demo store and enforced across all MCP toolchains.
30
+ </p>
31
+ </div>
32
+
33
+ <div className="grid gap-4 md:grid-cols-2">
34
+ {highlights.map((item) => (
35
+ <div
36
+ key={item.title}
37
+ className="rounded-2xl border border-white/5 bg-white/5 p-4 text-slate-200 transition hover:border-cyan-400/40"
38
+ >
39
+ <p className="text-sm font-semibold text-cyan-300">{item.title}</p>
40
+ <p className="mt-2 text-sm text-slate-300">{item.description}</p>
41
+ </div>
42
+ ))}
43
+ </div>
44
+
45
+ <div className="flex flex-wrap gap-3">
46
+ <button className="rounded-full bg-gradient-to-r from-cyan-400 to-blue-500 px-6 py-2 text-sm font-semibold text-slate-950 shadow-lg shadow-cyan-500/30">
47
+ Launch Admin Console
48
+ </button>
49
+ <button className="rounded-full border border-white/20 px-6 py-2 text-sm font-semibold text-white hover:border-cyan-400/60">
50
+ View Rule Templates
51
+ </button>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ );
56
+ }
57
+