Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>FUNCTIONGEMMA | TUTORIAL</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Bungee&family=JetBrains+Mono:wght@400;700;800&family=Space+Grotesk:wght@400;700&display=swap" rel="stylesheet"> | |
| <!-- Google Analytics --> | |
| <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 { | |
| --black: #000000; | |
| --white: #FFFFFF; | |
| --yellow: #FFFF00; | |
| --red: #FF0000; | |
| --green: #00FF00; | |
| --cyan: #00FFFF; | |
| --border-width: 4px; | |
| --shadow-offset: 8px; | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Space Grotesk', sans-serif; | |
| background: var(--black); | |
| color: var(--white); | |
| min-height: 100vh; | |
| padding: 20px; | |
| line-height: 1.6; | |
| position: relative; | |
| overflow-x: hidden; | |
| } | |
| body::before { | |
| content: ''; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: | |
| repeating-linear-gradient(90deg, transparent, transparent 50px, rgba(255,255,0,0.03) 50px, rgba(255,255,0,0.03) 52px), | |
| repeating-linear-gradient(0deg, transparent, transparent 50px, rgba(255,0,0,0.03) 50px, rgba(255,0,0,0.03) 52px); | |
| pointer-events: none; | |
| z-index: 0; | |
| } | |
| .container { | |
| max-width: 1600px; | |
| margin: 0 auto; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .header { | |
| background: var(--black); | |
| border: var(--border-width) solid var(--white); | |
| padding: 40px; | |
| margin-bottom: 30px; | |
| box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--yellow); | |
| position: relative; | |
| animation: slideDown 0.6s ease-out; | |
| } | |
| @keyframes slideDown { | |
| from { | |
| opacity: 0; | |
| transform: translateY(-30px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .header::before { | |
| content: ''; | |
| position: absolute; | |
| top: -4px; | |
| left: -4px; | |
| right: -4px; | |
| bottom: -4px; | |
| border: 2px solid var(--yellow); | |
| z-index: -1; | |
| } | |
| .header h1 { | |
| font-family: 'Bungee', cursive; | |
| font-size: 3.5em; | |
| color: var(--yellow); | |
| margin-bottom: 15px; | |
| text-transform: uppercase; | |
| letter-spacing: 2px; | |
| text-shadow: 4px 4px 0 var(--red); | |
| line-height: 1.1; | |
| } | |
| .header p { | |
| font-size: 1.3em; | |
| color: var(--white); | |
| font-weight: 700; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| } | |
| .model-loading-overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: var(--black); | |
| z-index: 10000; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| border: var(--border-width) solid var(--yellow); | |
| } | |
| .model-loading-overlay.hidden { | |
| display: none; | |
| } | |
| .loading-content { | |
| max-width: 800px; | |
| padding: 40px; | |
| background: var(--black); | |
| border: var(--border-width) solid var(--white); | |
| box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--cyan); | |
| } | |
| .loading-title { | |
| font-family: 'Bungee', cursive; | |
| font-size: 2.5em; | |
| color: var(--yellow); | |
| margin-bottom: 30px; | |
| text-transform: uppercase; | |
| text-align: center; | |
| animation: pulse 2s ease-in-out infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.7; } | |
| } | |
| .loading-steps { | |
| list-style: none; | |
| margin: 20px 0; | |
| } | |
| .loading-step { | |
| padding: 15px; | |
| margin: 10px 0; | |
| background: var(--black); | |
| border: 2px solid var(--white); | |
| color: var(--white); | |
| font-family: 'JetBrains Mono', monospace; | |
| position: relative; | |
| transition: all 0.3s ease; | |
| } | |
| .loading-step.active { | |
| border-color: var(--yellow); | |
| background: rgba(255, 255, 0, 0.1); | |
| box-shadow: 4px 4px 0 var(--yellow); | |
| } | |
| .loading-step.completed { | |
| border-color: var(--green); | |
| background: rgba(0, 255, 0, 0.1); | |
| } | |
| .loading-step.completed::after { | |
| content: ' ✓'; | |
| color: var(--green); | |
| font-weight: bold; | |
| } | |
| .model-info-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); | |
| gap: 20px; | |
| margin: 30px 0; | |
| } | |
| .info-card { | |
| background: var(--black); | |
| border: var(--border-width) solid var(--white); | |
| padding: 20px; | |
| box-shadow: 6px 6px 0 var(--cyan); | |
| } | |
| .info-card h3 { | |
| font-family: 'Bungee', cursive; | |
| color: var(--cyan); | |
| font-size: 1.2em; | |
| margin-bottom: 10px; | |
| text-transform: uppercase; | |
| } | |
| .info-card p { | |
| font-family: 'JetBrains Mono', monospace; | |
| color: var(--white); | |
| font-size: 0.9em; | |
| } | |
| .resource-links { | |
| margin: 30px 0; | |
| padding: 20px; | |
| background: var(--black); | |
| border: var(--border-width) solid var(--yellow); | |
| box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--red); | |
| } | |
| .resource-links h3 { | |
| font-family: 'Bungee', cursive; | |
| color: var(--yellow); | |
| margin-bottom: 15px; | |
| text-transform: uppercase; | |
| font-size: 1.5em; | |
| } | |
| .resource-links ul { | |
| list-style: none; | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | |
| gap: 15px; | |
| } | |
| .resource-links li { | |
| padding: 15px; | |
| background: var(--black); | |
| border: 2px solid var(--white); | |
| } | |
| .resource-links a { | |
| color: var(--cyan); | |
| text-decoration: none; | |
| font-family: 'JetBrains Mono', monospace; | |
| font-weight: 700; | |
| transition: all 0.2s ease; | |
| display: block; | |
| } | |
| .resource-links a:hover { | |
| color: var(--yellow); | |
| transform: translateX(5px); | |
| } | |
| .progress-bar { | |
| background: var(--black); | |
| border: var(--border-width) solid var(--white); | |
| padding: 20px; | |
| margin-bottom: 30px; | |
| display: flex; | |
| align-items: center; | |
| gap: 20px; | |
| box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--cyan); | |
| } | |
| .progress-fill { | |
| flex: 1; | |
| height: 40px; | |
| background: var(--black); | |
| border: 2px solid var(--white); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .progress-inner { | |
| height: 100%; | |
| background: var(--yellow); | |
| transition: width 0.5s ease; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: var(--black); | |
| font-weight: 800; | |
| font-family: 'Bungee', cursive; | |
| font-size: 1.1em; | |
| text-transform: uppercase; | |
| } | |
| .main-content { | |
| display: grid; | |
| grid-template-columns: 320px 1fr; | |
| gap: 30px; | |
| } | |
| .sidebar { | |
| background: var(--black); | |
| border: var(--border-width) solid var(--white); | |
| padding: 25px; | |
| box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--red); | |
| height: fit-content; | |
| position: sticky; | |
| top: 20px; | |
| } | |
| .model-status { | |
| background: var(--black); | |
| border: 2px solid var(--white); | |
| padding: 15px; | |
| margin-bottom: 25px; | |
| text-align: center; | |
| font-family: 'JetBrains Mono', monospace; | |
| font-weight: 700; | |
| } | |
| .model-status.loaded { | |
| border-color: var(--green); | |
| box-shadow: 4px 4px 0 var(--green); | |
| } | |
| .model-status-text { | |
| color: var(--white); | |
| } | |
| .model-status.loaded .model-status-text { | |
| color: var(--green); | |
| } | |
| .lesson-list { | |
| list-style: none; | |
| } | |
| .lesson-item { | |
| padding: 18px; | |
| margin: 12px 0; | |
| background: var(--black); | |
| border: 2px solid var(--white); | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| font-weight: 700; | |
| text-transform: uppercase; | |
| font-size: 0.9em; | |
| letter-spacing: 0.5px; | |
| } | |
| .lesson-item:hover { | |
| transform: translate(4px, 4px); | |
| box-shadow: -4px -4px 0 var(--yellow); | |
| } | |
| .lesson-item.active { | |
| background: var(--yellow); | |
| color: var(--black); | |
| border-color: var(--yellow); | |
| box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--red); | |
| } | |
| .lesson-item.completed { | |
| border-color: var(--green); | |
| } | |
| .lesson-item.completed::after { | |
| content: " ✓"; | |
| color: var(--green); | |
| font-weight: bold; | |
| } | |
| .content-area { | |
| background: var(--black); | |
| border: var(--border-width) solid var(--white); | |
| padding: 40px; | |
| box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--cyan); | |
| min-height: 600px; | |
| } | |
| .lesson-content { | |
| display: none; | |
| } | |
| .lesson-content.active { | |
| display: block; | |
| animation: fadeInSlide 0.5s ease; | |
| } | |
| @keyframes fadeInSlide { | |
| from { | |
| opacity: 0; | |
| transform: translateY(20px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .lesson-title { | |
| font-family: 'Bungee', cursive; | |
| font-size: 2.5em; | |
| margin-bottom: 25px; | |
| color: var(--yellow); | |
| text-transform: uppercase; | |
| text-shadow: 3px 3px 0 var(--red); | |
| border-bottom: var(--border-width) solid var(--yellow); | |
| padding-bottom: 15px; | |
| } | |
| .lesson-description { | |
| font-size: 1.1em; | |
| line-height: 1.8; | |
| margin-bottom: 30px; | |
| color: var(--white); | |
| } | |
| .code-block { | |
| background: var(--black); | |
| border: var(--border-width) solid var(--yellow); | |
| padding: 25px; | |
| margin: 25px 0; | |
| overflow-x: auto; | |
| position: relative; | |
| box-shadow: 6px 6px 0 var(--cyan); | |
| } | |
| .code-block pre { | |
| color: var(--green); | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 14px; | |
| line-height: 1.8; | |
| margin: 0; | |
| font-weight: 400; | |
| } | |
| .code-comment { | |
| color: #888; | |
| font-style: italic; | |
| } | |
| .code-keyword { | |
| color: var(--red); | |
| font-weight: 700; | |
| } | |
| .code-string { | |
| color: var(--cyan); | |
| } | |
| .code-function { | |
| color: var(--yellow); | |
| } | |
| .interactive-demo { | |
| background: var(--black); | |
| border: var(--border-width) solid var(--white); | |
| padding: 25px; | |
| margin: 25px 0; | |
| box-shadow: 6px 6px 0 var(--red); | |
| } | |
| .interactive-demo h3 { | |
| font-family: 'Bungee', cursive; | |
| color: var(--yellow); | |
| margin-bottom: 20px; | |
| text-transform: uppercase; | |
| font-size: 1.5em; | |
| } | |
| .demo-controls { | |
| display: flex; | |
| gap: 15px; | |
| margin-bottom: 20px; | |
| flex-wrap: wrap; | |
| } | |
| button { | |
| background: var(--black); | |
| color: var(--white); | |
| border: var(--border-width) solid var(--white); | |
| padding: 15px 30px; | |
| font-size: 16px; | |
| font-weight: 800; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| font-family: 'Space Grotesk', sans-serif; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| box-shadow: 4px 4px 0 var(--yellow); | |
| } | |
| button:hover:not(:disabled) { | |
| transform: translate(2px, 2px); | |
| box-shadow: 2px 2px 0 var(--yellow); | |
| } | |
| button:active:not(:disabled) { | |
| transform: translate(4px, 4px); | |
| box-shadow: 0 0 0 var(--yellow); | |
| } | |
| button:disabled { | |
| background: #333; | |
| border-color: #666; | |
| color: #666; | |
| cursor: not-allowed; | |
| box-shadow: none; | |
| } | |
| .btn-success { | |
| border-color: var(--green); | |
| box-shadow: 4px 4px 0 var(--green); | |
| color: var(--green); | |
| } | |
| .btn-success:hover:not(:disabled) { | |
| box-shadow: 2px 2px 0 var(--green); | |
| } | |
| .btn-danger { | |
| border-color: var(--red); | |
| box-shadow: 4px 4px 0 var(--red); | |
| color: var(--red); | |
| } | |
| .btn-danger:hover:not(:disabled) { | |
| box-shadow: 2px 2px 0 var(--red); | |
| } | |
| .output-area { | |
| background: var(--black); | |
| border: var(--border-width) solid var(--cyan); | |
| padding: 20px; | |
| margin: 20px 0; | |
| min-height: 150px; | |
| max-height: 500px; | |
| overflow-y: auto; | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 13px; | |
| box-shadow: 6px 6px 0 var(--yellow); | |
| } | |
| .token-visualization { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 15px; | |
| margin: 25px 0; | |
| } | |
| .token-box { | |
| background: var(--black); | |
| border: 2px solid var(--white); | |
| padding: 15px 20px; | |
| text-align: center; | |
| min-width: 140px; | |
| transition: all 0.2s ease; | |
| box-shadow: 4px 4px 0 var(--cyan); | |
| } | |
| .token-box:hover { | |
| transform: translate(-2px, -2px); | |
| box-shadow: 6px 6px 0 var(--yellow); | |
| border-color: var(--yellow); | |
| } | |
| .token-id { | |
| font-size: 11px; | |
| color: #888; | |
| margin-bottom: 8px; | |
| font-family: 'JetBrains Mono', monospace; | |
| } | |
| .token-text { | |
| font-size: 18px; | |
| color: var(--green); | |
| font-weight: 800; | |
| font-family: 'JetBrains Mono', monospace; | |
| } | |
| .hint-box, .info-box, .success-box, .error-box { | |
| background: var(--black); | |
| border-left: var(--border-width) solid; | |
| padding: 20px; | |
| margin: 20px 0; | |
| box-shadow: 4px 4px 0; | |
| } | |
| .hint-box { | |
| border-color: var(--yellow); | |
| box-shadow: 4px 4px 0 var(--yellow); | |
| } | |
| .hint-box h4 { | |
| color: var(--yellow); | |
| margin-bottom: 12px; | |
| font-family: 'Bungee', cursive; | |
| text-transform: uppercase; | |
| font-size: 1.2em; | |
| } | |
| .info-box { | |
| border-color: var(--cyan); | |
| box-shadow: 4px 4px 0 var(--cyan); | |
| } | |
| .info-box h4 { | |
| color: var(--cyan); | |
| margin-bottom: 12px; | |
| font-family: 'Bungee', cursive; | |
| text-transform: uppercase; | |
| font-size: 1.2em; | |
| } | |
| .success-box { | |
| border-color: var(--green); | |
| box-shadow: 4px 4px 0 var(--green); | |
| } | |
| .success-box h4 { | |
| color: var(--green); | |
| margin-bottom: 12px; | |
| font-family: 'Bungee', cursive; | |
| text-transform: uppercase; | |
| font-size: 1.2em; | |
| } | |
| .error-box { | |
| border-color: var(--red); | |
| box-shadow: 4px 4px 0 var(--red); | |
| } | |
| .error-box h4 { | |
| color: var(--red); | |
| margin-bottom: 12px; | |
| font-family: 'Bungee', cursive; | |
| text-transform: uppercase; | |
| font-size: 1.2em; | |
| } | |
| .comparison-table { | |
| width: 100%; | |
| border-collapse: separate; | |
| border-spacing: 0; | |
| margin: 25px 0; | |
| border: var(--border-width) solid var(--white); | |
| } | |
| .comparison-table th, | |
| .comparison-table td { | |
| padding: 15px; | |
| text-align: left; | |
| border-bottom: 2px solid var(--white); | |
| border-right: 2px solid var(--white); | |
| } | |
| .comparison-table th { | |
| background: var(--yellow); | |
| color: var(--black); | |
| font-family: 'Bungee', cursive; | |
| text-transform: uppercase; | |
| font-weight: 400; | |
| } | |
| .comparison-table tr:hover { | |
| background: rgba(255, 255, 0, 0.1); | |
| } | |
| .comparison-table td:last-child, | |
| .comparison-table th:last-child { | |
| border-right: none; | |
| } | |
| .achievement-badge { | |
| display: inline-block; | |
| background: var(--yellow); | |
| color: var(--black); | |
| padding: 8px 15px; | |
| border: 2px solid var(--black); | |
| font-size: 12px; | |
| font-weight: 800; | |
| margin: 5px; | |
| text-transform: uppercase; | |
| font-family: 'Bungee', cursive; | |
| box-shadow: 3px 3px 0 var(--red); | |
| } | |
| input[type="text"], | |
| textarea { | |
| background: var(--black); | |
| border: var(--border-width) solid var(--white); | |
| color: var(--white); | |
| padding: 15px; | |
| font-size: 14px; | |
| width: 100%; | |
| font-family: 'JetBrains Mono', monospace; | |
| margin-bottom: 15px; | |
| box-shadow: 4px 4px 0 var(--cyan); | |
| } | |
| input[type="text"]:focus, | |
| textarea:focus { | |
| outline: none; | |
| border-color: var(--yellow); | |
| box-shadow: 4px 4px 0 var(--yellow); | |
| } | |
| .loading-spinner { | |
| display: inline-block; | |
| width: 24px; | |
| height: 24px; | |
| border: 3px solid var(--white); | |
| border-top-color: var(--yellow); | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| .playground-grid { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 25px; | |
| margin: 25px 0; | |
| } | |
| .playground-section { | |
| background: var(--black); | |
| border: var(--border-width) solid var(--white); | |
| padding: 25px; | |
| box-shadow: 6px 6px 0 var(--red); | |
| } | |
| .playground-section h3 { | |
| font-family: 'Bungee', cursive; | |
| color: var(--yellow); | |
| margin-bottom: 15px; | |
| text-transform: uppercase; | |
| } | |
| textarea { | |
| min-height: 300px; | |
| resize: vertical; | |
| } | |
| .resource-badge { | |
| display: inline-block; | |
| background: var(--black); | |
| border: 2px solid var(--cyan); | |
| padding: 5px 10px; | |
| margin: 5px; | |
| font-size: 11px; | |
| font-family: 'JetBrains Mono', monospace; | |
| color: var(--cyan); | |
| text-transform: uppercase; | |
| } | |
| .example-item { | |
| background: var(--black); | |
| border: 2px solid var(--white); | |
| padding: 20px; | |
| margin: 15px 0; | |
| box-shadow: 4px 4px 0 var(--cyan); | |
| position: relative; | |
| } | |
| .example-item-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 15px; | |
| } | |
| .example-item h4 { | |
| font-family: 'Bungee', cursive; | |
| color: var(--yellow); | |
| font-size: 1em; | |
| margin: 0; | |
| text-transform: uppercase; | |
| } | |
| .example-item input, | |
| .example-item textarea { | |
| width: 100%; | |
| margin-bottom: 10px; | |
| font-size: 13px; | |
| } | |
| .example-item textarea { | |
| min-height: 60px; | |
| resize: vertical; | |
| } | |
| .remove-example-btn { | |
| background: var(--black); | |
| color: var(--red); | |
| border: 2px solid var(--red); | |
| padding: 8px 15px; | |
| font-size: 12px; | |
| cursor: pointer; | |
| font-weight: 700; | |
| text-transform: uppercase; | |
| box-shadow: 3px 3px 0 var(--red); | |
| } | |
| .remove-example-btn:hover { | |
| transform: translate(1px, 1px); | |
| box-shadow: 2px 2px 0 var(--red); | |
| } | |
| @media (max-width: 1024px) { | |
| .main-content { | |
| grid-template-columns: 1fr; | |
| } | |
| .sidebar { | |
| position: static; | |
| } | |
| .playground-grid { | |
| grid-template-columns: 1fr; | |
| } | |
| .header h1 { | |
| font-size: 2.5em; | |
| } | |
| } | |
| @media (max-width: 768px) { | |
| .header h1 { | |
| font-size: 2em; | |
| } | |
| .lesson-title { | |
| font-size: 1.8em; | |
| } | |
| .model-info-grid { | |
| grid-template-columns: 1fr; | |
| } | |
| .resource-links ul { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Model Loading Overlay --> | |
| <div class="model-loading-overlay" id="loadingOverlay"> | |
| <div class="loading-content"> | |
| <h2 class="loading-title">LOADING FUNCTIONGEMMA</h2> | |
| <ul class="loading-steps" id="loadingSteps"> | |
| <li class="loading-step" id="step1">Initializing transformers.js...</li> | |
| <li class="loading-step" id="step2">Loading tokenizer...</li> | |
| <li class="loading-step" id="step3">Detecting device capabilities...</li> | |
| <li class="loading-step" id="step4">Loading ONNX model...</li> | |
| <li class="loading-step" id="step5">Model ready!</li> | |
| </ul> | |
| <div class="model-info-grid" id="modelInfoGrid" style="display: none;"> | |
| <div class="info-card"> | |
| <h3>MODEL</h3> | |
| <p>functiongemma-270m-it-ONNX</p> | |
| </div> | |
| <div class="info-card"> | |
| <h3>PARAMETERS</h3> | |
| <p>270 Million</p> | |
| </div> | |
| <div class="info-card"> | |
| <h3>FORMAT</h3> | |
| <p id="modelFormat">Detecting...</p> | |
| </div> | |
| <div class="info-card"> | |
| <h3>DEVICE</h3> | |
| <p id="modelDevice">Detecting...</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1>FUNCTIONGEMMA</h1> | |
| <p>INTERACTIVE TUTORIAL</p> | |
| <p style="font-size: 0.9em; margin-top: 10px; font-weight: 400; text-transform: none;">Master Function Calling, Tokenization & Prompt Engineering</p> | |
| </div> | |
| <div class="progress-bar"> | |
| <div class="progress-fill"> | |
| <div class="progress-inner" id="progressBar" style="width: 0%">0% COMPLETE</div> | |
| </div> | |
| <div id="achievements"></div> | |
| </div> | |
| <div class="main-content"> | |
| <div class="sidebar"> | |
| <div class="model-status" id="modelStatus"> | |
| <strong>STATUS:</strong> <span class="model-status-text" id="modelStatusText">READY</span> | |
| </div> | |
| <ul class="lesson-list"> | |
| <li class="lesson-item active" data-lesson="0">🚀 WELCOME</li> | |
| <li class="lesson-item" data-lesson="1">🔤 TOKENIZATION</li> | |
| <li class="lesson-item" data-lesson="2">❌ ZERO-SHOT</li> | |
| <li class="lesson-item" data-lesson="3">⚠️ ONE-SHOT</li> | |
| <li class="lesson-item" data-lesson="4">✅ FEW-SHOT</li> | |
| <li class="lesson-item" data-lesson="5">🔍 TOKEN DIVE</li> | |
| <li class="lesson-item" data-lesson="6">🎯 PLAYGROUND</li> | |
| <li class="lesson-item" data-lesson="7">📚 RESOURCES</li> | |
| </ul> | |
| </div> | |
| <div class="content-area"> | |
| <!-- Lesson 0: Welcome --> | |
| <div class="lesson-content active" data-lesson="0"> | |
| <h2 class="lesson-title">WELCOME TO FUNCTIONGEMMA</h2> | |
| <div class="lesson-description"> | |
| <p>Welcome to the tutorial on FunctionGemma. This interactive experience will teach you everything about function calling, tokenization, and prompt engineering through hands-on experimentation.</p> | |
| <div class="info-box"> | |
| <h4>WHAT YOU'LL LEARN</h4> | |
| <ul style="margin-left: 20px; line-height: 2.5;"> | |
| <li>How tokenization works in language models</li> | |
| <li>Why zero-shot function calling fails</li> | |
| <li>How few-shot examples solve the problem</li> | |
| <li>Token-level analysis and debugging</li> | |
| <li>Best practices for prompt engineering</li> | |
| <li>ONNX model optimization and deployment</li> | |
| </ul> | |
| </div> | |
| <div class="hint-box"> | |
| <h4>ABOUT FUNCTIONGEMMA-270M-IT-ONNX</h4> | |
| <p><strong>Model:</strong> onnx-community/functiongemma-270m-it-ONNX</p> | |
| <p><strong>Size:</strong> 270 million parameters</p> | |
| <p><strong>Purpose:</strong> Specialized for function calling tasks</p> | |
| <p><strong>Format:</strong> ONNX quantized (q4 for WebGPU, q8 for WASM)</p> | |
| <p><strong>Key Finding:</strong> Requires few-shot examples to generate correct function calls!</p> | |
| <p><strong>Architecture:</strong> Based on Google's Gemma 3 270M, fine-tuned for function calling</p> | |
| </div> | |
| <div class="success-box"> | |
| <h4>MODEL LOADED SUCCESSFULLY</h4> | |
| <p>The model has been automatically loaded and is ready to use. You can now proceed with the lessons!</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Lesson 1: Tokenization --> | |
| <div class="lesson-content" data-lesson="1"> | |
| <h2 class="lesson-title">TOKENIZATION BASICS</h2> | |
| <div class="lesson-description"> | |
| <p>Tokenization is the process of converting text into tokens (numbers) that the model can understand. Let's explore this interactively!</p> | |
| <div class="info-box"> | |
| <h4>WHAT IS TOKENIZATION?</h4> | |
| <p>Language models don't understand words directly. They work with <strong>tokens</strong> - numeric IDs that represent pieces of text. A token can be a word, part of a word, or even a single character.</p> | |
| </div> | |
| <div class="interactive-demo"> | |
| <h3>TRY IT YOURSELF</h3> | |
| <input type="text" id="tokenizeInput" placeholder="Enter text to tokenize..." value="call:get_current_temperature"> | |
| <button onclick="demonstrateTokenization()">TOKENIZE</button> | |
| <div id="tokenizationOutput" class="output-area" style="margin-top: 15px;"></div> | |
| </div> | |
| <div class="code-block"> | |
| <pre><span class="code-comment">// How tokenization works in code:</span> | |
| <span class="code-keyword">const</span> text = <span class="code-string">"call:get_current_temperature"</span>; | |
| <span class="code-comment">// Tokenize the text</span> | |
| <span class="code-keyword">const</span> tokens = <span class="code-function">await tokenizer</span>.encode(text); | |
| <span class="code-comment">// Result: [6639, 236787, 828, 236779, 4002, 236779, 27495]</span> | |
| <span class="code-comment">// Each number represents a token ID</span> | |
| <span class="code-comment">// Decode tokens back to text</span> | |
| <span class="code-keyword">const</span> decoded = <span class="code-function">await tokenizer</span>.decode(tokens); | |
| <span class="code-comment">// Result: "call:get_current_temperature"</span></pre> | |
| </div> | |
| <div class="hint-box"> | |
| <h4>KEY INSIGHT</h4> | |
| <p>Special tokens like <code><start_function_call></code> have specific token IDs (e.g., token 48). The model uses these to understand structure.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Lesson 2: Zero-Shot --> | |
| <div class="lesson-content" data-lesson="2"> | |
| <h2 class="lesson-title">ZERO-SHOT FUNCTION CALLING (WHY IT FAILS)</h2> | |
| <div class="lesson-description"> | |
| <p>Zero-shot means asking the model to do something without showing it an example. Let's see what happens!</p> | |
| <div class="error-box"> | |
| <h4>THE PROBLEM</h4> | |
| <p>Without examples, FunctionGemma generates <code>error:</code> instead of <code>call:</code> after <code><start_function_call></code>.</p> | |
| </div> | |
| <div class="interactive-demo"> | |
| <h3>TEST ZERO-SHOT APPROACH</h3> | |
| <input type="text" id="zeroShotQuery" value="What's the temperature in London?" placeholder="Enter your query..."> | |
| <button onclick="testZeroShot()">TEST ZERO-SHOT</button> | |
| <div id="zeroShotOutput" class="output-area" style="margin-top: 15px;"></div> | |
| </div> | |
| <div class="code-block"> | |
| <pre><span class="code-comment">// Zero-shot approach - NO examples provided</span> | |
| <span class="code-keyword">const</span> messages = [ | |
| { | |
| role: <span class="code-string">"developer"</span>, | |
| content: <span class="code-string">"You are a model that can do function calling..."</span> | |
| }, | |
| { | |
| role: <span class="code-string">"user"</span>, | |
| content: <span class="code-string">"What's the temperature in London?"</span> | |
| } | |
| <span class="code-comment">// ❌ No example shown to the model!</span> | |
| ]; | |
| <span class="code-comment">// Result: Model generates "error:" instead of "call:"</span> | |
| <span class="code-comment">// Token 1899 ("error") is chosen instead of token 6639 ("call")</span></pre> | |
| </div> | |
| <div class="hint-box"> | |
| <h4>TOKEN ANALYSIS</h4> | |
| <p>After <code><start_function_call></code> (token 48), the model's probability distribution favors token 1899 ("error") over token 6639 ("call") when no example is provided.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Lesson 3: One-Shot --> | |
| <div class="lesson-content" data-lesson="3"> | |
| <h2 class="lesson-title">ONE-SHOT FUNCTION CALLING (PARTIAL SUCCESS)</h2> | |
| <div class="lesson-description"> | |
| <p>One-shot means showing the model ONE example. Let's see if this helps!</p> | |
| <div class="interactive-demo"> | |
| <h3>TEST ONE-SHOT APPROACH</h3> | |
| <input type="text" id="oneShotQuery" value="What's the temperature in Tokyo?" placeholder="Enter your query..."> | |
| <button onclick="testOneShot()">TEST ONE-SHOT</button> | |
| <div id="oneShotOutput" class="output-area" style="margin-top: 15px;"></div> | |
| </div> | |
| <div class="code-block"> | |
| <pre><span class="code-comment">// One-shot approach - ONE example provided</span> | |
| <span class="code-keyword">const</span> messages = [ | |
| { | |
| role: <span class="code-string">"developer"</span>, | |
| content: <span class="code-string">"You are a model that can do function calling..."</span> | |
| }, | |
| { | |
| role: <span class="code-string">"user"</span>, | |
| content: <span class="code-string">"What's the temperature in Paris?"</span> | |
| }, | |
| { | |
| role: <span class="code-string">"assistant"</span>, | |
| <span class="code-comment">// ✅ ONE example showing correct format</span> | |
| content: <span class="code-string">"<start_function_call>call:get_current_temperature{location:<escape>Paris<escape>}<end_function_call>"</span> | |
| }, | |
| { | |
| role: <span class="code-string">"user"</span>, | |
| content: <span class="code-string">"What's the temperature in Tokyo?"</span> | |
| } | |
| ];</pre> | |
| </div> | |
| <div class="info-box"> | |
| <h4>RESULTS MAY VARY</h4> | |
| <p>One-shot can work sometimes, but it's not as reliable as few-shot. The model needs more context to consistently generate correct function calls.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Lesson 4: Few-Shot --> | |
| <div class="lesson-content" data-lesson="4"> | |
| <h2 class="lesson-title">FEW-SHOT FUNCTION CALLING (THE SOLUTION!)</h2> | |
| <div class="lesson-description"> | |
| <p>Few-shot means showing the model multiple examples. This is the proven solution!</p> | |
| <div class="success-box"> | |
| <h4>THE SOLUTION</h4> | |
| <p>By providing a few-shot example, we shift the model's token probabilities. Token 6639 ("call") becomes more likely than token 1899 ("error").</p> | |
| </div> | |
| <div class="interactive-demo"> | |
| <h3>TEST FEW-SHOT APPROACH</h3> | |
| <input type="text" id="fewShotQuery" value="What's the temperature in New York?" placeholder="Enter your query..."> | |
| <button onclick="testFewShot()">TEST FEW-SHOT</button> | |
| <div id="fewShotOutput" class="output-area" style="margin-top: 15px;"></div> | |
| </div> | |
| <div class="code-block"> | |
| <pre><span class="code-comment">// ✅ FEW-SHOT APPROACH (PROVEN TO WORK):</span> | |
| <span class="code-comment">// Add example conversation showing correct format</span> | |
| <span class="code-keyword">const</span> messages = [ | |
| { | |
| role: <span class="code-string">"developer"</span>, | |
| content: <span class="code-string">"You are a model that can do function calling with the following functions"</span> | |
| }, | |
| { | |
| role: <span class="code-string">"user"</span>, | |
| content: <span class="code-string">"What's the temperature in Paris?"</span> | |
| }, | |
| { | |
| role: <span class="code-string">"assistant"</span>, | |
| <span class="code-comment">// ✅ Example showing the EXACT format we want</span> | |
| content: <span class="code-string">"<start_function_call>call:get_current_temperature{location:<escape>Paris<escape>}<end_function_call>"</span> | |
| }, | |
| { | |
| role: <span class="code-string">"user"</span>, | |
| content: query <span class="code-comment">// Your actual query</span> | |
| } | |
| ]; | |
| <span class="code-comment">// Apply chat template with tools</span> | |
| <span class="code-keyword">const</span> inputs = <span class="code-function">await tokenizer</span>.apply_chat_template(messages, { | |
| tools: [weatherFunction], | |
| tokenize: <span class="code-keyword">true</span>, | |
| add_generation_prompt: <span class="code-keyword">true</span>, | |
| return_dict: <span class="code-keyword">true</span> | |
| }); | |
| <span class="code-comment">// Generate response</span> | |
| <span class="code-keyword">const</span> output = <span class="code-function">await model</span>.generate({ | |
| ...inputs, | |
| max_new_tokens: <span class="code-keyword">512</span>, | |
| do_sample: <span class="code-keyword">false</span>, | |
| temperature: <span class="code-keyword">0.0</span> | |
| }); | |
| <span class="code-comment">// ✅ Result: Correct function call generated!</span> | |
| <span class="code-comment">// <start_function_call>call:get_current_temperature{location:<escape>New York<escape>}<end_function_call></span></pre> | |
| </div> | |
| <div class="hint-box"> | |
| <h4>WHY FEW-SHOT WORKS</h4> | |
| <ul style="margin-left: 20px; line-height: 2.5;"> | |
| <li>Shows the model the <strong>exact format</strong> we expect</li> | |
| <li>Shifts token probabilities in favor of "call:" instead of "error:"</li> | |
| <li>Provides context about the task structure</li> | |
| <li>Works consistently with the quantized ONNX model</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Lesson 5: Token Analysis --> | |
| <div class="lesson-content" data-lesson="5"> | |
| <h2 class="lesson-title">TOKEN-LEVEL DEEP DIVE</h2> | |
| <div class="lesson-description"> | |
| <p>Let's examine what happens at the token level when the model generates function calls.</p> | |
| <div class="interactive-demo"> | |
| <h3>TOKEN-LEVEL ANALYSIS</h3> | |
| <button onclick="analyzeTokens()">ANALYZE TOKEN GENERATION</button> | |
| <div id="tokenAnalysisOutput" class="output-area" style="margin-top: 15px;"></div> | |
| <div id="tokenVisualization" class="token-visualization" style="margin-top: 15px;"></div> | |
| </div> | |
| <table class="comparison-table"> | |
| <thead> | |
| <tr> | |
| <th>TOKEN ID</th> | |
| <th>TOKEN TEXT</th> | |
| <th>CONTEXT</th> | |
| <th>PROBABILITY SHIFT</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td>48</td> | |
| <td><start_function_call></td> | |
| <td>Always correct</td> | |
| <td>N/A</td> | |
| </tr> | |
| <tr> | |
| <td>1899</td> | |
| <td>"error"</td> | |
| <td>Zero-shot (no example)</td> | |
| <td>❌ High probability</td> | |
| </tr> | |
| <tr> | |
| <td>6639</td> | |
| <td>"call"</td> | |
| <td>Few-shot (with example)</td> | |
| <td>✅ High probability</td> | |
| </tr> | |
| <tr> | |
| <td>236787</td> | |
| <td>":"</td> | |
| <td>Always correct</td> | |
| <td>N/A</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <div class="code-block"> | |
| <pre><span class="code-comment">// Token-level analysis of generated output</span> | |
| <span class="code-comment">// First 20 generated tokens:</span> | |
| <span class="code-comment">// Token 48: "<start_function_call>" ✅</span> | |
| <span class="code-comment">// Token 6639: "call" ✅ (with few-shot) or Token 1899: "error" ❌ (zero-shot)</span> | |
| <span class="code-comment">// Token 236787: ":" ✅</span> | |
| <span class="code-comment">// Token 828: "get" ✅</span> | |
| <span class="code-comment">// Token 236779: "_" ✅</span> | |
| <span class="code-comment">// Token 4002: "current" ✅</span> | |
| <span class="code-comment">// Token 236779: "_" ✅</span> | |
| <span class="code-comment">// Token 27495: "temperature" ✅</span> | |
| <span class="code-comment">// Token 236782: "{" ✅</span> | |
| <span class="code-comment">// Token 7125: "location" ✅</span> | |
| <span class="code-comment">// Token 236787: ":" ✅</span> | |
| <span class="code-comment">// Token 52: "<escape>" ✅</span> | |
| <span class="code-comment">// Token 27822: "London" ✅</span> | |
| <span class="code-comment">// Token 52: "<escape>" ✅</span> | |
| <span class="code-comment">// Token 236783: "}" ✅</span> | |
| <span class="code-comment">// Token 49: "<end_function_call>" ✅</span> | |
| <span class="code-comment">// The critical decision point is after token 48:</span> | |
| <span class="code-comment">// - Without example: Token 1899 ("error") is more likely</span> | |
| <span class="code-comment">// - With example: Token 6639 ("call") is more likely</span></pre> | |
| </div> | |
| <div class="info-box"> | |
| <h4>HYPOTHESIS</h4> | |
| <p>The model was trained on function calling data that included error handling examples. Without context, it defaults to the error generation pattern. Few-shot examples provide the necessary context to trigger the correct generation path.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Lesson 6: Playground --> | |
| <div class="lesson-content" data-lesson="6"> | |
| <h2 class="lesson-title">INTERACTIVE PLAYGROUND</h2> | |
| <div class="lesson-description"> | |
| <p>Now it's your turn! Experiment with different queries and see how the model responds. Add your own examples to test zero-shot, one-shot, and few-shot approaches.</p> | |
| <div class="playground-grid"> | |
| <div class="playground-section"> | |
| <h3>FUNCTION SCHEMA</h3> | |
| <textarea id="playgroundSchema" rows="15" style="font-family: 'JetBrains Mono', monospace; font-size: 12px;">{ | |
| "type": "function", | |
| "function": { | |
| "name": "get_current_temperature", | |
| "description": "Gets the current temperature for a given location.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "location": { | |
| "type": "string", | |
| "description": "The city name, e.g. San Francisco" | |
| } | |
| }, | |
| "required": ["location"] | |
| } | |
| } | |
| }</textarea> | |
| </div> | |
| <div class="playground-section"> | |
| <h3>SYSTEM MESSAGE</h3> | |
| <textarea id="playgroundSystemMessage" rows="3" style="font-family: 'JetBrains Mono', monospace; font-size: 12px; margin-bottom: 15px;">You are a model that can do function calling with the following functions</textarea> | |
| <h3 style="margin-top: 20px;">YOUR QUERY</h3> | |
| <input type="text" id="playgroundQuery" value="What's the temperature in London?" placeholder="Enter your query..."> | |
| <div class="demo-controls" style="margin-top: 15px;"> | |
| <button onclick="playgroundTest('zero')" class="btn-danger">ZERO-SHOT</button> | |
| <button onclick="playgroundTest('one')">ONE-SHOT</button> | |
| <button onclick="playgroundTest('few')" class="btn-success">FEW-SHOT</button> | |
| </div> | |
| <div style="margin-top: 15px;"> | |
| <label style="display: block; margin-bottom: 10px; font-weight: 700;">MAX TOKENS:</label> | |
| <input type="number" id="maxTokens" value="512" min="50" max="1024" style="width: 100px;"> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="interactive-demo" style="margin-top: 25px;"> | |
| <h3>CUSTOM EXAMPLES</h3> | |
| <p style="margin-bottom: 15px; font-size: 0.9em; color: #888;">Add example conversations to use in one-shot and few-shot modes. Each example should show a user query and the expected assistant response with function call.</p> | |
| <div id="playgroundExamples" style="margin-bottom: 15px;"> | |
| <!-- Examples will be added here dynamically --> | |
| </div> | |
| <button onclick="addPlaygroundExample()" style="margin-top: 10px;">+ ADD EXAMPLE</button> | |
| <div class="info-box" style="margin-top: 15px;"> | |
| <h4>EXAMPLE FORMAT</h4> | |
| <p style="font-family: 'JetBrains Mono', monospace; font-size: 0.85em; margin-top: 10px;"> | |
| User: "What's the temperature in Paris?"<br> | |
| Assistant: "<start_function_call>call:get_current_temperature{location:<escape>Paris<escape>}<end_function_call>" | |
| </p> | |
| <p style="margin-top: 10px; font-size: 0.9em;">For few-shot, add multiple examples. For one-shot, only the first example will be used. For zero-shot, no examples are used.</p> | |
| </div> | |
| </div> | |
| <div class="interactive-demo"> | |
| <h3>OUTPUT</h3> | |
| <div id="playgroundOutput" class="output-area"></div> | |
| </div> | |
| <div class="hint-box"> | |
| <h4>TIPS FOR EXPERIMENTATION</h4> | |
| <ul style="margin-left: 20px; line-height: 2.5;"> | |
| <li>Try different cities and locations</li> | |
| <li>Compare zero-shot vs few-shot results</li> | |
| <li>Modify the function schema and see what happens</li> | |
| <li>Add custom examples to test different scenarios</li> | |
| <li>Watch the token visualization to understand the generation process</li> | |
| <li>Experiment with different max_tokens values</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Lesson 7: Resources --> | |
| <div class="lesson-content" data-lesson="7"> | |
| <h2 class="lesson-title">RESOURCES & LINKS</h2> | |
| <div class="lesson-description"> | |
| <p>Explore these resources to deepen your understanding of FunctionGemma, ONNX, and function calling.</p> | |
| <div class="resource-links"> | |
| <h3>OFFICIAL DOCUMENTATION</h3> | |
| <ul> | |
| <li><a href="https://ai.google.dev/gemma/docs/functiongemma" target="_blank">FunctionGemma Model Overview - Google AI</a></li> | |
| <li><a href="https://ai.google.dev/gemma/docs/capabilities/function-calling" target="_blank">Function Calling with Gemma - Google AI</a></li> | |
| <li><a href="https://ai.google.dev/gemma/docs/functiongemma/full-function-calling-sequence-with-functiongemma" target="_blank">Full Function Calling Sequence - Google AI</a></li> | |
| <li><a href="https://blog.google/technology/developers/functiongemma/" target="_blank">FunctionGemma Blog Post - Google</a></li> | |
| </ul> | |
| </div> | |
| <div class="resource-links"> | |
| <h3>TUTORIALS & GUIDES</h3> | |
| <ul> | |
| <li><a href="https://docs.unsloth.ai/models/functiongemma" target="_blank">FunctionGemma: How to Run & Fine-tune - Unsloth</a></li> | |
| <li><a href="https://huggingface.co/onnx-community/functiongemma-270m-it-ONNX" target="_blank">FunctionGemma ONNX Model - Hugging Face</a></li> | |
| <li><a href="https://huggingface.co/docs/transformers.js" target="_blank">Transformers.js Documentation</a></li> | |
| </ul> | |
| </div> | |
| <div class="resource-links"> | |
| <h3>ONNX & OPTIMIZATION</h3> | |
| <ul> | |
| <li><a href="https://onnx.ai/" target="_blank">ONNX - Open Neural Network Exchange</a></li> | |
| <li><a href="https://onnx.ai/onnx/" target="_blank">ONNX Runtime Documentation</a></li> | |
| <li><a href="https://huggingface.co/docs/optimum/onnxruntime/usage_guides/quantization" target="_blank">ONNX Model Quantization Guide</a></li> | |
| </ul> | |
| </div> | |
| <div class="resource-links"> | |
| <h3>FUNCTION CALLING & AI</h3> | |
| <ul> | |
| <li><a href="https://platform.openai.com/docs/guides/function-calling" target="_blank">OpenAI Function Calling Guide</a></li> | |
| <li><a href="https://ai.google.dev/gemma/docs/functiongemma" target="_blank">Google Function Calling Best Practices</a></li> | |
| <li><a href="https://blog.google/technology/developers/functiongemma/" target="_blank">FunctionGemma Physics Playground Demo</a></li> | |
| </ul> | |
| </div> | |
| <div class="info-box"> | |
| <h4>KEY RESOURCES SUMMARY</h4> | |
| <p><strong>FunctionGemma</strong> is a specialized 270M parameter model fine-tuned from Google's Gemma 3 for function calling tasks. It's optimized for edge deployment and requires few-shot examples for reliable function call generation.</p> | |
| <p><strong>ONNX</strong> (Open Neural Network Exchange) is an open format for representing machine learning models, enabling interoperability between different frameworks and optimized inference across platforms.</p> | |
| <p>The model is available in quantized formats (q4 for WebGPU, q8 for WASM) to enable efficient browser-based inference.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script type="module"> | |
| // Global state | |
| let model = null; | |
| let tokenizer = null; | |
| let currentLesson = 0; | |
| let completedLessons = new Set(); | |
| let achievements = new Set(); | |
| // Initialize | |
| document.addEventListener('DOMContentLoaded', () => { | |
| setupLessonNavigation(); | |
| updateProgress(); | |
| initializePlayground(); // Initialize playground with default example | |
| loadModel(); // Auto-load model | |
| }); | |
| function setupLessonNavigation() { | |
| document.querySelectorAll('.lesson-item').forEach(item => { | |
| item.addEventListener('click', () => { | |
| const lessonNum = parseInt(item.dataset.lesson); | |
| switchLesson(lessonNum); | |
| }); | |
| }); | |
| } | |
| function switchLesson(lessonNum) { | |
| // Update active lesson | |
| document.querySelectorAll('.lesson-item').forEach(item => { | |
| item.classList.remove('active'); | |
| if (parseInt(item.dataset.lesson) === lessonNum) { | |
| item.classList.add('active'); | |
| } | |
| }); | |
| // Update content | |
| document.querySelectorAll('.lesson-content').forEach(content => { | |
| content.classList.remove('active'); | |
| if (parseInt(content.dataset.lesson) === lessonNum) { | |
| content.classList.add('active'); | |
| } | |
| }); | |
| currentLesson = lessonNum; | |
| // Initialize playground when switching to lesson 6 | |
| if (lessonNum === 6) { | |
| setTimeout(() => initializePlayground(), 100); | |
| } | |
| } | |
| function completeLesson(lessonNum) { | |
| completedLessons.add(lessonNum); | |
| document.querySelectorAll('.lesson-item').forEach(item => { | |
| if (parseInt(item.dataset.lesson) === lessonNum) { | |
| item.classList.add('completed'); | |
| } | |
| }); | |
| updateProgress(); | |
| } | |
| function addAchievement(text) { | |
| achievements.add(text); | |
| const achievementsDiv = document.getElementById('achievements'); | |
| achievementsDiv.innerHTML = Array.from(achievements).map(a => | |
| `<span class="achievement-badge">${a}</span>` | |
| ).join(''); | |
| } | |
| function updateProgress() { | |
| const total = 8; | |
| const completed = completedLessons.size; | |
| const percentage = Math.round((completed / total) * 100); | |
| document.getElementById('progressBar').style.width = percentage + '%'; | |
| document.getElementById('progressBar').textContent = `${percentage}% COMPLETE`; | |
| } | |
| function updateLoadingStep(stepId, status) { | |
| const step = document.getElementById(stepId); | |
| if (!step) return; | |
| step.classList.remove('active', 'completed'); | |
| if (status === 'active') { | |
| step.classList.add('active'); | |
| } else if (status === 'completed') { | |
| step.classList.add('completed'); | |
| } | |
| } | |
| function log(message, type = 'info', targetId = null) { | |
| const colors = { | |
| error: '#FF0000', | |
| success: '#00FF00', | |
| log: '#FFFF00', | |
| info: '#00FFFF', | |
| warning: '#FF6B6B' | |
| }; | |
| const icon = { | |
| error: '❌', | |
| success: '✅', | |
| log: '📝', | |
| info: 'ℹ️', | |
| warning: '⚠️' | |
| }; | |
| const output = targetId ? document.getElementById(targetId) : null; | |
| if (output) { | |
| const timestamp = new Date().toLocaleTimeString(); | |
| const div = document.createElement('div'); | |
| div.style.color = colors[type] || colors.info; | |
| div.style.marginBottom = '8px'; | |
| div.style.fontFamily = "'JetBrains Mono', monospace"; | |
| div.textContent = `[${timestamp}] ${icon[type] || ''} ${message}`; | |
| output.appendChild(div); | |
| output.scrollTop = output.scrollHeight; | |
| } | |
| console.log(`[${type.toUpperCase()}]`, message); | |
| } | |
| async function loadModel() { | |
| const overlay = document.getElementById('loadingOverlay'); | |
| const statusText = document.getElementById('modelStatusText'); | |
| const modelStatus = document.getElementById('modelStatus'); | |
| const modelInfoGrid = document.getElementById('modelInfoGrid'); | |
| try { | |
| updateLoadingStep('step1', 'active'); | |
| log('🚀 Starting model load...', 'log'); | |
| await new Promise(resolve => setTimeout(resolve, 500)); | |
| log('📦 Importing transformers.js...', 'log'); | |
| const { env, AutoTokenizer, AutoModelForCausalLM } = await import( | |
| "https://cdn.jsdelivr.net/npm/@huggingface/transformers@latest" | |
| ); | |
| env.allowRemoteModels = true; | |
| env.allowLocalModels = false; | |
| env.useBrowserCache = true; | |
| updateLoadingStep('step1', 'completed'); | |
| updateLoadingStep('step2', 'active'); | |
| await new Promise(resolve => setTimeout(resolve, 300)); | |
| const modelId = "onnx-community/functiongemma-270m-it-ONNX"; | |
| log('🔤 Loading tokenizer...', 'log'); | |
| tokenizer = await AutoTokenizer.from_pretrained(modelId); | |
| log('✅ Tokenizer loaded', 'success'); | |
| updateLoadingStep('step2', 'completed'); | |
| updateLoadingStep('step3', 'active'); | |
| await new Promise(resolve => setTimeout(resolve, 300)); | |
| log('🤖 Detecting device capabilities...', 'log'); | |
| const hasWebGPU = !!navigator.gpu; | |
| const modelConfig = hasWebGPU | |
| ? { dtype: "q4", device: "webgpu" } | |
| : { dtype: "q8", device: "wasm" }; | |
| document.getElementById('modelFormat').textContent = hasWebGPU ? 'q4 (WebGPU)' : 'q8 (WASM)'; | |
| document.getElementById('modelDevice').textContent = hasWebGPU ? 'WebGPU' : 'WASM'; | |
| modelInfoGrid.style.display = 'grid'; | |
| log(`⚙️ Using config: ${JSON.stringify(modelConfig)}`, 'info'); | |
| updateLoadingStep('step3', 'completed'); | |
| updateLoadingStep('step4', 'active'); | |
| await new Promise(resolve => setTimeout(resolve, 300)); | |
| log('🤖 Loading model...', 'log'); | |
| model = await AutoModelForCausalLM.from_pretrained(modelId, modelConfig); | |
| log('✅ Model loaded successfully!', 'success'); | |
| updateLoadingStep('step4', 'completed'); | |
| updateLoadingStep('step5', 'active'); | |
| await new Promise(resolve => setTimeout(resolve, 500)); | |
| updateLoadingStep('step5', 'completed'); | |
| statusText.textContent = 'READY'; | |
| modelStatus.classList.add('loaded'); | |
| addAchievement('🎯 MODEL LOADED'); | |
| completeLesson(0); | |
| // Hide overlay after a brief delay | |
| setTimeout(() => { | |
| overlay.classList.add('hidden'); | |
| }, 1000); | |
| } catch (error) { | |
| log(`❌ Error loading model: ${error.message}`, 'error'); | |
| statusText.textContent = 'ERROR'; | |
| console.error(error); | |
| overlay.innerHTML = ` | |
| <div class="loading-content"> | |
| <h2 class="loading-title" style="color: #FF0000;">LOADING FAILED</h2> | |
| <p style="color: #FFFFFF; font-family: 'JetBrains Mono', monospace; margin-top: 20px;">${error.message}</p> | |
| <button onclick="location.reload()" style="margin-top: 30px;">RETRY</button> | |
| </div> | |
| `; | |
| } | |
| } | |
| async function demonstrateTokenization() { | |
| if (!tokenizer) { | |
| alert('Model not loaded yet!'); | |
| return; | |
| } | |
| const input = document.getElementById('tokenizeInput').value; | |
| const output = document.getElementById('tokenizationOutput'); | |
| output.innerHTML = ''; | |
| log(`Tokenizing: "${input}"`, 'info', 'tokenizationOutput'); | |
| try { | |
| const tokens = await tokenizer.encode(input, { return_tensors: false }); | |
| log(`Token IDs: [${tokens.join(', ')}]`, 'info', 'tokenizationOutput'); | |
| log(`Total tokens: ${tokens.length}`, 'info', 'tokenizationOutput'); | |
| // Visualize tokens | |
| const viz = document.createElement('div'); | |
| viz.className = 'token-visualization'; | |
| viz.style.marginTop = '15px'; | |
| for (let i = 0; i < Math.min(tokens.length, 20); i++) { | |
| const tokenId = tokens[i]; | |
| const tokenText = await tokenizer.decode([tokenId], { skip_special_tokens: false }); | |
| const tokenBox = document.createElement('div'); | |
| tokenBox.className = 'token-box'; | |
| tokenBox.innerHTML = ` | |
| <div class="token-id">ID: ${tokenId}</div> | |
| <div class="token-text">${tokenText.replace(/</g, '<').replace(/>/g, '>')}</div> | |
| `; | |
| viz.appendChild(tokenBox); | |
| } | |
| output.appendChild(viz); | |
| addAchievement('🔤 TOKEN MASTER'); | |
| } catch (error) { | |
| log(`Error: ${error.message}`, 'error', 'tokenizationOutput'); | |
| } | |
| } | |
| async function testZeroShot() { | |
| if (!model || !tokenizer) { | |
| alert('Model not loaded yet!'); | |
| return; | |
| } | |
| const query = document.getElementById('zeroShotQuery').value; | |
| const output = document.getElementById('zeroShotOutput'); | |
| output.innerHTML = ''; | |
| log('🧪 Testing Zero-Shot Approach...', 'log', 'zeroShotOutput'); | |
| log(`Query: "${query}"`, 'info', 'zeroShotOutput'); | |
| try { | |
| const weatherFunction = { | |
| type: "function", | |
| function: { | |
| name: "get_current_temperature", | |
| description: "Gets the current temperature for a given location.", | |
| parameters: { | |
| type: "object", | |
| properties: { | |
| location: { | |
| type: "string", | |
| description: "The city name, e.g. San Francisco", | |
| }, | |
| }, | |
| required: ["location"], | |
| }, | |
| }, | |
| }; | |
| const messages = [ | |
| { | |
| role: "developer", | |
| content: "You are a model that can do function calling with the following functions" | |
| }, | |
| { | |
| role: "user", | |
| content: query | |
| } | |
| ]; | |
| log('❌ No example provided to the model', 'warning', 'zeroShotOutput'); | |
| const inputs = await tokenizer.apply_chat_template(messages, { | |
| tools: [weatherFunction], | |
| tokenize: true, | |
| add_generation_prompt: true, | |
| return_dict: true | |
| }); | |
| const output_tensor = await model.generate({ | |
| ...inputs, | |
| max_new_tokens: 512, | |
| do_sample: false, | |
| temperature: 0.0 | |
| }); | |
| const seqLen = inputs.input_ids.dims[1]; | |
| const generated = output_tensor.slice(0, [seqLen, null]); | |
| const decoded = await tokenizer.decode(generated, { skip_special_tokens: false }); | |
| log('📤 Generated output:', 'info', 'zeroShotOutput'); | |
| log(decoded, 'log', 'zeroShotOutput'); | |
| if (decoded.includes('error:')) { | |
| log('❌ Model generated "error:" instead of "call:"', 'error', 'zeroShotOutput'); | |
| log('💡 This is why zero-shot fails!', 'info', 'zeroShotOutput'); | |
| } else if (decoded.includes('call:')) { | |
| log('✅ Unexpected success! (This is rare)', 'success', 'zeroShotOutput'); | |
| } | |
| completeLesson(2); | |
| } catch (error) { | |
| log(`Error: ${error.message}`, 'error', 'zeroShotOutput'); | |
| } | |
| } | |
| async function testOneShot() { | |
| if (!model || !tokenizer) { | |
| alert('Model not loaded yet!'); | |
| return; | |
| } | |
| const query = document.getElementById('oneShotQuery').value; | |
| const output = document.getElementById('oneShotOutput'); | |
| output.innerHTML = ''; | |
| log('🧪 Testing One-Shot Approach...', 'log', 'oneShotOutput'); | |
| log(`Query: "${query}"`, 'info', 'oneShotOutput'); | |
| try { | |
| const weatherFunction = { | |
| type: "function", | |
| function: { | |
| name: "get_current_temperature", | |
| description: "Gets the current temperature for a given location.", | |
| parameters: { | |
| type: "object", | |
| properties: { | |
| location: { | |
| type: "string", | |
| description: "The city name, e.g. San Francisco", | |
| }, | |
| }, | |
| required: ["location"], | |
| }, | |
| }, | |
| }; | |
| // One-shot: ONE example | |
| const messages = [ | |
| { | |
| role: "developer", | |
| content: "You are a model that can do function calling with the following functions" | |
| }, | |
| { | |
| role: "user", | |
| content: "What's the temperature in Paris?" | |
| }, | |
| { | |
| role: "assistant", | |
| content: "<start_function_call>call:get_current_temperature{location:<escape>Paris<escape>}<end_function_call>" | |
| }, | |
| { | |
| role: "user", | |
| content: query | |
| } | |
| ]; | |
| log('⚠️ One example provided', 'info', 'oneShotOutput'); | |
| const inputs = await tokenizer.apply_chat_template(messages, { | |
| tools: [weatherFunction], | |
| tokenize: true, | |
| add_generation_prompt: true, | |
| return_dict: true | |
| }); | |
| const output_tensor = await model.generate({ | |
| ...inputs, | |
| max_new_tokens: 512, | |
| do_sample: false, | |
| temperature: 0.0 | |
| }); | |
| const seqLen = inputs.input_ids.dims[1]; | |
| const generated = output_tensor.slice(0, [seqLen, null]); | |
| const decoded = await tokenizer.decode(generated, { skip_special_tokens: false }); | |
| log('📤 Generated output:', 'info', 'oneShotOutput'); | |
| log(decoded, 'log', 'oneShotOutput'); | |
| if (decoded.includes('call:')) { | |
| log('✅ Success! One-shot worked', 'success', 'oneShotOutput'); | |
| } else { | |
| log('⚠️ One-shot may not always work reliably', 'warning', 'oneShotOutput'); | |
| } | |
| completeLesson(3); | |
| } catch (error) { | |
| log(`Error: ${error.message}`, 'error', 'oneShotOutput'); | |
| } | |
| } | |
| async function testFewShot() { | |
| if (!model || !tokenizer) { | |
| alert('Model not loaded yet!'); | |
| return; | |
| } | |
| const query = document.getElementById('fewShotQuery').value; | |
| const output = document.getElementById('fewShotOutput'); | |
| output.innerHTML = ''; | |
| log('🧪 Testing Few-Shot Approach...', 'log', 'fewShotOutput'); | |
| log(`Query: "${query}"`, 'info', 'fewShotOutput'); | |
| try { | |
| const weatherFunction = { | |
| type: "function", | |
| function: { | |
| name: "get_current_temperature", | |
| description: "Gets the current temperature for a given location.", | |
| parameters: { | |
| type: "object", | |
| properties: { | |
| location: { | |
| type: "string", | |
| description: "The city name, e.g. San Francisco", | |
| }, | |
| }, | |
| required: ["location"], | |
| }, | |
| }, | |
| }; | |
| // Few-shot: Example provided | |
| const messages = [ | |
| { | |
| role: "developer", | |
| content: "You are a model that can do function calling with the following functions" | |
| }, | |
| { | |
| role: "user", | |
| content: "What's the temperature in Paris?" | |
| }, | |
| { | |
| role: "assistant", | |
| content: "<start_function_call>call:get_current_temperature{location:<escape>Paris<escape>}<end_function_call>" | |
| }, | |
| { | |
| role: "user", | |
| content: query | |
| } | |
| ]; | |
| log('✅ Few-shot example provided', 'success', 'fewShotOutput'); | |
| const inputs = await tokenizer.apply_chat_template(messages, { | |
| tools: [weatherFunction], | |
| tokenize: true, | |
| add_generation_prompt: true, | |
| return_dict: true | |
| }); | |
| const output_tensor = await model.generate({ | |
| ...inputs, | |
| max_new_tokens: 512, | |
| do_sample: false, | |
| temperature: 0.0 | |
| }); | |
| const seqLen = inputs.input_ids.dims[1]; | |
| const generated = output_tensor.slice(0, [seqLen, null]); | |
| const decoded = await tokenizer.decode(generated, { skip_special_tokens: false }); | |
| log('📤 Generated output:', 'info', 'fewShotOutput'); | |
| log(decoded, 'log', 'fewShotOutput'); | |
| if (decoded.includes('call:')) { | |
| log('✅ SUCCESS! Few-shot works perfectly!', 'success', 'fewShotOutput'); | |
| addAchievement('🎯 FEW-SHOT MASTER'); | |
| } | |
| completeLesson(4); | |
| } catch (error) { | |
| log(`Error: ${error.message}`, 'error', 'fewShotOutput'); | |
| } | |
| } | |
| async function analyzeTokens() { | |
| if (!model || !tokenizer) { | |
| alert('Model not loaded yet!'); | |
| return; | |
| } | |
| const output = document.getElementById('tokenAnalysisOutput'); | |
| const viz = document.getElementById('tokenVisualization'); | |
| output.innerHTML = ''; | |
| viz.innerHTML = ''; | |
| log('🔍 Analyzing token generation...', 'log', 'tokenAnalysisOutput'); | |
| try { | |
| const weatherFunction = { | |
| type: "function", | |
| function: { | |
| name: "get_current_temperature", | |
| description: "Gets the current temperature for a given location.", | |
| parameters: { | |
| type: "object", | |
| properties: { | |
| location: { | |
| type: "string", | |
| description: "The city name, e.g. San Francisco", | |
| }, | |
| }, | |
| required: ["location"], | |
| }, | |
| }, | |
| }; | |
| const messages = [ | |
| { | |
| role: "developer", | |
| content: "You are a model that can do function calling with the following functions" | |
| }, | |
| { | |
| role: "user", | |
| content: "What's the temperature in Paris?" | |
| }, | |
| { | |
| role: "assistant", | |
| content: "<start_function_call>call:get_current_temperature{location:<escape>Paris<escape>}<end_function_call>" | |
| }, | |
| { | |
| role: "user", | |
| content: "What's the temperature in London?" | |
| } | |
| ]; | |
| const inputs = await tokenizer.apply_chat_template(messages, { | |
| tools: [weatherFunction], | |
| tokenize: true, | |
| add_generation_prompt: true, | |
| return_dict: true | |
| }); | |
| const output_tensor = await model.generate({ | |
| ...inputs, | |
| max_new_tokens: 512, | |
| do_sample: false, | |
| temperature: 0.0 | |
| }); | |
| const seqLen = inputs.input_ids.dims[1]; | |
| const generated = output_tensor.slice(0, [seqLen, null]); | |
| const generatedTokens = Array.from(generated.data.slice(0, 20)); | |
| log('🔢 First 20 generated token IDs:', 'info', 'tokenAnalysisOutput'); | |
| log(`[${generatedTokens.join(', ')}]`, 'log', 'tokenAnalysisOutput'); | |
| log('🔤 Decoding tokens individually:', 'info', 'tokenAnalysisOutput'); | |
| for (let i = 0; i < generatedTokens.length; i++) { | |
| const tokenId = generatedTokens[i]; | |
| const tokenText = await tokenizer.decode([tokenId], { skip_special_tokens: false }); | |
| log(`Token ${tokenId}: "${tokenText}"`, 'info', 'tokenAnalysisOutput'); | |
| const tokenBox = document.createElement('div'); | |
| tokenBox.className = 'token-box'; | |
| tokenBox.innerHTML = ` | |
| <div class="token-id">ID: ${tokenId}</div> | |
| <div class="token-text">${tokenText.replace(/</g, '<').replace(/>/g, '>')}</div> | |
| `; | |
| viz.appendChild(tokenBox); | |
| } | |
| log('✅ Token analysis complete!', 'success', 'tokenAnalysisOutput'); | |
| addAchievement('🔍 TOKEN ANALYST'); | |
| completeLesson(5); | |
| } catch (error) { | |
| log(`Error: ${error.message}`, 'error', 'tokenAnalysisOutput'); | |
| } | |
| } | |
| function addPlaygroundExample() { | |
| const examplesContainer = document.getElementById('playgroundExamples'); | |
| const exampleIndex = examplesContainer.children.length; | |
| const exampleDiv = document.createElement('div'); | |
| exampleDiv.className = 'example-item'; | |
| exampleDiv.dataset.index = exampleIndex; | |
| const defaultUserQuery = exampleIndex === 0 ? 'What\'s the temperature in Paris?' : ''; | |
| const defaultAssistantResponse = exampleIndex === 0 ? '<start_function_call>call:get_current_temperature{location:<escape>Paris<escape>}<end_function_call>' : ''; | |
| exampleDiv.innerHTML = ` | |
| <div class="example-item-header"> | |
| <h4>EXAMPLE ${exampleIndex + 1}</h4> | |
| <button class="remove-example-btn" data-remove-index="${exampleIndex}">REMOVE</button> | |
| </div> | |
| <label style="display: block; margin-bottom: 5px; font-weight: 700; font-size: 0.9em;">USER QUERY:</label> | |
| <input type="text" class="example-user-query" placeholder="Enter user query..." value="${defaultUserQuery}"> | |
| <label style="display: block; margin-bottom: 5px; margin-top: 10px; font-weight: 700; font-size: 0.9em;">ASSISTANT RESPONSE (with function call):</label> | |
| <textarea class="example-assistant-response" placeholder="Enter assistant response with function call..." rows="2">${defaultAssistantResponse}</textarea> | |
| `; | |
| // Attach event listener to remove button | |
| const removeBtn = exampleDiv.querySelector('.remove-example-btn'); | |
| removeBtn.addEventListener('click', () => { | |
| removePlaygroundExample(exampleIndex); | |
| }); | |
| examplesContainer.appendChild(exampleDiv); | |
| } | |
| function removePlaygroundExample(index) { | |
| const examplesContainer = document.getElementById('playgroundExamples'); | |
| const exampleItem = examplesContainer.querySelector(`[data-index="${index}"]`); | |
| if (exampleItem) { | |
| exampleItem.remove(); | |
| // Reindex remaining examples | |
| Array.from(examplesContainer.children).forEach((item, idx) => { | |
| item.dataset.index = idx; | |
| item.querySelector('h4').textContent = `EXAMPLE ${idx + 1}`; | |
| const removeBtn = item.querySelector('.remove-example-btn'); | |
| // Remove old listener and add new one | |
| const newRemoveBtn = removeBtn.cloneNode(true); | |
| removeBtn.parentNode.replaceChild(newRemoveBtn, removeBtn); | |
| newRemoveBtn.addEventListener('click', () => { | |
| removePlaygroundExample(idx); | |
| }); | |
| }); | |
| } | |
| } | |
| function getPlaygroundExamples() { | |
| const examplesContainer = document.getElementById('playgroundExamples'); | |
| const examples = []; | |
| Array.from(examplesContainer.children).forEach(item => { | |
| const userQuery = item.querySelector('.example-user-query').value.trim(); | |
| const assistantResponse = item.querySelector('.example-assistant-response').value.trim(); | |
| if (userQuery && assistantResponse) { | |
| examples.push({ | |
| user: userQuery, | |
| assistant: assistantResponse | |
| }); | |
| } | |
| }); | |
| return examples; | |
| } | |
| async function playgroundTest(mode) { | |
| if (!model || !tokenizer) { | |
| alert('Model not loaded yet!'); | |
| return; | |
| } | |
| const query = document.getElementById('playgroundQuery').value; | |
| const schemaText = document.getElementById('playgroundSchema').value; | |
| const systemMessage = document.getElementById('playgroundSystemMessage').value.trim() || "You are a model that can do function calling with the following functions"; | |
| const maxTokens = parseInt(document.getElementById('maxTokens')?.value || 512); | |
| const output = document.getElementById('playgroundOutput'); | |
| output.innerHTML = ''; | |
| let weatherFunction; | |
| try { | |
| weatherFunction = JSON.parse(schemaText); | |
| } catch (e) { | |
| log('❌ Invalid JSON schema', 'error', 'playgroundOutput'); | |
| return; | |
| } | |
| // Get custom examples | |
| const customExamples = getPlaygroundExamples(); | |
| log(`🧪 Testing ${mode === 'zero' ? 'Zero-Shot' : mode === 'one' ? 'One-Shot' : 'Few-Shot'} approach...`, 'log', 'playgroundOutput'); | |
| log(`Query: "${query}"`, 'info', 'playgroundOutput'); | |
| log(`Max Tokens: ${maxTokens}`, 'info', 'playgroundOutput'); | |
| log(`Custom Examples: ${customExamples.length}`, 'info', 'playgroundOutput'); | |
| try { | |
| let messages = [ | |
| { | |
| role: "developer", | |
| content: systemMessage | |
| } | |
| ]; | |
| if (mode === 'zero') { | |
| // Zero-shot: No examples | |
| messages.push({ | |
| role: "user", | |
| content: query | |
| }); | |
| } else if (mode === 'one') { | |
| // One-shot: Use first example if available, otherwise use default | |
| if (customExamples.length > 0) { | |
| messages.push({ | |
| role: "user", | |
| content: customExamples[0].user | |
| }); | |
| messages.push({ | |
| role: "assistant", | |
| content: customExamples[0].assistant | |
| }); | |
| } else { | |
| // Fallback to default example | |
| messages.push({ | |
| role: "user", | |
| content: "What's the temperature in Paris?" | |
| }); | |
| messages.push({ | |
| role: "assistant", | |
| content: `<start_function_call>call:${weatherFunction.function.name}{location:<escape>Paris<escape>}<end_function_call>` | |
| }); | |
| } | |
| messages.push({ | |
| role: "user", | |
| content: query | |
| }); | |
| } else { | |
| // Few-shot: Use all available examples | |
| if (customExamples.length > 0) { | |
| customExamples.forEach(example => { | |
| messages.push({ | |
| role: "user", | |
| content: example.user | |
| }); | |
| messages.push({ | |
| role: "assistant", | |
| content: example.assistant | |
| }); | |
| }); | |
| } else { | |
| // Fallback to default example | |
| messages.push({ | |
| role: "user", | |
| content: "What's the temperature in Paris?" | |
| }); | |
| messages.push({ | |
| role: "assistant", | |
| content: `<start_function_call>call:${weatherFunction.function.name}{location:<escape>Paris<escape>}<end_function_call>` | |
| }); | |
| } | |
| messages.push({ | |
| role: "user", | |
| content: query | |
| }); | |
| } | |
| log(`📝 Message count: ${messages.length}`, 'info', 'playgroundOutput'); | |
| const inputs = await tokenizer.apply_chat_template(messages, { | |
| tools: [weatherFunction], | |
| tokenize: true, | |
| add_generation_prompt: true, | |
| return_dict: true | |
| }); | |
| const output_tensor = await model.generate({ | |
| ...inputs, | |
| max_new_tokens: maxTokens, | |
| do_sample: false, | |
| temperature: 0.0 | |
| }); | |
| const seqLen = inputs.input_ids.dims[1]; | |
| const generated = output_tensor.slice(0, [seqLen, null]); | |
| const decoded = await tokenizer.decode(generated, { skip_special_tokens: false }); | |
| log('📤 Generated output:', 'info', 'playgroundOutput'); | |
| log(decoded, 'log', 'playgroundOutput'); | |
| if (decoded.includes('call:')) { | |
| log('✅ Function call generated successfully!', 'success', 'playgroundOutput'); | |
| } else if (decoded.includes('error:')) { | |
| log('❌ Model generated error instead of call', 'error', 'playgroundOutput'); | |
| } | |
| completeLesson(6); | |
| addAchievement('🎮 PLAYGROUND EXPLORER'); | |
| } catch (error) { | |
| log(`Error: ${error.message}`, 'error', 'playgroundOutput'); | |
| console.error(error); | |
| } | |
| } | |
| // Initialize playground with default example | |
| function initializePlayground() { | |
| const examplesContainer = document.getElementById('playgroundExamples'); | |
| if (examplesContainer && examplesContainer.children.length === 0) { | |
| addPlaygroundExample(); | |
| } | |
| } | |
| // Make functions available globally | |
| window.loadModel = loadModel; | |
| window.demonstrateTokenization = demonstrateTokenization; | |
| window.testZeroShot = testZeroShot; | |
| window.testOneShot = testOneShot; | |
| window.testFewShot = testFewShot; | |
| window.analyzeTokens = analyzeTokens; | |
| window.playgroundTest = playgroundTest; | |
| window.addPlaygroundExample = addPlaygroundExample; | |
| window.removePlaygroundExample = removePlaygroundExample; | |
| </script> | |
| </body> | |
| </html> |