Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions lib/prompt-templates/create-local-circuit-prompt.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
fp,
getFootprintNamesByType,
getFootprintSizes,
fp,
} from "@tscircuit/footprinter"

async function fetchFileContent(url: string): Promise<string> {
Expand All @@ -19,6 +19,24 @@ async function fetchFileContent(url: string): Promise<string> {
}
}

const generatedDocsUrl = "https://docs.tscircuit.com/ai.txt"
let generatedDocsPromise: Promise<string> | undefined

export function clearGeneratedDocsCacheForTests() {
generatedDocsPromise = undefined
}

async function fetchGeneratedDocs(): Promise<string> {
generatedDocsPromise ??= fetch(generatedDocsUrl)
.then((response) => {
if (!response.ok) return ""
return response.text()
})
.catch(() => "")

return generatedDocsPromise
}

export const createLocalCircuitPrompt = async () => {
const footprintNamesByType = getFootprintNamesByType()
const footprintSizes = getFootprintSizes()
Expand All @@ -33,10 +51,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")
Expand All @@ -51,6 +71,8 @@ YOU MUST ABIDE BY THE RULES IN THE RULES SECTION

## tscircuit API overview

${generatedDocs ? `Generated tscircuit documentation:\n\n${generatedDocs}\n\n` : ""}

Here's an overview of the tscircuit API:

<board width="10mm" height="10mm" /> // usually the root component
Expand Down
63 changes: 63 additions & 0 deletions tests/prompt-templates/create-local-circuit-prompt.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
import {
clearGeneratedDocsCacheForTests,
createLocalCircuitPrompt,
} from "../../lib/prompt-templates/create-local-circuit-prompt"

const originalFetch = globalThis.fetch

beforeEach(() => {
clearGeneratedDocsCacheForTests()
})

afterEach(() => {
globalThis.fetch = originalFetch
clearGeneratedDocsCacheForTests()
})

function mockDocsFetch(generatedDocsResponse: Response | Error) {
const fetchMock = mock(async (url: string | URL | Request) => {
const requestUrl = String(url)

if (requestUrl === "https://docs.tscircuit.com/ai.txt") {
if (generatedDocsResponse instanceof Error) {
throw generatedDocsResponse
}

return generatedDocsResponse
}

if (
requestUrl ===
"https://raw.githubusercontent.com/tscircuit/props/main/generated/COMPONENT_TYPES.md"
) {
return new Response("# Components\n\n<resistor />")
}

throw new Error(`Unexpected fetch URL: ${requestUrl}`)
})

globalThis.fetch = fetchMock as unknown as typeof fetch

return fetchMock
}

describe("createLocalCircuitPrompt", () => {
it("includes the generated tscircuit docs when they are available", async () => {
mockDocsFetch(new Response("Generated docs content"))

const prompt = await createLocalCircuitPrompt()

expect(prompt).toContain("Generated tscircuit documentation:")
expect(prompt).toContain("Generated docs content")
})

it("keeps creating the prompt when generated docs cannot be fetched", async () => {
mockDocsFetch(new Error("docs unavailable"))

const prompt = await createLocalCircuitPrompt()

expect(prompt).toContain("Here's an overview of the tscircuit API:")
expect(prompt).not.toContain("Generated tscircuit documentation:")
})
})
77 changes: 41 additions & 36 deletions tests/tscircuitCoder.test.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,44 @@
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 streamedChunks: string[] = []
let vfsUpdated = false
const tscircuitCoder = createTscircuitCoder()
tscircuitCoder.on("streamedChunk", (chunk: string) => {
streamedChunks.push(chunk)
})
tscircuitCoder.on("vfsChanged", () => {
vfsUpdated = true
})

await tscircuitCoder.submitPrompt({
prompt: "create bridge rectifier circuit",
})

await tscircuitCoder.submitPrompt({
prompt: "add a transistor component",
})

let codeWithTransistor = getPrimarySourceCodeFromVfs(tscircuitCoder.vfs)
expect(codeWithTransistor).toInclude("transistor")

await tscircuitCoder.submitPrompt({
prompt: "add a tssop20 chip",
})

let codeWithChip = getPrimarySourceCodeFromVfs(tscircuitCoder.vfs)
expect(codeWithChip).toInclude("tssop20")
expect(codeWithChip).toInclude("transistor")

expect(streamedChunks.length).toBeGreaterThan(0)
const vfsKeys = Object.keys(tscircuitCoder.vfs)
expect(vfsKeys.length).toBeGreaterThan(0)
expect(vfsUpdated).toBe(true)
})
const testWithOpenAi = process.env.OPENAI_API_KEY ? test : test.skip

testWithOpenAi(
"TscircuitCoder submitPrompt streams and updates vfs",
async () => {
const streamedChunks: string[] = []
let vfsUpdated = false
const tscircuitCoder = createTscircuitCoder()
tscircuitCoder.on("streamedChunk", (chunk: string) => {
streamedChunks.push(chunk)
})
tscircuitCoder.on("vfsChanged", () => {
vfsUpdated = true
})

await tscircuitCoder.submitPrompt({
prompt: "create bridge rectifier circuit",
})

await tscircuitCoder.submitPrompt({
prompt: "add a transistor component",
})

const codeWithTransistor = getPrimarySourceCodeFromVfs(tscircuitCoder.vfs)
expect(codeWithTransistor).toInclude("transistor")

await tscircuitCoder.submitPrompt({
prompt: "add a tssop20 chip",
})

const codeWithChip = getPrimarySourceCodeFromVfs(tscircuitCoder.vfs)
expect(codeWithChip).toInclude("tssop20")
expect(codeWithChip).toInclude("transistor")

expect(streamedChunks.length).toBeGreaterThan(0)
const vfsKeys = Object.keys(tscircuitCoder.vfs)
expect(vfsKeys.length).toBeGreaterThan(0)
expect(vfsUpdated).toBe(true)
},
)
6 changes: 4 additions & 2 deletions tests/utils/generate-random-prompts.test.ts
Original file line number Diff line number Diff line change
@@ -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"

const itWithOpenAi = process.env.OPENAI_API_KEY ? it : it.skip

describe("generateRandomPrompts", () => {
it("should return an array of prompts", async () => {
itWithOpenAi("should return an array of prompts", async () => {
const prompts = await generateRandomPrompts(3)

expect(Array.isArray(prompts)).toBe(true)
Expand Down
Loading