Skip to content

Commit d235c26

Browse files
committed
feat: add token bucket algo visualization for learning
1 parent 51237f7 commit d235c26

4 files changed

Lines changed: 983 additions & 8 deletions

File tree

examples/playground-react/client/components/SettingsDrawer.jsx

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ import { useApp } from '../context/AppContext';
66
import { getApiKey, saveApiKey } from '../utils';
77
import { MODELS_API_URL } from '../utils/constants';
88
import { getProviderIds, getProviderDisplayName } from '../utils/providerUtils';
9-
import { FaTimes, FaServer, FaKey, FaSlidersH, FaCode, FaFileAlt, FaShieldAlt, FaTint } from 'react-icons/fa';
9+
import { FaTimes, FaServer, FaKey, FaSlidersH, FaCode, FaFileAlt, FaShieldAlt, FaTint, FaInfoCircle, FaArrowLeft } from 'react-icons/fa';
10+
import { TokenBucketDemo } from './TokenBucketDemo';
1011

1112
// Helper function to render resilience settings section
12-
function ResilienceSettingsSection({ config, updateConfig, sectionRef }) {
13+
function ResilienceSettingsSection({ config, updateConfig, sectionRef, onShowTokenBucket }) {
1314
return (
1415
<section ref={sectionRef} className="config-section">
1516
<div className="config-section-header">
@@ -72,6 +73,37 @@ function ResilienceSettingsSection({ config, updateConfig, sectionRef }) {
7273
<span>Rate limiting</span>
7374
</div>
7475
<small className="resilience-hint resilience-hint-note">Token bucket algorithm controls request and token consumption. When empty, requests wait for tokens to replenish. Applied per LLM provider.</small>
76+
<button
77+
onClick={onShowTokenBucket}
78+
style={{
79+
display: 'inline-flex',
80+
alignItems: 'center',
81+
gap: '6px',
82+
padding: '8px 12px',
83+
marginTop: '8px',
84+
marginBottom: '12px',
85+
background: '#f4f4f5',
86+
border: '1px solid #e4e4e7',
87+
borderRadius: '6px',
88+
fontSize: '12px',
89+
color: '#09090b',
90+
cursor: 'pointer',
91+
transition: 'all 0.2s',
92+
fontWeight: 500,
93+
}}
94+
onMouseEnter={(e) => {
95+
e.currentTarget.style.background = '#e4e4e7';
96+
e.currentTarget.style.borderColor = '#d4d4d8';
97+
}}
98+
onMouseLeave={(e) => {
99+
e.currentTarget.style.background = '#f4f4f5';
100+
e.currentTarget.style.borderColor = '#e4e4e7';
101+
}}
102+
title="Learn about Token Bucket Rate Algorithm"
103+
>
104+
<FaInfoCircle />
105+
Visualize Token Bucket Algorithm
106+
</button>
75107
<div className="resilience-grid">
76108
<div className="resilience-field">
77109
<label>Requests/min</label>
@@ -145,7 +177,7 @@ function ResilienceSettingsSection({ config, updateConfig, sectionRef }) {
145177
}
146178

147179
export function SettingsDrawer() {
148-
const { settingsOpen, setSettingsOpen, config, setConfig, saveConversation, settingsDefaultSection } = useApp();
180+
const { settingsOpen, setSettingsOpen, config, setConfig, saveConversation, settingsDefaultSection, currentRoute, setCurrentRoute } = useApp();
149181
const [apiKey, setApiKeyState] = useState('');
150182
const [models, setModels] = useState([]);
151183
const [filteredModels, setFilteredModels] = useState([]);
@@ -504,13 +536,48 @@ export function SettingsDrawer() {
504536
</button>
505537
</div>
506538
<div className="settings-drawer-body">
507-
<div className="config-panel">
508-
{settingsDefaultSection === 'resilience' ? (
539+
{currentRoute === 'token-bucket' ? (
540+
<div style={{ padding: '20px', height: '100%', overflowY: 'auto' }}>
541+
<button
542+
onClick={() => setCurrentRoute('playground')}
543+
style={{
544+
display: 'inline-flex',
545+
alignItems: 'center',
546+
gap: '8px',
547+
marginBottom: '20px',
548+
padding: '8px 12px',
549+
background: '#f4f4f5',
550+
border: '1px solid #e4e4e7',
551+
borderRadius: '6px',
552+
fontSize: '13px',
553+
color: '#09090b',
554+
cursor: 'pointer',
555+
transition: 'all 0.2s',
556+
fontWeight: 500,
557+
}}
558+
onMouseEnter={(e) => {
559+
e.currentTarget.style.background = '#e4e4e7';
560+
e.currentTarget.style.borderColor = '#d4d4d8';
561+
}}
562+
onMouseLeave={(e) => {
563+
e.currentTarget.style.background = '#f4f4f5';
564+
e.currentTarget.style.borderColor = '#e4e4e7';
565+
}}
566+
>
567+
<FaArrowLeft />
568+
Back to Settings
569+
</button>
570+
<TokenBucketDemo onBack={() => setCurrentRoute('playground')} />
571+
</div>
572+
) : (
573+
<div className="config-panel">
574+
{settingsDefaultSection === 'resilience' ? (
509575
<>
510576
<ResilienceSettingsSection
511577
config={config}
512578
updateConfig={updateConfig}
513579
sectionRef={resilienceSectionRef}
580+
onShowTokenBucket={() => setCurrentRoute('token-bucket')}
514581
/>
515582
<section ref={modelsSectionRef} className="config-section">
516583
<div className="config-section-header">
@@ -840,10 +907,12 @@ export function SettingsDrawer() {
840907
config={config}
841908
updateConfig={updateConfig}
842909
sectionRef={resilienceSectionRef}
910+
onShowTokenBucket={() => setCurrentRoute('token-bucket')}
843911
/>
844912
</>
845913
)}
846-
</div>
914+
</div>
915+
)}
847916
</div>
848917
</aside>
849918
</>

0 commit comments

Comments
 (0)