Compare commits

...

1 Commits

Author SHA1 Message Date
Ryan Vogel
bc3fe8f4f5 add LLM-based enterprise lead scoring and auto-replies
Extract delivery logic from the enterprise route into a dedicated
module that grades inquiries via the Zen responses API, falls back
to simple regex scoring on failure, and sends templated auto-replies
for generic and procurement-blocked leads.

Made-with: Cursor
2026-03-19 17:38:16 -04:00
5 changed files with 380 additions and 52 deletions

View File

@@ -11,6 +11,7 @@
"start": "vite start"
},
"dependencies": {
"@ai-sdk/openai": "2.0.89",
"@cloudflare/vite-plugin": "1.15.2",
"@ibm/plex": "6.4.1",
"@jsx-email/render": "1.1.1",
@@ -26,6 +27,7 @@
"@solidjs/router": "catalog:",
"@solidjs/start": "catalog:",
"@stripe/stripe-js": "8.6.1",
"ai": "catalog:",
"chart.js": "4.5.1",
"nitro": "3.0.1-alpha.1",
"solid-js": "catalog:",

View File

@@ -0,0 +1,309 @@
import { createOpenAI } from "@ai-sdk/openai"
import { AWS } from "@opencode-ai/console-core/aws.js"
import { generateObject } from "ai"
import { z } from "zod"
import { createLead } from "./salesforce"
const links = [
{ label: "Docs", url: "https://opencode.ai/docs" },
{ label: "Discord Community", url: "https://discord.gg/scN9YX6Fdd" },
{ label: "GitHub", url: "https://github.com/anomalyco/opencode" },
]
const from = "Stefan <stefan@anoma.ly>"
const sign = "Stefan"
const shape = z.object({
company: z.string().nullable().describe("Company name. Use null when unknown."),
size: z
.enum(["1-50", "51-250", "251-1000", "1001+"])
.nullable()
.describe("Company size bucket. Use null when unknown."),
first: z.string().nullable().describe("First name only. Use null when unknown."),
title: z.string().nullable().describe("Job title or role. Use null when unknown."),
seats: z.number().int().positive().nullable().describe("Approximate seat count. Use null when unknown."),
procurement: z
.boolean()
.describe("True when the inquiry is blocked on procurement, legal, vendor, security, or compliance review."),
effort: z
.enum(["low", "medium", "high"])
.describe("Lead quality based on how specific and commercially relevant the inquiry is."),
summary: z.string().nullable().describe("One sentence summary for the sales team. Use null when unnecessary."),
})
const system = [
"You triage inbound enterprise inquiries for OpenCode.",
"Extract the fields from the form data and message.",
"Do not invent facts. Use null when a field is unknown.",
"First name should only contain the given name.",
"Seats should only be set when the inquiry mentions or strongly implies a team, user, developer, or seat count.",
"Procurement should be true when the inquiry mentions approval, review, legal, vendor, security, or compliance processes.",
"Effort is low for vague or generic inquiries, medium for some business context, and high for strong buying intent, rollout scope, or blockers.",
].join("\n")
export interface Inquiry {
name: string
role: string
company?: string
email: string
phone?: string
alias?: string
message: string
}
export type Score = z.infer<typeof shape>
type Kind = "generic" | "procurement"
type Mail = {
subject: string
text: string
html: string
}
function field(text?: string | null) {
const value = text?.trim()
if (!value) return null
return value
}
function safe(text: string) {
return text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/\"/g, "&quot;")
.replace(/'/g, "&#39;")
}
function html(text: string) {
return safe(text).replace(/\n/g, "<br>")
}
export function fallback(input: Inquiry): Score {
const text = [input.role, input.company, input.message].filter(Boolean).join("\n").toLowerCase()
const procurement = /procurement|security|vendor|legal|approval|questionnaire|compliance/.test(text)
const words = input.message.trim().split(/\s+/).filter(Boolean).length
return {
company: field(input.company),
size: null,
first: input.name.split(/\s+/)[0] ?? null,
title: field(input.role),
seats: null,
procurement,
effort: procurement ? "high" : words < 18 ? "low" : "medium",
summary: null,
}
}
async function grade(input: Inquiry): Promise<Score> {
const zen = createOpenAI({
apiKey: "public",
baseURL: "https://opencode.ai/zen/v1",
})
return generateObject({
model: zen.responses("gpt-5"),
schema: shape,
system,
prompt: JSON.stringify(
{
name: input.name,
role: input.role,
company: field(input.company),
email: input.email,
phone: field(input.phone),
message: input.message,
},
null,
2,
),
})
.then((result) => result.object)
.catch((err) => {
console.error("Failed to grade enterprise inquiry:", err)
return fallback(input)
})
}
export function kind(score: Score): Kind | null {
if (score.procurement) return "procurement"
if (score.effort === "low") return "generic"
return null
}
function refs(kind: Kind) {
const text = links.map(
(item) => `${item.label}: ${item.url}${kind === "procurement" && item.label === "GitHub" ? " (MIT licensed)" : ""}`,
)
const markup = links
.map(
(item) =>
`<li><a href="${item.url}">${safe(item.label)}</a>${kind === "procurement" && item.label === "GitHub" ? " (MIT licensed)" : ""}</li>`,
)
.join("")
return { text, markup }
}
export function reply(kind: Kind, name: string | null): Mail {
const who = name ?? "there"
const list = refs(kind)
if (kind === "generic") {
return {
subject: "Thanks for reaching out to OpenCode",
text: [
`Hi ${who},`,
"",
"Thanks for reaching out, we're happy to hear from you! We've received your message and are working through it. We're a small team doing our best to get back to everyone, so thank you for bearing with us.",
"",
"To help while you wait, here are some great places to start:",
...list.text,
"",
"Hope you find what you need in there! Don't hesitate to reply if you have something more specific in mind.",
"",
"Best,",
sign,
].join("\n"),
html: [
`<p>Hi ${safe(who)},</p>`,
"<p>Thanks for reaching out, we're happy to hear from you! We've received your message and are working through it. We're a small team doing our best to get back to everyone, so thank you for bearing with us.</p>",
"<p>To help while you wait, here are some great places to start:</p>",
`<ul>${list.markup}</ul>`,
"<p>Hope you find what you need in there! Don&#39;t hesitate to reply if you have something more specific in mind.</p>",
`<p>Best,<br>${safe(sign)}</p>`,
].join(""),
}
}
return {
subject: "OpenCode security and procurement notes",
text: [
`Hi ${who},`,
"",
"Thanks for reaching out! We're a small team working through messages as fast as we can, so thanks for bearing with us.",
"",
"A few notes that may help while this moves through security or procurement:",
"- OpenCode is open source and MIT licensed.",
"- Our managed offering is SOC 1 compliant.",
"- Our managed offering is currently in the observation period for SOC 2.",
"",
"If anything is held up on the procurement or legal side, just reply and I'll get you whatever you need to keep things moving.",
"",
"To help while you wait, here are some great places to start:",
...list.text,
"",
"Best,",
sign,
].join("\n"),
html: [
`<p>Hi ${safe(who)},</p>`,
"<p>Thanks for reaching out! We&#39;re a small team working through messages as fast as we can, so thanks for bearing with us.</p>",
"<p>A few notes that may help while this moves through security or procurement:</p>",
"<ul><li>OpenCode is open source and MIT licensed.</li><li>Our managed offering is SOC 1 compliant.</li><li>Our managed offering is currently in the observation period for SOC 2.</li></ul>",
"<p>If anything is held up on the procurement or legal side, just reply and I&#39;ll get you whatever you need to keep things moving.</p>",
"<p>To help while you wait, here are some great places to start:</p>",
`<ul>${list.markup}</ul>`,
`<p>Best,<br>${safe(sign)}</p>`,
].join(""),
}
}
function rows(input: Inquiry, score: Score, kind: Kind | null) {
return [
{ label: "Name", value: input.name },
{ label: "Email", value: input.email },
{ label: "Phone", value: field(input.phone) ?? "Unknown" },
{ label: "Auto Reply", value: kind ?? "manual" },
{ label: "Company", value: score.company ?? "Unknown" },
{ label: "Company Size", value: score.size ?? "Unknown" },
{ label: "First Name", value: score.first ?? "Unknown" },
{ label: "Title", value: score.title ?? "Unknown" },
{ label: "Seats", value: score.seats ? String(score.seats) : "Unknown" },
{ label: "Procurement", value: score.procurement ? "Yes" : "No" },
{ label: "Effort", value: score.effort },
{ label: "Summary", value: score.summary ?? "None" },
]
}
function report(input: Inquiry, score: Score, kind: Kind | null): Mail {
const list = rows(input, score, kind)
return {
subject: `Enterprise Inquiry from ${input.name}${kind ? ` (${kind})` : ""}`,
text: [
"New enterprise inquiry",
"",
...list.map((item) => `${item.label}: ${item.value}`),
"",
"Message:",
input.message,
].join("\n"),
html: [
"<p><strong>New enterprise inquiry</strong></p>",
...list.map((item) => `<p><strong>${safe(item.label)}:</strong> ${html(item.value)}</p>`),
`<p><strong>Message:</strong><br>${html(input.message)}</p>`,
].join(""),
}
}
function note(input: Inquiry, score: Score, kind: Kind | null) {
return [input.message, "", "---", ...rows(input, score, kind).map((item) => `${item.label}: ${item.value}`)].join(
"\n",
)
}
export async function deliver(input: Inquiry) {
const score = await grade(input)
const next = kind(score)
const msg = report(input, score, next)
const auto = next ? reply(next, score.first) : null
const jobs = [
{
name: "salesforce",
job: createLead({
name: input.name,
role: score.title ?? input.role,
company: score.company ?? field(input.company) ?? undefined,
email: input.email,
phone: field(input.phone) ?? undefined,
message: note(input, score, next),
}),
},
{
name: "internal",
job: AWS.sendEmail({
from,
to: "contact@anoma.ly",
subject: msg.subject,
body: msg.text,
html: msg.html,
replyTo: input.email,
}),
},
...(auto
? [
{
name: "reply",
job: AWS.sendEmail({
from,
to: input.email,
subject: auto.subject,
body: auto.text,
html: auto.html,
}),
},
]
: []),
]
const out = await Promise.allSettled(jobs.map((item) => item.job))
out.forEach((item, index) => {
const name = jobs[index]!.name
if (item.status === "rejected") {
console.error(`Enterprise ${name} failed:`, item.reason)
return
}
if (name === "salesforce" && !item.value) {
console.error("Enterprise salesforce lead failed")
}
})
}

View File

@@ -1,23 +1,13 @@
import type { APIEvent } from "@solidjs/start/server"
import { AWS } from "@opencode-ai/console-core/aws.js"
import { waitUntil } from "@opencode-ai/console-resource"
import { i18n } from "~/i18n"
import { localeFromRequest } from "~/lib/language"
import { createLead } from "~/lib/salesforce"
interface EnterpriseFormData {
name: string
role: string
company?: string
email: string
phone?: string
alias?: string
message: string
}
import { deliver, type Inquiry } from "~/lib/enterprise"
export async function POST(event: APIEvent) {
const dict = i18n(localeFromRequest(event.request))
try {
const body = (await event.request.json()) as EnterpriseFormData
const body = (await event.request.json()) as Inquiry
const trap = typeof body.alias === "string" ? body.alias.trim() : ""
if (trap) {
@@ -33,45 +23,14 @@ export async function POST(event: APIEvent) {
return Response.json({ error: dict["enterprise.form.error.invalidEmailFormat"] }, { status: 400 })
}
const emailContent = `
${body.message}<br><br>
--<br>
${body.name}<br>
${body.role}<br>
${body.company ? `${body.company}<br>` : ""}${body.email}<br>
${body.phone ? `${body.phone}<br>` : ""}`.trim()
const [lead, mail] = await Promise.all([
createLead({
name: body.name,
role: body.role,
company: body.company,
email: body.email,
phone: body.phone,
message: body.message,
}),
AWS.sendEmail({
to: "contact@anoma.ly",
subject: `Enterprise Inquiry from ${body.name}`,
body: emailContent,
replyTo: body.email,
}).then(
() => true,
(err) => {
console.error("Failed to send enterprise email:", err)
return false
},
),
])
if (!lead && !mail) {
console.error("Enterprise inquiry delivery failed", { email: body.email })
return Response.json({ error: dict["enterprise.form.error.internalServer"] }, { status: 500 })
}
const job = deliver(body).catch((error) => {
console.error("Error processing enterprise form:", error)
})
void waitUntil(job)
return Response.json({ success: true, message: dict["enterprise.form.success.submitted"] }, { status: 200 })
} catch (error) {
console.error("Error processing enterprise form:", error)
console.error("Error reading enterprise form:", error)
return Response.json({ error: dict["enterprise.form.error.internalServer"] }, { status: 500 })
}
}

View File

@@ -0,0 +1,53 @@
import { describe, expect, test } from "bun:test"
import { fallback, kind, reply, type Score } from "../src/lib/enterprise"
describe("enterprise lead routing", () => {
test("routes procurement blockers to procurement reply", () => {
const score = fallback({
name: "Jane Doe",
role: "CTO",
company: "Acme",
email: "jane@acme.com",
message: "We're stuck in procurement, security review, and vendor approval through Coupa.",
})
expect(score.procurement).toBe(true)
expect(kind(score)).toBe("procurement")
})
test("routes vague inquiries to the generic reply", () => {
const score = fallback({
name: "Jane Doe",
role: "Engineer",
email: "jane@example.com",
message: "Can you tell me more about enterprise pricing?",
})
expect(score.effort).toBe("low")
expect(kind(score)).toBe("generic")
})
test("keeps high intent leads for manual follow-up", () => {
const score: Score = {
company: "Acme",
size: "1001+",
first: "Jane",
title: "CTO",
seats: 500,
procurement: false,
effort: "high",
summary: "Large rollout with clear buying intent.",
}
expect(kind(score)).toBeNull()
})
test("renders the procurement reply with security notes", () => {
const mail = reply("procurement", "Jane")
expect(mail.subject).toContain("security")
expect(mail.text).toContain("SOC 1 compliant")
expect(mail.text).toContain("MIT licensed")
expect(mail.html).toContain("Stefan")
})
})

View File

@@ -19,12 +19,17 @@ export namespace AWS {
export const sendEmail = fn(
z.object({
from: z.string().optional(),
to: z.string(),
subject: z.string(),
body: z.string(),
text: z.string().optional(),
html: z.string().optional(),
replyTo: z.string().optional(),
}),
async (input) => {
const text = input.text ?? input.body
const html = input.html ?? input.body
const res = await createClient().fetch("https://email.us-east-1.amazonaws.com/v2/email/outbound-emails", {
method: "POST",
headers: {
@@ -32,7 +37,7 @@ export namespace AWS {
"Content-Type": "application/json",
},
body: JSON.stringify({
FromEmailAddress: `OpenCode Zen <contact@anoma.ly>`,
FromEmailAddress: input.from ?? "OpenCode Zen <contact@anoma.ly>",
Destination: {
ToAddresses: [input.to],
},
@@ -46,11 +51,11 @@ export namespace AWS {
Body: {
Text: {
Charset: "UTF-8",
Data: input.body,
Data: text,
},
Html: {
Charset: "UTF-8",
Data: input.body,
Data: html,
},
},
},