import { useState, useEffect, useCallback } from "react"; import Dropdown from "./Dropdown"; import NumberInput from "./NumberInput"; import { getModelLayerCounts, initializeDefaultCookies, } from "../utils/modelCookies"; const Recipe = ({ layerRecipe, setLayerRecipe, embeddingLambdas, setEmbeddingLambdas, linearLambdas, setLinearLambdas, numLayers, selectedModel1, selectedModel2, }) => { const [modelLayerCounts, setModelLayerCounts] = useState({ model1: 12, model2: 12, }); const [expandedBlock, setExpandedBlock] = useState(null); useEffect(() => { initializeDefaultCookies(selectedModel1, selectedModel2); const counts = getModelLayerCounts(selectedModel1, selectedModel2); setModelLayerCounts(counts); }, [selectedModel1, selectedModel2]); const adjustLayerRecipe = useCallback(() => { const currentLength = layerRecipe.length; if (currentLength === numLayers) { return; // No change needed } let newRecipe = [...layerRecipe]; if (currentLength < numLayers) { // Add new layers to the end for (let i = currentLength; i < numLayers; i++) { newRecipe.push([[1, 0, 1.0]]); } } else { // Remove layers from the end newRecipe = newRecipe.slice(0, numLayers); } setLayerRecipe(newRecipe); }, [numLayers, layerRecipe, setLayerRecipe]); const initializeLayerRecipe = useCallback(() => { const recipe = []; for (let i = 0; i < numLayers; i++) { recipe.push([[1, 0, 1.0]]); } setLayerRecipe(recipe); }, [numLayers, setLayerRecipe]); useEffect(() => { if (layerRecipe.length === 0) { // Initialize if recipe is empty initializeLayerRecipe(); } else if (layerRecipe.length !== numLayers) { // Adjust existing recipe adjustLayerRecipe(); } }, [numLayers, layerRecipe.length, initializeLayerRecipe, adjustLayerRecipe]); const addBlockToLayer = (layerIndex) => { const newRecipe = [...layerRecipe]; // layer 1, model 0 or 1, weight 0.5 const newBlock = [1, Math.random() < 0.5 ? 0 : 1, 0.5]; newRecipe[layerIndex] = [...newRecipe[layerIndex], newBlock]; setLayerRecipe(newRecipe); }; const removeBlockFromLayer = (layerIndex, blockIndex) => { const newRecipe = [...layerRecipe]; if (newRecipe[layerIndex].length > 1) { newRecipe[layerIndex] = newRecipe[layerIndex].filter( (_, i) => i !== blockIndex ); setLayerRecipe(newRecipe); } }; const updateBlock = (layerIndex, blockIndex, field, value) => { const newRecipe = [...layerRecipe]; const block = [...newRecipe[layerIndex][blockIndex]]; if (field === "model") { block[1] = value; block[0] = 1; } else if (field === "sourceLayer") { // Ensure layer value is within valid range (1 to max layers for selected model) const selectedModel = block[1]; const maxLayers = selectedModel === 0 ? modelLayerCounts.model1 : modelLayerCounts.model2; const maxLayerValue = maxLayers === "N/A" ? 1 : maxLayers; block[0] = Math.max(1, Math.min(maxLayerValue, value)); } else if (field === "percentage") { block[2] = value / 100; } newRecipe[layerIndex][blockIndex] = block; setLayerRecipe(newRecipe); }; const updateEmbeddingLambda = (index, value) => { const newLambdas = [...embeddingLambdas]; newLambdas[index] = value / 100; setEmbeddingLambdas(newLambdas); }; const updateLinearLambda = (index, value) => { const newLambdas = [...linearLambdas]; newLambdas[index] = value / 100; setLinearLambdas(newLambdas); }; const modelOptions = [ { value: 0, label: "Model 1" }, { value: 1, label: "Model 2" }, ]; const getModelName = (modelValue) => { return modelValue === 0 ? "Model 1" : "Model 2"; }; const generateRandomRecipe = () => { const randomEmbedding1 = Math.random(); const randomEmbedding2 = 1 - randomEmbedding1; setEmbeddingLambdas([randomEmbedding1, randomEmbedding2]); const randomLinear1 = Math.random(); const randomLinear2 = 1 - randomLinear1; setLinearLambdas([randomLinear1, randomLinear2]); const newRecipe = []; for (let i = 0; i < numLayers; i++) { const numBlocks = Math.floor(Math.random() * 5) + 1; const layer = []; const weights = []; for (let j = 0; j < numBlocks; j++) { weights.push(Math.random()); } const totalWeight = weights.reduce((sum, w) => sum + w, 0); const normalizedWeights = weights.map((w) => w / totalWeight); for (let j = 0; j < numBlocks; j++) { const randomModel = Math.floor(Math.random() * 2); // 0 or 1 const maxLayers = randomModel === 0 ? modelLayerCounts.model1 : modelLayerCounts.model2; const maxLayerValue = maxLayers === "N/A" ? 1 : maxLayers; const randomLayer = Math.floor(Math.random() * maxLayerValue) + 1; layer.push([randomLayer, randomModel, normalizedWeights[j]]); } newRecipe.push(layer); } setLayerRecipe(newRecipe); }; const getBlockId = (layerIndex, blockIndex) => { return `${layerIndex}-${blockIndex}`; }; const toggleBlockExpanded = (layerIndex, blockIndex) => { const blockId = getBlockId(layerIndex, blockIndex); setExpandedBlock(expandedBlock === blockId ? null : blockId); }; return (

Layer Recipe

Embedding Layers

updateEmbeddingLambda(0, value)} min={0} max={100} /> updateEmbeddingLambda(1, value)} min={0} max={100} />

Linear Layers

updateLinearLambda(0, value)} min={0} max={100} /> updateLinearLambda(1, value)} min={0} max={100} />

Transformer Layers

Model 1:{" "} {modelLayerCounts.model1 === "N/A" ? "N/A layers" : `${modelLayerCounts.model1} layers`} Model 2:{" "} {modelLayerCounts.model2 === "N/A" ? "N/A layers" : `${modelLayerCounts.model2} layers`}
{layerRecipe.map((layer, layerIndex) => (

Layer {layerIndex + 1}

Total:{" "} {Math.round( layer.reduce((sum, block) => sum + block[2], 0) * 100 )} %
{layer.map((block, blockIndex) => { const blockId = getBlockId(layerIndex, blockIndex); const isExpanded = expandedBlock === blockId; const modelName = getModelName(block[1]); return (
{isExpanded && (
opt.value === block[1] )} onSelect={(option) => { updateBlock( layerIndex, blockIndex, "model", option.value ); }} options={modelOptions} placeholder="Select model..." className="w-full" />
updateBlock( layerIndex, blockIndex, "sourceLayer", value ) } min={1} max={ block[1] === 0 ? modelLayerCounts.model1 === "N/A" ? 1 : modelLayerCounts.model1 : modelLayerCounts.model2 === "N/A" ? 1 : modelLayerCounts.model2 } compact={true} />
updateBlock( layerIndex, blockIndex, "percentage", value ) } min={0} max={100} compact={true} />
{layer.length > 1 && ( )}
)}
); })}
))}
); }; export default Recipe;