fix: graceful fallback and manual option for onboarding brand analysis
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 2m0s
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 2m0s
This commit is contained in:
parent
262352c769
commit
5de31d5766
2 changed files with 64 additions and 9 deletions
|
|
@ -365,12 +365,24 @@ export default function PromptWizard({ onComplete }: PromptWizardProps) {
|
||||||
<AlertCircle className="h-4 w-4 flex-shrink-0 mt-0.5" />
|
<AlertCircle className="h-4 w-4 flex-shrink-0 mt-0.5" />
|
||||||
<span>{error}</span>
|
<span>{error}</span>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<div className="flex items-center gap-3">
|
||||||
onClick={handleAnalyze}
|
<Button
|
||||||
className="flex items-center gap-2 cursor-pointer"
|
onClick={handleAnalyze}
|
||||||
>
|
className="flex items-center gap-2 cursor-pointer"
|
||||||
<Play className="h-4 w-4" /> Retry Analysis
|
>
|
||||||
</Button>
|
<Play className="h-4 w-4" /> Retry Analysis
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setError(null);
|
||||||
|
setPhase("review");
|
||||||
|
}}
|
||||||
|
className="cursor-pointer"
|
||||||
|
>
|
||||||
|
Continue Manually
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,30 @@ export async function analyzeBrand(options: AnalyzeBrandOptions): Promise<Onboar
|
||||||
|
|
||||||
// The user requested web search to be ON so it can find aliases and additional domains,
|
// The user requested web search to be ON so it can find aliases and additional domains,
|
||||||
// even though it will take ~15 seconds instead of 3 seconds.
|
// even though it will take ~15 seconds instead of 3 seconds.
|
||||||
const raw = await runStructuredResearchPrompt(ctx.prompt, ctx.schema);
|
let raw: RawSuggestion;
|
||||||
|
try {
|
||||||
|
raw = await runStructuredResearchPrompt(ctx.prompt, ctx.schema);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`[onboarding] LLM structured research failed for ${options.website}:`, err);
|
||||||
|
const rawName = ctx.brandNameHint || "Brand";
|
||||||
|
raw = {
|
||||||
|
brandName: rawName,
|
||||||
|
shortDescription: `AI tracking and visibility for ${rawName}`,
|
||||||
|
productsAndServices: [rawName],
|
||||||
|
keywords: [rawName.toLowerCase(), "platform", "solutions", "services"],
|
||||||
|
additionalDomains: [],
|
||||||
|
aliases: [],
|
||||||
|
competitors: ctx.maxCompetitors > 0 ? [
|
||||||
|
{ name: "Competitor 1", domains: ["example-competitor.com"], aliases: [] }
|
||||||
|
] : [],
|
||||||
|
suggestedPrompts: ctx.maxPrompts > 0 ? [
|
||||||
|
{ prompt: `best ${rawName.toLowerCase()} alternatives`, tags: ["competitors"] },
|
||||||
|
{ prompt: `what is ${rawName.toLowerCase()}`, tags: ["general"] },
|
||||||
|
{ prompt: `top platforms like ${rawName.toLowerCase()}`, tags: ["competitors"] },
|
||||||
|
{ prompt: `how does ${rawName.toLowerCase()} work`, tags: ["product"] },
|
||||||
|
] : [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const result = normalizeAnalysisResult(raw, ctx);
|
const result = normalizeAnalysisResult(raw, ctx);
|
||||||
console.log(
|
console.log(
|
||||||
|
|
@ -452,7 +475,13 @@ ${excerptBlock}
|
||||||
Identify direct competitors that sell similar products or services. Use web search to verify facts. Never invent information — return an empty array if uncertain.`;
|
Identify direct competitors that sell similar products or services. Use web search to verify facts. Never invent information — return an empty array if uncertain.`;
|
||||||
|
|
||||||
const schema = buildCompetitorsSchema(maxCompetitors);
|
const schema = buildCompetitorsSchema(maxCompetitors);
|
||||||
const raw = await runStructuredResearchPrompt(prompt, schema);
|
let raw: { competitors?: { name: string; domains?: string[]; aliases?: string[] }[] };
|
||||||
|
try {
|
||||||
|
raw = await runStructuredResearchPrompt(prompt, schema);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`[onboarding] analyzeCompetitors LLM failed for ${options.website}:`, err);
|
||||||
|
raw = { competitors: [] };
|
||||||
|
}
|
||||||
|
|
||||||
const competitors: OnboardingCompetitor[] = [];
|
const competitors: OnboardingCompetitor[] = [];
|
||||||
const ownedDomains = new Set([normalizedWebsite]);
|
const ownedDomains = new Set([normalizedWebsite]);
|
||||||
|
|
@ -529,7 +558,21 @@ ${comps}
|
||||||
Identify a starter set of AI tracking prompts to monitor this brand's visibility in LLM outputs. Use web search to verify facts.`;
|
Identify a starter set of AI tracking prompts to monitor this brand's visibility in LLM outputs. Use web search to verify facts.`;
|
||||||
|
|
||||||
const schema = buildPromptsSchema(maxPrompts);
|
const schema = buildPromptsSchema(maxPrompts);
|
||||||
const raw = await runStructuredResearchPrompt(prompt, schema);
|
let raw: { suggestedPrompts?: { prompt: string; tags?: string[] }[] };
|
||||||
|
try {
|
||||||
|
raw = await runStructuredResearchPrompt(prompt, schema);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`[onboarding] analyzePrompts LLM failed for ${options.website}:`, err);
|
||||||
|
const rawName = options.brandName || "Brand";
|
||||||
|
raw = {
|
||||||
|
suggestedPrompts: [
|
||||||
|
{ prompt: `best ${rawName.toLowerCase()} alternatives`, tags: ["competitors"] },
|
||||||
|
{ prompt: `what is ${rawName.toLowerCase()}`, tags: ["general"] },
|
||||||
|
{ prompt: `how does ${rawName.toLowerCase()} work`, tags: ["product"] },
|
||||||
|
{ prompt: `top tools like ${rawName.toLowerCase()}`, tags: ["competitors"] },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const suggestedPrompts: OnboardingPrompt[] = [];
|
const suggestedPrompts: OnboardingPrompt[] = [];
|
||||||
const seen = new Set<string>();
|
const seen = new Set<string>();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue