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
31 changes: 31 additions & 0 deletions lib/prompt-templates/create-local-circuit-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ async function fetchFileContent(url: string): Promise<string> {
}
}

async function fetchOptionalFileContent(url: string): Promise<string> {
try {
const response = await fetch(url)
if (!response.ok) {
console.warn(
`Skipping optional prompt context from ${url}: ${response.status} ${response.statusText}`,
)
return ""
}
return await response.text()
} catch (error) {
console.warn(`Skipping optional prompt context from ${url}:`, error)
return ""
}
}

export const createLocalCircuitPrompt = async () => {
const footprintNamesByType = getFootprintNamesByType()
const footprintSizes = getFootprintSizes()
Expand All @@ -38,6 +54,10 @@ export const createLocalCircuitPrompt = async () => {
"https://raw.githubusercontent.com/tscircuit/props/main/generated/COMPONENT_TYPES.md",
)) || ""

const generatedDocs = (
await fetchOptionalFileContent("https://docs.tscircuit.com/ai.txt")
).trim()

const cleanedPropsDoc = propsDoc
.split("\n")
.filter((line) => !line.startsWith("#"))
Expand Down Expand Up @@ -118,6 +138,17 @@ keep in mind that num_pins can be replaced with a number directly infront of the

${cleanedPropsDoc}

${
generatedDocs
? `### Auto-generated tscircuit docs

The following docs are generated from the current tscircuit documentation site:

${generatedDocs}
`
: ""
}

- Here is a list of unsupported components:

1- powersource
Expand Down
52 changes: 52 additions & 0 deletions tests/create-local-circuit-prompt.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { afterEach, describe, expect, test } from "bun:test"
import { createLocalCircuitPrompt } from "lib/prompt-templates/create-local-circuit-prompt"

const originalFetch = globalThis.fetch

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

const mockFetch = (handler: (url: string) => Response | Promise<Response>) => {
globalThis.fetch = ((input: RequestInfo | URL) => {
const url = input.toString()
return Promise.resolve(handler(url))
}) as typeof fetch
}

describe("createLocalCircuitPrompt", () => {
test("includes generated tscircuit docs when ai.txt is available", async () => {
mockFetch((url) => {
if (url === "https://docs.tscircuit.com/ai.txt") {
return new Response("Generated docs: prefer <chip /> pinLabels", {
status: 200,
})
}

return new Response("# Components\n\n<resistor />\n", { status: 200 })
})

const prompt = await createLocalCircuitPrompt()

expect(prompt).toContain("### Auto-generated tscircuit docs")
expect(prompt).toContain("Generated docs: prefer <chip /> pinLabels")
})

test("still creates a prompt when generated docs cannot be fetched", async () => {
mockFetch((url) => {
if (url === "https://docs.tscircuit.com/ai.txt") {
return new Response("not found", {
status: 404,
statusText: "Not Found",
})
}

return new Response("# Components\n\n<resistor />\n", { status: 200 })
})

const prompt = await createLocalCircuitPrompt()

expect(prompt).toContain("## tscircuit API overview")
expect(prompt).not.toContain("### Auto-generated tscircuit docs")
})
})
75 changes: 40 additions & 35 deletions tests/tscircuitCoder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,43 @@ import { createTscircuitCoder } from "lib/tscircuit-coder/tscircuitCoder"
import { expect, test } from "bun:test"
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 testWithOpenAiKey = process.env.OPENAI_API_KEY ? test : test.skip

testWithOpenAiKey(
"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)
},
)
4 changes: 3 additions & 1 deletion 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 { generateRandomPrompts } from "../../lib/utils/generate-random-prompts"

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

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

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