scripts/smoke-eval-setup.mjs
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env node
/**
* Sets up a prompt + 10 varied calls so /evals has data to score.
*
* Creates:
* 1. Prompt "customer-support-smoke" v1 — friendly customer support persona
* 2. 10 proxy calls with X-Spanlens-Prompt-Version header
* Mix of "easy" and "harder" customer messages so the judge sees a
* range of response qualities.
*/
const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY
const SUPABASE_URL = process.env.SUPABASE_URL ?? 'http://127.0.0.1:54321'
const SUPABASE_SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY
const SPANLENS_KEY = process.env.SPANLENS_KEY
const ORG_ID = process.env.ORG_ID
const PROJECT_ID = process.env.PROJECT_ID
for (const [k, v] of Object.entries({
ENCRYPTION_KEY, SUPABASE_SERVICE_ROLE_KEY, SPANLENS_KEY, ORG_ID, PROJECT_ID,
})) {
if (!v) { console.error(`Missing env: ${k}`); process.exit(1) }
}
async function pgInsert(table, body) {
const res = await fetch(`${SUPABASE_URL}/rest/v1/${table}`, {
method: 'POST',
headers: {
'apikey': SUPABASE_SERVICE_ROLE_KEY,
'Authorization': `Bearer ${SUPABASE_SERVICE_ROLE_KEY}`,
'Content-Type': 'application/json',
'Prefer': 'return=representation',
},
body: JSON.stringify(body),
})
const text = await res.text()
if (!res.ok) throw new Error(`${table} INSERT failed (${res.status}): ${text}`)
return JSON.parse(text)
}
// ── 1. Create prompt version ────────────────────────────────────────────────
const promptContent = `You are a helpful, friendly customer support agent for SpanLens, an LLM observability platform.
Your goal: resolve customer issues efficiently and warmly. Always:
- Greet the customer
- Acknowledge their issue
- Provide a clear answer or next step
- End on a friendly note
If you don't know the answer, say so politely and offer to escalate.`
console.log('1. Creating prompt version...')
const [promptVersion] = await pgInsert('prompt_versions', {
organization_id: ORG_ID,
project_id: PROJECT_ID,
name: 'customer-support-smoke',
version: 1,
content: promptContent,
variables: [],
metadata: { source: 'smoke-eval-setup' },
})
console.log(` prompt_version_id = ${promptVersion.id}`)
console.log(` name = ${promptVersion.name}, version = ${promptVersion.version}`)
// ── 2. Make 10 varied calls referencing this prompt version ────────────────
const messages = [
'My subscription was charged twice this month, can you help?',
'How do I export my data?',
'WHY DOES YOUR APP CRASH ALL THE TIME???', // angry user — tests if model stays friendly
'Can you tell me how to integrate Spanlens with my Python app?',
'thanks', // very short — tests if model handles graceful close
'내 결제가 안 됐는데 어떻게 해야 하지?', // Korean — tests if it acknowledges + answers
'I need to cancel my subscription immediately, this is urgent!',
'What is the meaning of life?', // off-topic
'My API key keeps getting rejected, help',
'Hi! I just signed up and I love the dashboard 😊', // happy user — easy win for friendliness
]
console.log(`\n2. Running ${messages.length} OpenAI calls with X-Spanlens-Prompt-Version...`)
let success = 0
let failed = 0
for (let i = 0; i < messages.length; i++) {
const msg = messages[i]
try {
const res = await fetch('http://localhost:3001/proxy/openai/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${SPANLENS_KEY}`,
'Content-Type': 'application/json',
// Linking the call to the prompt version so /evals can find it
'X-Spanlens-Prompt-Version': `${promptVersion.name}@${promptVersion.version}`,
},
body: JSON.stringify({
model: 'gpt-4o-mini',
messages: [
{ role: 'system', content: promptContent },
{ role: 'user', content: msg },
],
max_tokens: 150,
}),
})
if (res.ok) {
const json = await res.json()
const reply = json.choices?.[0]?.message?.content ?? ''
console.log(` ${(i + 1).toString().padStart(2)}/10 ✓ "${msg.slice(0, 40)}..." → "${reply.slice(0, 60)}..."`)
success++
} else {
console.log(` ${(i + 1).toString().padStart(2)}/10 ✗ status=${res.status}`)
failed++
}
} catch (err) {
console.log(` ${(i + 1).toString().padStart(2)}/10 ✗ ${err.message}`)
failed++
}
await new Promise((r) => setTimeout(r, 300))
}
console.log(`\nResult: ${success} ok, ${failed} failed`)
console.log('\n── Next steps in the browser ───────────────────────────────────')
console.log(`Prompt name: ${promptVersion.name}`)
console.log(`Prompt version: v${promptVersion.version}`)
console.log()
console.log('1. /prompts → click "customer-support-smoke" to see version + calls')
console.log('2. /evals → "+ New evaluator"')
console.log(' - Prompt: customer-support-smoke')
console.log(' - Name: Friendliness')
console.log(' - Criterion: Is the response friendly, polite, and clearly addresses the customer issue?')
console.log(' - Judge provider: choose any (OpenAI / Anthropic / Gemini)')
console.log(' - Judge model: e.g. gpt-4o-mini or gemini-2.5-flash-lite (cheap)')
console.log('3. After saving → "Run" the evaluator → see scores roll in')
console.log('─────────────────────────────────────────────────────────────────')