From 82a9f7fb7d37c40131731e1cf983546b6d93aad8 Mon Sep 17 00:00:00 2001 From: jordans <65936156+jorgeschulz@users.noreply.github.com> Date: Tue, 12 May 2026 16:10:22 +0200 Subject: [PATCH] Include generated docs in local circuit prompt --- .../create-local-circuit-prompt.ts | 38 ++++++++++-- .../create-local-circuit-prompt.test.ts | 59 +++++++++++++++++++ ...tCoder.test.ts => tscircuit-coder.test.ts} | 10 ++-- tests/utils/generate-random-prompts.test.ts | 6 +- 4 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 tests/prompt-templates/create-local-circuit-prompt.test.ts rename tests/{tscircuitCoder.test.ts => tscircuit-coder.test.ts} (78%) diff --git a/lib/prompt-templates/create-local-circuit-prompt.ts b/lib/prompt-templates/create-local-circuit-prompt.ts index a93f11f..eb5e8ed 100644 --- a/lib/prompt-templates/create-local-circuit-prompt.ts +++ b/lib/prompt-templates/create-local-circuit-prompt.ts @@ -1,7 +1,7 @@ import { + fp, getFootprintNamesByType, getFootprintSizes, - fp, } from "@tscircuit/footprinter" async function fetchFileContent(url: string): Promise { @@ -19,6 +19,25 @@ async function fetchFileContent(url: string): Promise { } } +const GENERATED_DOCS_URL = "https://docs.tscircuit.com/ai.txt" + +let generatedDocsPromise: Promise | null = null + +const fetchGeneratedDocs = async () => { + generatedDocsPromise ??= fetchFileContent(GENERATED_DOCS_URL).catch( + (error) => { + console.error("Error fetching generated tscircuit docs:", error) + return "" + }, + ) + + return generatedDocsPromise +} + +export const clearGeneratedDocsCacheForTests = () => { + generatedDocsPromise = null +} + export const createLocalCircuitPrompt = async () => { const footprintNamesByType = getFootprintNamesByType() const footprintSizes = getFootprintSizes() @@ -33,10 +52,12 @@ export const createLocalCircuitPrompt = async () => { "", ) - const propsDoc = - (await fetchFileContent( + const [propsDoc, generatedDocs] = await Promise.all([ + fetchFileContent( "https://raw.githubusercontent.com/tscircuit/props/main/generated/COMPONENT_TYPES.md", - )) || "" + ), + fetchGeneratedDocs(), + ]) const cleanedPropsDoc = propsDoc .split("\n") @@ -44,11 +65,20 @@ export const createLocalCircuitPrompt = async () => { .join("\n") .replace(/\n\n+/g, "\n\n") + const generatedDocsSection = generatedDocs.trim() + ? ` +## Generated tscircuit docs + +${generatedDocs.trim()} +` + : "" + return ` You are an expert in electronic circuit design and tscircuit, and your job is to create a circuit board in tscircuit with the user-provided description. YOU MUST ABIDE BY THE RULES IN THE RULES SECTION +${generatedDocsSection} ## tscircuit API overview Here's an overview of the tscircuit API: diff --git a/tests/prompt-templates/create-local-circuit-prompt.test.ts b/tests/prompt-templates/create-local-circuit-prompt.test.ts new file mode 100644 index 0000000..a51049b --- /dev/null +++ b/tests/prompt-templates/create-local-circuit-prompt.test.ts @@ -0,0 +1,59 @@ +import { afterEach, beforeEach, expect, it } from "bun:test" +import { + clearGeneratedDocsCacheForTests, + createLocalCircuitPrompt, +} from "lib/prompt-templates/create-local-circuit-prompt" + +const originalFetch = globalThis.fetch +const originalConsoleError = console.error + +beforeEach(() => { + clearGeneratedDocsCacheForTests() + console.error = () => {} +}) + +afterEach(() => { + globalThis.fetch = originalFetch + console.error = originalConsoleError + clearGeneratedDocsCacheForTests() +}) + +it("includes generated tscircuit docs in the local circuit prompt", async () => { + const requestedUrls: string[] = [] + + globalThis.fetch = (async (input: RequestInfo | URL) => { + const url = input.toString() + requestedUrls.push(url) + + if (url === "https://docs.tscircuit.com/ai.txt") { + return new Response("GENERATED_AI_DOCS_SENTINEL", { status: 200 }) + } + + return new Response("# Components\nmock component props", { status: 200 }) + }) as typeof fetch + + const prompt = await createLocalCircuitPrompt() + + expect(prompt).toContain("## Generated tscircuit docs") + expect(prompt).toContain("GENERATED_AI_DOCS_SENTINEL") + expect(prompt).toContain("mock component props") + expect(requestedUrls).toContain("https://docs.tscircuit.com/ai.txt") +}) + +it("still creates a local circuit prompt when generated docs fetch fails", async () => { + globalThis.fetch = (async (input: RequestInfo | URL) => { + const url = input.toString() + + if (url === "https://docs.tscircuit.com/ai.txt") { + return new Response("not found", { status: 404, statusText: "Not Found" }) + } + + return new Response("# Components\nmock component props", { status: 200 }) + }) as typeof fetch + + const prompt = await createLocalCircuitPrompt() + + expect(prompt).toContain("## tscircuit API overview") + expect(prompt).toContain("mock component props") + expect(prompt).not.toContain("## Generated tscircuit docs") +}) diff --git a/tests/tscircuitCoder.test.ts b/tests/tscircuit-coder.test.ts similarity index 78% rename from tests/tscircuitCoder.test.ts rename to tests/tscircuit-coder.test.ts index d66c022..504cc78 100644 --- a/tests/tscircuitCoder.test.ts +++ b/tests/tscircuit-coder.test.ts @@ -1,8 +1,10 @@ -import { createTscircuitCoder } from "lib/tscircuit-coder/tscircuitCoder" import { expect, test } from "bun:test" +import { createTscircuitCoder } from "lib/tscircuit-coder/tscircuitCoder" import { getPrimarySourceCodeFromVfs } from "lib/utils/get-primary-source-code-from-vfs" -test("TscircuitCoder submitPrompt streams and updates vfs", async () => { +const openAiTest = process.env.OPENAI_API_KEY ? test : test.skip + +openAiTest("TscircuitCoder submitPrompt streams and updates vfs", async () => { const streamedChunks: string[] = [] let vfsUpdated = false const tscircuitCoder = createTscircuitCoder() @@ -21,14 +23,14 @@ test("TscircuitCoder submitPrompt streams and updates vfs", async () => { prompt: "add a transistor component", }) - let codeWithTransistor = getPrimarySourceCodeFromVfs(tscircuitCoder.vfs) + const codeWithTransistor = getPrimarySourceCodeFromVfs(tscircuitCoder.vfs) expect(codeWithTransistor).toInclude("transistor") await tscircuitCoder.submitPrompt({ prompt: "add a tssop20 chip", }) - let codeWithChip = getPrimarySourceCodeFromVfs(tscircuitCoder.vfs) + const codeWithChip = getPrimarySourceCodeFromVfs(tscircuitCoder.vfs) expect(codeWithChip).toInclude("tssop20") expect(codeWithChip).toInclude("transistor") diff --git a/tests/utils/generate-random-prompts.test.ts b/tests/utils/generate-random-prompts.test.ts index 41a061c..4b8b6e9 100644 --- a/tests/utils/generate-random-prompts.test.ts +++ b/tests/utils/generate-random-prompts.test.ts @@ -1,8 +1,10 @@ -import { describe, it, expect } from "bun:test" +import { describe, expect, it } from "bun:test" import { generateRandomPrompts } from "../../lib/utils/generate-random-prompts" describe("generateRandomPrompts", () => { - it("should return an array of prompts", async () => { + const openAiIt = process.env.OPENAI_API_KEY ? it : it.skip + + openAiIt("should return an array of prompts", async () => { const prompts = await generateRandomPrompts(3) expect(Array.isArray(prompts)).toBe(true)