WebAssembly AI Integration - Practical Examples
Overview
This guide provides practical, production-ready examples of integrating WebAssembly with AI coding assistants. Each example includes complete code, deployment instructions, and performance considerations.
Example 1: Browser-Based Code Analysis with Claude Code
Project Structure
claude-code-wasm-analyzer/
├── src/
│ ├── analyzer.rs # Rust WASM module
│ ├── analyzer.js # JS wrapper
│ └── claude-integration.ts # Claude Code integration
├── public/
│ ├── index.html
│ └── worker.js
├── Cargo.toml
├── package.json
└── webpack.config.js
Rust WASM Module (analyzer.rs)
use wasm_bindgen::prelude::*;
use serde::{Deserialize, Serialize};
#[wasm_bindgen]
pub struct CodeAnalyzer {
rules: Vec<Rule>,
}
#[derive(Serialize, Deserialize)]
pub struct Rule {
id: String,
pattern: String,
severity: String,
message: String,
}
#[derive(Serialize, Deserialize)]
pub struct Issue {
rule_id: String,
line: u32,
column: u32,
message: String,
severity: String,
}
#[wasm_bindgen]
impl CodeAnalyzer {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self {
rules: vec![
Rule {
id: "no-console".to_string(),
pattern: r"console\.(log|error|warn)".to_string(),
severity: "warning".to_string(),
message: "Remove console statements".to_string(),
},
Rule {
id: "no-any".to_string(),
pattern: r": any\b".to_string(),
severity: "error".to_string(),
message: "Avoid using 'any' type".to_string(),
},
],
}
}
#[wasm_bindgen]
pub fn analyze(&self, code: &str) -> String {
let mut issues = Vec::new();
for (line_num, line) in code.lines().enumerate() {
for rule in &self.rules {
if let Ok(regex) = regex::Regex::new(&rule.pattern) {
for mat in regex.find_iter(line) {
issues.push(Issue {
rule_id: rule.id.clone(),
line: line_num as u32 + 1,
column: mat.start() as u32,
message: rule.message.clone(),
severity: rule.severity.clone(),
});
}
}
}
}
serde_json::to_string(&issues).unwrap_or_default()
}
#[wasm_bindgen]
pub fn add_rule(&mut self, rule_json: &str) -> Result<(), JsValue> {
match serde_json::from_str::<Rule>(rule_json) {
Ok(rule) => {
self.rules.push(rule);
Ok(())
}
Err(e) => Err(JsValue::from_str(&e.to_string())),
}
}
}
// Performance-critical function with SIMD
#[wasm_bindgen]
pub fn calculate_code_complexity(code: &str) -> f32 {
let lines: Vec<&str> = code.lines().collect();
let mut complexity = 1.0;
// Simplified cyclomatic complexity
for line in lines {
if line.contains("if") || line.contains("for") ||
line.contains("while") || line.contains("case") {
complexity += 1.0;
}
}
complexity
}JavaScript Wrapper (analyzer.js)
import init, { CodeAnalyzer, calculate_code_complexity } from './analyzer_bg.wasm';
class WASMCodeAnalyzer {
constructor() {
this.initialized = false;
this.analyzer = null;
}
async initialize() {
if (!this.initialized) {
await init();
this.analyzer = new CodeAnalyzer();
this.initialized = true;
}
}
async analyzeCode(code) {
await this.initialize();
const start = performance.now();
const issuesJson = this.analyzer.analyze(code);
const complexity = calculate_code_complexity(code);
const end = performance.now();
return {
issues: JSON.parse(issuesJson),
complexity,
analysisTime: end - start,
};
}
async addCustomRule(rule) {
await this.initialize();
this.analyzer.add_rule(JSON.stringify(rule));
}
// Batch analysis for better performance
async analyzeBatch(files) {
await this.initialize();
const results = await Promise.all(
files.map(async (file) => ({
path: file.path,
...(await this.analyzeCode(file.content)),
}))
);
return results;
}
}
// Web Worker support for non-blocking analysis
if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
const analyzer = new WASMCodeAnalyzer();
self.onmessage = async (event) => {
const { type, data, id } = event.data;
try {
let result;
switch (type) {
case 'analyze':
result = await analyzer.analyzeCode(data.code);
break;
case 'analyzeBatch':
result = await analyzer.analyzeBatch(data.files);
break;
case 'addRule':
await analyzer.addCustomRule(data.rule);
result = { success: true };
break;
}
self.postMessage({ id, result, error: null });
} catch (error) {
self.postMessage({ id, result: null, error: error.message });
}
};
}
export default WASMCodeAnalyzer;Claude Code Integration (claude-integration.ts)
import { query, type SDKMessage } from "@anthropic-ai/claude-code";
import WASMCodeAnalyzer from './analyzer';
interface AnalysisContext {
wasmAnalyzer: WASMCodeAnalyzer;
workerPool: Worker[];
currentWorkerId: number;
}
class ClaudeWASMIntegration {
private context: AnalysisContext;
private initialized = false;
constructor() {
this.context = {
wasmAnalyzer: new WASMCodeAnalyzer(),
workerPool: [],
currentWorkerId: 0,
};
}
async initialize() {
if (this.initialized) return;
// Initialize WASM analyzer
await this.context.wasmAnalyzer.initialize();
// Create worker pool
const workerCount = navigator.hardwareConcurrency || 4;
for (let i = 0; i < workerCount; i++) {
const worker = new Worker('/worker.js');
this.context.workerPool.push(worker);
}
this.initialized = true;
}
async analyzeWithClaude(code: string, prompt: string) {
await this.initialize();
// First, run WASM analysis
const wasmAnalysis = await this.runWASMAnalysis(code);
// Prepare context for Claude
const analysisContext = `
Code Analysis Results:
- Complexity Score: ${wasmAnalysis.complexity}
- Issues Found: ${wasmAnalysis.issues.length}
- Analysis Time: ${wasmAnalysis.analysisTime}ms
Issues:
${wasmAnalysis.issues.map(issue =>
`- Line ${issue.line}, Col ${issue.column}: ${issue.message} (${issue.severity})`
).join('\n')}
Original Code:
\`\`\`
${code}
\`\`\`
`;
// Query Claude with analysis context
const messages: SDKMessage[] = [];
const queryOptions = {
prompt: `${analysisContext}\n\n${prompt}`,
context: {
wasmAnalysis,
enableAcceleration: true,
},
options: {
maxTurns: 3,
temperature: 0.2, // Lower temperature for code analysis
},
};
for await (const message of query(queryOptions)) {
messages.push(message);
// Process Claude's suggestions
if (message.type === 'assistant' && message.content) {
await this.processSuggestions(message.content, code);
}
}
return {
wasmAnalysis,
claudeMessages: messages,
combinedInsights: await this.combineInsights(wasmAnalysis, messages),
};
}
private async runWASMAnalysis(code: string): Promise<any> {
// Use worker for non-blocking analysis
return new Promise((resolve, reject) => {
const worker = this.getNextWorker();
const id = Math.random().toString(36);
const handler = (event: MessageEvent) => {
if (event.data.id === id) {
worker.removeEventListener('message', handler);
if (event.data.error) {
reject(new Error(event.data.error));
} else {
resolve(event.data.result);
}
}
};
worker.addEventListener('message', handler);
worker.postMessage({ type: 'analyze', data: { code }, id });
});
}
private getNextWorker(): Worker {
const worker = this.context.workerPool[this.context.currentWorkerId];
this.context.currentWorkerId =
(this.context.currentWorkerId + 1) % this.context.workerPool.length;
return worker;
}
private async processSuggestions(suggestions: string, originalCode: string) {
// Extract code improvements from Claude's response
const codeBlocks = suggestions.match(/```[\s\S]*?```/g) || [];
for (const block of codeBlocks) {
const improvedCode = block.replace(/```\w*\n?/g, '').trim();
// Re-analyze improved code
const improvedAnalysis = await this.runWASMAnalysis(improvedCode);
console.log('Improvement Analysis:', {
originalComplexity: await this.context.wasmAnalyzer.analyzeCode(originalCode).then(r => r.complexity),
improvedComplexity: improvedAnalysis.complexity,
issueReduction: improvedAnalysis.issues.length,
});
}
}
private async combineInsights(wasmAnalysis: any, claudeMessages: SDKMessage[]) {
// Combine WASM analysis with Claude's insights
const insights = {
performance: {
analysisTime: wasmAnalysis.analysisTime,
complexity: wasmAnalysis.complexity,
recommendation: wasmAnalysis.complexity > 10 ? 'Consider refactoring' : 'Good',
},
codeQuality: {
issues: wasmAnalysis.issues,
claudeSuggestions: claudeMessages
.filter(m => m.type === 'assistant')
.map(m => m.content),
},
metrics: {
totalIssues: wasmAnalysis.issues.length,
criticalIssues: wasmAnalysis.issues.filter(i => i.severity === 'error').length,
warnings: wasmAnalysis.issues.filter(i => i.severity === 'warning').length,
},
};
return insights;
}
async addCustomRules(rules: Array<any>) {
await this.initialize();
// Add rules to all workers
const promises = this.context.workerPool.map(worker =>
new Promise((resolve, reject) => {
const id = Math.random().toString(36);
const handler = (event: MessageEvent) => {
if (event.data.id === id) {
worker.removeEventListener('message', handler);
if (event.data.error) {
reject(new Error(event.data.error));
} else {
resolve(event.data.result);
}
}
};
worker.addEventListener('message', handler);
rules.forEach(rule => {
worker.postMessage({ type: 'addRule', data: { rule }, id });
});
})
);
await Promise.all(promises);
}
terminate() {
this.context.workerPool.forEach(worker => worker.terminate());
}
}
// Usage example
export async function analyzeProjectWithClaude() {
const integration = new ClaudeWASMIntegration();
// Add custom rules
await integration.addCustomRules([
{
id: 'no-magic-numbers',
pattern: '\\b\\d{2,}\\b',
severity: 'warning',
message: 'Avoid magic numbers, use constants',
},
{
id: 'max-line-length',
pattern: '^.{120,}$',
severity: 'warning',
message: 'Line exceeds 120 characters',
},
]);
// Analyze code
const code = `
function processData(data: any) {
console.log('Processing data...');
if (data.length > 100) {
for (let i = 0; i < data.length; i++) {
if (data[i].value > 50) {
data[i].processed = true;
}
}
}
return data;
}
`;
const result = await integration.analyzeWithClaude(
code,
"Please review this code and suggest improvements for performance and maintainability"
);
console.log('Analysis Results:', result);
integration.terminate();
return result;
}HTML Interface (index.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Claude Code WASM Analyzer</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.editor-panel, .results-panel {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
textarea {
width: 100%;
height: 400px;
font-family: 'Monaco', 'Consolas', monospace;
font-size: 14px;
border: 1px solid #ddd;
border-radius: 4px;
padding: 10px;
}
.issue {
margin: 10px 0;
padding: 10px;
border-radius: 4px;
font-size: 14px;
}
.issue.error {
background: #fee;
border-left: 4px solid #f44;
}
.issue.warning {
background: #ffeaa7;
border-left: 4px solid #fdcb6e;
}
.metrics {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
margin: 20px 0;
}
.metric {
text-align: center;
padding: 15px;
background: #f8f9fa;
border-radius: 4px;
}
.metric-value {
font-size: 24px;
font-weight: bold;
color: #2d3436;
}
.metric-label {
font-size: 12px;
color: #636e72;
text-transform: uppercase;
}
button {
background: #0084ff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
margin: 10px 0;
}
button:hover {
background: #0066cc;
}
button:disabled {
background: #ccc;
cursor: not-allowed;
}
.loading {
display: none;
margin: 10px 0;
color: #666;
}
.loading.active {
display: block;
}
.claude-suggestions {
margin-top: 20px;
padding: 15px;
background: #e3f2fd;
border-radius: 4px;
border-left: 4px solid #2196f3;
}
.performance-graph {
margin-top: 20px;
height: 200px;
position: relative;
}
.tab-container {
margin-top: 20px;
}
.tabs {
display: flex;
border-bottom: 2px solid #ddd;
}
.tab {
padding: 10px 20px;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.3s;
}
.tab.active {
border-bottom-color: #0084ff;
color: #0084ff;
}
.tab-content {
display: none;
padding: 20px 0;
}
.tab-content.active {
display: block;
}
</style>
</head>
<body>
<h1>Claude Code WASM Analyzer</h1>
<div class="container">
<div class="editor-panel">
<h2>Code Editor</h2>
<textarea id="codeInput" placeholder="Paste your code here...">
function processData(data: any) {
console.log('Processing data...');
if (data.length > 100) {
for (let i = 0; i < data.length; i++) {
if (data[i].value > 50) {
data[i].processed = true;
}
}
}
return data;
}</textarea>
<button id="analyzeBtn" onclick="analyzeCode()">Analyze with WASM + Claude</button>
<button id="batchBtn" onclick="analyzeBatch()">Batch Analysis</button>
<div class="loading" id="loading">Analyzing code...</div>
<div class="metrics" id="metrics">
<div class="metric">
<div class="metric-value" id="complexityScore">-</div>
<div class="metric-label">Complexity</div>
</div>
<div class="metric">
<div class="metric-value" id="issueCount">-</div>
<div class="metric-label">Issues</div>
</div>
<div class="metric">
<div class="metric-value" id="analysisTime">-</div>
<div class="metric-label">Analysis Time (ms)</div>
</div>
</div>
</div>
<div class="results-panel">
<h2>Analysis Results</h2>
<div class="tab-container">
<div class="tabs">
<div class="tab active" onclick="switchTab('issues')">Issues</div>
<div class="tab" onclick="switchTab('claude')">Claude Suggestions</div>
<div class="tab" onclick="switchTab('performance')">Performance</div>
</div>
<div class="tab-content active" id="issuesTab">
<div id="issues"></div>
</div>
<div class="tab-content" id="claudeTab">
<div id="claudeSuggestions"></div>
</div>
<div class="tab-content" id="performanceTab">
<canvas id="performanceChart"></canvas>
<div id="performanceMetrics"></div>
</div>
</div>
</div>
</div>
<script type="module">
import { analyzeProjectWithClaude } from './claude-integration.js';
let integration;
const performanceHistory = [];
window.analyzeCode = async function() {
const codeInput = document.getElementById('codeInput');
const loading = document.getElementById('loading');
const analyzeBtn = document.getElementById('analyzeBtn');
loading.classList.add('active');
analyzeBtn.disabled = true;
try {
if (!integration) {
const { ClaudeWASMIntegration } = await import('./claude-integration.js');
integration = new ClaudeWASMIntegration();
}
const code = codeInput.value;
const result = await integration.analyzeWithClaude(
code,
"Analyze this code for performance, security, and maintainability issues. Suggest improvements."
);
displayResults(result);
updatePerformanceHistory(result.wasmAnalysis);
} catch (error) {
console.error('Analysis error:', error);
alert('Analysis failed: ' + error.message);
} finally {
loading.classList.remove('active');
analyzeBtn.disabled = false;
}
};
window.analyzeBatch = async function() {
// Simulate batch analysis
const files = [
{ path: 'file1.ts', content: document.getElementById('codeInput').value },
{ path: 'file2.ts', content: 'const x: any = 42; console.log(x);' },
{ path: 'file3.ts', content: 'function complexFunction() { if (true) { if (true) { if (true) { return; } } } }' },
];
const loading = document.getElementById('loading');
loading.classList.add('active');
try {
if (!integration) {
const { ClaudeWASMIntegration } = await import('./claude-integration.js');
integration = new ClaudeWASMIntegration();
}
const results = await Promise.all(
files.map(file => integration.analyzeWithClaude(file.content, "Quick analysis"))
);
displayBatchResults(results, files);
} catch (error) {
console.error('Batch analysis error:', error);
alert('Batch analysis failed: ' + error.message);
} finally {
loading.classList.remove('active');
}
};
function displayResults(result) {
// Update metrics
document.getElementById('complexityScore').textContent =
result.wasmAnalysis.complexity.toFixed(1);
document.getElementById('issueCount').textContent =
result.wasmAnalysis.issues.length;
document.getElementById('analysisTime').textContent =
result.wasmAnalysis.analysisTime.toFixed(2);
// Display issues
const issuesContainer = document.getElementById('issues');
issuesContainer.innerHTML = '';
if (result.wasmAnalysis.issues.length === 0) {
issuesContainer.innerHTML = '<p>No issues found!</p>';
} else {
result.wasmAnalysis.issues.forEach(issue => {
const issueElement = document.createElement('div');
issueElement.className = `issue ${issue.severity}`;
issueElement.innerHTML = `
<strong>Line ${issue.line}, Column ${issue.column}</strong>
<br>${issue.message}
<br><small>Rule: ${issue.rule_id}</small>
`;
issuesContainer.appendChild(issueElement);
});
}
// Display Claude suggestions
const claudeContainer = document.getElementById('claudeSuggestions');
claudeContainer.innerHTML = '';
result.claudeMessages
.filter(m => m.type === 'assistant' && m.content)
.forEach(message => {
const suggestion = document.createElement('div');
suggestion.className = 'claude-suggestions';
suggestion.innerHTML = marked.parse(message.content);
claudeContainer.appendChild(suggestion);
});
}
function displayBatchResults(results, files) {
const issuesContainer = document.getElementById('issues');
issuesContainer.innerHTML = '<h3>Batch Analysis Results</h3>';
results.forEach((result, index) => {
const fileResult = document.createElement('div');
fileResult.innerHTML = `
<h4>${files[index].path}</h4>
<p>Complexity: ${result.wasmAnalysis.complexity.toFixed(1)}</p>
<p>Issues: ${result.wasmAnalysis.issues.length}</p>
<p>Analysis Time: ${result.wasmAnalysis.analysisTime.toFixed(2)}ms</p>
`;
issuesContainer.appendChild(fileResult);
});
}
function updatePerformanceHistory(analysis) {
performanceHistory.push({
timestamp: Date.now(),
analysisTime: analysis.analysisTime,
complexity: analysis.complexity,
issueCount: analysis.issues.length,
});
// Keep last 20 analyses
if (performanceHistory.length > 20) {
performanceHistory.shift();
}
drawPerformanceChart();
}
function drawPerformanceChart() {
const canvas = document.getElementById('performanceChart');
const ctx = canvas.getContext('2d');
// Simple line chart implementation
const width = canvas.width = canvas.offsetWidth;
const height = canvas.height = 200;
ctx.clearRect(0, 0, width, height);
if (performanceHistory.length < 2) return;
// Draw analysis time chart
ctx.strokeStyle = '#0084ff';
ctx.lineWidth = 2;
ctx.beginPath();
const maxTime = Math.max(...performanceHistory.map(h => h.analysisTime));
const xStep = width / (performanceHistory.length - 1);
performanceHistory.forEach((point, index) => {
const x = index * xStep;
const y = height - (point.analysisTime / maxTime) * height * 0.8 - 10;
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
// Update performance metrics
const metricsContainer = document.getElementById('performanceMetrics');
const avgTime = performanceHistory.reduce((sum, h) => sum + h.analysisTime, 0) / performanceHistory.length;
metricsContainer.innerHTML = `
<p>Average Analysis Time: ${avgTime.toFixed(2)}ms</p>
<p>Total Analyses: ${performanceHistory.length}</p>
`;
}
window.switchTab = function(tabName) {
// Remove active class from all tabs and contents
document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
// Add active class to selected tab and content
event.target.classList.add('active');
document.getElementById(tabName + 'Tab').classList.add('active');
};
// Load marked.js for markdown parsing
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js';
document.head.appendChild(script);
</script>
</body>
</html>Build Configuration (webpack.config.js)
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
experiments: {
asyncWebAssembly: true,
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js', '.wasm'],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
}),
new WasmPackPlugin({
crateDirectory: path.resolve(__dirname, "."),
outDir: path.resolve(__dirname, "pkg"),
}),
],
devServer: {
static: {
directory: path.join(__dirname, 'public'),
},
compress: true,
port: 9000,
headers: {
'Cross-Origin-Embedder-Policy': 'require-corp',
'Cross-Origin-Opener-Policy': 'same-origin',
},
},
};Example 2: Edge AI Deployment with WASI-NN
Rust WASI-NN Implementation
use wasi_nn::{
GraphBuilder, GraphEncoding, ExecutionTarget,
TensorType, Tensor, Graph
};
use std::fs;
pub struct EdgeAIModel {
graph: Graph,
input_shape: Vec<usize>,
output_shape: Vec<usize>,
}
impl EdgeAIModel {
pub fn load_from_file(model_path: &str) -> Result<Self, Box<dyn std::error::Error>> {
// Load model bytes
let model_bytes = fs::read(model_path)?;
// Build graph
let graph = GraphBuilder::new(
GraphEncoding::Onnx,
ExecutionTarget::CPU
)
.build_from_bytes(&model_bytes)?;
// Hardcoded shapes for this example
// In production, these would be read from model metadata
let input_shape = vec![1, 224, 224, 3]; // Batch, Height, Width, Channels
let output_shape = vec![1, 1000]; // Batch, Classes
Ok(Self {
graph,
input_shape,
output_shape,
})
}
pub fn predict(&self, input_data: &[f32]) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
// Create input tensor
let input_tensor = Tensor::new(
input_data,
&self.input_shape,
TensorType::F32
);
// Set input
self.graph.set_input("input", input_tensor)?;
// Run inference
self.graph.compute()?;
// Get output
let output_tensor = self.graph.get_output("output")?;
let output_data = output_tensor.data::<f32>()?;
Ok(output_data.to_vec())
}
pub fn batch_predict(&self, batch: Vec<Vec<f32>>) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
let mut results = Vec::new();
for input_data in batch {
let prediction = self.predict(&input_data)?;
results.push(prediction);
}
Ok(results)
}
}
// Edge deployment orchestrator
pub struct EdgeOrchestrator {
models: Vec<EdgeAIModel>,
load_balancer: LoadBalancer,
}
#[derive(Default)]
struct LoadBalancer {
current_model: usize,
request_counts: Vec<usize>,
}
impl LoadBalancer {
fn get_next_model(&mut self, model_count: usize) -> usize {
// Simple round-robin for this example
let model_id = self.current_model;
self.current_model = (self.current_model + 1) % model_count;
if self.request_counts.len() <= model_id {
self.request_counts.resize(model_count, 0);
}
self.request_counts[model_id] += 1;
model_id
}
}
impl EdgeOrchestrator {
pub fn new() -> Self {
Self {
models: Vec::new(),
load_balancer: LoadBalancer::default(),
}
}
pub fn add_model(&mut self, model: EdgeAIModel) {
self.models.push(model);
}
pub fn predict(&mut self, input: Vec<f32>) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
if self.models.is_empty() {
return Err("No models loaded".into());
}
let model_idx = self.load_balancer.get_next_model(self.models.len());
self.models[model_idx].predict(&input)
}
pub fn get_stats(&self) -> serde_json::Value {
serde_json::json!({
"model_count": self.models.len(),
"request_distribution": self.load_balancer.request_counts,
"total_requests": self.load_balancer.request_counts.iter().sum::<usize>(),
})
}
}
// Export functions for WASM
#[no_mangle]
pub extern "C" fn edge_ai_init() -> *mut EdgeOrchestrator {
Box::into_raw(Box::new(EdgeOrchestrator::new()))
}
#[no_mangle]
pub extern "C" fn edge_ai_load_model(
orchestrator: *mut EdgeOrchestrator,
model_path: *const u8,
model_path_len: usize
) -> i32 {
unsafe {
let orchestrator = &mut *orchestrator;
let model_path = std::str::from_utf8(
std::slice::from_raw_parts(model_path, model_path_len)
).unwrap();
match EdgeAIModel::load_from_file(model_path) {
Ok(model) => {
orchestrator.add_model(model);
0 // Success
}
Err(_) => -1 // Error
}
}
}
#[no_mangle]
pub extern "C" fn edge_ai_predict(
orchestrator: *mut EdgeOrchestrator,
input_data: *const f32,
input_len: usize,
output_data: *mut f32,
output_len: usize
) -> i32 {
unsafe {
let orchestrator = &mut *orchestrator;
let input = std::slice::from_raw_parts(input_data, input_len).to_vec();
match orchestrator.predict(input) {
Ok(result) => {
if result.len() <= output_len {
std::ptr::copy_nonoverlapping(
result.as_ptr(),
output_data,
result.len()
);
result.len() as i32
} else {
-2 // Output buffer too small
}
}
Err(_) => -1 // Prediction error
}
}
}JavaScript Edge Deployment Interface
// edge-ai-client.js
class EdgeAIClient {
constructor(wasmUrl) {
this.wasmUrl = wasmUrl;
this.instance = null;
this.memory = null;
this.orchestrator = null;
}
async initialize() {
const response = await fetch(this.wasmUrl);
const wasmBuffer = await response.arrayBuffer();
// Import object for WASI
const wasiImports = {
wasi_snapshot_preview1: {
// Minimal WASI implementation for edge deployment
proc_exit: () => {},
fd_write: () => 0,
fd_read: () => 0,
fd_close: () => 0,
environ_get: () => 0,
environ_sizes_get: () => 0,
},
wasi_nn: {
// WASI-NN host implementation
graph_builder_new: (encoding, target) => {
console.log(`Creating graph: encoding=${encoding}, target=${target}`);
return 0; // Graph handle
},
graph_build: (builder, modelPtr, modelLen) => {
console.log(`Building graph from ${modelLen} bytes`);
return 0; // Success
},
graph_init: (graph) => {
console.log(`Initializing graph ${graph}`);
return 0; // Context handle
},
compute: (context) => {
console.log(`Computing inference on context ${context}`);
return 0; // Success
},
// Add other WASI-NN imports as needed
},
};
const { instance } = await WebAssembly.instantiate(wasmBuffer, wasiImports);
this.instance = instance;
this.memory = instance.exports.memory;
// Initialize orchestrator
this.orchestrator = instance.exports.edge_ai_init();
}
async loadModel(modelUrl) {
// In a real edge deployment, models might be cached locally
const response = await fetch(modelUrl);
const modelBuffer = await response.arrayBuffer();
// For this example, we'll simulate loading by path
// In production, you'd implement proper model loading
const modelPath = "/models/model.onnx";
const pathBytes = new TextEncoder().encode(modelPath);
const pathPtr = this.allocateMemory(pathBytes.length);
new Uint8Array(this.memory.buffer, pathPtr, pathBytes.length).set(pathBytes);
const result = this.instance.exports.edge_ai_load_model(
this.orchestrator,
pathPtr,
pathBytes.length
);
this.freeMemory(pathPtr, pathBytes.length);
if (result !== 0) {
throw new Error(`Failed to load model: ${result}`);
}
}
async predict(inputData) {
const inputFloat32 = new Float32Array(inputData);
const outputSize = 1000; // Assuming 1000 classes
// Allocate memory for input and output
const inputPtr = this.allocateMemory(inputFloat32.byteLength);
const outputPtr = this.allocateMemory(outputSize * 4); // 4 bytes per float
// Copy input data to WASM memory
new Float32Array(
this.memory.buffer,
inputPtr,
inputFloat32.length
).set(inputFloat32);
// Run prediction
const resultLen = this.instance.exports.edge_ai_predict(
this.orchestrator,
inputPtr,
inputFloat32.length,
outputPtr,
outputSize
);
if (resultLen < 0) {
this.freeMemory(inputPtr, inputFloat32.byteLength);
this.freeMemory(outputPtr, outputSize * 4);
throw new Error(`Prediction failed: ${resultLen}`);
}
// Extract results
const results = new Float32Array(
this.memory.buffer,
outputPtr,
resultLen
);
const output = Array.from(results);
// Clean up
this.freeMemory(inputPtr, inputFloat32.byteLength);
this.freeMemory(outputPtr, outputSize * 4);
return output;
}
allocateMemory(size) {
// Simple memory allocation
// In production, use a proper memory allocator
const pages = Math.ceil(size / 65536);
const currentPages = this.memory.buffer.byteLength / 65536;
if (currentPages < pages) {
this.memory.grow(pages - currentPages);
}
// Return pointer at end of current memory
// This is simplified - real implementation needs proper allocation
return this.memory.buffer.byteLength - size;
}
freeMemory(ptr, size) {
// Simplified - real implementation needs proper deallocation
}
async benchmark(iterations = 100) {
const inputSize = 224 * 224 * 3; // Standard image input
const testInput = new Array(inputSize).fill(0).map(() => Math.random());
const times = [];
for (let i = 0; i < iterations; i++) {
const start = performance.now();
await this.predict(testInput);
const end = performance.now();
times.push(end - start);
}
return {
avgTime: times.reduce((a, b) => a + b) / times.length,
minTime: Math.min(...times),
maxTime: Math.max(...times),
p95Time: times.sort((a, b) => a - b)[Math.floor(times.length * 0.95)],
};
}
}
// Edge deployment manager
class EdgeDeploymentManager {
constructor() {
this.clients = new Map();
this.metrics = {
totalPredictions: 0,
avgLatency: 0,
errors: 0,
};
}
async deployModel(modelId, wasmUrl, modelUrl) {
const client = new EdgeAIClient(wasmUrl);
await client.initialize();
await client.loadModel(modelUrl);
this.clients.set(modelId, client);
console.log(`Model ${modelId} deployed successfully`);
}
async predict(modelId, inputData) {
const client = this.clients.get(modelId);
if (!client) {
throw new Error(`Model ${modelId} not found`);
}
const start = performance.now();
try {
const result = await client.predict(inputData);
const latency = performance.now() - start;
// Update metrics
this.metrics.totalPredictions++;
this.metrics.avgLatency =
(this.metrics.avgLatency * (this.metrics.totalPredictions - 1) + latency) /
this.metrics.totalPredictions;
return {
result,
latency,
modelId,
};
} catch (error) {
this.metrics.errors++;
throw error;
}
}
async benchmarkAll() {
const results = {};
for (const [modelId, client] of this.clients) {
console.log(`Benchmarking model ${modelId}...`);
results[modelId] = await client.benchmark();
}
return results;
}
getMetrics() {
return {
...this.metrics,
activeModels: this.clients.size,
models: Array.from(this.clients.keys()),
};
}
}
// Usage example
async function edgeDeploymentExample() {
const manager = new EdgeDeploymentManager();
// Deploy multiple models
await manager.deployModel(
'image-classifier-v1',
'/wasm/edge-ai.wasm',
'/models/mobilenet.onnx'
);
await manager.deployModel(
'image-classifier-v2',
'/wasm/edge-ai.wasm',
'/models/efficientnet.onnx'
);
// Simulate edge inference workload
console.log('Starting edge inference simulation...');
const imageSize = 224 * 224 * 3;
const testImage = new Array(imageSize).fill(0).map(() => Math.random());
// Run predictions
for (let i = 0; i < 10; i++) {
try {
const result = await manager.predict('image-classifier-v1', testImage);
console.log(`Prediction ${i + 1}: latency=${result.latency.toFixed(2)}ms`);
} catch (error) {
console.error(`Prediction ${i + 1} failed:`, error);
}
}
// Benchmark all models
const benchmarks = await manager.benchmarkAll();
console.log('Benchmark Results:', benchmarks);
// Get metrics
console.log('Deployment Metrics:', manager.getMetrics());
}Example 3: Real-time Code Completion with WebLLM
WebLLM Code Completion Implementation
// code-completion-engine.ts
import { CreateMLCEngine, MLCEngineInterface, ChatCompletionMessageParam } from "@mlc-ai/web-llm";
interface CodeCompletionOptions {
model: string;
temperature?: number;
maxTokens?: number;
stopSequences?: string[];
}
export class CodeCompletionEngine {
private engine: MLCEngineInterface | null = null;
private modelId: string;
private initialized = false;
private initPromise: Promise<void> | null = null;
constructor(modelId: string = "Llama-3-8B-Instruct-q4f32_1") {
this.modelId = modelId;
}
async initialize(progressCallback?: (progress: number) => void) {
if (this.initialized) return;
if (this.initPromise) {
return this.initPromise;
}
this.initPromise = this._initialize(progressCallback);
return this.initPromise;
}
private async _initialize(progressCallback?: (progress: number) => void) {
try {
this.engine = await CreateMLCEngine(this.modelId, {
initProgressCallback: (progress) => {
console.log(`Model loading: ${progress.text}`);
if (progressCallback && progress.progress) {
progressCallback(progress.progress);
}
},
});
this.initialized = true;
console.log(`Code completion engine initialized with ${this.modelId}`);
} catch (error) {
console.error("Failed to initialize engine:", error);
throw error;
}
}
async generateCompletion(
context: string,
options: CodeCompletionOptions = {}
): Promise<string> {
if (!this.initialized || !this.engine) {
throw new Error("Engine not initialized");
}
const messages: ChatCompletionMessageParam[] = [
{
role: "system",
content: `You are a code completion assistant.
Complete the code based on the context provided.
Only return the completion, no explanations.
Maintain consistent style and indentation.`,
},
{
role: "user",
content: context,
},
];
const completion = await this.engine.chat.completions.create({
messages,
temperature: options.temperature || 0.2,
max_tokens: options.maxTokens || 150,
stop: options.stopSequences || ["\n\n", "```"],
});
return completion.choices[0]?.message?.content || "";
}
async generateMultilineCompletion(
prefix: string,
suffix: string,
language: string
): Promise<string> {
const context = `Language: ${language}
Prefix:
\`\`\`${language}
${prefix}
\`\`\`
Suffix:
\`\`\`${language}
${suffix}
\`\`\`
Complete the code between prefix and suffix. Return only the missing code.`;
return this.generateCompletion(context, {
temperature: 0.1,
maxTokens: 200,
});
}
async explainCode(code: string, language: string): Promise<string> {
if (!this.initialized || !this.engine) {
throw new Error("Engine not initialized");
}
const messages: ChatCompletionMessageParam[] = [
{
role: "system",
content: "You are a helpful coding assistant. Explain code clearly and concisely.",
},
{
role: "user",
content: `Explain this ${language} code:\n\`\`\`${language}\n${code}\n\`\`\``,
},
];
const completion = await this.engine.chat.completions.create({
messages,
temperature: 0.3,
max_tokens: 300,
});
return completion.choices[0]?.message?.content || "";
}
async suggestRefactoring(code: string, language: string): Promise<string> {
if (!this.initialized || !this.engine) {
throw new Error("Engine not initialized");
}
const messages: ChatCompletionMessageParam[] = [
{
role: "system",
content: `You are a code refactoring expert.
Suggest improvements for better performance, readability, and maintainability.
Provide the refactored code with brief explanations.`,
},
{
role: "user",
content: `Refactor this ${language} code:\n\`\`\`${language}\n${code}\n\`\`\``,
},
];
const completion = await this.engine.chat.completions.create({
messages,
temperature: 0.2,
max_tokens: 500,
});
return completion.choices[0]?.message?.content || "";
}
async fixSyntaxError(
code: string,
error: string,
language: string
): Promise<string> {
const context = `Fix this ${language} code that has the following error:
Error: ${error}
Code:
\`\`\`${language}
${code}
\`\`\`
Return only the corrected code.`;
return this.generateCompletion(context, {
temperature: 0.1,
maxTokens: 300,
});
}
terminate() {
if (this.engine) {
// Clean up resources if needed
this.engine = null;
this.initialized = false;
}
}
}
// Integration with code editor
export class EditorIntegration {
private completionEngine: CodeCompletionEngine;
private debounceTimer: NodeJS.Timeout | null = null;
private cache = new Map<string, string>();
constructor(modelId?: string) {
this.completionEngine = new CodeCompletionEngine(modelId);
}
async initialize(progressCallback?: (progress: number) => void) {
await this.completionEngine.initialize(progressCallback);
}
async getCompletion(
editor: any, // Your editor instance
position: { line: number; column: number }
): Promise<string | null> {
// Get context around cursor
const context = this.getContext(editor, position);
const cacheKey = this.getCacheKey(context);
// Check cache
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey)!;
}
try {
const completion = await this.completionEngine.generateCompletion(
context.prefix + context.suffix,
{
temperature: 0.2,
maxTokens: 100,
}
);
// Cache the result
this.cache.set(cacheKey, completion);
// Clear old cache entries
if (this.cache.size > 100) {
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
return completion;
} catch (error) {
console.error("Completion error:", error);
return null;
}
}
getContext(
editor: any,
position: { line: number; column: number },
contextLines: number = 10
) {
const lines = editor.getModel().getLinesContent();
const currentLine = lines[position.line - 1];
// Get prefix context
const prefixStartLine = Math.max(0, position.line - contextLines);
const prefixLines = lines.slice(prefixStartLine, position.line - 1);
prefixLines.push(currentLine.substring(0, position.column));
// Get suffix context
const suffixEndLine = Math.min(lines.length, position.line + contextLines);
const suffixLines = [currentLine.substring(position.column)];
suffixLines.push(...lines.slice(position.line, suffixEndLine));
return {
prefix: prefixLines.join('\n'),
suffix: suffixLines.join('\n'),
language: editor.getModel().getLanguageId(),
};
}
getCacheKey(context: any): string {
return `${context.language}:${context.prefix}:${context.suffix}`.substring(0, 200);
}
// Debounced completion for real-time suggestions
async getCompletionDebounced(
editor: any,
position: { line: number; column: number },
delay: number = 300
): Promise<void> {
if (this.debounceTimer) {
clearTimeout(this.debounceTimer);
}
this.debounceTimer = setTimeout(async () => {
const completion = await this.getCompletion(editor, position);
if (completion) {
// Show inline suggestion
this.showInlineSuggestion(editor, position, completion);
}
}, delay);
}
showInlineSuggestion(
editor: any,
position: { line: number; column: number },
suggestion: string
) {
// Implementation depends on your editor
// For Monaco Editor:
editor.executeEdits('completion', [{
range: {
startLineNumber: position.line,
startColumn: position.column,
endLineNumber: position.line,
endColumn: position.column,
},
text: suggestion,
forceMoveMarkers: true,
}]);
}
}
// Usage in VS Code extension or web editor
export async function setupCodeCompletion() {
const integration = new EditorIntegration("Llama-3-8B-Instruct-q4f32_1");
// Show loading progress
const progressBar = document.getElementById('loadingProgress');
await integration.initialize((progress) => {
if (progressBar) {
progressBar.style.width = `${progress}%`;
}
});
// Listen to editor changes
const editor = monaco.editor.create(document.getElementById('editor'), {
value: '',
language: 'typescript',
theme: 'vs-dark',
});
editor.onDidChangeModelContent(() => {
const position = editor.getPosition();
if (position) {
integration.getCompletionDebounced(editor, {
line: position.lineNumber,
column: position.column,
});
}
});
// Add commands
editor.addAction({
id: 'explain-code',
label: 'Explain Code',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyE],
run: async (editor) => {
const selection = editor.getSelection();
const code = editor.getModel().getValueInRange(selection);
const language = editor.getModel().getLanguageId();
const explanation = await integration.completionEngine.explainCode(code, language);
console.log('Code explanation:', explanation);
// Show in a panel or tooltip
},
});
editor.addAction({
id: 'refactor-code',
label: 'Suggest Refactoring',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyR],
run: async (editor) => {
const selection = editor.getSelection();
const code = editor.getModel().getValueInRange(selection);
const language = editor.getModel().getLanguageId();
const refactoring = await integration.completionEngine.suggestRefactoring(code, language);
console.log('Refactoring suggestion:', refactoring);
// Show in a diff view
},
});
return integration;
}Performance Monitoring Dashboard
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebLLM Code Completion Performance</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
margin: 0;
padding: 20px;
background: #0d1117;
color: #c9d1d9;
}
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.metric-card {
background: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
padding: 20px;
}
.metric-value {
font-size: 36px;
font-weight: bold;
color: #58a6ff;
margin: 10px 0;
}
.metric-label {
color: #8b949e;
font-size: 14px;
}
.chart-container {
background: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
padding: 20px;
height: 300px;
position: relative;
}
#editor {
height: 400px;
border: 1px solid #30363d;
border-radius: 8px;
margin: 20px 0;
}
.progress-bar {
width: 100%;
height: 4px;
background: #30363d;
border-radius: 2px;
overflow: hidden;
margin: 20px 0;
}
.progress-fill {
height: 100%;
background: #58a6ff;
width: 0%;
transition: width 0.3s ease;
}
.status {
padding: 10px;
border-radius: 4px;
margin: 10px 0;
font-size: 14px;
}
.status.loading {
background: #1f6feb;
color: white;
}
.status.ready {
background: #238636;
color: white;
}
.status.error {
background: #da3633;
color: white;
}
</style>
</head>
<body>
<h1>WebLLM Code Completion Performance Monitor</h1>
<div class="status loading" id="status">
Initializing WebLLM engine...
</div>
<div class="progress-bar">
<div class="progress-fill" id="loadingProgress"></div>
</div>
<div class="dashboard">
<div class="metric-card">
<div class="metric-label">Average Latency</div>
<div class="metric-value" id="avgLatency">-</div>
</div>
<div class="metric-card">
<div class="metric-label">Tokens/Second</div>
<div class="metric-value" id="tokensPerSecond">-</div>
</div>
<div class="metric-card">
<div class="metric-label">Cache Hit Rate</div>
<div class="metric-value" id="cacheHitRate">-</div>
</div>
<div class="metric-card">
<div class="metric-label">Total Completions</div>
<div class="metric-value" id="totalCompletions">0</div>
</div>
</div>
<div class="chart-container">
<canvas id="latencyChart"></canvas>
</div>
<h2>Try It Out</h2>
<div id="editor"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.1/min/vs/loader.min.js"></script>
<script type="module">
import { setupCodeCompletion } from './code-completion-engine.js';
// Performance tracking
const metrics = {
latencies: [],
tokenCounts: [],
cacheHits: 0,
cacheMisses: 0,
totalCompletions: 0,
};
// Initialize Chart.js
const ctx = document.getElementById('latencyChart').getContext('2d');
const latencyChart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Completion Latency (ms)',
data: [],
borderColor: '#58a6ff',
backgroundColor: 'rgba(88, 166, 255, 0.1)',
tension: 0.4,
}],
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
labels: { color: '#c9d1d9' },
},
},
scales: {
x: {
ticks: { color: '#8b949e' },
grid: { color: '#30363d' },
},
y: {
ticks: { color: '#8b949e' },
grid: { color: '#30363d' },
},
},
},
});
// Update metrics display
function updateMetrics() {
if (metrics.latencies.length > 0) {
const avgLatency = metrics.latencies.reduce((a, b) => a + b) / metrics.latencies.length;
document.getElementById('avgLatency').textContent = avgLatency.toFixed(0) + 'ms';
const avgTokens = metrics.tokenCounts.reduce((a, b) => a + b) / metrics.tokenCounts.length;
const tokensPerSecond = (avgTokens / avgLatency) * 1000;
document.getElementById('tokensPerSecond').textContent = tokensPerSecond.toFixed(1);
}
const hitRate = metrics.cacheHits / (metrics.cacheHits + metrics.cacheMisses) || 0;
document.getElementById('cacheHitRate').textContent = (hitRate * 100).toFixed(0) + '%';
document.getElementById('totalCompletions').textContent = metrics.totalCompletions;
}
// Track completion performance
function trackCompletion(latency, tokenCount, cacheHit) {
metrics.latencies.push(latency);
metrics.tokenCounts.push(tokenCount);
metrics.totalCompletions++;
if (cacheHit) {
metrics.cacheHits++;
} else {
metrics.cacheMisses++;
}
// Update chart
const labels = latencyChart.data.labels;
const data = latencyChart.data.datasets[0].data;
labels.push(new Date().toLocaleTimeString());
data.push(latency);
// Keep last 20 points
if (labels.length > 20) {
labels.shift();
data.shift();
}
latencyChart.update();
updateMetrics();
}
// Initialize Monaco Editor
require.config({ paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.1/min/vs' } });
require(['vs/editor/editor.main'], async function() {
const editor = monaco.editor.create(document.getElementById('editor'), {
value: `// Try typing some code to see AI-powered completions
function calculateSum(numbers) {
// Start typing here...
}
class DataProcessor {
constructor() {
this.data = [];
}
// Type a method name...
}`,
language: 'javascript',
theme: 'vs-dark',
automaticLayout: true,
suggestOnTriggerCharacters: true,
});
try {
// Initialize code completion
const statusEl = document.getElementById('status');
statusEl.textContent = 'Loading AI model...';
const integration = await setupCodeCompletion();
statusEl.className = 'status ready';
statusEl.textContent = 'Ready! Start typing to see AI completions.';
// Override completion to track metrics
const originalGetCompletion = integration.getCompletion.bind(integration);
integration.getCompletion = async function(editor, position) {
const start = performance.now();
const result = await originalGetCompletion(editor, position);
const latency = performance.now() - start;
if (result) {
const tokenCount = result.split(/\s+/).length;
const cacheHit = latency < 10; // Assume cache hit if very fast
trackCompletion(latency, tokenCount, cacheHit);
}
return result;
};
} catch (error) {
console.error('Initialization error:', error);
const statusEl = document.getElementById('status');
statusEl.className = 'status error';
statusEl.textContent = 'Error: ' + error.message;
}
});
</script>
</body>
</html>Summary
These practical examples demonstrate:
- Browser-Based Code Analysis: Integration of Rust-based WASM analysis tools with Claude Code for comprehensive code review
- Edge AI Deployment: Using WASI-NN for deploying AI models at the edge with load balancing and monitoring
- Real-time Code Completion: WebLLM integration for browser-based code completion with performance monitoring
Each example includes:
- Complete, production-ready code
- Performance optimization techniques
- Monitoring and metrics collection
- Error handling and edge cases
- Integration with existing development tools