| | <!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>Function Arena // BUILD_YOUR_TOOLS</title> |
| | |
| | <link href="https://api.fontshare.com/v2/css?f[]=clash-display@700,600,500&f[]=space-mono@400,700&display=swap" rel="stylesheet"> |
| | |
| | |
| | <script async src="https://www.googletagmanager.com/gtag/js?id=G-2Q4M55VKPR"></script> |
| | <script> |
| | window.dataLayer = window.dataLayer || []; |
| | function gtag(){dataLayer.push(arguments);} |
| | gtag('js', new Date()); |
| | gtag('config', 'G-2Q4M55VKPR', { |
| | page_path: window.location.pathname, |
| | anonymize_ip: true |
| | }); |
| | </script> |
| | |
| | <style> |
| | :root { |
| | --void: #050505; |
| | --panel: #111111; |
| | --acid: #ccff00; |
| | --acid-dim: #4d6000; |
| | --hyper-purple: #7000ff; |
| | --alert: #ff3300; |
| | --success: #00ff88; |
| | --text-main: #f0f0f0; |
| | --text-muted: #666; |
| | --border-thick: 3px; |
| | --hard-shadow: 6px 6px 0px var(--hyper-purple); |
| | --ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1); |
| | } |
| | |
| | * { margin: 0; padding: 0; box-sizing: border-box; } |
| | |
| | body { |
| | background-color: var(--void); |
| | color: var(--text-main); |
| | font-family: 'Space Mono', monospace; |
| | min-height: 100vh; |
| | overflow-x: hidden; |
| | display: flex; |
| | flex-direction: column; |
| | } |
| | |
| | .noise-overlay { |
| | position: fixed; |
| | top: 0; left: 0; width: 100%; height: 100%; |
| | pointer-events: none; |
| | z-index: 9999; |
| | opacity: 0.05; |
| | background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E"); |
| | } |
| | |
| | .scanlines { |
| | position: fixed; |
| | top: 0; left: 0; width: 100%; height: 100%; |
| | background: linear-gradient(to bottom, rgba(255,255,255,0), rgba(255,255,255,0) 50%, rgba(0,0,0,0.1) 50%, rgba(0,0,0,0.1)); |
| | background-size: 100% 4px; |
| | z-index: 9998; |
| | pointer-events: none; |
| | } |
| | |
| | h1, h2, h3, .display-font { |
| | font-family: 'Clash Display', sans-serif; |
| | text-transform: uppercase; |
| | letter-spacing: -0.02em; |
| | } |
| | |
| | #boot-sequence { |
| | position: fixed; |
| | inset: 0; |
| | background: var(--void); |
| | z-index: 10000; |
| | display: flex; |
| | flex-direction: column; |
| | justify-content: flex-end; |
| | padding: 40px; |
| | font-size: 14px; |
| | } |
| | |
| | .boot-line { |
| | opacity: 0; |
| | transform: translateY(10px); |
| | color: var(--acid); |
| | margin-bottom: 5px; |
| | } |
| | |
| | .app-container { |
| | max-width: 1800px; |
| | margin: 0 auto; |
| | width: 100%; |
| | padding: 20px; |
| | display: grid; |
| | grid-template-columns: 350px 1fr; |
| | gap: 20px; |
| | flex: 1; |
| | opacity: 0; |
| | transition: opacity 0.5s ease; |
| | } |
| | |
| | @media (max-width: 1200px) { |
| | .app-container { grid-template-columns: 1fr; } |
| | } |
| | |
| | .sidebar { |
| | display: flex; |
| | flex-direction: column; |
| | gap: 20px; |
| | } |
| | |
| | .panel { |
| | background: var(--panel); |
| | border: var(--border-thick) solid var(--text-main); |
| | padding: 20px; |
| | position: relative; |
| | } |
| | |
| | .panel-title { |
| | background: var(--text-main); |
| | color: var(--void); |
| | padding: 5px 10px; |
| | font-weight: 700; |
| | display: inline-block; |
| | margin-bottom: 15px; |
| | font-size: 12px; |
| | } |
| | |
| | .stat-row { |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: baseline; |
| | margin-bottom: 10px; |
| | border-bottom: 1px solid #333; |
| | padding-bottom: 5px; |
| | } |
| | |
| | .stat-value { |
| | font-family: 'Clash Display'; |
| | font-size: 24px; |
| | color: var(--acid); |
| | } |
| | |
| | .game-area { |
| | display: flex; |
| | flex-direction: column; |
| | gap: 20px; |
| | } |
| | |
| | .level-header { |
| | border: var(--border-thick) solid var(--acid); |
| | background: var(--void); |
| | padding: 30px; |
| | position: relative; |
| | box-shadow: var(--hard-shadow); |
| | } |
| | |
| | .level-title { |
| | font-size: 2.5rem; |
| | line-height: 0.9; |
| | margin-bottom: 10px; |
| | } |
| | |
| | .objective-badge { |
| | display: inline-block; |
| | background: var(--hyper-purple); |
| | color: var(--acid); |
| | font-weight: 900; |
| | padding: 8px 16px; |
| | font-size: 1rem; |
| | margin-bottom: 15px; |
| | } |
| | |
| | .scenario-grid { |
| | display: grid; |
| | gap: 10px; |
| | margin-bottom: 20px; |
| | } |
| | |
| | .scenario-card { |
| | background: rgba(255,255,255,0.02); |
| | border: 1px solid #333; |
| | padding: 15px; |
| | display: grid; |
| | grid-template-columns: 40px 1fr 100px; |
| | align-items: center; |
| | gap: 15px; |
| | transition: all 0.2s; |
| | } |
| | |
| | .scenario-card:hover { |
| | border-color: var(--acid); |
| | background: rgba(204, 255, 0, 0.03); |
| | } |
| | |
| | .scenario-icon { |
| | font-size: 24px; |
| | text-align: center; |
| | } |
| | |
| | .scenario-text { |
| | font-size: 0.9rem; |
| | line-height: 1.4; |
| | } |
| | |
| | .scenario-expected { |
| | text-align: center; |
| | font-weight: bold; |
| | font-size: 0.8rem; |
| | } |
| | |
| | .scenario-expected.should-trigger { |
| | color: var(--success); |
| | } |
| | |
| | .scenario-expected.should-not { |
| | color: var(--alert); |
| | } |
| | |
| | .scenario-result { |
| | position: absolute; |
| | right: 10px; |
| | top: 50%; |
| | transform: translateY(-50%); |
| | font-size: 20px; |
| | } |
| | |
| | .editor-section { |
| | display: grid; |
| | grid-template-columns: 1fr 1fr; |
| | gap: 20px; |
| | margin-top: 20px; |
| | } |
| | |
| | @media (max-width: 900px) { |
| | .editor-section { grid-template-columns: 1fr; } |
| | } |
| | |
| | .editor-panel { |
| | border: 2px solid #333; |
| | background: #000; |
| | } |
| | |
| | .editor-header { |
| | background: #1a1a1a; |
| | padding: 10px 15px; |
| | border-bottom: 1px solid #333; |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: center; |
| | } |
| | |
| | .editor-title { |
| | color: var(--acid); |
| | font-size: 0.9rem; |
| | font-weight: bold; |
| | } |
| | |
| | textarea, .json-output { |
| | width: 100%; |
| | background: #000; |
| | border: none; |
| | color: var(--text-main); |
| | padding: 20px; |
| | font-family: 'Space Mono', monospace; |
| | font-size: 0.9rem; |
| | min-height: 300px; |
| | resize: vertical; |
| | overflow-y: auto; |
| | } |
| | |
| | textarea { |
| | height: auto; |
| | } |
| | |
| | textarea:focus { |
| | outline: none; |
| | } |
| | |
| | .json-output { |
| | color: var(--success); |
| | overflow-x: auto; |
| | white-space: pre-wrap; |
| | } |
| | |
| | .action-bar { |
| | display: flex; |
| | gap: 10px; |
| | margin-top: 20px; |
| | } |
| | |
| | button { |
| | background: var(--text-main); |
| | color: var(--void); |
| | border: none; |
| | font-family: 'Clash Display', sans-serif; |
| | font-weight: 700; |
| | text-transform: uppercase; |
| | font-size: 1rem; |
| | padding: 15px 30px; |
| | cursor: pointer; |
| | transition: all 0.2s; |
| | flex: 1; |
| | } |
| | |
| | button:hover:not(:disabled) { |
| | background: var(--acid); |
| | transform: translate(-4px, -4px); |
| | box-shadow: 6px 6px 0px var(--text-main); |
| | } |
| | |
| | button:active:not(:disabled) { |
| | transform: translate(0, 0); |
| | box-shadow: none; |
| | } |
| | |
| | button:disabled { |
| | background: #333; |
| | color: #666; |
| | cursor: not-allowed; |
| | } |
| | |
| | button.secondary { |
| | background: transparent; |
| | border: 2px solid var(--text-main); |
| | color: var(--text-main); |
| | } |
| | |
| | button.secondary:hover:not(:disabled) { |
| | background: var(--text-main); |
| | color: var(--void); |
| | } |
| | |
| | .hint-panel { |
| | background: rgba(112, 0, 255, 0.1); |
| | border: 1px solid var(--hyper-purple); |
| | padding: 15px; |
| | margin-top: 20px; |
| | display: none; |
| | } |
| | |
| | .hint-panel.visible { |
| | display: block; |
| | } |
| | |
| | .hint-title { |
| | color: var(--hyper-purple); |
| | font-weight: bold; |
| | margin-bottom: 10px; |
| | font-size: 0.9rem; |
| | } |
| | |
| | .hint-content { |
| | font-size: 0.85rem; |
| | line-height: 1.5; |
| | color: var(--text-muted); |
| | } |
| | |
| | .competitor-functions { |
| | display: grid; |
| | gap: 10px; |
| | margin-top: 15px; |
| | } |
| | |
| | .competitor-fn { |
| | background: rgba(255,0,0,0.05); |
| | border: 1px solid #4a0000; |
| | padding: 12px; |
| | font-size: 0.85rem; |
| | } |
| | |
| | .fn-name-display { |
| | color: var(--alert); |
| | font-weight: bold; |
| | margin-bottom: 5px; |
| | } |
| | |
| | .result-panel { |
| | margin-top: 20px; |
| | padding: 20px; |
| | background: #080808; |
| | border: 2px solid #333; |
| | display: none; |
| | } |
| | |
| | .result-panel.visible { |
| | display: block; |
| | animation: flash 0.2s; |
| | } |
| | |
| | .result-header { |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: center; |
| | margin-bottom: 20px; |
| | padding-bottom: 15px; |
| | border-bottom: 2px solid #333; |
| | } |
| | |
| | .result-title { |
| | font-size: 1.5rem; |
| | font-family: 'Clash Display'; |
| | } |
| | |
| | .result-score { |
| | font-size: 2rem; |
| | font-family: 'Clash Display'; |
| | } |
| | |
| | .result-title.success, .result-score.success { |
| | color: var(--success); |
| | } |
| | |
| | .result-title.fail, .result-score.fail { |
| | color: var(--alert); |
| | } |
| | |
| | .result-breakdown { |
| | display: grid; |
| | gap: 8px; |
| | } |
| | |
| | .result-item { |
| | display: flex; |
| | justify-content: space-between; |
| | padding: 8px; |
| | background: rgba(255,255,255,0.02); |
| | border-left: 3px solid #333; |
| | } |
| | |
| | .result-item.correct { |
| | border-left-color: var(--success); |
| | } |
| | |
| | .result-item.wrong { |
| | border-left-color: var(--alert); |
| | } |
| | |
| | .execution-logs { |
| | border: 2px solid #333; |
| | background: #000; |
| | margin-top: 20px; |
| | } |
| | |
| | .logs-header { |
| | background: #1a1a1a; |
| | padding: 12px 15px; |
| | border-bottom: 1px solid #333; |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: center; |
| | } |
| | |
| | .logs-title { |
| | color: var(--acid); |
| | font-size: 0.9rem; |
| | font-weight: bold; |
| | } |
| | |
| | .logs-clear { |
| | background: transparent; |
| | border: 1px solid #333; |
| | color: #666; |
| | padding: 4px 12px; |
| | font-size: 0.8rem; |
| | cursor: pointer; |
| | } |
| | |
| | .logs-clear:hover { |
| | border-color: #666; |
| | color: #999; |
| | } |
| | |
| | .logs-content { |
| | padding: 15px; |
| | font-family: 'Space Mono', monospace; |
| | font-size: 0.85rem; |
| | color: var(--text-main); |
| | max-height: 400px; |
| | overflow-y: auto; |
| | background: #0a0a0a; |
| | } |
| | |
| | .log-entry { |
| | margin-bottom: 10px; |
| | padding-bottom: 10px; |
| | border-bottom: 1px solid #222; |
| | opacity: 0; |
| | animation: fadeIn 0.3s forwards; |
| | } |
| | |
| | .log-entry:last-child { |
| | border-bottom: none; |
| | } |
| | |
| | @keyframes fadeIn { |
| | from { opacity: 0; transform: translateY(-5px); } |
| | to { opacity: 1; transform: translateY(0); } |
| | } |
| | |
| | .log-time { |
| | color: #666; |
| | font-size: 0.75rem; |
| | } |
| | |
| | .log-label { |
| | color: var(--acid); |
| | font-weight: bold; |
| | display: inline-block; |
| | min-width: 120px; |
| | } |
| | |
| | .log-prompt { |
| | color: #90ee90; |
| | padding: 8px; |
| | background: rgba(144, 238, 144, 0.1); |
| | border-left: 3px solid #90ee90; |
| | margin: 8px 0; |
| | word-break: break-word; |
| | } |
| | |
| | .log-tools { |
| | color: #87ceeb; |
| | padding: 8px; |
| | background: rgba(135, 206, 235, 0.1); |
| | border-left: 3px solid #87ceeb; |
| | margin: 8px 0; |
| | max-height: 200px; |
| | overflow-y: auto; |
| | } |
| | |
| | .log-function { |
| | padding: 4px 8px; |
| | margin: 4px 0; |
| | background: #1a1a1a; |
| | border-radius: 3px; |
| | } |
| | |
| | .log-function-name { |
| | color: var(--acid); |
| | font-weight: bold; |
| | } |
| | |
| | .log-success { |
| | color: #00ff88; |
| | } |
| | |
| | .log-error { |
| | color: #ff3300; |
| | } |
| | |
| | .log-info { |
| | color: #999; |
| | } |
| | |
| | @keyframes flash { |
| | 0% { background-color: var(--text-main); } |
| | 100% { background-color: #080808; } |
| | } |
| | |
| | .rules-modal { |
| | position: fixed; |
| | inset: 0; |
| | background: rgba(0,0,0,0.9); |
| | z-index: 5000; |
| | display: none; |
| | align-items: center; |
| | justify-content: center; |
| | padding: 20px; |
| | } |
| | |
| | .rules-modal.visible { |
| | display: flex; |
| | } |
| | |
| | .rules-content { |
| | background: var(--panel); |
| | border: var(--border-thick) solid var(--acid); |
| | max-width: 800px; |
| | max-height: 90vh; |
| | overflow-y: auto; |
| | padding: 30px; |
| | } |
| | |
| | .rules-section { |
| | margin-bottom: 25px; |
| | } |
| | |
| | .rules-section h3 { |
| | color: var(--acid); |
| | margin-bottom: 10px; |
| | font-size: 1.2rem; |
| | } |
| | |
| | .rules-section p, .rules-section ul { |
| | line-height: 1.6; |
| | color: var(--text-muted); |
| | margin-bottom: 10px; |
| | } |
| | |
| | .rules-section ul { |
| | margin-left: 20px; |
| | } |
| | |
| | .rules-section code { |
| | background: #000; |
| | padding: 2px 6px; |
| | border: 1px solid #333; |
| | color: var(--acid); |
| | font-size: 0.85rem; |
| | } |
| | |
| | .close-rules { |
| | width: 100%; |
| | margin-top: 20px; |
| | } |
| | |
| | .loader-overlay { |
| | position: fixed; |
| | bottom: 20px; |
| | right: 20px; |
| | background: var(--panel); |
| | border: 2px solid var(--acid); |
| | padding: 15px 20px; |
| | z-index: 10001; |
| | display: flex; |
| | flex-direction: column; |
| | align-items: center; |
| | gap: 10px; |
| | min-width: 250px; |
| | box-shadow: var(--hard-shadow); |
| | opacity: 0.9; |
| | } |
| | |
| | .loader-overlay.hidden { |
| | display: none; |
| | } |
| | |
| | .loader-spinner { |
| | width: 30px; |
| | height: 30px; |
| | border: 3px solid var(--panel); |
| | border-top: 3px solid var(--acid); |
| | border-radius: 50%; |
| | animation: spin 1s linear infinite; |
| | } |
| | |
| | @keyframes spin { |
| | 0% { transform: rotate(0deg); } |
| | 100% { transform: rotate(360deg); } |
| | } |
| | |
| | @keyframes slideInRight { |
| | from { |
| | transform: translateX(400px); |
| | opacity: 0; |
| | } |
| | to { |
| | transform: translateX(0); |
| | opacity: 1; |
| | } |
| | } |
| | |
| | @keyframes slideOutRight { |
| | from { |
| | transform: translateX(0); |
| | opacity: 1; |
| | } |
| | to { |
| | transform: translateX(400px); |
| | opacity: 0; |
| | } |
| | } |
| | |
| | @keyframes pulse { |
| | 0%, 100% { transform: scale(1); } |
| | 50% { transform: scale(1.05); } |
| | } |
| | |
| | .scenario-card.correct { |
| | border-color: var(--success); |
| | background: rgba(0, 255, 136, 0.1); |
| | animation: pulse 0.3s; |
| | } |
| | |
| | .scenario-card.wrong { |
| | border-color: var(--alert); |
| | background: rgba(255, 51, 0, 0.1); |
| | animation: pulse 0.3s; |
| | } |
| | |
| | .loader-text { |
| | color: var(--acid); |
| | font-family: 'Clash Display', sans-serif; |
| | font-size: 0.9rem; |
| | text-transform: uppercase; |
| | letter-spacing: 0.05em; |
| | text-align: center; |
| | } |
| | |
| | .loader-progress { |
| | width: 200px; |
| | height: 3px; |
| | background: var(--panel); |
| | border: 1px solid var(--text-main); |
| | position: relative; |
| | overflow: hidden; |
| | } |
| | |
| | .loader-progress-bar { |
| | height: 100%; |
| | background: var(--acid); |
| | width: 0%; |
| | transition: width 0.3s ease; |
| | box-shadow: 0 0 5px var(--acid); |
| | } |
| | |
| | .test-loader { |
| | position: fixed; |
| | bottom: 30px; |
| | right: 30px; |
| | background: var(--panel); |
| | border: var(--border-thick) solid var(--acid); |
| | padding: 20px 30px; |
| | z-index: 5000; |
| | display: none; |
| | align-items: center; |
| | gap: 15px; |
| | box-shadow: var(--hard-shadow); |
| | } |
| | |
| | .test-loader.visible { |
| | display: flex; |
| | animation: slideInUp 0.3s var(--ease-out-expo); |
| | } |
| | |
| | @keyframes slideInUp { |
| | from { |
| | transform: translateY(100px); |
| | opacity: 0; |
| | } |
| | to { |
| | transform: translateY(0); |
| | opacity: 1; |
| | } |
| | } |
| | |
| | .test-loader-spinner { |
| | width: 24px; |
| | height: 24px; |
| | border: 3px solid var(--panel); |
| | border-top: 3px solid var(--acid); |
| | border-radius: 50%; |
| | animation: spin 0.8s linear infinite; |
| | } |
| | |
| | .test-loader-text { |
| | color: var(--acid); |
| | font-family: 'Space Mono', monospace; |
| | font-size: 0.9rem; |
| | } |
| | |
| | .test-loader-progress { |
| | color: var(--text-muted); |
| | font-size: 0.8rem; |
| | } |
| | |
| | .model-info { |
| | position: fixed; |
| | bottom: 10px; |
| | left: 10px; |
| | background: var(--panel); |
| | border: 1px solid var(--text-main); |
| | padding: 8px 12px; |
| | font-size: 0.75rem; |
| | color: var(--text-muted); |
| | z-index: 100; |
| | opacity: 0.7; |
| | transition: opacity 0.2s; |
| | display: flex; |
| | align-items: center; |
| | gap: 15px; |
| | flex-wrap: wrap; |
| | } |
| | |
| | .model-info:hover { |
| | opacity: 1; |
| | } |
| | |
| | .model-info strong { |
| | color: var(--acid); |
| | } |
| | |
| | .model-info .divider { |
| | color: #333; |
| | margin: 0 5px; |
| | } |
| | |
| | .model-info .creator-info { |
| | display: flex; |
| | align-items: center; |
| | gap: 8px; |
| | } |
| | |
| | .model-info .creator-info a { |
| | color: var(--acid); |
| | text-decoration: none; |
| | transition: color 0.2s; |
| | } |
| | |
| | .model-info .creator-info a:hover { |
| | color: var(--success); |
| | } |
| | |
| | .model-info .creator-name { |
| | color: var(--text-main); |
| | font-weight: bold; |
| | } |
| | </style> |
| | </head> |
| | <body> |
| |
|
| | <div class="noise-overlay"></div> |
| | <div class="scanlines"></div> |
| |
|
| | <div id="boot-sequence"></div> |
| |
|
| | <div class="loader-overlay" id="modelLoader"> |
| | <div class="loader-spinner"></div> |
| | <div class="loader-text" id="loaderText">Loading AI Model</div> |
| | <div class="loader-progress"> |
| | <div class="loader-progress-bar" id="modelProgressBar"></div> |
| | </div> |
| | <div id="loaderSubtext" style="color: var(--text-muted); font-size: 0.75rem; text-align: center; max-width: 200px;"> |
| | <span id="deviceInfo"></span> |
| | </div> |
| | </div> |
| |
|
| | <div class="test-loader" id="testLoader"> |
| | <div class="test-loader-spinner"></div> |
| | <div> |
| | <div class="test-loader-text">TESTING FUNCTION</div> |
| | <div class="test-loader-progress" id="testProgress">Initializing...</div> |
| | </div> |
| | </div> |
| |
|
| | <div class="rules-modal" id="rulesModal"> |
| | <div class="rules-content"> |
| | <h2 style="color: var(--acid); margin-bottom: 20px; font-size: 2rem;">FUNCTION ARENA // RULES</h2> |
| | |
| | <div class="rules-section"> |
| | <h3>π― OBJECTIVE</h3> |
| | <p>Create precise function definitions that trigger in the RIGHT scenarios and stay silent in the WRONG ones. You're competing against other functions for attention.</p> |
| | </div> |
| |
|
| | <div class="rules-section"> |
| | <h3>π HOW TO PLAY</h3> |
| | <p>1. Review the test scenarios - some should trigger your function (β SHOULD), others shouldn't (β SHOULD NOT)</p> |
| | <p>2. Write your function definition with a clear name and description</p> |
| | <p>3. Add parameters with proper types and descriptions (optional but helps)</p> |
| | <p>4. Use enums to constrain values when appropriate</p> |
| | <p>5. Test your function against all scenarios</p> |
| | </div> |
| |
|
| | <div class="rules-section"> |
| | <h3>π§ FUNCTION SCHEMA FORMAT</h3> |
| | <p>Your function follows the JSON Schema / OpenAPI format:</p> |
| | <ul> |
| | <li><code>name</code>: Function identifier (e.g., "get_weather")</li> |
| | <li><code>description</code>: What your function does - BE SPECIFIC</li> |
| | <li><code>parameters</code>: Object with type definitions</li> |
| | <li><code>properties</code>: Define each parameter with type, description</li> |
| | <li><code>required</code>: Array of required parameter names</li> |
| | <li><code>enum</code>: Use to limit values (e.g., ["celsius", "fahrenheit"])</li> |
| | </ul> |
| | </div> |
| |
|
| | <div class="rules-section"> |
| | <h3>π‘ PRO TIPS</h3> |
| | <p><strong>Names matter:</strong> Specific names beat generic ones. "fetch_user_profile" > "get_data"</p> |
| | <p><strong>Descriptions are key:</strong> AI reads your description to decide. Be clear about WHAT and WHEN.</p> |
| | <p><strong>Use enums wisely:</strong> They constrain inputs and make intent clearer.</p> |
| | <p><strong>Think like AI:</strong> What keywords would make YOUR function the obvious choice?</p> |
| | </div> |
| |
|
| | <div class="rules-section"> |
| | <h3>π SCORING</h3> |
| | <p>+100 points for each correct trigger (when you SHOULD)</p> |
| | <p>-50 points for each incorrect trigger (when you SHOULDN'T)</p> |
| | <p>+50 bonus points for each FALSE scenario where you correctly didn't trigger</p> |
| | <p>Compete against 0-5 competitor functions trying to steal your scenarios!</p> |
| | </div> |
| |
|
| | <div class="rules-section"> |
| | <h3>π LEARNING PATH</h3> |
| | <p>Levels progress from basic scenarios to complex edge cases:</p> |
| | <p>β’ <strong>Novice:</strong> Simple, clear scenarios</p> |
| | <p>β’ <strong>Intermediate:</strong> Ambiguous queries, need precision</p> |
| | <p>β’ <strong>Advanced:</strong> Multiple competitors, overlapping domains</p> |
| | <p>β’ <strong>Expert:</strong> Complex parameters, enums required</p> |
| | <p>β’ <strong>Master:</strong> Multi-domain confusion, extreme precision needed</p> |
| | </div> |
| |
|
| | <button class="close-rules" onclick="toggleRules()">CLOSE // BEGIN TRAINING</button> |
| | </div> |
| | </div> |
| |
|
| | <div class="app-container" id="app"> |
| | |
| | <aside class="sidebar"> |
| | <div class="panel"> |
| | <div class="panel-title">OPERATOR_STATS</div> |
| | <div class="stat-row"> |
| | <span>LEVEL</span> |
| | <span class="stat-value" id="levelDisp">01</span> |
| | </div> |
| | <div class="stat-row"> |
| | <span>SCENARIOS</span> |
| | <span class="stat-value" id="scenarioCount">0</span> |
| | </div> |
| | <div class="stat-row"> |
| | <span>SCORE</span> |
| | <span class="stat-value" id="scoreDisp">0000</span> |
| | </div> |
| | <div class="stat-row"> |
| | <span>ACCURACY</span> |
| | <span class="stat-value" id="accuracyDisp" style="color: var(--success)">--</span> |
| | </div> |
| | <div class="stat-row" id="streakRow" style="display: none;"> |
| | <span>STREAK</span> |
| | <span class="stat-value" id="streakDisp" style="color: var(--hyper-purple)">0</span> |
| | </div> |
| | </div> |
| |
|
| |
|
| | <div class="panel"> |
| | <div class="panel-title">COMPETITOR_FUNCTIONS</div> |
| | <div id="competitorList" style="font-size: 0.8rem; color: #666;"> |
| | None loaded... |
| | </div> |
| | </div> |
| |
|
| | <div class="panel"> |
| | <div class="panel-title">ACHIEVEMENTS</div> |
| | <div id="achievementsList" style="font-size: 0.8rem; color: #666; min-height: 40px;"> |
| | No achievements yet... |
| | </div> |
| | </div> |
| |
|
| | <button onclick="toggleRules()" style="border: 1px solid var(--text-main); background: transparent;"> |
| | VIEW RULES [?] |
| | </button> |
| |
|
| | <button onclick="toggleHints()" style="border: 1px solid var(--hyper-purple); background: transparent; color: var(--hyper-purple);"> |
| | SHOW HINTS [π‘] |
| | </button> |
| |
|
| | <button onclick="resetProgress()" style="border: 1px solid var(--alert); background: transparent; color: var(--alert); margin-top: 10px; font-size: 0.85rem;"> |
| | RESET PROGRESS [β οΈ] |
| | </button> |
| | </aside> |
| |
|
| | <main class="game-area"> |
| | <header class="level-header"> |
| | <div class="objective-badge" id="objectiveBadge">BUILD YOUR FUNCTION</div> |
| | <h1 class="level-title" id="lvlName">INITIALIZING...</h1> |
| | <p style="max-width: 800px; color: var(--text-muted); margin-top: 10px;" id="lvlDesc">Loading challenge parameters...</p> |
| | </header> |
| |
|
| | <div class="panel" style="border-color: var(--acid);"> |
| | <div class="panel-title" style="background: var(--acid); color: black;">TEST_SCENARIOS</div> |
| | <div class="scenario-grid" id="scenarioGrid"></div> |
| | </div> |
| |
|
| | <div class="editor-section"> |
| | <div class="editor-panel"> |
| | <div class="editor-header"> |
| | <span class="editor-title">YOUR FUNCTION DEFINITION</span> |
| | <span style="color: #666; font-size: 0.8rem;">β Pre-filled template β’ Edit to refine</span> |
| | </div> |
| | <textarea id="functionEditor" placeholder='{\n "name": "my_function",\n "description": "What my function does...",\n "parameters": {\n "type": "object",\n "properties": {\n "param1": {\n "type": "string",\n "description": "First parameter"\n }\n },\n "required": ["param1"]\n }\n}'></textarea> |
| | </div> |
| |
|
| | <div class="editor-panel"> |
| | <div class="editor-header"> |
| | <span class="editor-title">PARSED OUTPUT</span> |
| | <span style="color: #666; font-size: 0.8rem;">Validation</span> |
| | </div> |
| | <div class="json-output" id="jsonOutput">// Your JSON will be validated here...</div> |
| | </div> |
| | </div> |
| |
|
| | <div class="hint-panel" id="hintPanel"> |
| | <div class="hint-title">π‘ STRATEGIC HINTS</div> |
| | <div class="hint-content" id="hintContent"></div> |
| | </div> |
| |
|
| | <div class="action-bar"> |
| | <button id="testBtn" onclick="testFunction()">TEST FUNCTION</button> |
| | <button class="secondary" onclick="resetEditor()">RESET</button> |
| | <button class="secondary" onclick="loadTemplate()">π STANDARD TEMPLATE</button> |
| | <button class="secondary" id="nextBtn" onclick="nextLevel()" style="display: none;">NEXT LEVEL β</button> |
| | </div> |
| |
|
| | <div class="result-panel" id="resultPanel"> |
| | <div class="result-header"> |
| | <span class="result-title" id="resultTitle">RESULTS</span> |
| | <span class="result-score" id="resultScore">+0</span> |
| | </div> |
| | <div class="result-breakdown" id="resultBreakdown"></div> |
| | </div> |
| |
|
| | <div class="execution-logs"> |
| | <div class="logs-header"> |
| | <span class="logs-title">βοΈ EXECUTION LOGS // Real-time Model Behavior</span> |
| | <button class="logs-clear" onclick="clearLogs()">CLEAR LOGS</button> |
| | </div> |
| | <div class="logs-content" id="logsContent"> |
| | <div style="color: #666; text-align: center; padding: 40px 20px;"> |
| | Logs will appear here when you test a function... |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | </main> |
| | </div> |
| |
|
| | <div class="model-info" id="modelInfo" style="display: none;"> |
| | <div> |
| | <strong>Model:</strong> <span id="modelName">-</span> | |
| | <strong>Version:</strong> <span id="modelVersion">-</span> | |
| | <strong>Quantization:</strong> <span id="modelQuantization">-</span> | |
| | <strong>Device:</strong> <span id="modelDevice">-</span> | |
| |
|
| | <strong>Made by Gaurav Chauhan</strong> | |
| | <strong>2796gaurav@gmail.com</strong> | |
| | <strong><a href="https://www.linkedin.com/in/gauravc2708/" target="_blank" rel="noopener noreferrer">LinkedIn</a></strong> | |
| | |
| | </div> |
| | |
| | </div> |
| |
|
| | <script type="module"> |
| | |
| | import { openDB } from 'https://cdn.jsdelivr.net/npm/idb@7/+esm'; |
| | |
| | |
| | const dbPromise = openDB('function-arena-cache', 1, { |
| | upgrade(db) { |
| | db.createObjectStore('models'); |
| | db.createObjectStore('tokenizers'); |
| | db.createObjectStore('level-data'); |
| | }, |
| | }); |
| | |
| | |
| | const telemetry = { |
| | modelLoadTime: 0, |
| | averageGenerationTime: 0, |
| | cacheHitRate: 0, |
| | deviceType: null, |
| | generationTimes: [], |
| | cacheHits: 0, |
| | cacheMisses: 0, |
| | init() { |
| | |
| | const hasWebGPU = !!navigator.gpu; |
| | const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); |
| | this.deviceType = hasWebGPU ? 'webgpu' : (isMobile ? 'mobile-cpu' : 'desktop-cpu'); |
| | |
| | |
| | if (typeof gtag !== 'undefined') { |
| | gtag('event', 'page_load', { |
| | device_type: this.deviceType, |
| | timestamp: new Date().toISOString() |
| | }); |
| | } |
| | }, |
| | trackModelLoad(time) { |
| | this.modelLoadTime = time; |
| | if (typeof gtag !== 'undefined') { |
| | gtag('event', 'model_load', { |
| | load_time: time, |
| | device_type: this.deviceType |
| | }); |
| | } |
| | }, |
| | trackGeneration(time) { |
| | this.generationTimes.push(time); |
| | |
| | if (this.generationTimes.length > 100) { |
| | this.generationTimes.shift(); |
| | } |
| | this.averageGenerationTime = this.generationTimes.reduce((a, b) => a + b, 0) / this.generationTimes.length; |
| | |
| | if (typeof gtag !== 'undefined') { |
| | gtag('event', 'function_generation', { |
| | generation_time: time, |
| | average_time: this.averageGenerationTime |
| | }); |
| | } |
| | }, |
| | trackCacheHit(hit) { |
| | if (hit) { |
| | this.cacheHits++; |
| | } else { |
| | this.cacheMisses++; |
| | } |
| | const total = this.cacheHits + this.cacheMisses; |
| | this.cacheHitRate = total > 0 ? (this.cacheHits / total) * 100 : 0; |
| | |
| | if (typeof gtag !== 'undefined') { |
| | gtag('event', 'cache_access', { |
| | cache_hit: hit, |
| | hit_rate: this.cacheHitRate |
| | }); |
| | } |
| | }, |
| | trackLevelComplete(levelId, score, passed) { |
| | if (typeof gtag !== 'undefined') { |
| | gtag('event', 'level_complete', { |
| | level_id: levelId, |
| | score: score, |
| | passed: passed, |
| | average_generation_time: this.averageGenerationTime, |
| | cache_hit_rate: this.cacheHitRate |
| | }); |
| | } |
| | }, |
| | getMetrics() { |
| | return { |
| | modelLoadTime: this.modelLoadTime, |
| | averageGenerationTime: this.averageGenerationTime, |
| | cacheHitRate: this.cacheHitRate, |
| | deviceType: this.deviceType, |
| | totalGenerations: this.generationTimes.length |
| | }; |
| | } |
| | }; |
| | |
| | |
| | telemetry.init(); |
| | |
| | |
| | function setupCacheWarmup() { |
| | if ('requestIdleCallback' in window) { |
| | requestIdleCallback(() => { |
| | |
| | const nextLevel = levels[state.levelIdx + 1]; |
| | if (nextLevel) { |
| | |
| | dbPromise.then(db => { |
| | return db.put('level-data', nextLevel, `level-${nextLevel.id}`); |
| | }).then(() => { |
| | console.log('β Next level data cached'); |
| | telemetry.trackCacheHit(true); |
| | }).catch(err => { |
| | console.log('Cache warmup failed:', err); |
| | }); |
| | } |
| | }, { timeout: 5000 }); |
| | } else { |
| | |
| | setTimeout(() => { |
| | const nextLevel = levels[state.levelIdx + 1]; |
| | if (nextLevel) { |
| | dbPromise.then(db => { |
| | return db.put('level-data', nextLevel, `level-${nextLevel.id}`); |
| | }).catch(err => console.log('Cache warmup failed:', err)); |
| | } |
| | }, 2000); |
| | } |
| | } |
| | |
| | |
| | const levels = [ |
| | { |
| | id: 1, |
| | title: "BASIC_TRIGGER", |
| | description: "Simple weather function. Make it trigger for weather queries only.", |
| | difficulty: "NOVICE", |
| | scenarios: [ |
| | { query: "What's the weather in Tokyo?", shouldTrigger: true, icon: "βοΈ" }, |
| | { query: "Get weather for London", shouldTrigger: true, icon: "π§οΈ" }, |
| | { query: "Tell me about Paris history", shouldTrigger: false, icon: "π" }, |
| | { query: "What time is it?", shouldTrigger: false, icon: "π" }, |
| | ], |
| | competitors: [], |
| | hints: [ |
| | "Focus on weather-related keywords in your description", |
| | "Use a clear name like 'get_weather' or 'fetch_weather'", |
| | "Add a location parameter to make it more specific" |
| | ], |
| | starterCode: { |
| | name: "get_weather", |
| | description: "Get current weather information for a specified location", |
| | parameters: { |
| | type: "object", |
| | properties: { |
| | location: { |
| | type: "string", |
| | description: "The city or location name" |
| | } |
| | }, |
| | required: ["location"] |
| | } |
| | } |
| | }, |
| | { |
| | id: 2, |
| | title: "PRECISION_TARGETING", |
| | description: "Build a user profile function. Avoid triggering on similar but different queries.", |
| | difficulty: "INTERMEDIATE", |
| | scenarios: [ |
| | { query: "Get user profile for john_doe", shouldTrigger: true, icon: "π€" }, |
| | { query: "Fetch user info for ID 12345", shouldTrigger: true, icon: "π" }, |
| | { query: "Show user analytics", shouldTrigger: false, icon: "π" }, |
| | { query: "Delete user account", shouldTrigger: false, icon: "ποΈ" }, |
| | { query: "Update user settings", shouldTrigger: false, icon: "βοΈ" }, |
| | { query: "Get profile data", shouldTrigger: true, icon: "πΎ" }, |
| | ], |
| | competitors: [ |
| | { |
| | name: "get_user_analytics", |
| | description: "Retrieve user behavior analytics and statistics" |
| | }, |
| | { |
| | name: "update_user", |
| | description: "Update user information and settings" |
| | } |
| | ], |
| | hints: [ |
| | "Emphasize 'retrieve' or 'get' operations, not modify/delete", |
| | "Specify that it's for profile/information retrieval", |
| | "Distinguish from analytics (which is aggregate data)", |
| | "Use parameters to show what data you return" |
| | ], |
| | starterCode: { |
| | name: "get_user_profile", |
| | description: "Retrieve user profile information including name, email, and account details", |
| | parameters: { |
| | type: "object", |
| | properties: { |
| | user_id: { |
| | type: "string", |
| | description: "The unique identifier or username of the user" |
| | } |
| | }, |
| | required: ["user_id"] |
| | } |
| | } |
| | }, |
| | { |
| | id: 3, |
| | title: "ENUM_MASTERY", |
| | description: "Create a task management function. Use enums to constrain priority and status.", |
| | difficulty: "ADVANCED", |
| | scenarios: [ |
| | { query: "Create high priority task", shouldTrigger: true, icon: "π" }, |
| | { query: "Add new task", shouldTrigger: true, icon: "β" }, |
| | { query: "List completed tasks", shouldTrigger: false, icon: "π" }, |
| | { query: "Mark task as done", shouldTrigger: false, icon: "β
" }, |
| | { query: "Create task with low priority", shouldTrigger: true, icon: "π" }, |
| | { query: "Delete tasks", shouldTrigger: false, icon: "ποΈ" }, |
| | { query: "Update task deadline", shouldTrigger: false, icon: "π
" }, |
| | ], |
| | competitors: [ |
| | { |
| | name: "list_tasks", |
| | description: "Retrieve and list existing tasks with filters" |
| | }, |
| | { |
| | name: "update_task_status", |
| | description: "Change the status of an existing task" |
| | }, |
| | { |
| | name: "delete_task", |
| | description: "Remove a task from the system" |
| | } |
| | ], |
| | hints: [ |
| | "Focus on CREATE/ADD operations only", |
| | "Use enum for priority: ['low', 'medium', 'high']", |
| | "Use enum for initial status: ['pending', 'in_progress']", |
| | "Make your description explicitly mention 'create' or 'add'", |
| | "Distinguish from update, delete, and list operations" |
| | ], |
| | starterCode: { |
| | name: "create_task", |
| | description: "Create a new task with title, description, priority level, and initial status", |
| | parameters: { |
| | type: "object", |
| | properties: { |
| | title: { |
| | type: "string", |
| | description: "The task title or name" |
| | }, |
| | priority: { |
| | type: "string", |
| | enum: ["low", "medium", "high"], |
| | description: "Priority level of the task" |
| | }, |
| | status: { |
| | type: "string", |
| | enum: ["pending", "in_progress"], |
| | description: "Initial status of the task" |
| | } |
| | }, |
| | required: ["title", "priority"] |
| | } |
| | } |
| | }, |
| | { |
| | id: 4, |
| | title: "MULTI_DOMAIN_CHAOS", |
| | description: "Calendar event creation vs. reminders vs. notifications. Extreme precision required.", |
| | difficulty: "EXPERT", |
| | scenarios: [ |
| | { query: "Schedule meeting for tomorrow", shouldTrigger: true, icon: "π
" }, |
| | { query: "Create calendar event", shouldTrigger: true, icon: "π₯" }, |
| | { query: "Set reminder to call mom", shouldTrigger: false, icon: "π" }, |
| | { query: "Send notification", shouldTrigger: false, icon: "π¬" }, |
| | { query: "Add event to calendar", shouldTrigger: true, icon: "π" }, |
| | { query: "Remind me about meeting", shouldTrigger: false, icon: "β°" }, |
| | { query: "List calendar events", shouldTrigger: false, icon: "π" }, |
| | { query: "Block time on calendar", shouldTrigger: true, icon: "π―" }, |
| | ], |
| | competitors: [ |
| | { |
| | name: "create_reminder", |
| | description: "Set a reminder notification for a specific time or event" |
| | }, |
| | { |
| | name: "send_notification", |
| | description: "Send an immediate or scheduled notification to the user" |
| | }, |
| | { |
| | name: "list_events", |
| | description: "Retrieve calendar events within a date range" |
| | }, |
| | { |
| | name: "update_event", |
| | description: "Modify an existing calendar event" |
| | } |
| | ], |
| | hints: [ |
| | "Calendar events are time-blocked entries with start/end times", |
| | "Reminders are notifications, not calendar entries", |
| | "Use parameters: title, start_time, end_time, location", |
| | "Emphasize 'calendar', 'schedule', 'meeting', 'event' in description", |
| | "Explicitly exclude reminders and notifications in your description" |
| | ], |
| | starterCode: { |
| | name: "create_calendar_event", |
| | description: "Create a calendar event with time-blocked scheduling for meetings and appointments", |
| | parameters: { |
| | type: "object", |
| | properties: { |
| | title: { |
| | type: "string", |
| | description: "Event title or meeting name" |
| | }, |
| | start_time: { |
| | type: "string", |
| | description: "Event start time (ISO 8601 format)" |
| | }, |
| | end_time: { |
| | type: "string", |
| | description: "Event end time (ISO 8601 format)" |
| | }, |
| | location: { |
| | type: "string", |
| | description: "Physical or virtual meeting location" |
| | } |
| | }, |
| | required: ["title", "start_time", "end_time"] |
| | } |
| | } |
| | }, |
| | { |
| | id: 5, |
| | title: "ULTIMATE_PRECISION", |
| | description: "Payment processing with strict parameters. Security-critical function calling.", |
| | difficulty: "MASTER", |
| | scenarios: [ |
| | { query: "Process payment $99.99", shouldTrigger: true, icon: "π³" }, |
| | { query: "Charge customer $250", shouldTrigger: true, icon: "π°" }, |
| | { query: "Show payment history", shouldTrigger: false, icon: "π" }, |
| | { query: "Refund transaction", shouldTrigger: false, icon: "β©οΈ" }, |
| | { query: "Process payment $1500", shouldTrigger: true, icon: "π¦" }, |
| | { query: "Verify payment status", shouldTrigger: false, icon: "π" }, |
| | { query: "Cancel payment", shouldTrigger: false, icon: "β" }, |
| | { query: "Charge $45.50", shouldTrigger: true, icon: "π΅" }, |
| | { query: "Update payment method", shouldTrigger: false, icon: "βοΈ" }, |
| | ], |
| | competitors: [ |
| | { |
| | name: "refund_payment", |
| | description: "Issue a refund for a completed transaction" |
| | }, |
| | { |
| | name: "verify_payment", |
| | description: "Check the status and validity of a payment transaction" |
| | }, |
| | { |
| | name: "get_payment_history", |
| | description: "Retrieve past payment transactions and history" |
| | }, |
| | { |
| | name: "cancel_payment", |
| | description: "Cancel a pending or scheduled payment" |
| | }, |
| | { |
| | name: "update_payment_method", |
| | description: "Modify or change stored payment method information" |
| | } |
| | ], |
| | hints: [ |
| | "This is about PROCESSING/CHARGING, not viewing or refunding", |
| | "Require amount parameter with number type", |
| | "Use enum for payment_method: ['credit_card', 'debit_card', 'bank_transfer', 'saved_method']", |
| | "Add currency parameter with enum: ['USD', 'EUR', 'GBP']", |
| | "Make description security-conscious and specific to charging", |
| | "Distinguish clearly from refund, verify, and cancel operations" |
| | ], |
| | starterCode: { |
| | name: "process_payment", |
| | description: "Process a payment transaction with secure handling of payment method and currency", |
| | parameters: { |
| | type: "object", |
| | properties: { |
| | amount: { |
| | type: "number", |
| | description: "Payment amount in the specified currency" |
| | }, |
| | currency: { |
| | type: "string", |
| | enum: ["USD", "EUR", "GBP"], |
| | description: "Currency code for the transaction" |
| | }, |
| | payment_method: { |
| | type: "string", |
| | enum: ["credit_card", "debit_card", "bank_transfer", "saved_method"], |
| | description: "The payment method to use" |
| | }, |
| | customer_id: { |
| | type: "string", |
| | description: "Customer identifier for the transaction" |
| | } |
| | }, |
| | required: ["amount", "currency", "payment_method", "customer_id"] |
| | } |
| | } |
| | } |
| | ]; |
| | |
| | let state = { |
| | levelIdx: 0, |
| | totalScore: 0, |
| | model: null, |
| | tokenizer: null, |
| | booted: false, |
| | currentFunction: null, |
| | achievements: [], |
| | streak: 0, |
| | bestStreak: 0, |
| | levelAttempts: {}, |
| | hintsUsed: 0, |
| | modelInfo: { |
| | name: null, |
| | version: null, |
| | quantization: null, |
| | device: null |
| | } |
| | }; |
| | |
| | |
| | function loadState() { |
| | try { |
| | const saved = localStorage.getItem('functionArenaState'); |
| | if (saved) { |
| | const parsed = JSON.parse(saved); |
| | state.totalScore = parsed.totalScore || 0; |
| | state.achievements = parsed.achievements || []; |
| | state.bestStreak = parsed.bestStreak || 0; |
| | state.levelAttempts = parsed.levelAttempts || {}; |
| | state.hintsUsed = parsed.hintsUsed || 0; |
| | } |
| | } catch(e) { |
| | console.log('No saved state found'); |
| | } |
| | } |
| | |
| | |
| | function saveState() { |
| | try { |
| | localStorage.setItem('functionArenaState', JSON.stringify({ |
| | totalScore: state.totalScore, |
| | achievements: state.achievements, |
| | bestStreak: state.bestStreak, |
| | levelAttempts: state.levelAttempts, |
| | hintsUsed: state.hintsUsed |
| | })); |
| | } catch(e) { |
| | console.error('Failed to save state:', e); |
| | } |
| | } |
| | |
| | |
| | loadState(); |
| | |
| | |
| | |
| | async function bootSequence() { |
| | const instructions = [ |
| | "π― WELCOME TO FUNCTION ARENA", |
| | "", |
| | "Your mission: Create precise function definitions that trigger", |
| | "in the RIGHT scenarios and stay silent in the WRONG ones.", |
| | "", |
| | "π HOW TO PLAY:", |
| | "1. Review test scenarios - some should trigger (β), others shouldn't (β)", |
| | "2. Write your function with a clear name and description", |
| | "3. Add parameters with proper types and descriptions", |
| | "4. Use enums to constrain values when appropriate", |
| | "5. Test your function against all scenarios", |
| | "", |
| | "π‘ KEY TIPS:", |
| | "β’ Function names matter: 'get_weather' beats 'do_stuff'", |
| | "β’ Descriptions are KEY - AI reads this to decide when to call", |
| | "β’ Be specific about WHAT your function does AND what it doesn't", |
| | "β’ Use enums wisely to constrain inputs and clarify intent", |
| | "", |
| | "π SCORING:", |
| | "+100 points for correct triggers (when you SHOULD)", |
| | "-50 points for incorrect triggers (when you SHOULDN'T)", |
| | "+50 bonus for correctly NOT triggering (when you SHOULDN'T)", |
| | "", |
| | "β³ Please read the instructions above carefully...", |
| | " Model is loading in the background (may take a moment on first load)" |
| | ]; |
| | |
| | const container = document.getElementById('boot-sequence'); |
| | container.style.justifyContent = 'center'; |
| | container.style.padding = '60px 40px'; |
| | |
| | |
| | const instructionBox = document.createElement('div'); |
| | instructionBox.style.cssText = ` |
| | max-width: 800px; |
| | line-height: 1.8; |
| | font-size: 16px; |
| | `; |
| | |
| | instructions.forEach((line, i) => { |
| | const div = document.createElement('div'); |
| | div.className = 'boot-line'; |
| | div.textContent = line; |
| | div.style.marginBottom = line === '' ? '10px' : '5px'; |
| | div.style.fontSize = line.includes('π―') ? '24px' : |
| | line.includes('π') || line.includes('π‘') || line.includes('π') || line.includes('β³') ? '18px' : '16px'; |
| | div.style.fontWeight = line.includes('π―') || line.includes('π') || line.includes('π‘') || line.includes('π') || line.includes('β³') ? 'bold' : 'normal'; |
| | div.style.color = line.includes('β³') ? 'var(--text-muted)' : 'var(--acid)'; |
| | instructionBox.appendChild(div); |
| | |
| | |
| | setTimeout(() => { |
| | div.style.opacity = 1; |
| | div.style.transform = "translateY(0)"; |
| | }, i * 80); |
| | }); |
| | |
| | container.innerHTML = ''; |
| | container.appendChild(instructionBox); |
| | |
| | |
| | const modelLoadPromise = loadModel(); |
| | |
| | |
| | |
| | await new Promise(r => setTimeout(r, 8000)); |
| | |
| | |
| | const lastInstruction = instructionBox.lastChild; |
| | lastInstruction.textContent = "β³ Waiting for model to finish loading..."; |
| | lastInstruction.style.color = 'var(--acid)'; |
| | |
| | await modelLoadPromise; |
| | |
| | container.style.transition = "opacity 0.5s"; |
| | container.style.opacity = 0; |
| | setTimeout(() => { |
| | container.style.display = 'none'; |
| | document.getElementById('app').style.opacity = 1; |
| | state.booted = true; |
| | renderLevel(); |
| | toggleRules(); |
| | }, 500); |
| | } |
| | |
| | async function loadModel() { |
| | const loadStartTime = performance.now(); |
| | const loader = document.getElementById('modelLoader'); |
| | const progressBar = document.getElementById('modelProgressBar'); |
| | const loaderText = document.getElementById('loaderText'); |
| | const loaderSubtext = document.getElementById('loaderSubtext'); |
| | const deviceInfo = document.getElementById('deviceInfo'); |
| | |
| | loader.classList.remove('hidden'); |
| | |
| | |
| | const hasWebGPU = !!navigator.gpu; |
| | const modelId = "onnx-community/functiongemma-270m-it-ONNX"; |
| | |
| | |
| | if (hasWebGPU) { |
| | deviceInfo.textContent = 'Using GPU acceleration'; |
| | deviceInfo.style.color = 'var(--success)'; |
| | } else { |
| | deviceInfo.textContent = 'Using CPU (may be slower)'; |
| | deviceInfo.style.color = 'var(--text-muted)'; |
| | } |
| | |
| | try { |
| | |
| | loaderText.textContent = 'Loading AI Engine'; |
| | loaderSubtext.innerHTML = '<span style="color: var(--text-muted); font-size: 0.75rem;">Preparing...</span>'; |
| | progressBar.style.width = '15%'; |
| | |
| | const { env, AutoTokenizer, AutoModelForCausalLM } = await import( |
| | "https://cdn.jsdelivr.net/npm/@huggingface/transformers@latest" |
| | ); |
| | |
| | env.allowRemoteModels = true; |
| | env.allowLocalModels = false; |
| | env.useBrowserCache = true; |
| | |
| | |
| | loaderText.textContent = 'Loading AI Engine'; |
| | loaderSubtext.innerHTML = '<span style="color: var(--text-muted); font-size: 0.75rem;">Preparing language processor...</span>'; |
| | progressBar.style.width = '30%'; |
| | |
| | let tokenizerCached = false; |
| | state.tokenizer = await AutoTokenizer.from_pretrained(modelId, { |
| | progress_callback: (progress) => { |
| | if (progress && (progress.status === 'done' || progress.file)) { |
| | tokenizerCached = progress.cached || false; |
| | if (tokenizerCached) { |
| | loaderSubtext.innerHTML = '<span style="color: var(--success); font-size: 0.75rem;">Using cached data</span>'; |
| | } else { |
| | loaderSubtext.innerHTML = '<span style="color: var(--text-muted); font-size: 0.75rem;">Downloading...</span>'; |
| | } |
| | console.log(`β Tokenizer ${tokenizerCached ? 'cached' : 'downloaded'}`); |
| | } |
| | } |
| | }); |
| | |
| | |
| | progressBar.style.width = '40%'; |
| | |
| | |
| | let modelConfig = {}; |
| | let modelCached = false; |
| | |
| | if (hasWebGPU) { |
| | |
| | modelConfig = { |
| | dtype: "q4", |
| | device: "webgpu" |
| | }; |
| | loaderText.textContent = 'Loading AI Model'; |
| | loaderSubtext.innerHTML = '<span style="color: var(--text-muted); font-size: 0.75rem;">Downloading model...</span>'; |
| | console.log("β Using WebGPU with q4 quantization"); |
| | } else { |
| | |
| | modelConfig = { |
| | dtype: "q8", |
| | device: "wasm" |
| | }; |
| | loaderText.textContent = 'Loading AI Model'; |
| | loaderSubtext.innerHTML = '<span style="color: var(--text-muted); font-size: 0.75rem;">Downloading model...</span>'; |
| | console.log("β WebGPU unavailable, using WASM with q8 quantization"); |
| | } |
| | |
| | state.model = await AutoModelForCausalLM.from_pretrained(modelId, { |
| | ...modelConfig, |
| | progress_callback: (progress) => { |
| | if (!progress) return; |
| | |
| | |
| | if (progress.status === 'progress' || (progress.loaded && progress.total)) { |
| | const loaded = progress.loaded || 0; |
| | const total = progress.total || 1; |
| | const percent = 40 + Math.round((loaded / total) * 55); |
| | progressBar.style.width = `${percent}%`; |
| | |
| | if (progress.cached) { |
| | modelCached = true; |
| | loaderText.textContent = 'Loading AI Model'; |
| | loaderSubtext.innerHTML = `<span style="color: var(--success); font-size: 0.75rem;">${percent}% (cached)</span>`; |
| | } else { |
| | loaderText.textContent = 'Loading AI Model'; |
| | loaderSubtext.innerHTML = `<span style="color: var(--text-muted); font-size: 0.75rem;">${percent}%</span>`; |
| | } |
| | } else if (progress.status === 'done' || progress.file) { |
| | progressBar.style.width = '98%'; |
| | if (progress.cached || modelCached) { |
| | loaderText.textContent = 'AI Model Ready'; |
| | loaderSubtext.innerHTML = '<span style="color: var(--success); font-size: 0.75rem;">Loaded from cache</span>'; |
| | console.log("β Model loaded from cache"); |
| | } else { |
| | loaderText.textContent = 'AI Model Ready'; |
| | loaderSubtext.innerHTML = '<span style="color: var(--success); font-size: 0.75rem;">Downloaded & cached</span>'; |
| | console.log("β Model downloaded and cached"); |
| | } |
| | } |
| | } |
| | }); |
| | |
| | |
| | progressBar.style.width = '100%'; |
| | const loadTime = ((performance.now() - loadStartTime) / 1000).toFixed(1); |
| | loaderText.textContent = 'Ready!'; |
| | loaderSubtext.innerHTML = `<span style="color: var(--success); font-size: 0.75rem;">Loaded in ${loadTime}s</span>`; |
| | await new Promise(r => setTimeout(r, 500)); |
| | |
| | |
| | state.modelInfo = { |
| | name: "functiongemma-270m", |
| | version: "it-ONNX", |
| | quantization: modelConfig.dtype || 'fp32', |
| | device: modelConfig.device || 'default' |
| | }; |
| | |
| | |
| | telemetry.trackModelLoad(parseFloat(loadTime)); |
| | telemetry.trackCacheHit(modelCached || tokenizerCached); |
| | |
| | |
| | try { |
| | const db = await dbPromise; |
| | await db.put('models', { |
| | modelId: modelId, |
| | config: modelConfig, |
| | loadTime: loadTime, |
| | cached: modelCached || tokenizerCached, |
| | timestamp: Date.now() |
| | }, 'current-model'); |
| | } catch (e) { |
| | console.log('IndexedDB cache write failed:', e); |
| | } |
| | |
| | console.log("β Model loaded successfully"); |
| | console.log(` Device: ${modelConfig.device || 'default'}`); |
| | console.log(` Quantization: ${modelConfig.dtype || 'fp32'}`); |
| | console.log(` Cached: ${modelCached || tokenizerCached ? 'Yes' : 'No (first load)'}`); |
| | console.log(` Load time: ${loadTime}s`); |
| | |
| | |
| | updateModelInfoDisplay(); |
| | |
| | |
| | setupCacheWarmup(); |
| | |
| | } catch(e) { |
| | console.error("Model load failed:", e); |
| | loaderText.textContent = 'Loading Failed'; |
| | loaderSubtext.innerHTML = `<span style="color: var(--alert); font-size: 0.75rem;">Please refresh</span>`; |
| | progressBar.style.background = 'var(--alert)'; |
| | |
| | |
| | if (e.message && e.message.includes('webgpu') && hasWebGPU) { |
| | console.log("β WebGPU failed, retrying with WASM fallback..."); |
| | loaderText.textContent = 'Retrying...'; |
| | loaderSubtext.innerHTML = '<span style="color: var(--text-muted); font-size: 0.75rem;">Using CPU mode</span>'; |
| | progressBar.style.background = 'var(--acid)'; |
| | |
| | try { |
| | state.model = await AutoModelForCausalLM.from_pretrained(modelId, { |
| | dtype: "q8", |
| | device: "wasm" |
| | }); |
| | progressBar.style.width = '100%'; |
| | progressBar.style.background = 'var(--acid)'; |
| | loaderText.textContent = 'Ready!'; |
| | loaderSubtext.innerHTML = '<span style="color: var(--success); font-size: 0.75rem;">Loaded (CPU mode)</span>'; |
| | console.log("β Model loaded with CPU fallback"); |
| | |
| | |
| | state.modelInfo = { |
| | name: "functiongemma-270m", |
| | version: "it-ONNX", |
| | quantization: "q8", |
| | device: "wasm" |
| | }; |
| | updateModelInfoDisplay(); |
| | } catch(fallbackError) { |
| | console.error("Fallback also failed:", fallbackError); |
| | throw fallbackError; |
| | } |
| | } else { |
| | throw e; |
| | } |
| | } finally { |
| | |
| | setTimeout(() => { |
| | loader.classList.add('hidden'); |
| | }, 500); |
| | } |
| | } |
| | |
| | |
| | function renderLevel() { |
| | const lvl = levels[state.levelIdx]; |
| | |
| | document.getElementById('lvlName').innerText = lvl.title; |
| | document.getElementById('lvlDesc').innerText = lvl.description; |
| | document.getElementById('objectiveBadge').innerText = `LEVEL ${lvl.id} // ${lvl.difficulty}`; |
| | document.getElementById('levelDisp').innerText = `0${lvl.id}`; |
| | document.getElementById('scenarioCount').innerText = lvl.scenarios.length; |
| | document.getElementById('scoreDisp').innerText = state.totalScore.toString().padStart(4, '0'); |
| | |
| | |
| | const grid = document.getElementById('scenarioGrid'); |
| | grid.innerHTML = ''; |
| | lvl.scenarios.forEach((s, i) => { |
| | const div = document.createElement('div'); |
| | div.className = 'scenario-card'; |
| | div.innerHTML = ` |
| | <div class="scenario-icon">${s.icon}</div> |
| | <div class="scenario-text">${s.query}</div> |
| | <div class="scenario-expected ${s.shouldTrigger ? 'should-trigger' : 'should-not'}"> |
| | ${s.shouldTrigger ? 'β SHOULD' : 'β SHOULD NOT'} |
| | </div> |
| | `; |
| | div.style.opacity = '0'; |
| | div.style.transform = 'translateY(10px)'; |
| | grid.appendChild(div); |
| | |
| | setTimeout(() => { |
| | div.style.transition = 'all 0.3s'; |
| | div.style.opacity = '1'; |
| | div.style.transform = 'translateY(0)'; |
| | }, i * 50); |
| | }); |
| | |
| | |
| | const compList = document.getElementById('competitorList'); |
| | if (lvl.competitors.length === 0) { |
| | compList.innerHTML = '<div style="color: var(--success);">No competitors this level!</div>'; |
| | } else { |
| | compList.innerHTML = lvl.competitors.map(c => ` |
| | <div class="competitor-fn"> |
| | <div class="fn-name-display">${c.name}()</div> |
| | <div style="font-size: 0.75rem; color: #888;">${c.description}</div> |
| | </div> |
| | `).join(''); |
| | } |
| | |
| | |
| | const achievementsList = document.getElementById('achievementsList'); |
| | if (achievementsList) { |
| | if (state.achievements.length === 0) { |
| | achievementsList.innerHTML = '<div style="color: #666;">No achievements yet...</div>'; |
| | } else { |
| | const names = { |
| | 'first_perfect': 'π Perfect Score', |
| | 'streak_3': 'π₯ On Fire!', |
| | 'streak_5': 'β‘ Unstoppable!', |
| | 'score_1k': 'π― Score Master', |
| | 'all_levels': 'π Master Engineer' |
| | }; |
| | achievementsList.innerHTML = state.achievements.map(a => |
| | `<div style="color: var(--acid); margin-bottom: 5px;">${names[a] || a}</div>` |
| | ).join(''); |
| | } |
| | } |
| | |
| | |
| | updateStreakDisplay(); |
| | |
| | |
| | document.getElementById('functionEditor').value = JSON.stringify(lvl.starterCode, null, 2); |
| | validateJSON(); |
| | |
| | setTimeout(autoResizeTextarea, 100); |
| | |
| | |
| | document.getElementById('resultPanel').classList.remove('visible'); |
| | document.getElementById('nextBtn').style.display = 'none'; |
| | } |
| | |
| | |
| | const functionEditor = document.getElementById('functionEditor'); |
| | functionEditor.addEventListener('input', () => { |
| | validateJSON(); |
| | autoResizeTextarea(); |
| | }); |
| | |
| | function getStandardTemplate() { |
| | |
| | return { |
| | name: "function_name", |
| | description: "Clear, descriptive description of what this function does. Mention key operations and purpose.", |
| | parameters: { |
| | type: "object", |
| | properties: { |
| | param_1: { |
| | type: "string", |
| | description: "Description of the first parameter" |
| | }, |
| | param_2: { |
| | type: "string", |
| | enum: ["option_a", "option_b", "option_c"], |
| | description: "Parameter with fixed options (remove enum if not needed)" |
| | }, |
| | param_3: { |
| | type: "number", |
| | description: "Numeric parameter (use number for amounts, counts, etc)" |
| | } |
| | }, |
| | required: ["param_1", "param_2"] |
| | }, |
| | _hints: [ |
| | "π‘ TIP: Function name should be specific and descriptive", |
| | "π‘ TIP: Description is KEY - AI reads this to decide when to call your function", |
| | "π‘ TIP: Use enums to constrain values and make intent clearer", |
| | "π‘ TIP: Required fields help the model understand what's essential", |
| | "π‘ TIP: Be explicit about what your function does AND what it doesn't do" |
| | ] |
| | }; |
| | } |
| | |
| | function getTemplateWithComments() { |
| | return `{ |
| | // π‘ FUNCTION DEFINITION TEMPLATE WITH HINTS |
| | // =========================================== |
| | // |
| | // π‘ TIP: Function name should be specific and descriptive |
| | // Good: "get_weather", "create_task", "process_payment" |
| | // Bad: "do_stuff", "function1", "data" |
| | // |
| | "name": "function_name", |
| | |
| | // π‘ TIP: Description is THE MOST IMPORTANT part! |
| | // - AI reads this to decide when to call your function |
| | // - Be specific about what it does |
| | // - Mention key operations (get, create, process, etc.) |
| | // - Explicitly exclude what it doesn't do (if needed) |
| | // |
| | "description": "Clear, descriptive description of what this function does. Mention key operations and purpose.", |
| | |
| | "parameters": { |
| | "type": "object", |
| | "properties": { |
| | // π‘ TIP: Parameter names should be clear and descriptive |
| | "param_1": { |
| | "type": "string", |
| | // π‘ TIP: Parameter description helps AI understand what to extract |
| | "description": "Description of the first parameter" |
| | }, |
| | |
| | // π‘ TIP: Use enums to constrain values - makes intent clearer! |
| | // Remove the enum array if you don't need fixed options |
| | "param_2": { |
| | "type": "string", |
| | "enum": ["option_a", "option_b", "option_c"], |
| | "description": "Parameter with fixed options (remove enum if not needed)" |
| | }, |
| | |
| | // π‘ TIP: Use "number" type for amounts, counts, quantities |
| | // Use "string" for text, IDs, names |
| | // Use "boolean" for true/false values |
| | "param_3": { |
| | "type": "number", |
| | "description": "Numeric parameter (use number for amounts, counts, etc)" |
| | } |
| | }, |
| | // π‘ TIP: Required fields help the model understand what's essential |
| | // Only include parameters that are truly required |
| | "required": ["param_1", "param_2"] |
| | } |
| | }`; |
| | } |
| | |
| | window.loadTemplate = function() { |
| | const editor = document.getElementById('functionEditor'); |
| | |
| | editor.value = getTemplateWithComments(); |
| | |
| | try { |
| | |
| | const cleaned = editor.value.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, ''); |
| | const parsed = JSON.parse(cleaned); |
| | state.currentFunction = parsed; |
| | document.getElementById('jsonOutput').style.color = 'var(--success)'; |
| | document.getElementById('jsonOutput').textContent = 'β Template loaded (comments are for reference only)'; |
| | } catch(e) { |
| | document.getElementById('jsonOutput').style.color = 'var(--text-muted)'; |
| | document.getElementById('jsonOutput').textContent = 'π‘ Template with hints loaded. Remove comments (// lines) before testing.'; |
| | } |
| | |
| | |
| | setTimeout(autoResizeTextarea, 100); |
| | }; |
| | |
| | |
| | function addLog(label, content, type = 'info') { |
| | const logsContent = document.getElementById('logsContent'); |
| | |
| | |
| | if (logsContent.innerHTML.includes('Logs will appear here')) { |
| | logsContent.innerHTML = ''; |
| | } |
| | |
| | const entry = document.createElement('div'); |
| | entry.className = 'log-entry'; |
| | |
| | const time = new Date().toLocaleTimeString(); |
| | let html = `<div class="log-time">[${time}]</div>`; |
| | html += `<span class="log-label">${label}</span>`; |
| | |
| | if (type === 'prompt') { |
| | html += `<div class="log-prompt"><strong>π PROMPT:</strong><br>"${content}"</div>`; |
| | } else if (type === 'tools') { |
| | html += `<div class="log-tools"><strong>π§ AVAILABLE FUNCTIONS:</strong><br>${content}</div>`; |
| | } else if (type === 'triggered') { |
| | html += `<span class="log-success">β
${content}</span>`; |
| | } else if (type === 'error') { |
| | html += `<span class="log-error">β ${content}</span>`; |
| | } else { |
| | html += `<span class="log-${type}">${content}</span>`; |
| | } |
| | |
| | entry.innerHTML = html; |
| | logsContent.insertBefore(entry, logsContent.firstChild); |
| | logsContent.scrollTop = 0; |
| | } |
| | |
| | window.clearLogs = function() { |
| | const logsContent = document.getElementById('logsContent'); |
| | logsContent.innerHTML = '<div style="color: #666; text-align: center; padding: 40px 20px;">Logs cleared. Run a test to see new logs...</div>'; |
| | }; |
| | |
| | |
| | function autoResizeTextarea() { |
| | const editor = document.getElementById('functionEditor'); |
| | if (editor) { |
| | |
| | editor.style.height = 'auto'; |
| | |
| | const newHeight = Math.max(300, editor.scrollHeight); |
| | editor.style.height = newHeight + 'px'; |
| | } |
| | } |
| | |
| | function validateJSON() { |
| | const editor = document.getElementById('functionEditor'); |
| | const output = document.getElementById('jsonOutput'); |
| | |
| | try { |
| | |
| | let cleaned = editor.value |
| | .replace(/\/\/.*$/gm, '') |
| | .replace(/\/\*[\s\S]*?\*\//g, ''); |
| | |
| | const parsed = JSON.parse(cleaned); |
| | state.currentFunction = parsed; |
| | |
| | |
| | if (!parsed.name || !parsed.description) { |
| | throw new Error("Missing 'name' or 'description'"); |
| | } |
| | |
| | |
| | const warnings = []; |
| | if (parsed.name === 'function_name' || parsed.name.includes('_name')) { |
| | warnings.push('β οΈ Consider changing the function name to something specific'); |
| | } |
| | if (parsed.description.length < 20) { |
| | warnings.push('β οΈ Description seems short - be more descriptive!'); |
| | } |
| | if (parsed.description === 'Clear, descriptive description of what this function does. Mention key operations and purpose.') { |
| | warnings.push('β οΈ Update the description to match your function!'); |
| | } |
| | |
| | output.style.color = 'var(--success)'; |
| | let outputText = 'β Valid JSON Schema\n\n' + JSON.stringify(parsed, null, 2); |
| | if (warnings.length > 0) { |
| | outputText = warnings.join('\n') + '\n\n' + outputText; |
| | output.style.color = 'var(--acid)'; |
| | } |
| | output.textContent = outputText; |
| | } catch(e) { |
| | output.style.color = 'var(--alert)'; |
| | output.textContent = `β JSON Error: ${e.message}\n\nπ‘ Tip: Remove any comments (// lines) before testing.\nπ‘ Tip: Make sure all strings are in double quotes.`; |
| | state.currentFunction = null; |
| | } |
| | |
| | |
| | autoResizeTextarea(); |
| | } |
| | |
| | |
| | window.testFunction = async function() { |
| | if (!state.currentFunction) { |
| | alert('Please fix your JSON first!'); |
| | return; |
| | } |
| | |
| | const btn = document.getElementById('testBtn'); |
| | const testLoader = document.getElementById('testLoader'); |
| | const testProgress = document.getElementById('testProgress'); |
| | |
| | btn.disabled = true; |
| | btn.innerText = 'TESTING...'; |
| | |
| | |
| | let loaderTimeout = setTimeout(() => { |
| | testLoader.classList.add('visible'); |
| | testProgress.textContent = 'Preparing test environment...'; |
| | }, 100); |
| | |
| | try { |
| | const lvl = levels[state.levelIdx]; |
| | const results = []; |
| | let score = 0; |
| | const totalScenarios = lvl.scenarios.length; |
| | |
| | |
| | const tools = [ |
| | { |
| | type: "function", |
| | function: state.currentFunction |
| | }, |
| | ...lvl.competitors.map(c => ({ |
| | type: "function", |
| | function: c |
| | })) |
| | ]; |
| | |
| | |
| | addLog('FUNCTION_SCHEMA', 'Loaded function definitions for testing', 'info'); |
| | const toolsHtml = tools.map(t => |
| | `<div class="log-function"><span class="log-function-name">${t.function.name}</span>() - ${t.function.description}</div>` |
| | ).join(''); |
| | addLog('AVAILABLE_TOOLS', toolsHtml, 'tools'); |
| | |
| | |
| | for (let i = 0; i < lvl.scenarios.length; i++) { |
| | const scenario = lvl.scenarios[i]; |
| | const scenarioNum = i + 1; |
| | testProgress.textContent = `Testing scenario ${scenarioNum}/${totalScenarios}: "${scenario.query.substring(0, 40)}${scenario.query.length > 40 ? '...' : ''}"`; |
| | addLog('TEST_START', `Testing scenario: "${scenario.query}"`, 'info'); |
| | |
| | const messages = [ |
| | { role: "developer", content: "You are a function calling model. Select the most appropriate function for the user's request." }, |
| | { role: "user", content: scenario.query } |
| | ]; |
| | |
| | addLog('USER_PROMPT', scenario.query, 'prompt'); |
| | |
| | try { |
| | const inputs = await state.tokenizer.apply_chat_template(messages, { |
| | tools: tools, |
| | tokenize: true, |
| | add_generation_prompt: true, |
| | return_dict: true |
| | }); |
| | |
| | const genStart = performance.now(); |
| | const output = await state.model.generate({ |
| | ...inputs, |
| | max_new_tokens: 128, |
| | do_sample: false |
| | }); |
| | const genTime = performance.now() - genStart; |
| | |
| | |
| | telemetry.trackGeneration(genTime); |
| | |
| | const decoded = await state.tokenizer.decode(output.slice(0, [inputs.input_ids.dims[1], null]), { skip_special_tokens: false }); |
| | |
| | addLog('MODEL_OUTPUT', `Generated in ${genTime.toFixed(0)}ms`, 'info'); |
| | addLog('RAW_OUTPUT', decoded.substring(0, 200) + (decoded.length > 200 ? '...' : ''), 'info'); |
| | |
| | |
| | let triggered = null; |
| | |
| | |
| | const match1 = decoded.match(/call:\s*"?(\w+)"?/i); |
| | if (match1) triggered = match1[1]; |
| | |
| | |
| | if (!triggered) { |
| | const match2 = decoded.match(/function:\s*"?(\w+)"?/i); |
| | if (match2) triggered = match2[1]; |
| | } |
| | |
| | |
| | if (!triggered) { |
| | const match3 = decoded.match(/"name"\s*:\s*"(\w+)"/i); |
| | if (match3) triggered = match3[1]; |
| | } |
| | |
| | |
| | if (!triggered) { |
| | const match4 = decoded.match(/<tool_call[^>]*>[\s\S]*?name["\s:=]+(\w+)/i); |
| | if (match4) triggered = match4[1]; |
| | } |
| | |
| | |
| | if (!triggered) { |
| | const allFunctionNames = tools.map(t => t.function.name); |
| | for (const fnName of allFunctionNames) { |
| | |
| | const regex = new RegExp(`\\b${fnName}\\b`, 'i'); |
| | if (regex.test(decoded)) { |
| | |
| | const contextMatch = decoded.match(new RegExp(`(call|function|name|invoke|use|select)[\\s:="]*${fnName}`, 'i')); |
| | if (contextMatch) { |
| | triggered = fnName; |
| | break; |
| | } |
| | } |
| | } |
| | } |
| | |
| | addLog('FUNCTION_DETECTED', triggered ? triggered : 'NONE', triggered ? 'triggered' : 'error'); |
| | |
| | const triggeredYourFunction = triggered === state.currentFunction.name; |
| | const shouldHaveTriggered = scenario.shouldTrigger; |
| | |
| | |
| | let result = { |
| | query: scenario.query, |
| | expected: shouldHaveTriggered, |
| | triggered: triggeredYourFunction, |
| | correctFunction: triggered, |
| | icon: scenario.icon |
| | }; |
| | |
| | if (shouldHaveTriggered && triggeredYourFunction) { |
| | |
| | result.status = 'correct'; |
| | result.points = 100; |
| | score += 100; |
| | addLog('RESULT_EVAL', `β
CORRECT! Expected: ${state.currentFunction.name}, Got: ${triggered}`, 'success'); |
| | } else if (!shouldHaveTriggered && !triggeredYourFunction) { |
| | |
| | result.status = 'correct'; |
| | result.points = 50; |
| | score += 50; |
| | addLog('RESULT_EVAL', `β
CORRECT! Expected: NO TRIGGER, Got: NONE`, 'success'); |
| | } else if (shouldHaveTriggered && !triggeredYourFunction) { |
| | |
| | result.status = 'wrong'; |
| | result.points = 0; |
| | addLog('RESULT_EVAL', `β WRONG! Expected: ${state.currentFunction.name}, Got: ${triggered || 'NONE'}`, 'error'); |
| | } else { |
| | |
| | result.status = 'wrong'; |
| | result.points = -50; |
| | score -= 50; |
| | addLog('RESULT_EVAL', `β WRONG! Expected: NO TRIGGER, Got: ${triggered || 'NONE'}`, 'error'); |
| | } |
| | |
| | results.push(result); |
| | |
| | |
| | const scenarioCards = document.querySelectorAll('.scenario-card'); |
| | if (scenarioCards[i]) { |
| | scenarioCards[i].classList.add(result.status); |
| | setTimeout(() => { |
| | scenarioCards[i].classList.remove(result.status); |
| | }, 2000); |
| | } |
| | |
| | } catch(e) { |
| | console.error('Test error:', e); |
| | addLog('ERROR', `${e.message}`, 'error'); |
| | results.push({ |
| | query: scenario.query, |
| | status: 'error', |
| | points: 0, |
| | icon: scenario.icon |
| | }); |
| | } |
| | } |
| | |
| | |
| | displayResults(results, score); |
| | } catch(error) { |
| | console.error('Test function error:', error); |
| | addLog('FATAL_ERROR', `Test failed: ${error.message}`, 'error'); |
| | } finally { |
| | |
| | clearTimeout(loaderTimeout); |
| | testLoader.classList.remove('visible'); |
| | btn.disabled = false; |
| | btn.innerText = 'TEST FUNCTION'; |
| | } |
| | }; |
| | |
| | function displayResults(results, score) { |
| | const panel = document.getElementById('resultPanel'); |
| | const title = document.getElementById('resultTitle'); |
| | const scoreEl = document.getElementById('resultScore'); |
| | const breakdown = document.getElementById('resultBreakdown'); |
| | |
| | const passed = results.every(r => r.status === 'correct'); |
| | |
| | title.innerText = passed ? 'β ALL TESTS PASSED' : 'β SOME TESTS FAILED'; |
| | title.className = `result-title ${passed ? 'success' : 'fail'}`; |
| | |
| | scoreEl.innerText = (score >= 0 ? '+' : '') + score; |
| | scoreEl.className = `result-score ${score >= 0 ? 'success' : 'fail'}`; |
| | |
| | breakdown.innerHTML = results.map(r => ` |
| | <div class="result-item ${r.status}"> |
| | <span>${r.icon} ${r.query}</span> |
| | <span style="font-weight: bold;"> |
| | ${r.points >= 0 ? '+' : ''}${r.points} pts |
| | ${r.correctFunction && r.correctFunction !== 'null' ? ` (${r.correctFunction})` : ''} |
| | </span> |
| | </div> |
| | `).join(''); |
| | |
| | panel.classList.add('visible'); |
| | |
| | |
| | document.getElementById('nextBtn').style.display = 'block'; |
| | |
| | if (passed) { |
| | |
| | state.totalScore += score; |
| | document.getElementById('scoreDisp').innerText = state.totalScore.toString().padStart(4, '0'); |
| | |
| | |
| | telemetry.trackLevelComplete(state.levelIdx + 1, score, true); |
| | |
| | |
| | checkAchievements(results, score); |
| | |
| | |
| | updateStreakDisplay(); |
| | } else { |
| | |
| | state.streak = 0; |
| | updateStreakDisplay(); |
| | |
| | |
| | telemetry.trackLevelComplete(state.levelIdx + 1, score, false); |
| | } |
| | |
| | |
| | setupCacheWarmup(); |
| | |
| | |
| | const totalScenarios = (state.levelIdx + 1) * levels[state.levelIdx].scenarios.length; |
| | const accuracy = totalScenarios > 0 ? Math.round((state.totalScore / (totalScenarios * 100)) * 100) : 0; |
| | document.getElementById('accuracyDisp').innerText = `${accuracy}%`; |
| | |
| | |
| | const levelKey = `level_${state.levelIdx + 1}`; |
| | state.levelAttempts[levelKey] = (state.levelAttempts[levelKey] || 0) + 1; |
| | saveState(); |
| | } |
| | |
| | |
| | window.nextLevel = function() { |
| | state.levelIdx++; |
| | if (state.levelIdx >= levels.length) { |
| | victory(); |
| | } else { |
| | clearLogs(); |
| | renderLevel(); |
| | |
| | |
| | if (typeof gtag !== 'undefined') { |
| | gtag('event', 'level_navigation', { |
| | level_id: state.levelIdx + 1, |
| | action: 'next' |
| | }); |
| | } |
| | } |
| | }; |
| | |
| | |
| | window.resetEditor = function() { |
| | const lvl = levels[state.levelIdx]; |
| | document.getElementById('functionEditor').value = JSON.stringify(lvl.starterCode, null, 2); |
| | validateJSON(); |
| | setTimeout(autoResizeTextarea, 100); |
| | }; |
| | |
| | |
| | function checkAchievements(results, score) { |
| | const newAchievements = []; |
| | |
| | |
| | if (results.every(r => r.status === 'correct') && !state.achievements.includes('first_perfect')) { |
| | newAchievements.push({ id: 'first_perfect', name: 'Perfect Score', desc: 'Got all scenarios correct!' }); |
| | state.achievements.push('first_perfect'); |
| | } |
| | |
| | |
| | if (results.every(r => r.status === 'correct')) { |
| | state.streak++; |
| | if (state.streak > state.bestStreak) { |
| | state.bestStreak = state.streak; |
| | } |
| | if (state.streak === 3 && !state.achievements.includes('streak_3')) { |
| | newAchievements.push({ id: 'streak_3', name: 'On Fire!', desc: '3 perfect levels in a row!' }); |
| | state.achievements.push('streak_3'); |
| | } |
| | if (state.streak === 5 && !state.achievements.includes('streak_5')) { |
| | newAchievements.push({ id: 'streak_5', name: 'Unstoppable!', desc: '5 perfect levels in a row!' }); |
| | state.achievements.push('streak_5'); |
| | } |
| | } else { |
| | state.streak = 0; |
| | } |
| | |
| | |
| | if (state.totalScore >= 1000 && !state.achievements.includes('score_1k')) { |
| | newAchievements.push({ id: 'score_1k', name: 'Score Master', desc: 'Reached 1000 points!' }); |
| | state.achievements.push('score_1k'); |
| | } |
| | |
| | |
| | if (state.levelIdx === 4 && !state.achievements.includes('all_levels')) { |
| | newAchievements.push({ id: 'all_levels', name: 'Master Engineer', desc: 'Completed all levels!' }); |
| | state.achievements.push('all_levels'); |
| | } |
| | |
| | |
| | if (newAchievements.length > 0) { |
| | showAchievementNotification(newAchievements); |
| | } |
| | |
| | saveState(); |
| | } |
| | |
| | function showAchievementNotification(achievements) { |
| | achievements.forEach((ach, idx) => { |
| | setTimeout(() => { |
| | const notif = document.createElement('div'); |
| | notif.style.cssText = ` |
| | position: fixed; |
| | top: 20px; |
| | right: 20px; |
| | background: var(--panel); |
| | border: 3px solid var(--acid); |
| | padding: 20px; |
| | z-index: 10002; |
| | animation: slideInRight 0.5s var(--ease-out-expo); |
| | box-shadow: var(--hard-shadow); |
| | max-width: 300px; |
| | `; |
| | notif.innerHTML = ` |
| | <div style="color: var(--acid); font-weight: bold; margin-bottom: 5px;">π ACHIEVEMENT UNLOCKED</div> |
| | <div style="font-size: 1.2rem; margin-bottom: 5px;">${ach.name}</div> |
| | <div style="color: var(--text-muted); font-size: 0.9rem;">${ach.desc}</div> |
| | `; |
| | document.body.appendChild(notif); |
| | |
| | setTimeout(() => { |
| | notif.style.animation = 'slideOutRight 0.5s var(--ease-out-expo)'; |
| | setTimeout(() => notif.remove(), 500); |
| | }, 3000); |
| | }, idx * 400); |
| | }); |
| | } |
| | |
| | |
| | function updateStreakDisplay() { |
| | |
| | const streakEl = document.getElementById('streakDisp'); |
| | if (streakEl) { |
| | streakEl.innerText = state.streak; |
| | streakEl.parentElement.style.display = state.streak > 0 ? 'flex' : 'none'; |
| | } |
| | } |
| | |
| | function updateModelInfoDisplay() { |
| | const modelInfoEl = document.getElementById('modelInfo'); |
| | if (modelInfoEl && state.modelInfo.name) { |
| | document.getElementById('modelName').textContent = state.modelInfo.name; |
| | document.getElementById('modelVersion').textContent = state.modelInfo.version || '-'; |
| | document.getElementById('modelQuantization').textContent = state.modelInfo.quantization.toUpperCase(); |
| | document.getElementById('modelDevice').textContent = state.modelInfo.device.toUpperCase(); |
| | modelInfoEl.style.display = 'block'; |
| | } |
| | } |
| | |
| | |
| | window.toggleHints = function() { |
| | const panel = document.getElementById('hintPanel'); |
| | const content = document.getElementById('hintContent'); |
| | const lvl = levels[state.levelIdx]; |
| | |
| | if (panel.classList.contains('visible')) { |
| | panel.classList.remove('visible'); |
| | } else { |
| | state.hintsUsed++; |
| | saveState(); |
| | content.innerHTML = lvl.hints.map((h, i) => |
| | `<p style="margin-bottom: 8px;"><strong>${i+1}.</strong> ${h}</p>` |
| | ).join(''); |
| | panel.classList.add('visible'); |
| | } |
| | }; |
| | |
| | |
| | window.toggleRules = function() { |
| | const modal = document.getElementById('rulesModal'); |
| | modal.classList.toggle('visible'); |
| | }; |
| | |
| | |
| | window.resetProgress = function() { |
| | if (!confirm('β οΈ WARNING: This will reset ALL progress, achievements, and scores!\n\nAre you sure?')) { |
| | return; |
| | } |
| | |
| | state.totalScore = 0; |
| | state.achievements = []; |
| | state.streak = 0; |
| | state.bestStreak = 0; |
| | state.levelAttempts = {}; |
| | state.hintsUsed = 0; |
| | state.levelIdx = 0; |
| | |
| | localStorage.removeItem('functionArenaState'); |
| | |
| | |
| | const scoreDisp = document.getElementById('scoreDisp'); |
| | const accuracyDisp = document.getElementById('accuracyDisp'); |
| | if (scoreDisp) scoreDisp.innerText = '0000'; |
| | if (accuracyDisp) accuracyDisp.innerText = '--'; |
| | updateStreakDisplay(); |
| | |
| | const achievementsList = document.getElementById('achievementsList'); |
| | if (achievementsList) { |
| | achievementsList.innerHTML = '<div style="color: #666;">No achievements yet...</div>'; |
| | } |
| | |
| | |
| | clearLogs(); |
| | renderLevel(); |
| | |
| | alert('Progress reset! Starting fresh...'); |
| | }; |
| | |
| | |
| | function victory() { |
| | const achievementsCount = state.achievements.length; |
| | const totalAttempts = Object.values(state.levelAttempts).reduce((a, b) => a + b, 0); |
| | const avgAttempts = totalAttempts / 5; |
| | |
| | document.body.innerHTML = ` |
| | <div style="height:100vh; display:flex; flex-direction:column; justify-content:center; align-items:center; text-align:center; background: var(--void); padding: 20px;"> |
| | <div class="noise-overlay"></div> |
| | <div class="scanlines"></div> |
| | |
| | <h1 style="font-size: 4rem; color: var(--acid); margin-bottom: 20px; font-family: 'Clash Display'; text-transform: uppercase; animation: pulse 2s infinite;"> |
| | π TRAINING_COMPLETE π |
| | </h1> |
| | |
| | <p style="font-family: 'Space Mono'; margin-bottom: 40px; color: var(--text-muted); font-size: 1.2rem;"> |
| | You've mastered function definition engineering! |
| | </p> |
| | |
| | <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; max-width: 800px; width: 100%; margin-bottom: 40px;"> |
| | <div style="border: 3px solid var(--success); padding: 20px; background: var(--panel);"> |
| | <div style="font-size: 0.9rem; color: var(--text-muted); margin-bottom: 10px;">FINAL SCORE</div> |
| | <div style="font-size: 2.5rem; font-family: 'Clash Display'; color: var(--success);">${state.totalScore}</div> |
| | </div> |
| | |
| | <div style="border: 3px solid var(--hyper-purple); padding: 20px; background: var(--panel);"> |
| | <div style="font-size: 0.9rem; color: var(--text-muted); margin-bottom: 10px;">ACHIEVEMENTS</div> |
| | <div style="font-size: 2.5rem; font-family: 'Clash Display'; color: var(--hyper-purple);">${achievementsCount}/5</div> |
| | </div> |
| | |
| | <div style="border: 3px solid var(--acid); padding: 20px; background: var(--panel);"> |
| | <div style="font-size: 0.9rem; color: var(--text-muted); margin-bottom: 10px;">BEST STREAK</div> |
| | <div style="font-size: 2.5rem; font-family: 'Clash Display'; color: var(--acid);">${state.bestStreak}</div> |
| | </div> |
| | </div> |
| | |
| | ${achievementsCount > 0 ? ` |
| | <div style="max-width: 600px; margin-bottom: 40px; padding: 20px; background: var(--panel); border: 2px solid var(--acid);"> |
| | <div style="color: var(--acid); font-weight: bold; margin-bottom: 15px; font-size: 1.1rem;">YOUR ACHIEVEMENTS</div> |
| | <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px;"> |
| | ${state.achievements.map(a => { |
| | const names = { |
| | 'first_perfect': 'π Perfect Score', |
| | 'streak_3': 'π₯ On Fire!', |
| | 'streak_5': 'β‘ Unstoppable!', |
| | 'score_1k': 'π― Score Master', |
| | 'all_levels': 'π Master Engineer' |
| | }; |
| | return `<div style="color: var(--acid); padding: 10px; background: rgba(204,255,0,0.1);">${names[a] || a}</div>`; |
| | }).join('')} |
| | </div> |
| | </div> |
| | ` : ''} |
| | |
| | <p style="max-width: 600px; line-height: 1.6; color: var(--text-muted); margin-bottom: 40px;"> |
| | You now understand how to craft precise function definitions, use enums effectively, |
| | and distinguish your functions from competitors. Apply this knowledge to build better AI integrations! |
| | </p> |
| | |
| | <div style="display: flex; gap: 20px; flex-wrap: wrap; justify-content: center;"> |
| | <button onclick="location.reload()" style="background: var(--text-main); color: var(--void); padding: 20px 40px; font-family: 'Clash Display'; font-size: 1.2rem; border: none; cursor: pointer; font-weight: 700; transition: all 0.2s;"> |
| | TRAIN AGAIN |
| | </button> |
| | <button onclick="resetProgress(); location.reload();" style="background: transparent; color: var(--alert); padding: 20px 40px; font-family: 'Clash Display'; font-size: 1.2rem; border: 2px solid var(--alert); cursor: pointer; font-weight: 700; transition: all 0.2s;"> |
| | RESET & RESTART |
| | </button> |
| | </div> |
| | </div> |
| | `; |
| | } |
| | |
| | |
| | window.addEventListener('load', () => { |
| | loadState(); |
| | bootSequence(); |
| | }); |
| | |
| | |
| | setInterval(() => { |
| | if (state.booted) { |
| | saveState(); |
| | } |
| | }, 30000); |
| | </script> |
| | </body> |
| | </html> |