Skip to content

feat: implement OpenAI-powered search routing (#97)#122

Open
Pitrat-wav wants to merge 11 commits intotscircuit:mainfrom
Pitrat-wav:feat/openai-search-routing
Open

feat: implement OpenAI-powered search routing (#97)#122
Pitrat-wav wants to merge 11 commits intotscircuit:mainfrom
Pitrat-wav:feat/openai-search-routing

Conversation

@Pitrat-wav
Copy link

@Pitrat-wav Pitrat-wav commented Feb 23, 2026

This PR adds an intelligent search router powered by OpenAI.

Changes:

  • Added getOpenAiClient utility.
  • Updated /api/search to route natural language queries to specialized component tables (resistor, capacitor, etc.).
  • Implemented fail-fast logic when OPENAI_API_KEY is missing for complex queries.
  • Refactored general search logic for better maintainability.

Verified build and formatting pass locally.

openai = getOpenAiClient()
} catch (err) {
// If not configured, only fail for complex queries
const isComplex = q && (q.split(" ").length > 1 || /[0-9]/.test(q))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The complex query detection logic incorrectly flags simple LCSC part number queries (like "C123") as complex because they contain numbers. This will cause these simple queries to fail with a 400 error when OPENAI_API_KEY is not configured, even though the executeGeneralSearch function handles them perfectly fine without OpenAI (lines 55-59).

Fix by excluding LCSC format from complex detection:

const isComplex = q && !(/^c\d+$/i.test(q)) && (q.split(" ").length > 1 || /[0-9]/.test(q))
Suggested change
const isComplex = q && (q.split(" ").length > 1 || /[0-9]/.test(q))
const isComplex = q && !(/^c\d+$/i.test(q)) && (q.split(" ").length > 1 || /[0-9]/.test(q))

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Comment on lines +128 to +134
let specQuery = ctx.db
.selectFrom(routing.table as any)
.selectAll()
.limit(limit)
for (const [k, v] of Object.entries(routing.filters || {})) {
specQuery = specQuery.where(k as any, "=", v as any)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The routing.table value from OpenAI response is used directly in database query without validation. If OpenAI returns an unexpected table name or if the response is manipulated, this could query arbitrary database tables or cause runtime errors.

Should validate against allowed tables:

const allowedTables = ['resistor', 'capacitor', 'diode', 'led', 'voltage_regulator', 'microcontroller'];
if (!allowedTables.includes(routing.table)) {
  return executeGeneralSearch(routing.q || q)
}
let specQuery = ctx.db
  .selectFrom(routing.table as any)
Suggested change
let specQuery = ctx.db
.selectFrom(routing.table as any)
.selectAll()
.limit(limit)
for (const [k, v] of Object.entries(routing.filters || {})) {
specQuery = specQuery.where(k as any, "=", v as any)
}
const allowedTables = ['resistor', 'capacitor', 'diode', 'led', 'voltage_regulator', 'microcontroller']
if (!allowedTables.includes(routing.table)) {
return executeGeneralSearch(routing.q || q)
}
let specQuery = ctx.db
.selectFrom(routing.table as any)
.selectAll()
.limit(limit)
for (const [k, v] of Object.entries(routing.filters || {})) {
specQuery = specQuery.where(k as any, "=", v as any)
}

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

pitrat and others added 6 commits February 23, 2026 19:38
Fixes failing tests for queries like 'STM32F401RCT6', '555 Timer', etc.
These simple part numbers should not require OpenAI API key.

Payment info: Wise or USDC/SOL to GKqwBPH4m6bkZj35j3WVvyJHHdUxddtmETH6ZA8W1aZH (PayPal unavailable in Uzbekistan).

#MANUAL folder gets created on build, ignore it
/MANUAL No newline at end of file
/MANUALbun.lock
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file paths are concatenated on a single line. This should be two separate entries:

/MANUAL
bun.lock

Currently this ignores a file called "MANUALbun.lock" instead of ignoring both "/MANUAL" and "bun.lock" separately.

Suggested change
/MANUALbun.lock
/MANUAL
bun.lock

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

- Add cf-proxy as a workspace in root package.json to ensure dependencies are properly linked
- Pin kysely-d1 to exact version 0.4.0 to avoid version mismatches
This should resolve 'Cannot find package kysely-d1' errors in CI.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant