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
187 changes: 15 additions & 172 deletions lib/prompt-templates/create-local-circuit-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,23 @@ export const createLocalCircuitPrompt = async () => {
.join("\n")
.replace(/\n\n+/g, "\n\n")

// Fetch auto-generated tscircuit docs for richer AI context
let autoGeneratedDocs = ""
try {
autoGeneratedDocs = await fetchFileContent("https://docs.tscircuit.com/ai.txt")
} catch (_e) {
// Non-fatal: proceed without auto-generated docs if unreachable
}

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

## Auto-Generated tscircuit Documentation

${autoGeneratedDocs}

## tscircuit API overview

Here's an overview of the tscircuit API:
Expand Down Expand Up @@ -124,151 +136,11 @@ ${cleanedPropsDoc}
2- powersourcesimple
3- pinheader

- Here are examples of how you can take advantage of those props:

// Example of a custom chip footprint definition
const CustomChipFootprint = () => (
<footprint originalLayer="top">
// SMT pads for the chip
<smtpad name="1" shape="rect" width="1mm" height="2mm" pcbX="-3mm" pcbY="2mm" />
<smtpad name="2" shape="rect" width="1mm" height="2mm" pcbX="-3mm" pcbY="-2mm" />
<smtpad name="3" shape="rect" width="1mm" height="2mm" pcbX="3mm" pcbY="-2mm" />
<smtpad name="4" shape="rect" width="1mm" height="2mm" pcbX="3mm" pcbY="2mm" />

// Silkscreen markings for the chip outline
<silkscreenrect width="8mm" height="6mm" pcbX="0" pcbY="0" />
<silkscreencircle radius="0.3mm" pcbX="-4mm" pcbY="3mm" /> // Pin 1 indicator
<silkscreentext text="U" pcbX="0" pcbY="0" fontSize="1mm" />
</footprint>
)

// Example of a custom resistor footprint
const Resistor0603Footprint = () => (
<footprint originalLayer="top">
<smtpad name="1" shape="rect" width="0.8mm" height="0.95mm" pcbX="-0.75mm" pcbY="0" />
<smtpad name="2" shape="rect" width="0.8mm" height="0.95mm" pcbX="0.75mm" pcbY="0" />
<silkscreenrect width="1.6mm" height="0.8mm" pcbX="0" pcbY="0" />
</footprint>
)

// Example of a complete circuit
export const MyCircuit = () => (
<board width="50mm" height="40mm">
// Power section group
<group name="power-section">
// Decoupling capacitor arrangement
<chip
name="U1"
footprint="soic8"
pcbX="10mm"
pcbY="10mm"
pinLabels={{
1: "VCC",
2: "GND",
3: "IN",
4: "OUT"
}}
/>

<capacitor
name="C1"
capacitance="100nF"
footprint="0402"
pcbX="12mm"
pcbY="10mm"
decouplingFor=".U1 .pin1"
decouplingTo="net.GND"
/>
</group>

// Input protection group
<group name="input-protection">
<resistor
name="R1"
resistance="10k"
footprint="0603"
pcbX="15mm"
pcbY="15mm"
/>

<diode
name="D1"
footprint="sot32"
pcbX="18mm"
pcbY="15mm"
/>
</group>

// Power nets
<net name="VCC" />
<net name="GND" />

// Connections
<trace
from=".C1 > .pin1",
to="net.VCC"
]} />
<trace
from=".C1 > .pin2",
to="net.GND"
]} />

// Layout constraints
<constraint
pcb={true}
xDist="2mm"
left=".U1"
right=".C1"
centerToCenter={true}
/>
</board>
)

// Example of a custom module/component that can be reused
export const DecouplingCapacitor = ({
chipRef,
capName,
capValue = "100nF",
distance = "2mm"
}) => (
<group name={\`decoupling-\${capName}\`}>
<capacitor
name={capName}
capacitance={capValue}
footprint="0402"
decouplingFor={\`\${chipRef} .pin1\`}
decouplingTo="net.GND"
/>
<constraint
pcb={true}
xDist={distance}
left={chipRef}
right={\`.\${capName}\`}
centerToCenter={true}
/>
</group>
)

// Usage of the custom module
export const CircuitWithDecoupling = () => (
<board width="50mm" height="40mm">
<chip name="U1" footprint="soic8" pcbX="10mm" pcbY="10mm" />
<DecouplingCapacitor
chipRef=".U1"
capName="C1"
capValue="100nF"
distance="2mm"
/>
</board>
)


### RULES

- decouplingFor must contain the selector for the component and the pin(eg. ".U1 .pin1", ".T1 .pin1")
- Never pass the component name alone as a selector for decouplingFor, always use the component name reference and the pin number
- Don't use hole or port components, always connect to the component pins
- Don't use inline comments which are comments in the same line as components, they are forbidden
- Port components must be children to a chip component.
- Never use components in the "Unsupported components" list
- Never use footprints in the "Unsupported footprints" list
Expand All @@ -280,36 +152,15 @@ ${cleanedPropsDoc}
to connect components.
- Any component can have a \`name\` prop
- \`pcbX\` and \`pcbY\` are optional and default to 0.
- A board is centered on the origin (pcbX=0, pcbY=0), so to place a component
at the center it must be placed at pcbX=0,pcbY=0. Similarly, if you're trying
to layout components around the center, you would make ones to the left of
the center have negative pcbX values, below the center have negative pcbY,
and to the right of the center have positive pcbX values, and above the
center have positive pcbY values.
- A board is centered on the origin (pcbX=0, pcbY=0)
- Every component that is going to be placed must be given a footprint
- Traces can only take two ports
- Don't use path as prop for trace, only use from, to
- We don't support defining output ports, so don't defined port components
- Don't specify autorouter; don't use the autorouter prop
- Selectors for component pins must be of this format: ".U1 > .pin1" or ".U1 > .pin2" where U1 is the component name, and the pins must be numbers, so don't use names for pins but use pin1, pin2, pin3, pin4
- And instead of ".T1 > .base" you do do ".T1 > .pin2"
- "for" must have at least two selectors for constraints

### Trace Reference Syntax

Traces are created using the \`<trace />\` component. The \`from\` and \`to\`
fields are CSS selectors that reference the components to connect.

Examples:

<trace from=".U1 > .pin1" to=".R1 > .pin1" />
<trace from=".U1 > .D3" to=".U1 > .GND" />
<trace from=".U1 > .D2" to="net.VCC" />
- Selectors for component pins must be of this format: ".U1 > .pin1" or ".U1 > .pin2"

### Output

Use a codefence with the language "tsx" to wrap the code. You can use the
current_code of the user as a starting point (if provided).
Use a codefence with the language "tsx" to wrap the code.

You must export a higher-order component where the root component is \`<board />\`
inside the codefence. For example:
Expand All @@ -326,11 +177,3 @@ export const MyLed = () => (

`.trim()
}

// ### Importing Components

// You can import a variety of components from the tscircuit registry. tscircuit
// registry components are always prefixed with \`@tsci/\`. Make sure to include
// your imports at the top of the codefence.

// If you are not told explicitly that an import exists, do not import it.
67 changes: 36 additions & 31 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 { getPrimarySourceCodeFromVfs } from "lib/utils/get-primary-source-code-from-vfs"
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
})
test("TscircuitCoder submitPrompt streams and updates vfs", async () => {
if (!process.env.OPENAI_API_KEY) {
console.log("Skipping: OPENAI_API_KEY not set in CI")
return
}
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: "create bridge rectifier circuit",
})

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

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

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

let codeWithChip = getPrimarySourceCodeFromVfs(tscircuitCoder.vfs)
expect(codeWithChip).toInclude("tssop20")
expect(codeWithChip).toInclude("transistor")
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)
})
expect(streamedChunks.length).toBeGreaterThan(0)
const vfsKeys = Object.keys(tscircuitCoder.vfs)
expect(vfsKeys.length).toBeGreaterThan(0)
expect(vfsUpdated).toBe(true)
})

19 changes: 12 additions & 7 deletions tests/utils/generate-random-prompts.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { describe, it, expect } from "bun:test"
import { generateRandomPrompts } from "../../lib/utils/generate-random-prompts"
import { generateRandomPrompts } from "../../lib/utils/generate-random-prompts"

describe("generateRandomPrompts", () => {
it("should return an array of prompts", async () => {
const prompts = await generateRandomPrompts(3)
describe("generateRandomPrompts", () => {
it("should return an array of prompts", async () => {
if (!process.env.OPENAI_API_KEY) {
console.log("Skipping: OPENAI_API_KEY not set in CI")
return
}
const prompts = await generateRandomPrompts(3)

expect(Array.isArray(prompts)).toBe(true)
expect(prompts.length).toBe(3)
expect(Array.isArray(prompts)).toBe(true)
expect(prompts.length).toBe(3)
})
})
})
Loading