| | <!DOCTYPE html> |
| | <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"> |
| |
|
| | |
| | <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> |
| | |
| | <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"> |
| | |
| | <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> |
| |
|
| | |
| | <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> |
| |
|
| | |
| | <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> |
| |
|
| | |
| | <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> |
| |
|
| | |
| | <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> |
| |
|
| | |
| | <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> |
| |
|
| | |
| | <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;"> |
| | |
| | </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> |
| |
|
| | |
| | <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"> |
| | |
| | let model = null; |
| | let tokenizer = null; |
| | let currentLesson = 0; |
| | let completedLessons = new Set(); |
| | let achievements = new Set(); |
| | |
| | |
| | document.addEventListener('DOMContentLoaded', () => { |
| | setupLessonNavigation(); |
| | updateProgress(); |
| | initializePlayground(); |
| | loadModel(); |
| | }); |
| | |
| | function setupLessonNavigation() { |
| | document.querySelectorAll('.lesson-item').forEach(item => { |
| | item.addEventListener('click', () => { |
| | const lessonNum = parseInt(item.dataset.lesson); |
| | switchLesson(lessonNum); |
| | }); |
| | }); |
| | } |
| | |
| | function switchLesson(lessonNum) { |
| | |
| | document.querySelectorAll('.lesson-item').forEach(item => { |
| | item.classList.remove('active'); |
| | if (parseInt(item.dataset.lesson) === lessonNum) { |
| | item.classList.add('active'); |
| | } |
| | }); |
| | |
| | |
| | document.querySelectorAll('.lesson-content').forEach(content => { |
| | content.classList.remove('active'); |
| | if (parseInt(content.dataset.lesson) === lessonNum) { |
| | content.classList.add('active'); |
| | } |
| | }); |
| | |
| | currentLesson = lessonNum; |
| | |
| | |
| | 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); |
| | |
| | |
| | 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'); |
| | |
| | |
| | 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"], |
| | }, |
| | }, |
| | }; |
| | |
| | |
| | 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"], |
| | }, |
| | }, |
| | }; |
| | |
| | |
| | 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> |
| | `; |
| | |
| | |
| | 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(); |
| | |
| | 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'); |
| | |
| | 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; |
| | } |
| | |
| | |
| | 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') { |
| | |
| | messages.push({ |
| | role: "user", |
| | content: query |
| | }); |
| | } else if (mode === 'one') { |
| | |
| | if (customExamples.length > 0) { |
| | messages.push({ |
| | role: "user", |
| | content: customExamples[0].user |
| | }); |
| | messages.push({ |
| | role: "assistant", |
| | content: customExamples[0].assistant |
| | }); |
| | } else { |
| | |
| | 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 { |
| | |
| | if (customExamples.length > 0) { |
| | customExamples.forEach(example => { |
| | messages.push({ |
| | role: "user", |
| | content: example.user |
| | }); |
| | messages.push({ |
| | role: "assistant", |
| | content: example.assistant |
| | }); |
| | }); |
| | } else { |
| | |
| | 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); |
| | } |
| | } |
| | |
| | |
| | function initializePlayground() { |
| | const examplesContainer = document.getElementById('playgroundExamples'); |
| | if (examplesContainer && examplesContainer.children.length === 0) { |
| | addPlaygroundExample(); |
| | } |
| | } |
| | |
| | |
| | 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> |