#!/usr/bin/env node // Demo script that actually uses the PoC functionality to demonstrate real query generation import fs from "node:fs"; import OpenAI from "openai"; // Import PoC components (we'll need to extract them to make them reusable) const MODEL = process.env.OPENAI_MODEL || "gpt-5"; const ODMDB_BASE_PATH = "../smatchitObjectOdmdb"; const SCHEMA_PATH = `${ODMDB_BASE_PATH}/schema`; console.log("šŸš€ ODMDB NL to Query Demo - Live PoC Testing"); console.log("=".repeat(60)); // Check prerequisites if (!process.env.OPENAI_API_KEY) { console.log("āŒ Missing OPENAI_API_KEY environment variable"); console.log(" Set it with: export OPENAI_API_KEY=sk-your-api-key"); process.exit(1); } // Load schema (same function as in poc.js) function loadJsonSafe(path) { try { if (fs.existsSync(path)) { return JSON.parse(fs.readFileSync(path, "utf-8")); } } catch (e) { console.warn(`Warning: Could not load ${path}:`, e.message); } return null; } // Load actual ODMDB schemas const SCHEMAS = { seekers: loadJsonSafe(`${SCHEMA_PATH}/seekers.json`), main: loadJsonSafe("./main.json"), // Fallback consolidated schema }; // Simplified SchemaMapper for demo class DemoSchemaMapper { constructor(schemas) { this.seekersSchema = schemas.seekers; console.log( `šŸ“‹ Loaded seekers schema with ${ Object.keys(this.seekersSchema?.properties || {}).length } properties` ); } getRecruiterReadableFields() { if (!this.seekersSchema?.apxaccessrights?.recruiters?.R) { return ["alias", "email", "seekstatus", "seekworkingyear"]; } return this.seekersSchema.apxaccessrights.recruiters.R; } getAllSeekersFields() { if (!this.seekersSchema?.properties) return []; return Object.keys(this.seekersSchema.properties); } } const schemaMapper = new DemoSchemaMapper(SCHEMAS); // Sample queries to demonstrate with actual PoC execution const demoQueries = [ { nl: "show me seekers with status startasap and their email and experience", description: "Status-based filtering with field selection", }, { nl: "find seekers looking for jobs urgently with salary expectations", description: "Status synonym mapping + salary field", }, { nl: "get seekers with their contact info and personality types", description: "Multiple field types (contact + MBTI)", }, ]; console.log("ļæ½ Demo Queries - Testing Live PoC:"); // JSON Schema for query generation (same as poc.js) function buildResponseJsonSchema() { const recruiterReadableFields = schemaMapper.getRecruiterReadableFields(); return { type: "object", additionalProperties: false, properties: { object: { type: "string", enum: ["seekers"] }, condition: { type: "array", items: { type: "string" }, minItems: 1 }, fields: { type: "array", items: { type: "string", enum: recruiterReadableFields }, minItems: 1, }, }, required: ["object", "condition", "fields"], }; } // System prompt (simplified version from poc.js) function systemPrompt() { const availableFields = schemaMapper.getAllSeekersFields(); const recruiterReadableFields = schemaMapper.getRecruiterReadableFields(); return [ "You convert a natural language request into an ODMDB search payload.", "Return ONLY a compact JSON object that matches the provided JSON Schema.", "", "ODMDB DSL:", "- idx.(value) - for indexed fields", "- prop.(operator:value) - for direct property queries", "", "Available seekers fields:", availableFields.slice(0, 15).join(", ") + (availableFields.length > 15 ? "..." : ""), "", "Recruiter-readable fields (use these for field selection):", recruiterReadableFields.join(", "), "", "Field mappings:", "- 'email', 'contact info' → email", "- 'experience', 'years of experience' → seekworkingyear", "- 'status', 'availability' → seekstatus", "- 'salary', 'pay' → salaryexpectation", "- 'personality', 'MBTI' → mbti", "", "Status value mappings:", "- 'urgent', 'urgently', 'ASAP' → startasap", "- 'no rush', 'taking time' → norush", "- 'not looking' → notlooking", "", "Rules: Object must be 'seekers'. Use idx.seekstatus_alias for status queries.", ].join("\n"); } // OpenAI client and query function const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); async function generateQuery(nlText) { try { const resp = await client.responses.create({ model: MODEL, input: [ { role: "system", content: systemPrompt() }, { role: "user", content: `Natural language request: "${nlText}"\nReturn ONLY the JSON object.`, }, ], text: { format: { name: "OdmdbQuery", type: "json_schema", schema: buildResponseJsonSchema(), strict: true, }, }, }); const jsonText = resp.output_text || resp.output?.[0]?.content?.[0]?.text; return JSON.parse(jsonText); } catch (error) { console.error(`āŒ Query generation failed: ${error.message}`); return null; } } // Simple query execution (simplified from poc.js) function loadSeekersData() { const seekersItemsPath = `${ODMDB_BASE_PATH}/objects/seekers/itm`; try { const files = fs .readdirSync(seekersItemsPath) .filter((file) => file.endsWith(".json") && file !== "backup") .slice(0, 10); // Just 10 files for demo speed const seekers = []; for (const file of files) { try { const filePath = `${seekersItemsPath}/${file}`; const data = JSON.parse(fs.readFileSync(filePath, "utf-8")); seekers.push(data); } catch (error) { // Skip invalid files } } return seekers; } catch (error) { return []; } } async function executeQuery(query) { const allSeekers = loadSeekersData(); if (allSeekers.length === 0) return { data: [] }; let filteredSeekers = allSeekers; // Simple filtering for (const condition of query.condition) { if (condition.includes("idx.seekstatus_alias(startasap)")) { filteredSeekers = filteredSeekers.filter( (seeker) => seeker.seekstatus === "startasap" ); } if (condition.includes("prop.salaryexpectation(exists:true)")) { filteredSeekers = filteredSeekers.filter( (seeker) => seeker.salaryexpectation ); } if (condition.includes("prop.email(exists:true)")) { filteredSeekers = filteredSeekers.filter((seeker) => seeker.email); } if (condition.includes("prop.mbti(exists:true)")) { filteredSeekers = filteredSeekers.filter((seeker) => seeker.mbti); } } // Select only requested fields const results = filteredSeekers.map((seeker) => { const filtered = {}; for (const field of query.fields) { if (seeker.hasOwnProperty(field)) { filtered[field] = seeker[field]; } } return filtered; }); return { data: results }; } // Main demo execution async function runDemo() { const executeQueries = process.env.EXECUTE_DEMO === "true"; for (let i = 0; i < demoQueries.length; i++) { const query = demoQueries[i]; console.log(`\n${i + 1}. "${query.nl}"`); console.log(` Purpose: ${query.description}`); console.log(" šŸ¤– Generating query..."); const generatedQuery = await generateQuery(query.nl); if (generatedQuery) { console.log(" āœ… Generated ODMDB Query:"); console.log( ` ${JSON.stringify(generatedQuery, null, 6).replace(/\n/g, "\n ")}` ); if (executeQueries) { console.log(" šŸ” Executing query..."); const results = await executeQuery(generatedQuery); console.log(` šŸ“Š Found ${results.data.length} results`); if (results.data.length > 0) { console.log(" šŸ“‹ Sample result:"); console.log( ` ${JSON.stringify(results.data[0], null, 6).replace( /\n/g, "\n " )}` ); } } } else { console.log(" āŒ Failed to generate query"); } if (i < demoQueries.length - 1) { console.log(" " + "-".repeat(50)); } } if (!executeQueries) { console.log(`\nšŸ’” To execute queries and see results, run:`); console.log(` EXECUTE_DEMO=true node demo.js`); } } console.log("\nšŸ“Š ODMDB Status Check:"); // Check if ODMDB data is accessible const seekersPath = "../smatchitObjectOdmdb/objects/seekers/itm"; try { if (fs.existsSync(seekersPath)) { const files = fs .readdirSync(seekersPath) .filter((f) => f.endsWith(".json") && f !== "backup"); console.log(`āœ… Found ${files.length} seeker files in ${seekersPath}`); // Sample a few files to show data types const sampleFile = files[0]; const sampleData = JSON.parse( fs.readFileSync(`${seekersPath}/${sampleFile}`, "utf-8") ); console.log(`šŸ“„ Sample seeker data (${sampleFile}):`); console.log(` - alias: ${sampleData.alias}`); console.log(` - email: ${sampleData.email}`); console.log(` - seekstatus: ${sampleData.seekstatus}`); console.log(` - seekworkingyear: ${sampleData.seekworkingyear}`); console.log(` - dt_create: ${sampleData.dt_create}`); } else { console.log(`āŒ ODMDB data not found at ${seekersPath}`); } } catch (error) { console.log(`āŒ Error accessing ODMDB data: ${error.message}`); } const schemaPath = "../smatchitObjectOdmdb/schema/seekers.json"; try { if (fs.existsSync(schemaPath)) { const schema = JSON.parse(fs.readFileSync(schemaPath, "utf-8")); const fieldCount = Object.keys(schema.properties || {}).length; console.log(`āœ… Loaded seekers schema with ${fieldCount} properties`); // Show access rights info if (schema.apxaccessrights?.recruiters?.R) { console.log( `šŸ“‹ Recruiter-readable fields: ${schema.apxaccessrights.recruiters.R.slice( 0, 5 ).join(", ")}... (${schema.apxaccessrights.recruiters.R.length} total)` ); } // Show available indexes if (schema.apxidx) { const indexes = schema.apxidx.map((idx) => idx.name); console.log(`šŸ” Available indexes: ${indexes.join(", ")}`); } } else { console.log(`āŒ Schema not found at ${schemaPath}`); } } catch (error) { console.log(`āŒ Error loading schema: ${error.message}`); } console.log("\nšŸš€ Running Live PoC Demo..."); runDemo() .then(() => { console.log("\nāœ… Demo complete!"); }) .catch((error) => { console.error("\nāŒ Demo failed:", error.message); process.exit(1); });