diff --git a/website/docs/overview/architecture/system-architecture.md b/website/docs/overview/architecture/system-architecture.md index f6c7c785..2139c782 100644 --- a/website/docs/overview/architecture/system-architecture.md +++ b/website/docs/overview/architecture/system-architecture.md @@ -4,8 +4,10 @@ The Semantic Router implements a sophisticated Mixture-of-Models (MoM) architect ## High-Level Architecture Overview -```mermaid -graph TB +import ZoomableMermaid from '@site/src/components/ZoomableMermaid'; + + +{`graph TB subgraph "Client Layer" Client1[Web Application] Client2[Mobile App] @@ -62,8 +64,8 @@ graph TB ExtProc --> Prometheus Prometheus --> Grafana - ExtProc --> Logs -``` + ExtProc --> Logs`} + ## Core Components @@ -113,7 +115,7 @@ http_filters: type OpenAIRouter struct { Config *config.RouterConfig CategoryDescriptions []string - Classifier *classification.Classifier // ModernBERT-based + Classifier *classification.Classifier // ModernBERT-based PIIChecker *pii.PolicyChecker // Privacy protection Cache *cache.SemanticCache // Performance optimization ToolsDatabase *tools.ToolsDatabase // Tool selection @@ -125,8 +127,8 @@ type OpenAIRouter struct { **Processing Pipeline**: -```mermaid -sequenceDiagram + +{`sequenceDiagram participant E as Envoy participant R as Router participant C as Classifier @@ -152,8 +154,8 @@ sequenceDiagram E->>R: Response from model R->>Ca: Cache semantic representation R->>E: Final response - end -``` + end`} + ### 3. Classification System - Decision Engine @@ -161,8 +163,8 @@ The classification system uses ModernBERT models for multiple classification tas #### Category Classification -```mermaid -graph LR + +{`graph LR Query[User Query] --> Tokenizer[ModernBERT Tokenizer] Tokenizer --> Encoder[ModernBERT Encoder
768-dim embeddings] Encoder --> ClassifierHead[Classification Head
Category Prediction] @@ -182,8 +184,8 @@ graph LR Decision --> Code Decision --> General Decision --> Science - Decision --> Business -``` + Decision --> Business`} +
#### Multi-Task Architecture @@ -262,8 +264,8 @@ graph TB ### Response Processing Flow -```mermaid -sequenceDiagram + +{`sequenceDiagram participant C as Client participant E as Envoy participant R as Router @@ -285,8 +287,8 @@ sequenceDiagram R->>Me: Record routing metrics R->>E: Processed Response - E->>C: Final Response to Client -``` + E->>C: Final Response to Client`} + ## Threading and Concurrency Model @@ -514,8 +516,8 @@ func (cb *CircuitBreaker) Call(operation func() error) error { ### Fallback Strategies -```mermaid -graph TB + +{`graph TB Request[Incoming Request] --> PrimaryRoute[Primary Routing Decision] PrimaryRoute --> ModelA{Model A
Available?} @@ -534,8 +536,8 @@ graph TB ProcessA --> Success[Successful Response] ProcessB --> Success ProcessGeneral --> Success - ReturnCached --> Success -``` + ReturnCached --> Success`} +
## Monitoring and Observability diff --git a/website/src/components/ZoomableMermaid/index.js b/website/src/components/ZoomableMermaid/index.js new file mode 100644 index 00000000..3d6eefb8 --- /dev/null +++ b/website/src/components/ZoomableMermaid/index.js @@ -0,0 +1,235 @@ +import React, { useState, useRef, useEffect, useCallback } from 'react' +import { createPortal } from 'react-dom' +import Mermaid from '@theme/Mermaid' +import styles from './styles.module.css' + +const ZoomableMermaid = ({ children, title, defaultZoom = 1.2 }) => { + const [isModalOpen, setIsModalOpen] = useState(false) + const [isHovered, setIsHovered] = useState(false) + const [zoomLevel, setZoomLevel] = useState(defaultZoom) // Use defaultZoom prop + const modalRef = useRef(null) + const containerRef = useRef(null) + + const openModal = useCallback(() => { + setIsModalOpen(true) + setZoomLevel(defaultZoom) // Reset to default zoom when opening + document.body.style.overflow = 'hidden' + }, [defaultZoom]) + + const closeModal = useCallback(() => { + setIsModalOpen(false) + document.body.style.overflow = 'unset' + // Return focus to the original container + if (containerRef.current) { + containerRef.current.focus() + } + }, []) + + const zoomIn = useCallback(() => { + setZoomLevel(prev => Math.min(prev + 0.2, 5.0)) // Max 500% + }, []) + + const zoomOut = useCallback(() => { + setZoomLevel(prev => Math.max(prev - 0.2, 0.5)) // Min 50% + }, []) + + const resetZoom = useCallback(() => { + setZoomLevel(defaultZoom) // Reset to custom default instead of hardcoded 1.2 + }, [defaultZoom]) + + useEffect(() => { + const handleEscape = (e) => { + if (e.key === 'Escape' && isModalOpen) { + closeModal() + } + } + + const handleClickOutside = (e) => { + if (modalRef.current && !modalRef.current.contains(e.target)) { + closeModal() + } + } + + const handleKeydown = (e) => { + if (!isModalOpen) return + + if (e.key === '=' || e.key === '+') { + e.preventDefault() + zoomIn() + } + else if (e.key === '-') { + e.preventDefault() + zoomOut() + } + else if (e.key === '0') { + e.preventDefault() + resetZoom() + } + } + + if (isModalOpen) { + document.addEventListener('keydown', handleEscape) + document.addEventListener('mousedown', handleClickOutside) + document.addEventListener('keydown', handleKeydown) + + // Focus the modal content when opened + setTimeout(() => { + if (modalRef.current) { + modalRef.current.focus() + } + }, 100) + } + + return () => { + document.removeEventListener('keydown', handleEscape) + document.removeEventListener('mousedown', handleClickOutside) + document.removeEventListener('keydown', handleKeydown) + } + }, [isModalOpen, closeModal, zoomIn, zoomOut, resetZoom]) + + // Cleanup on unmount + useEffect(() => { + return () => { + document.body.style.overflow = 'unset' + } + }, []) + + const handleKeyDown = (e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault() + openModal() + } + } + + const modalContent = ( +
+
+
+ {title && ( + + )} +
+ + {Math.round(zoomLevel * 100)} + % + + + + + +
+
+ +
+
+ ) + + return ( + <> +
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + role="button" + tabIndex={0} + onKeyDown={handleKeyDown} + aria-label={`Click to enlarge ${title || 'Mermaid diagram'}`} + aria-expanded={isModalOpen} + > + + +
+ + {isModalOpen && createPortal(modalContent, document.body)} + + ) +} + +export default ZoomableMermaid diff --git a/website/src/components/ZoomableMermaid/styles.module.css b/website/src/components/ZoomableMermaid/styles.module.css new file mode 100644 index 00000000..aeb84e13 --- /dev/null +++ b/website/src/components/ZoomableMermaid/styles.module.css @@ -0,0 +1,375 @@ +.mermaidContainer { + position: relative; + cursor: pointer; + border-radius: 12px; + overflow: hidden; + transition: all 0.3s ease; + background: var(--tech-card-bg); + border: 1px solid var(--tech-border); + box-shadow: var(--tech-shadow); +} + +.mermaidContainer:hover { + transform: translateY(-2px); + box-shadow: 0 12px 32px rgba(9, 105, 218, 0.15); + border-color: var(--tech-border-accent); +} + +.mermaidContainer:focus { + outline: 2px solid var(--tech-primary-blue); + outline-offset: 2px; +} + +.zoomHint { + position: absolute; + top: 12px; + right: 12px; + background: rgba(9, 105, 218, 0.9); + color: white; + padding: 6px 10px; + border-radius: 6px; + font-size: 12px; + display: flex; + align-items: center; + gap: 4px; + opacity: 0; + transform: translateY(-4px); + transition: all 0.3s ease; + z-index: 10; + backdrop-filter: blur(10px); + font-weight: 500; + box-shadow: 0 4px 12px rgba(9, 105, 218, 0.3); +} + +.mermaidContainer:hover .zoomHint, +.mermaidContainer:focus .zoomHint { + opacity: 1; + transform: translateY(0); +} + +.modal { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + bottom: 0 !important; + width: 100vw !important; + height: 100vh !important; + background: rgba(0, 0, 0, 0.9); + backdrop-filter: blur(5px); + display: flex; + align-items: center; + justify-content: center; + z-index: 99999 !important; + padding: 0 !important; + margin: 0 !important; + animation: fadeIn 0.3s ease; +} + +.modalContent { + background: var(--tech-card-bg); + border-radius: 16px; + width: 70vw; + height: 70vh; + max-width: none; + max-height: none; + overflow: hidden; + box-shadow: 0 24px 64px rgba(0, 0, 0, 0.5); + border: 1px solid var(--tech-border); + display: flex; + flex-direction: column; + animation: slideIn 0.3s ease; + position: relative !important; + margin: auto !important; +} + +.modalHeader { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 24px 16px; + border-bottom: 1px solid var(--tech-border); + background: var(--tech-surface-bg); + flex-shrink: 0; +} + +.modalTitle { + margin: 0; + color: var(--tech-text-primary); + font-size: 18px; + font-weight: 600; +} + +.modalControls { + display: flex; + align-items: center; + gap: 8px; +} + +.zoomIndicator { + font-size: 12px; + color: var(--tech-text-secondary); + font-weight: 500; + min-width: 35px; + text-align: center; +} + +.zoomButton { + background: none; + border: none; + cursor: pointer; + padding: 6px; + border-radius: 6px; + color: var(--tech-text-secondary); + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; + width: 30px; + height: 30px; +} + +.zoomButton:hover:not(:disabled) { + background: rgba(9, 105, 218, 0.1); + color: var(--tech-primary-blue); + transform: scale(1.05); +} + +.zoomButton:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.zoomButton:focus:not(:disabled) { + outline: 2px solid var(--tech-primary-blue); + outline-offset: 2px; +} + +.resetButton { + background: none; + border: none; + cursor: pointer; + padding: 6px; + border-radius: 6px; + color: var(--tech-text-secondary); + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; + width: 30px; + height: 30px; +} + +.resetButton:hover { + background: rgba(156, 39, 176, 0.1); + color: var(--tech-accent-purple); + transform: scale(1.05); +} + +.resetButton:focus { + outline: 2px solid var(--tech-primary-blue); + outline-offset: 2px; +} + +.closeButton { + background: none; + border: none; + cursor: pointer; + padding: 6px; + border-radius: 6px; + color: var(--tech-text-secondary); + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; + width: 30px; + height: 30px; + margin-left: 4px; +} + +.closeButton:hover { + background: rgba(244, 67, 54, 0.1); + color: #f44336; + transform: scale(1.1); +} + +.closeButton:focus { + outline: 2px solid var(--tech-primary-blue); + outline-offset: 2px; +} + +.modalBody { + padding: 24px; + overflow: auto; + flex: 1; + display: flex; + /* must be flex-start. */ + align-items: flex-start; + justify-content: center; + min-height: 0; + background: var(--tech-surface-bg); +} + +.diagramContainer { + transition: transform 0.3s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.diagramContainer .mermaid { + background: transparent !important; + border: none !important; + box-shadow: none !important; + max-width: none !important; + max-height: none !important; + margin: 0 !important; + padding: 0 !important; +} + +/* Ensure Mermaid diagrams in modal are properly sized */ +.diagramContainer .mermaid svg { + max-width: none !important; + width: auto !important; + height: auto !important; + display: block !important; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes slideIn { + from { + opacity: 0; + transform: scale(0.9) translateY(20px); + } + to { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +/* Dark theme support */ +[data-theme='dark'] .zoomHint { + background: rgba(88, 166, 255, 0.9); + box-shadow: 0 4px 12px rgba(88, 166, 255, 0.3); +} + +[data-theme='dark'] .modal { + background: rgba(0, 0, 0, 0.95); +} + +/* Override any potential Docusaurus container constraints */ +.modal { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + bottom: 0 !important; + width: 100vw !important; + height: 100vh !important; + margin: 0 !important; + padding: 0 !important; + z-index: 99999 !important; +} + +.modalContent { + position: relative !important; + margin: auto !important; +} + +/* Mobile responsive */ +@media (max-width: 768px) { + .modal { + padding: 0; + width: 100vw; + height: 100vh; + } + + .modalContent { + width: 95vw !important; + height: 90vh !important; + border-radius: 12px; + } + + .modalHeader { + padding: 16px 20px 12px; + flex-shrink: 0; + } + + .modalTitle { + font-size: 16px; + } + + .modalBody { + padding: 16px; + flex: 1; + } + + .zoomHint { + font-size: 11px; + padding: 4px 8px; + top: 8px; + right: 8px; + } + + .closeButton, + .zoomButton, + .resetButton { + width: 28px; + height: 28px; + padding: 4px; + } + + .modalControls { + gap: 6px; + } + + .zoomIndicator { + font-size: 11px; + min-width: 30px; + } +} + +/* High contrast mode support */ +@media (prefers-contrast: high) { + .mermaidContainer { + border-width: 2px; + } + + .zoomHint { + background: #000; + color: #fff; + } + + [data-theme='dark'] .zoomHint { + background: #fff; + color: #000; + } +} + +/* Reduced motion support */ +@media (prefers-reduced-motion: reduce) { + .mermaidContainer, + .zoomHint, + .modal, + .modalContent, + .closeButton { + transition: none; + animation: none; + } + + .modal { + animation: none; + } + + .modalContent { + animation: none; + } +} \ No newline at end of file