Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧹 Lint (push) Failing after 35s
Monorepo Pipeline / 🧪 Test (push) Failing after 35s
Monorepo Pipeline / 🏗️ Build (push) Failing after 12s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Image Processor (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
122 lines
4.6 KiB
TypeScript
122 lines
4.6 KiB
TypeScript
// ============================================================================
|
|
// Step 00b: Research — Industry Research via @mintel/journaling (No LLM hallus)
|
|
// Uses Serper API for real web search results about the industry/company.
|
|
// ============================================================================
|
|
|
|
import type { ConceptState, StepResult } from "../types.js";
|
|
|
|
interface ResearchResult {
|
|
companyContext: string[];
|
|
industryInsights: string[];
|
|
competitorInfo: string[];
|
|
}
|
|
|
|
/**
|
|
* Research the company and industry using real web search data.
|
|
* Uses @mintel/journaling's ResearchAgent — results are grounded in real sources.
|
|
*
|
|
* NOTE: The journaling package can cause unhandled rejections that crash the process.
|
|
* We wrap each call in an additional safety layer.
|
|
*/
|
|
export async function executeResearch(
|
|
state: ConceptState,
|
|
): Promise<StepResult<ResearchResult>> {
|
|
const startTime = Date.now();
|
|
|
|
const companyName = state.siteProfile?.companyInfo?.name || "";
|
|
const websiteTopic = state.siteProfile?.services?.slice(0, 3).join(", ") || "";
|
|
const domain = state.siteProfile?.domain || "";
|
|
|
|
if (!companyName && !websiteTopic && !domain) {
|
|
return {
|
|
success: true,
|
|
data: { companyContext: [], industryInsights: [], competitorInfo: [] },
|
|
usage: { step: "00b-research", model: "none", promptTokens: 0, completionTokens: 0, cost: 0, durationMs: 0 },
|
|
};
|
|
}
|
|
|
|
// Safety wrapper: catch ANY unhandled rejections during this step
|
|
const safeCall = <T>(fn: () => Promise<T>, fallback: T): Promise<T> => {
|
|
return new Promise<T>((resolve) => {
|
|
const handler = (err: any) => {
|
|
console.warn(` ⚠️ Unhandled rejection caught in research: ${err?.message || err}`);
|
|
process.removeListener("unhandledRejection", handler);
|
|
resolve(fallback);
|
|
};
|
|
process.on("unhandledRejection", handler);
|
|
|
|
fn()
|
|
.then((result) => {
|
|
process.removeListener("unhandledRejection", handler);
|
|
resolve(result);
|
|
})
|
|
.catch((err) => {
|
|
process.removeListener("unhandledRejection", handler);
|
|
console.warn(` ⚠️ Research call failed: ${err?.message || err}`);
|
|
resolve(fallback);
|
|
});
|
|
});
|
|
};
|
|
|
|
try {
|
|
const { ResearchAgent } = await import("@mintel/journaling");
|
|
const agent = new ResearchAgent(process.env.OPENROUTER_API_KEY || "");
|
|
|
|
const results: ResearchResult = {
|
|
companyContext: [],
|
|
industryInsights: [],
|
|
competitorInfo: [],
|
|
};
|
|
|
|
// 1. Research the company itself
|
|
if (companyName || domain) {
|
|
const searchQuery = companyName
|
|
? `${companyName} ${websiteTopic} Unternehmen`
|
|
: `site:${domain}`;
|
|
|
|
console.log(` 🔍 Researching: "${searchQuery}"...`);
|
|
const facts = await safeCall(
|
|
() => agent.researchTopic(searchQuery),
|
|
[] as any[],
|
|
);
|
|
results.companyContext = (facts || [])
|
|
.filter((f: any) => f?.fact || f?.value || f?.text || f?.statement)
|
|
.map((f: any) => f.fact || f.value || f.text || f.statement)
|
|
.slice(0, 5);
|
|
}
|
|
|
|
// 2. Industry research
|
|
if (websiteTopic) {
|
|
console.log(` 🔍 Researching industry: "${websiteTopic}"...`);
|
|
const insights = await safeCall(
|
|
() => agent.researchCompetitors(websiteTopic),
|
|
[] as any[],
|
|
);
|
|
results.industryInsights = (insights || []).slice(0, 5);
|
|
}
|
|
|
|
const totalFacts = results.companyContext.length + results.industryInsights.length + results.competitorInfo.length;
|
|
console.log(` 📊 Research found ${totalFacts} data points.`);
|
|
|
|
return {
|
|
success: true,
|
|
data: results,
|
|
usage: {
|
|
step: "00b-research",
|
|
model: "serper/datacommons",
|
|
promptTokens: 0,
|
|
completionTokens: 0,
|
|
cost: 0,
|
|
durationMs: Date.now() - startTime,
|
|
},
|
|
};
|
|
} catch (err) {
|
|
console.warn(` ⚠️ Research step skipped: ${(err as Error).message}`);
|
|
return {
|
|
success: true,
|
|
data: { companyContext: [], industryInsights: [], competitorInfo: [] },
|
|
usage: { step: "00b-research", model: "none", promptTokens: 0, completionTokens: 0, cost: 0, durationMs: Date.now() - startTime },
|
|
};
|
|
}
|
|
}
|