Skip to content

Commit e44ddc5

Browse files
committed
Include generated docs in local circuit prompt
1 parent 0fd302e commit e44ddc5

4 files changed

Lines changed: 134 additions & 42 deletions

File tree

lib/prompt-templates/create-local-circuit-prompt.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {
2+
fp,
23
getFootprintNamesByType,
34
getFootprintSizes,
4-
fp,
55
} from "@tscircuit/footprinter"
66

77
async function fetchFileContent(url: string): Promise<string> {
@@ -19,6 +19,24 @@ async function fetchFileContent(url: string): Promise<string> {
1919
}
2020
}
2121

22+
const generatedDocsUrl = "https://docs.tscircuit.com/ai.txt"
23+
let generatedDocsPromise: Promise<string> | undefined
24+
25+
export function clearGeneratedDocsCacheForTests() {
26+
generatedDocsPromise = undefined
27+
}
28+
29+
async function fetchGeneratedDocs(): Promise<string> {
30+
generatedDocsPromise ??= fetch(generatedDocsUrl)
31+
.then((response) => {
32+
if (!response.ok) return ""
33+
return response.text()
34+
})
35+
.catch(() => "")
36+
37+
return generatedDocsPromise
38+
}
39+
2240
export const createLocalCircuitPrompt = async () => {
2341
const footprintNamesByType = getFootprintNamesByType()
2442
const footprintSizes = getFootprintSizes()
@@ -33,10 +51,12 @@ export const createLocalCircuitPrompt = async () => {
3351
"",
3452
)
3553

36-
const propsDoc =
37-
(await fetchFileContent(
54+
const [propsDoc, generatedDocs] = await Promise.all([
55+
fetchFileContent(
3856
"https://raw.githubusercontent.com/tscircuit/props/main/generated/COMPONENT_TYPES.md",
39-
)) || ""
57+
),
58+
fetchGeneratedDocs(),
59+
])
4060

4161
const cleanedPropsDoc = propsDoc
4262
.split("\n")
@@ -51,6 +71,8 @@ YOU MUST ABIDE BY THE RULES IN THE RULES SECTION
5171
5272
## tscircuit API overview
5373
74+
${generatedDocs ? `Generated tscircuit documentation:\n\n${generatedDocs}\n\n` : ""}
75+
5476
Here's an overview of the tscircuit API:
5577
5678
<board width="10mm" height="10mm" /> // usually the root component
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
2+
import {
3+
clearGeneratedDocsCacheForTests,
4+
createLocalCircuitPrompt,
5+
} from "../../lib/prompt-templates/create-local-circuit-prompt"
6+
7+
const originalFetch = globalThis.fetch
8+
9+
beforeEach(() => {
10+
clearGeneratedDocsCacheForTests()
11+
})
12+
13+
afterEach(() => {
14+
globalThis.fetch = originalFetch
15+
clearGeneratedDocsCacheForTests()
16+
})
17+
18+
function mockDocsFetch(generatedDocsResponse: Response | Error) {
19+
const fetchMock = mock(async (url: string | URL | Request) => {
20+
const requestUrl = String(url)
21+
22+
if (requestUrl === "https://docs.tscircuit.com/ai.txt") {
23+
if (generatedDocsResponse instanceof Error) {
24+
throw generatedDocsResponse
25+
}
26+
27+
return generatedDocsResponse
28+
}
29+
30+
if (
31+
requestUrl ===
32+
"https://raw.githubusercontent.com/tscircuit/props/main/generated/COMPONENT_TYPES.md"
33+
) {
34+
return new Response("# Components\n\n<resistor />")
35+
}
36+
37+
throw new Error(`Unexpected fetch URL: ${requestUrl}`)
38+
})
39+
40+
globalThis.fetch = fetchMock as unknown as typeof fetch
41+
42+
return fetchMock
43+
}
44+
45+
describe("createLocalCircuitPrompt", () => {
46+
it("includes the generated tscircuit docs when they are available", async () => {
47+
mockDocsFetch(new Response("Generated docs content"))
48+
49+
const prompt = await createLocalCircuitPrompt()
50+
51+
expect(prompt).toContain("Generated tscircuit documentation:")
52+
expect(prompt).toContain("Generated docs content")
53+
})
54+
55+
it("keeps creating the prompt when generated docs cannot be fetched", async () => {
56+
mockDocsFetch(new Error("docs unavailable"))
57+
58+
const prompt = await createLocalCircuitPrompt()
59+
60+
expect(prompt).toContain("Here's an overview of the tscircuit API:")
61+
expect(prompt).not.toContain("Generated tscircuit documentation:")
62+
})
63+
})

tests/tscircuitCoder.test.ts

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,44 @@
1-
import { createTscircuitCoder } from "lib/tscircuit-coder/tscircuitCoder"
21
import { expect, test } from "bun:test"
2+
import { createTscircuitCoder } from "lib/tscircuit-coder/tscircuitCoder"
33
import { getPrimarySourceCodeFromVfs } from "lib/utils/get-primary-source-code-from-vfs"
44

5-
test("TscircuitCoder submitPrompt streams and updates vfs", async () => {
6-
const streamedChunks: string[] = []
7-
let vfsUpdated = false
8-
const tscircuitCoder = createTscircuitCoder()
9-
tscircuitCoder.on("streamedChunk", (chunk: string) => {
10-
streamedChunks.push(chunk)
11-
})
12-
tscircuitCoder.on("vfsChanged", () => {
13-
vfsUpdated = true
14-
})
15-
16-
await tscircuitCoder.submitPrompt({
17-
prompt: "create bridge rectifier circuit",
18-
})
19-
20-
await tscircuitCoder.submitPrompt({
21-
prompt: "add a transistor component",
22-
})
23-
24-
let codeWithTransistor = getPrimarySourceCodeFromVfs(tscircuitCoder.vfs)
25-
expect(codeWithTransistor).toInclude("transistor")
26-
27-
await tscircuitCoder.submitPrompt({
28-
prompt: "add a tssop20 chip",
29-
})
30-
31-
let codeWithChip = getPrimarySourceCodeFromVfs(tscircuitCoder.vfs)
32-
expect(codeWithChip).toInclude("tssop20")
33-
expect(codeWithChip).toInclude("transistor")
34-
35-
expect(streamedChunks.length).toBeGreaterThan(0)
36-
const vfsKeys = Object.keys(tscircuitCoder.vfs)
37-
expect(vfsKeys.length).toBeGreaterThan(0)
38-
expect(vfsUpdated).toBe(true)
39-
})
5+
const testWithOpenAi = process.env.OPENAI_API_KEY ? test : test.skip
6+
7+
testWithOpenAi(
8+
"TscircuitCoder submitPrompt streams and updates vfs",
9+
async () => {
10+
const streamedChunks: string[] = []
11+
let vfsUpdated = false
12+
const tscircuitCoder = createTscircuitCoder()
13+
tscircuitCoder.on("streamedChunk", (chunk: string) => {
14+
streamedChunks.push(chunk)
15+
})
16+
tscircuitCoder.on("vfsChanged", () => {
17+
vfsUpdated = true
18+
})
19+
20+
await tscircuitCoder.submitPrompt({
21+
prompt: "create bridge rectifier circuit",
22+
})
23+
24+
await tscircuitCoder.submitPrompt({
25+
prompt: "add a transistor component",
26+
})
27+
28+
const codeWithTransistor = getPrimarySourceCodeFromVfs(tscircuitCoder.vfs)
29+
expect(codeWithTransistor).toInclude("transistor")
30+
31+
await tscircuitCoder.submitPrompt({
32+
prompt: "add a tssop20 chip",
33+
})
34+
35+
const codeWithChip = getPrimarySourceCodeFromVfs(tscircuitCoder.vfs)
36+
expect(codeWithChip).toInclude("tssop20")
37+
expect(codeWithChip).toInclude("transistor")
38+
39+
expect(streamedChunks.length).toBeGreaterThan(0)
40+
const vfsKeys = Object.keys(tscircuitCoder.vfs)
41+
expect(vfsKeys.length).toBeGreaterThan(0)
42+
expect(vfsUpdated).toBe(true)
43+
},
44+
)

tests/utils/generate-random-prompts.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { describe, it, expect } from "bun:test"
1+
import { describe, expect, it } from "bun:test"
22
import { generateRandomPrompts } from "../../lib/utils/generate-random-prompts"
33

4+
const itWithOpenAi = process.env.OPENAI_API_KEY ? it : it.skip
5+
46
describe("generateRandomPrompts", () => {
5-
it("should return an array of prompts", async () => {
7+
itWithOpenAi("should return an array of prompts", async () => {
68
const prompts = await generateRandomPrompts(3)
79

810
expect(Array.isArray(prompts)).toBe(true)

0 commit comments

Comments
 (0)