diff --git a/web-ui/src/app/settings/_components/blocks-tab.tsx b/web-ui/src/app/settings/_components/blocks-tab.tsx new file mode 100644 index 0000000..3aee0e5 --- /dev/null +++ b/web-ui/src/app/settings/_components/blocks-tab.tsx @@ -0,0 +1,128 @@ +"use client"; + +import { Layers, AlertCircle } from "lucide-react"; +import { Card, CardContent } from "@/components/ui/card"; +import { Skeleton } from "@/components/ui/skeleton"; +import { Badge } from "@/components/ui/badge"; +import { useMcpBlocks, type McpBlock } from "@/lib/api/settings"; + +const GEN_TYPE_LABEL: Record = { + "template-fill": "מילוי תבנית", + "paraphrase": "פרפרזה", + "reproduction": "שעתוק", + "guided-synthesis": "סינתזה מודרכת", + "rhetorical-construction": "בניה רטורית", +}; + +const GEN_TYPE_TONE: Record = { + "template-fill": "text-ink-muted border-rule", + "paraphrase": "text-info border-info/40", + "reproduction": "text-info border-info/40", + "guided-synthesis": "text-warn border-warn/40", + "rhetorical-construction": "text-gold-deep border-gold/40", +}; + +function BlockRow({ block }: { block: McpBlock }) { + const isLLM = block.model !== "script"; + return ( +
+
+
+ + {block.index} + +
+
+
+

{block.title}

+ + {block.id} + +
+
+ + {GEN_TYPE_LABEL[block.gen_type] ?? block.gen_type} + + + {block.model} + + {isLLM && block.temperature !== null && ( + + temp {block.temperature} + + )} + {block.max_tokens !== null && ( + + max {block.max_tokens} + + )} +
+ {(block.creac_role || block.jwm_purpose) && ( +
+ {block.creac_role && ( +
+ + CREAC: + + {block.creac_role} +
+ )} + {block.jwm_purpose && ( +
+ + JWM: + + {block.jwm_purpose} +
+ )} +
+ )} +
+
+
+ ); +} + +export function BlocksTab() { + const { data, isPending, error } = useMcpBlocks(); + + if (isPending) return ; + if (error) { + return ( + + + + שגיאה בטעינת בלוקים: {error.message} + + + ); + } + if (!data) return null; + + return ( +
+ + +
+ + + ארכיטקטורת 12 הבלוקים של החלטת ועדת ערר. מקור הסכימה:{" "} + + docs/block-schema.md + + . + +
+
+ {data.blocks.map((b) => ( + + ))} +
+
+
+
+ ); +} diff --git a/web-ui/src/app/settings/page.tsx b/web-ui/src/app/settings/page.tsx index 424d575..d9e88dd 100644 --- a/web-ui/src/app/settings/page.tsx +++ b/web-ui/src/app/settings/page.tsx @@ -1,13 +1,14 @@ "use client"; import Link from "next/link"; -import { Server, Wrench, Plug, Building2 } from "lucide-react"; +import { Server, Wrench, Plug, Building2, Layers } from "lucide-react"; import { AppShell } from "@/components/app-shell"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { PaperclipTab } from "./_components/paperclip-tab"; import { EnvironmentTab } from "./_components/environment-tab"; import { ToolsTab } from "./_components/tools-tab"; import { RegistrationsTab } from "./_components/registrations-tab"; +import { BlocksTab } from "./_components/blocks-tab"; export default function SettingsPage() { return ( @@ -43,6 +44,10 @@ export default function SettingsPage() { Tools + + + Blocks + Registrations @@ -52,6 +57,7 @@ export default function SettingsPage() { + diff --git a/web-ui/src/lib/api/settings.ts b/web-ui/src/lib/api/settings.ts index 135a064..048e461 100644 --- a/web-ui/src/lib/api/settings.ts +++ b/web-ui/src/lib/api/settings.ts @@ -169,3 +169,27 @@ export function useMcpRegistrations() { staleTime: 60_000, }); } + +export type McpBlock = { + id: string; + index: number; + title: string; + gen_type: string; + model: string; + temperature: number | null; + max_tokens: number | null; + creac_role: string | null; + jwm_purpose: string | null; +}; + +export function useMcpBlocks() { + return useQuery({ + queryKey: ["settings", "mcp-blocks"] as const, + queryFn: ({ signal }) => + apiRequest<{ blocks: McpBlock[]; count: number }>( + "/api/settings/mcp/blocks", + { signal }, + ), + staleTime: 5 * 60_000, // 5 minutes — static reference data + }); +} diff --git a/web/app.py b/web/app.py index a6f719a..02e406b 100644 --- a/web/app.py +++ b/web/app.py @@ -2830,6 +2830,53 @@ async def api_mcp_registrations(): return list_registrations() +@app.get("/api/settings/mcp/blocks") +async def api_mcp_blocks(): + """List the 12-block decision schema (read-only reference).""" + from legal_mcp.services.block_writer import BLOCK_CONFIG + + # CREAC role per block (from docs/block-schema.md). Static map — + # kept here rather than in BLOCK_CONFIG to avoid coupling LLM + # generation config to documentation metadata. + CREAC_ROLE = { + "block-alef": None, "block-bet": None, "block-gimel": None, + "block-dalet": None, "block-yod-bet": None, + "block-he": "Conclusion (preview)", + "block-vav": "Facts (R-context)", + "block-zayin": "Arguments", + "block-chet": "Procedural record", + "block-tet": "Rule (R)", + "block-yod": "C → R → E → A → C (full CREAC)", + "block-yod-alef": "Conclusion (final)", + } + # JWM functional purpose (Federal Judicial Center mapping) + JWM_PURPOSE = { + "block-alef": "Orientation", "block-bet": "Orientation", + "block-gimel": "Orientation", "block-dalet": "Orientation", + "block-he": "Orientation", + "block-vav": "Framing", "block-zayin": "Argumentation", + "block-chet": "Procedural record", + "block-tet": "Deliberation (rules)", + "block-yod": "Deliberation (analysis)", + "block-yod-alef": "Disposition", + "block-yod-bet": "Disposition (signatures)", + } + blocks = [] + for block_id, cfg in sorted(BLOCK_CONFIG.items(), key=lambda kv: kv[1]["index"]): + blocks.append({ + "id": block_id, + "index": cfg["index"], + "title": cfg["title"], + "gen_type": cfg["gen_type"], + "model": cfg["model"], + "temperature": cfg.get("temp"), + "max_tokens": cfg.get("max_tokens"), + "creac_role": CREAC_ROLE.get(block_id), + "jwm_purpose": JWM_PURPOSE.get(block_id), + }) + return {"blocks": blocks, "count": len(blocks)} + + # ── Settings: Tag → Company Mappings ────────────────────────────── @app.get("/api/settings/paperclip-companies")