Seth commited on
Commit
d2b0e4f
Β·
1 Parent(s): e72db37
backend/app/gpt_service.py CHANGED
@@ -428,9 +428,12 @@ def generate_linkedin_sequence(
428
  plan_extra = "\nStrict mapping:\n"
429
  for i, act in enumerate(li_actions, start=1):
430
  if act == "linkedin_connect":
431
- plan_extra += f" Message {i}: LinkedIn CONNECTION REQUEST note (short, under 300 characters when possible).\n"
 
 
 
432
  else:
433
- plan_extra += f" Message {i}: LinkedIn DM / follow-up after connection (professional, concise).\n"
434
  plan_extra += (
435
  "\nThe first LinkedIn touch in this list is always a connection request before any follow-up DMs.\n"
436
  )
@@ -461,6 +464,7 @@ Contact Information:
461
 
462
  Generate exactly {num_messages} messages (no more, no fewer) following the rules in the system prompt.
463
  {plan_extra}
 
464
  CRITICAL: Write for LinkedIn DMs or connection notes β€” short, plain, professional. No email-style subjects.
465
 
466
  Output format (strict) β€” use exactly these {num_messages} labeled blocks and nothing beyond:
 
428
  plan_extra = "\nStrict mapping:\n"
429
  for i, act in enumerate(li_actions, start=1):
430
  if act == "linkedin_connect":
431
+ plan_extra += (
432
+ f" Message {i}: LinkedIn CONNECTION REQUEST note only β€” under ~280 characters, "
433
+ "peer-to-peer and curious (no product pitch, no meeting ask, no vendor pressure).\n"
434
+ )
435
  else:
436
+ plan_extra += f" Message {i}: LinkedIn DM / follow-up after connection (where you may add detail, context, and a soft next step).\n"
437
  plan_extra += (
438
  "\nThe first LinkedIn touch in this list is always a connection request before any follow-up DMs.\n"
439
  )
 
464
 
465
  Generate exactly {num_messages} messages (no more, no fewer) following the rules in the system prompt.
466
  {plan_extra}
467
+ If the first message is a connection request: maximum ~280 characters, peer-to-peer curiosity only β€” no product name, no demo or meeting ask, no vendor pressure.
468
  CRITICAL: Write for LinkedIn DMs or connection notes β€” short, plain, professional. No email-style subjects.
469
 
470
  Output format (strict) β€” use exactly these {num_messages} labeled blocks and nothing beyond:
frontend/src/components/campaigns/CreateCampaignWizard.jsx CHANGED
@@ -615,7 +615,7 @@ export default function CreateCampaignWizard({
615
  if (!cur) {
616
  cur =
617
  LINKEDIN_DEFAULT_TEMPLATES[p.name] ||
618
- `πŸ”’ LINKEDIN SYSTEM PROMPT\n\nYou write LinkedIn connection notes and DMs for ${p.name}. Sign with {{sender_name}} when signing off.\nMatch the Message count in the generation request exactly.`;
619
  }
620
  if (!cur || cur.includes(WIZARD_SEQUENCE_TAG)) return;
621
  next[p.name] = `${cur}\n\n${appendix}`;
 
615
  if (!cur) {
616
  cur =
617
  LINKEDIN_DEFAULT_TEMPLATES[p.name] ||
618
+ `πŸ”’ LINKEDIN SYSTEM PROMPT\n\nYou write LinkedIn connection notes and DMs for ${p.name}. Message 1: peer-style connection only (~280 chars), no pitch; later messages add detail. Sign DMs with {{sender_name}}.\nMatch the Message count in the generation request exactly.`;
619
  }
620
  if (!cur || cur.includes(WIZARD_SEQUENCE_TAG)) return;
621
  next[p.name] = `${cur}\n\n${appendix}`;
frontend/src/components/prompts/PromptEditor.jsx CHANGED
@@ -686,62 +686,78 @@ Best,
686
  export const LINKEDIN_DEFAULT_TEMPLATES = {
687
  'Accounts Payable Automation': `πŸ”’ LINKEDIN SYSTEM PROMPT (DO NOT MODIFY)
688
 
689
- You are an expert B2B LinkedIn copywriter for EZOFIS Accounts Payable Automation. You write connection notes and follow-up DMs for AP / finance leaders at North American mid-market companies.
690
 
691
  πŸ”’ SENDER SIGN-OFF
692
 
693
- When you sign a message, end with this exact token on its own line (the app substitutes the LinkedIn profile display name from Settings):
694
  {{sender_name}}
695
- Never use the prospect as the sender. Never invent a fake sender name.
696
-
697
- πŸ”’ GOAL
698
-
699
- Earn a short reply or accept that leads to a 15–20 minute working conversation about AP invoice throughput, approvals, and month-end close β€” not generic "trends" chat.
700
 
701
  πŸ”’ NO FAKE PERSONALIZATION
702
 
703
- No "I saw your post", no invented news. Use only: first name, title, company, industry, location, employee count when provided.
704
 
705
- πŸ”’ MESSAGE 1 β€” CONNECTION REQUEST NOTE (hard cap ~280 characters including spaces)
706
 
707
- β€’ One line on why connecting (EZOFIS helps AP teams cut invoice cycle time / exceptions).
708
- β€’ One line tied to their role or industry (from inputs).
709
- β€’ One concise question or reason to accept.
710
- β€’ No buzzwords; no emojis; no "I'd love to pick your brain" fluff.
711
 
712
- πŸ”’ FOLLOW-UP MESSAGES (DMs) β€” LONGER THAN THE NOTE, STILL TIGHT
713
-
714
- β€’ Message 2 (first DM after connect): Reference the connection; state in 2 sentences what EZOFIS AP Automation changes (visibility + fewer stuck invoices); add one specific curiosity hook tied to their company type; ONE question about whether a 15-minute fit call next week would be useful.
715
- β€’ Further messages: add one new detail (e.g., exception handling, 3-way match, audit trail) each time; repeat at most one line of context from your prior message; always end with ONE clear ask (reply yes/no, book link placeholder "[calendar]", or "worth a two-minute Loom?").
 
 
716
 
717
- πŸ”’ PRODUCT RELEVANCE
718
 
719
- Every message must tie to AP invoice intake, approvals, accruals, or close β€” not generic "technology trends". If you discuss their world, connect it in the same breath to the problem EZOFIS solves.
 
 
 
 
720
 
721
  πŸ”’ OUTPUT
722
 
723
- Use exactly the labeled blocks Message 1 … Message N requested in the user prompt. No extra messages.`,
724
 
725
  'Sales Order Processing': `πŸ”’ LINKEDIN SYSTEM PROMPT
726
- You write LinkedIn connection notes and DMs for EZOFIS AR & Order Automation. Sign with the token {{sender_name}} on its own line when signing off (app substitutes LinkedIn display name). Never use the prospect as sender.
727
- Focus on customer POs, pick slips, delivery proof, and stalled orders β€” not generic industry trends. Goal: a short call to see if EZOFIS fits their order-to-cash handoffs.
728
- Connection note stays under ~280 characters. DMs: 3–6 short paragraphs max, one concrete operational angle per message, one question, product-relevant throughout.
 
 
 
729
  Match the exact Message 1…N count from the campaign user prompt.`,
730
 
731
  'Document Management': `πŸ”’ LINKEDIN SYSTEM PROMPT
732
- LinkedIn touches for EZOFIS DMS. Sign with {{sender_name}} on its own line when needed. Tie each message to capture, classification, search, or audit readiness β€” not vague "digital transformation". Goal: curiosity + optional short call.
733
- Match Message count from the user prompt; connection note short, DMs more detailed with one question each.`,
 
 
 
 
 
734
 
735
  'Invoice Processing': `πŸ”’ LINKEDIN SYSTEM PROMPT
736
- LinkedIn outreach for invoice / AP operations with EZOFIS relevance in every touch. Sign with {{sender_name}} when signing off. Connection note under ~120 words; DMs can be slightly longer with one operational detail + one meeting-oriented question per message.
 
 
 
737
  Follow the user prompt's Message count exactly.`,
738
 
739
  'Expense Management': `πŸ”’ LINKEDIN SYSTEM PROMPT
740
- LinkedIn DMs for finance teams about expense workflows. Professional, no buzzwords.
 
 
 
741
  Write exactly as many labeled Message sections as the user prompt specifies.`,
742
 
743
  'Procurement Automation': `πŸ”’ LINKEDIN SYSTEM PROMPT
744
- Procurement-focused LinkedIn touches. Practical questions only.
 
 
 
745
  Use only the Message 1…N blocks requested in the user prompt; same count as the campaign sequence.`,
746
  };
747
 
@@ -780,7 +796,7 @@ const PromptEditor = forwardRef(function PromptEditor(
780
  const emailDefault = DEFAULT_TEMPLATES[product.name];
781
  const liDefault = LINKEDIN_DEFAULT_TEMPLATES[product.name];
782
  const emailFallback = `Subject: {{first_name}}, let's talk about ${product.name}\n\nHi {{first_name}},\n\nI wanted to reach out about how ${product.name} could benefit {{company}}.\n\n[Your personalized message here]\n\nBest,\n{{sender_name}}`;
783
- const liFallback = `πŸ”’ LINKEDIN SYSTEM PROMPT\n\nYou write LinkedIn connection notes and DMs for ${product.name}. Sign with {{sender_name}} when signing off.\nFollow the exact number of labeled Message blocks in the generation request (Message 1 … Message N). Do not add extra messages.`;
784
  const email = savedEmail || emailDefault || emailFallback;
785
  if (includeLinkedinInCampaign) {
786
  const li = savedLi || liDefault || liFallback;
@@ -803,7 +819,7 @@ const PromptEditor = forwardRef(function PromptEditor(
803
  } else if (defaultTemplate) {
804
  newPrompts[product.name] = defaultTemplate;
805
  } else if (variant === 'linkedin') {
806
- newPrompts[product.name] = `πŸ”’ LINKEDIN SYSTEM PROMPT\n\nYou write LinkedIn connection notes and DMs for ${product.name}. Sign with {{sender_name}} when signing off.\nUse the exact Message 1…N count from the generation request; do not invent extra touches.\nUse {{first_name}}, {{company}}.`;
807
  } else {
808
  newPrompts[product.name] = `Subject: {{first_name}}, let's talk about ${product.name}\n\nHi {{first_name}},\n\nI wanted to reach out about how ${product.name} could benefit {{company}}.\n\n[Your personalized message here]\n\nBest,\n{{sender_name}}`;
809
  }
@@ -874,7 +890,7 @@ const PromptEditor = forwardRef(function PromptEditor(
874
  if (includeLinkedinInCampaign) {
875
  const liDefault =
876
  LINKEDIN_DEFAULT_TEMPLATES[productName] ||
877
- `πŸ”’ LINKEDIN SYSTEM PROMPT\n\nYou write LinkedIn connection notes and DMs for ${productName}. Sign with {{sender_name}} when signing off.\nMatch the Message count in the generation request exactly.`;
878
  handlePromptChange(
879
  productName,
880
  `${emailDefault}${CAMPAIGN_COMBINED_PROMPT_SPLIT}${liDefault}`
@@ -888,7 +904,7 @@ const PromptEditor = forwardRef(function PromptEditor(
888
  const defaultTemplate =
889
  library[productName] ||
890
  (variant === 'linkedin'
891
- ? `πŸ”’ LINKEDIN SYSTEM PROMPT\n\nYou write LinkedIn connection notes and DMs for ${productName}. Sign with {{sender_name}} when signing off.\nMatch the Message count in the generation request exactly.`
892
  : `Subject: {{first_name}}, let's talk about ${productName}\n\nHi {{first_name}},\n\nI wanted to reach out about how ${productName} could benefit {{company}}.\n\n[Your personalized message here]\n\nBest,\n{{sender_name}}`);
893
  handlePromptChange(productName, defaultTemplate);
894
  };
 
686
  export const LINKEDIN_DEFAULT_TEMPLATES = {
687
  'Accounts Payable Automation': `πŸ”’ LINKEDIN SYSTEM PROMPT (DO NOT MODIFY)
688
 
689
+ You write LinkedIn connection notes and follow-up DMs for AP / finance leaders (mid-market North America). Tone: peer curiosity first, substance after they accept β€” like a leader reaching out to compare notes, not a vendor opening with a pitch.
690
 
691
  πŸ”’ SENDER SIGN-OFF
692
 
693
+ In DMs, you may close with the token on its own line (app substitutes the LinkedIn display name from Settings):
694
  {{sender_name}}
695
+ Never use the prospect as the sender. For Message 1 (connection note), a sign-off is optional if space is tight; if you include one, use only {{sender_name}} on its own short line.
 
 
 
 
696
 
697
  πŸ”’ NO FAKE PERSONALIZATION
698
 
699
+ No "I saw your post", no invented awards or news. Hyper-personalize only from: first name, title, company, industry, location, employee count when provided.
700
 
701
+ πŸ”’ MESSAGE 1 β€” CONNECTION REQUEST NOTE ONLY (~280 characters max including spaces)
702
 
703
+ Purpose: open the door. Sound human and low-pressure.
 
 
 
704
 
705
+ Rules:
706
+ β€’ Do NOT name EZOFIS or any product. Do NOT propose a call, demo, or meeting. Do NOT ask them to evaluate a solution.
707
+ β€’ Do NOT use sales openers ("quick question", "picking your brain", "15 minutes", "ROI").
708
+ β€’ DO weave in one specific nod to their world (role + company or industry) so it feels one-to-one.
709
+ β€’ Style examples to emulate (rewrite in your own words each time): exploring how finance / operations leaders are approaching digitization and practical automation; would value connecting and exchanging perspectives; or a genuine note about synergy / learning from peers in their space.
710
+ β€’ 2–4 short sentences max; calm; no emojis; no em dashes.
711
 
712
+ πŸ”’ MESSAGE 2+ β€” FOLLOW-UP DMs (this is where detail belongs)
713
 
714
+ β€’ First DM after connect: Briefly reference that you just connected. Then share credible context: how you work with organizations on finance / AP-side automation from planning through execution; lean approach, practical use cases, visible outcomes (fewer stuck invoices, clearer approvals, less manual chase) β€” tie to their industry or company type from inputs.
715
+ β€’ Mention EZOFIS AP Automation by name only here (and in later DMs if needed), in plain language β€” not a feature dump.
716
+ β€’ One soft, no-obligation line about a conversation if useful (e.g. "happy to compare notes if your team is looking at this").
717
+ β€’ Later DMs: add one new layer each time (e.g. month-end, exceptions, 3-way match, audit trail) + optional line about how you typically start (consultation / understanding workflow / prototype "to-be" β€” describe generically, no fake claims about their process).
718
+ β€’ Each DM: 2–5 short paragraphs; end with one easy question or gentle opt-in β€” never pushy.
719
 
720
  πŸ”’ OUTPUT
721
 
722
+ Use exactly Message 1 … Message N from the user prompt. No extra messages.`,
723
 
724
  'Sales Order Processing': `πŸ”’ LINKEDIN SYSTEM PROMPT
725
+ You write connection notes and DMs for order-to-cash / AR operations leaders. Sign DMs with {{sender_name}} on its own line when signing off (app substitutes LinkedIn display name). Never use the prospect as sender.
726
+
727
+ Message 1 (connection, ~280 chars): peer opener only β€” how leaders approach order documentation, delivery proof, or operational digitization at their scale. Hyper-personalize with title, company, industry from inputs. No product name, no meeting ask, no pitch.
728
+
729
+ Message 2+: Reference the connection; then EZOFIS AR & Order Automation and practical detail (POs, pick slips, field capture, fewer stalled orders). Calm, structured paragraphs like a follow-up note to a peer; soft optional conversation. One question per DM.
730
+
731
  Match the exact Message 1…N count from the campaign user prompt.`,
732
 
733
  'Document Management': `πŸ”’ LINKEDIN SYSTEM PROMPT
734
+ LinkedIn for document / records leaders. {{sender_name}} on its own line when you sign a DM.
735
+
736
+ Message 1 (~280 chars): light, peer-style reason to connect β€” how teams handle documents, versions, or audit readiness in their industry; personalize with company/role/industry from inputs only. No product pitch or meeting ask.
737
+
738
+ Message 2+: EZOFIS DMS relevance, capture/classification/search in plain language, more detail and optional no-obligation chat. One question per DM.
739
+
740
+ Match Message count from the user prompt.`,
741
 
742
  'Invoice Processing': `πŸ”’ LINKEDIN SYSTEM PROMPT
743
+ Message 1: connection note ~280 characters β€” curious peer tone, personalized with {{first_name}}, company, industry from inputs; no product name, no demo/call ask.
744
+
745
+ Message 2+: invoice / AP automation context, EZOFIS where appropriate, structured follow-ups (similar rhythm to a multi-paragraph DM you would send after someone accepts). Sign with {{sender_name}} when closing DMs.
746
+
747
  Follow the user prompt's Message count exactly.`,
748
 
749
  'Expense Management': `πŸ”’ LINKEDIN SYSTEM PROMPT
750
+ Message 1 (~280 chars): connect as a peer exploring how finance teams handle expense workflows and visibility β€” personalize with role/company/industry from inputs. No product pitch in the connection note.
751
+
752
+ Message 2+: expense operations and your solution area with substance; sign DMs with {{sender_name}}. Professional, no buzzwords.
753
+
754
  Write exactly as many labeled Message sections as the user prompt specifies.`,
755
 
756
  'Procurement Automation': `πŸ”’ LINKEDIN SYSTEM PROMPT
757
+ Message 1 (~280 chars): light opener to procurement / sourcing leaders β€” growth, supplier coordination, or how peers approach digitization; hyper-personalize from inputs. No vendor pitch or meeting ask in the note.
758
+
759
+ Message 2+: practical procurement automation angles, soft opt-in to talk, {{sender_name}} on DM sign-offs.
760
+
761
  Use only the Message 1…N blocks requested in the user prompt; same count as the campaign sequence.`,
762
  };
763
 
 
796
  const emailDefault = DEFAULT_TEMPLATES[product.name];
797
  const liDefault = LINKEDIN_DEFAULT_TEMPLATES[product.name];
798
  const emailFallback = `Subject: {{first_name}}, let's talk about ${product.name}\n\nHi {{first_name}},\n\nI wanted to reach out about how ${product.name} could benefit {{company}}.\n\n[Your personalized message here]\n\nBest,\n{{sender_name}}`;
799
+ const liFallback = `πŸ”’ LINKEDIN SYSTEM PROMPT\n\nYou write LinkedIn connection notes and DMs for ${product.name}. Message 1: short peer-style connection note only (~280 chars), no product pitch or meeting ask β€” hyper-personalize from inputs. Later messages: add detail and relevance; sign DMs with {{sender_name}} on its own line.\nFollow the exact number of labeled Message blocks in the generation request (Message 1 … Message N). Do not add extra messages.`;
800
  const email = savedEmail || emailDefault || emailFallback;
801
  if (includeLinkedinInCampaign) {
802
  const li = savedLi || liDefault || liFallback;
 
819
  } else if (defaultTemplate) {
820
  newPrompts[product.name] = defaultTemplate;
821
  } else if (variant === 'linkedin') {
822
+ newPrompts[product.name] = `πŸ”’ LINKEDIN SYSTEM PROMPT\n\nYou write LinkedIn connection notes and DMs for ${product.name}. Message 1: ~280 character peer opener only β€” no pitch or meeting ask; personalize with {{first_name}}, {{company}} from inputs. Later messages: detail and soft next step; sign DMs with {{sender_name}}.\nUse the exact Message 1…N count from the generation request; do not invent extra touches.`;
823
  } else {
824
  newPrompts[product.name] = `Subject: {{first_name}}, let's talk about ${product.name}\n\nHi {{first_name}},\n\nI wanted to reach out about how ${product.name} could benefit {{company}}.\n\n[Your personalized message here]\n\nBest,\n{{sender_name}}`;
825
  }
 
890
  if (includeLinkedinInCampaign) {
891
  const liDefault =
892
  LINKEDIN_DEFAULT_TEMPLATES[productName] ||
893
+ `πŸ”’ LINKEDIN SYSTEM PROMPT\n\nYou write LinkedIn connection notes and DMs for ${productName}. Message 1: short peer connection note only, no pitch; later messages carry detail. Sign DMs with {{sender_name}}.\nMatch the Message count in the generation request exactly.`;
894
  handlePromptChange(
895
  productName,
896
  `${emailDefault}${CAMPAIGN_COMBINED_PROMPT_SPLIT}${liDefault}`
 
904
  const defaultTemplate =
905
  library[productName] ||
906
  (variant === 'linkedin'
907
+ ? `πŸ”’ LINKEDIN SYSTEM PROMPT\n\nYou write LinkedIn connection notes and DMs for ${productName}. Message 1: peer opener only (~280 chars), no product or meeting ask; later messages add substance. Sign DMs with {{sender_name}}.\nMatch the Message count in the generation request exactly.`
908
  : `Subject: {{first_name}}, let's talk about ${productName}\n\nHi {{first_name}},\n\nI wanted to reach out about how ${productName} could benefit {{company}}.\n\n[Your personalized message here]\n\nBest,\n{{sender_name}}`);
909
  handlePromptChange(productName, defaultTemplate);
910
  };