CoolFace
Apppublic

legends810/testingnew

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
selectStarterTemplate.ts306 linesDownload Raw Back to utils
1import ignore from 'ignore';2import type { ProviderInfo } from '~/types/model';3import type { Template } from '~/types/template';4import { STARTER_TEMPLATES } from './constants';5import Cookies from 'js-cookie';6 7const starterTemplateSelectionPrompt = (templates: Template[]) => `8You are an experienced developer who helps people choose the best starter template for their projects.9 10Available templates:11<template>12  <name>blank</name>13  <description>Empty starter for simple scripts and trivial tasks that don't require a full template setup</description>14  <tags>basic, script</tags>15</template>16${templates17  .map(18    (template) => `19<template>20  <name>${template.name}</name>21  <description>${template.description}</description>22  ${template.tags ? `<tags>${template.tags.join(', ')}</tags>` : ''}23</template>24`,25  )26  .join('\n')}27 28Response Format:29<selection>30  <templateName>{selected template name}</templateName>31  <title>{a proper title for the project}</title>32</selection>33 34Examples:35 36<example>37User: I need to build a todo app38Response:39<selection>40  <templateName>react-basic-starter</templateName>41  <title>Simple React todo application</title>42</selection>43</example>44 45<example>46User: Write a script to generate numbers from 1 to 10047Response:48<selection>49  <templateName>blank</templateName>50  <title>script to generate numbers from 1 to 100</title>51</selection>52</example>53 54Instructions:551. For trivial tasks and simple scripts, always recommend the blank template562. For more complex projects, recommend templates from the provided list573. Follow the exact XML format584. Consider both technical requirements and tags595. If no perfect match exists, recommend the closest option60 61Important: Provide only the selection tags in your response, no additional text.62MOST IMPORTANT: YOU DONT HAVE TIME TO THINK JUST START RESPONDING BASED ON HUNCH 63`;64 65const templates: Template[] = STARTER_TEMPLATES.filter((t) => !t.name.includes('shadcn'));66 67const parseSelectedTemplate = (llmOutput: string): { template: string; title: string } | null => {68  try {69    // Extract content between <templateName> tags70    const templateNameMatch = llmOutput.match(/<templateName>(.*?)<\/templateName>/);71    const titleMatch = llmOutput.match(/<title>(.*?)<\/title>/);72 73    if (!templateNameMatch) {74      return null;75    }76 77    return { template: templateNameMatch[1].trim(), title: titleMatch?.[1].trim() || 'Untitled Project' };78  } catch (error) {79    console.error('Error parsing template selection:', error);80    return null;81  }82};83 84export const selectStarterTemplate = async (options: { message: string; model: string; provider: ProviderInfo }) => {85  const { message, model, provider } = options;86  const requestBody = {87    message,88    model,89    provider,90    system: starterTemplateSelectionPrompt(templates),91  };92  const response = await fetch('/api/llmcall', {93    method: 'POST',94    body: JSON.stringify(requestBody),95  });96  const respJson: { text: string } = await response.json();97  console.log(respJson);98 99  const { text } = respJson;100  const selectedTemplate = parseSelectedTemplate(text);101 102  if (selectedTemplate) {103    return selectedTemplate;104  } else {105    console.log('No template selected, using blank template');106 107    return {108      template: 'blank',109      title: '',110    };111  }112};113 114const getGitHubRepoContent = async (115  repoName: string,116  path: string = '',117): Promise<{ name: string; path: string; content: string }[]> => {118  const baseUrl = 'https://api.github.com';119 120  try {121    const token = Cookies.get('githubToken') || import.meta.env.VITE_GITHUB_ACCESS_TOKEN;122 123    const headers: HeadersInit = {124      Accept: 'application/vnd.github.v3+json',125    };126 127    // Add your GitHub token if needed128    if (token) {129      headers.Authorization = 'token ' + token;130    }131 132    // Fetch contents of the path133    const response = await fetch(`${baseUrl}/repos/${repoName}/contents/${path}`, {134      headers,135    });136 137    if (!response.ok) {138      throw new Error(`HTTP error! status: ${response.status}`);139    }140 141    const data: any = await response.json();142 143    // If it's a single file, return its content144    if (!Array.isArray(data)) {145      if (data.type === 'file') {146        // If it's a file, get its content147        const content = atob(data.content); // Decode base64 content148        return [149          {150            name: data.name,151            path: data.path,152            content,153          },154        ];155      }156    }157 158    // Process directory contents recursively159    const contents = await Promise.all(160      data.map(async (item: any) => {161        if (item.type === 'dir') {162          // Recursively get contents of subdirectories163          return await getGitHubRepoContent(repoName, item.path);164        } else if (item.type === 'file') {165          // Fetch file content166          const fileResponse = await fetch(item.url, {167            headers,168          });169          const fileData: any = await fileResponse.json();170          const content = atob(fileData.content); // Decode base64 content171 172          return [173            {174              name: item.name,175              path: item.path,176              content,177            },178          ];179        }180 181        return [];182      }),183    );184 185    // Flatten the array of contents186    return contents.flat();187  } catch (error) {188    console.error('Error fetching repo contents:', error);189    throw error;190  }191};192 193export async function getTemplates(templateName: string, title?: string) {194  const template = STARTER_TEMPLATES.find((t) => t.name == templateName);195 196  if (!template) {197    return null;198  }199 200  const githubRepo = template.githubRepo;201  const files = await getGitHubRepoContent(githubRepo);202 203  let filteredFiles = files;204 205  /*206   * ignoring common unwanted files207   * exclude    .git208   */209  filteredFiles = filteredFiles.filter((x) => x.path.startsWith('.git') == false);210 211  // exclude    lock files212  const comminLockFiles = ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml'];213  filteredFiles = filteredFiles.filter((x) => comminLockFiles.includes(x.name) == false);214 215  // exclude    .bolt216  filteredFiles = filteredFiles.filter((x) => x.path.startsWith('.bolt') == false);217 218  // check for ignore file in .bolt folder219  const templateIgnoreFile = files.find((x) => x.path.startsWith('.bolt') && x.name == 'ignore');220 221  const filesToImport = {222    files: filteredFiles,223    ignoreFile: [] as typeof filteredFiles,224  };225 226  if (templateIgnoreFile) {227    // redacting files specified in ignore file228    const ignorepatterns = templateIgnoreFile.content.split('\n').map((x) => x.trim());229    const ig = ignore().add(ignorepatterns);230 231    // filteredFiles = filteredFiles.filter(x => !ig.ignores(x.path))232    const ignoredFiles = filteredFiles.filter((x) => ig.ignores(x.path));233 234    filesToImport.files = filteredFiles;235    filesToImport.ignoreFile = ignoredFiles;236  }237 238  const assistantMessage = `239<boltArtifact id="imported-files" title="${title || 'Importing Starter Files'}" type="bundled">240${filesToImport.files241  .map(242    (file) =>243      `<boltAction type="file" filePath="${file.path}">244${file.content}245</boltAction>`,246  )247  .join('\n')}248</boltArtifact>249`;250  let userMessage = ``;251  const templatePromptFile = files.filter((x) => x.path.startsWith('.bolt')).find((x) => x.name == 'prompt');252 253  if (templatePromptFile) {254    userMessage = `255TEMPLATE INSTRUCTIONS:256${templatePromptFile.content}257 258IMPORTANT: Dont Forget to install the dependencies before running the app259---260`;261  }262 263  if (filesToImport.ignoreFile.length > 0) {264    userMessage =265      userMessage +266      `267STRICT FILE ACCESS RULES - READ CAREFULLY:268 269The following files are READ-ONLY and must never be modified:270${filesToImport.ignoreFile.map((file) => `- ${file.path}`).join('\n')}271 272Permitted actions:273✓ Import these files as dependencies274✓ Read from these files275✓ Reference these files276 277Strictly forbidden actions:278❌ Modify any content within these files279❌ Delete these files280❌ Rename these files281❌ Move these files282❌ Create new versions of these files283❌ Suggest changes to these files284 285Any attempt to modify these protected files will result in immediate termination of the operation.286 287If you need to make changes to functionality, create new files instead of modifying the protected ones listed above.288---289`;290  }291 292  userMessage += `293---294template import is done, and you can now use the imported files,295edit only the files that need to be changed, and you can create new files as needed.296NO NOT EDIT/WRITE ANY FILES THAT ALREADY EXIST IN THE PROJECT AND DOES NOT NEED TO BE MODIFIED297---298Now that the Template is imported please continue with my original request299`;300 301  return {302    assistantMessage,303    userMessage,304  };305}306