Team Ai
Apppublic

reach-vb/github-issue-generator-webgpu

sourceHugging Faceupdated 2y agoView on Hugging Face
56likes
index.js223 linesDownload Raw Back to src
1import { prebuiltAppConfig, CreateMLCEngine } from "@mlc-ai/web-llm";2import hljs from "highlight.js";3import ace from "ace-builds";4 5// Required for ace to resolve the module correctly6require("ace-builds/src-noconflict/mode-javascript");7require("ace-builds/webpack-resolver");8 9let engine = null;10let useCustomGrammar = false;11 12document.addEventListener("DOMContentLoaded", () => {13  const modelSelection = document.getElementById("model-selection");14  const promptTextarea = document.getElementById("prompt");15  const outputDiv = document.getElementById("output");16  const statsParagraph = document.getElementById("stats");17 18  // Populate model selection dropdown19  const availableModels = prebuiltAppConfig.model_list20    .filter(21      (m) =>22        m.model_id.startsWith("SmolLM2")23    )24    .map((m) => m.model_id);25 26  let selectedModel = availableModels[0];27 28  availableModels.forEach((modelId) => {29    const option = document.createElement("option");30    option.value = modelId;31    option.textContent = modelId;32    modelSelection.appendChild(option);33  });34 35  modelSelection.value = selectedModel;36 37  modelSelection.onchange = (e) => {38    selectedModel = e.target.value;39    engine = null; // Reset the engine when the model changes40  };41 42  // Setup JSON Schema Editor43  const jsonSchemaEditor = ace.edit("schema", {44    mode: "ace/mode/javascript",45    theme: "ace/theme/github",46    wrap: true,47  });48 49  // Set default schema50  jsonSchemaEditor.setValue(`{51    "title": "GitHubIssue",52    "type": "object",53    "properties": {54      "title": {55        "type": "string",56        "description": "The title of the issue"57      },58      "description": {59        "type": "string",60        "description": "Detailed description of the issue"61      },62      "labels": {63        "type": "array",64        "items": {65          "type": "string",66          "enum": ["bug", "enhancement", "documentation", "high-priority", "security"]67        },68        "description": "Labels to categorize the issue"69      },70      "priority": {71        "type": "string",72        "enum": ["low", "medium", "high", "critical"],73        "description": "Priority level of the issue"74      },75      "assignees": {76        "type": "array",77        "items": {78          "type": "string"79        },80        "maxItems": 3,81        "description": "GitHub usernames of assigned users (max 3)"82      },83      "estimated_time": {84        "type": "string",85        "pattern": "^[0-9]+[hdwm]$",86        "description": "Estimated time to resolve (e.g., '2h', '3d', '1w', '1m')"87      }88    },89    "required": [90      "title",91      "description",92      "priority",93      "labels"94    ]95  }`);96 97  // Set default prompt98  promptTextarea.value = `Create a detailed issue for the following problem:99 100The login page is not properly handling password reset requests. Users report that they are not receiving the password reset emails, and when they do receive them, the reset links are sometimes expired. This is affecting about 20% of our users and needs immediate attention.101 102Please include appropriate labels, priority level, and time estimation.`;103 104  // Generate button click handler105  document.getElementById("generate").onclick = async () => {106    try {107      outputDiv.innerHTML = '<div class="loading">Initializing model...</div>';108      109      if (!engine) {110        engine = await CreateMLCEngine(selectedModel, {111          initProgressCallback: (progress) => {112            console.log(progress);113            outputDiv.innerHTML = `<div class="loading">${progress.text}</div>`;114          },115        });116      }117 118      const schemaInput = jsonSchemaEditor.getValue();119      const schema = JSON.stringify(JSON.parse(schemaInput)); // Validate JSON120      const response_format = { type: "json_object", schema };121 122      const request = {123        stream: true,124        stream_options: { include_usage: true },125        messages: [{ role: "user", content: promptTextarea.value }],126        max_tokens: 512,127        response_format,128      };129 130      let curMessage = "";131      let usage = null;132      outputDiv.innerHTML = '<div class="loading">Generating response...</div>';133      134      const generator = await engine.chatCompletion(request);135 136      for await (const chunk of generator) {137        const curDelta = chunk.choices[0]?.delta.content;138        if (curDelta) curMessage += curDelta;139        if (chunk.usage) {140          console.log(chunk.usage);141          usage = chunk.usage;142        }143        outputDiv.innerHTML = `<pre><code class="language-json">${curMessage}</code></pre>`;144      }145 146      const finalMessage = await engine.getMessage();147      outputDiv.innerHTML = hljs.highlight(finalMessage, {148        language: "json",149      }).value;150 151      try {152        const issueData = JSON.parse(finalMessage);153        updateIssuePreview(issueData);154      } catch (error) {155        console.error('Failed to parse issue data:', error);156      }157 158      if (usage) {159        const statsTextParts = [];160        if (usage.extra.prefill_tokens_per_s) {161          statsTextParts.push(`Prefill Speed: ${usage.extra.prefill_tokens_per_s.toFixed(1)} tok/s`);162        }163        if (usage.extra.decode_tokens_per_s) {164          statsTextParts.push(`Decode Speed: ${usage.extra.decode_tokens_per_s.toFixed(1)} tok/s`);165        }166        statsParagraph.textContent = statsTextParts.join(" | ");167        statsParagraph.classList.remove("hidden");168      }169    } catch (error) {170      console.error("Generation error:", error);171      outputDiv.innerHTML = `<div class="error">Error: ${error.message}</div>`;172    }173  };174});175 176function updateIssuePreview(issueData) {177  const preview = document.getElementById('issue-preview');178  179  // Update title180  document.getElementById('preview-title').textContent = issueData.title;181  182  // Update priority badge183  const priorityBadge = document.getElementById('preview-priority');184  priorityBadge.textContent = issueData.priority.toUpperCase();185  priorityBadge.className = `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium priority-${issueData.priority}`;186  187  // Update estimated time188  document.getElementById('preview-time-text').textContent = issueData.estimated_time;189  190  // Update labels191  const labelsContainer = document.getElementById('preview-labels');192  labelsContainer.innerHTML = '';193  issueData.labels.forEach(label => {194    const labelEl = document.createElement('span');195    labelEl.className = `issue-label label-${label}`;196    labelEl.textContent = label;197    labelsContainer.appendChild(labelEl);198  });199  200  // Update description201  document.getElementById('preview-description').textContent = issueData.description;202  203  // Update assignees204  const assigneesContainer = document.getElementById('preview-assignees');205  assigneesContainer.innerHTML = '';206  if (issueData.assignees && issueData.assignees.length > 0) {207    issueData.assignees.forEach(assignee => {208      const avatarEl = document.createElement('div');209      avatarEl.className = 'assignee-avatar';210      avatarEl.textContent = assignee.charAt(0).toUpperCase();211      assigneesContainer.appendChild(avatarEl);212    });213  } else {214    assigneesContainer.innerHTML = '<span class="text-sm text-gray-500">No assignees</span>';215  }216  217  // Show the preview with animation218  preview.classList.remove('hidden');219  // Force a reflow220  preview.offsetHeight;221  preview.classList.add('visible');222}223