Hunter Companion: fix hallucination (strict no-invent rules), better text formatting with spacing
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled
This commit is contained in:
@ -63,23 +63,26 @@ async def _get_user_tier(db: AsyncSession, user: User) -> str:
|
|||||||
def _build_system_prompt(path: str) -> str:
|
def _build_system_prompt(path: str) -> str:
|
||||||
tools = tool_catalog_for_prompt(path)
|
tools = tool_catalog_for_prompt(path)
|
||||||
return (
|
return (
|
||||||
"You are the Pounce Hunter Companion, an expert domain trading assistant. Always respond in English.\n"
|
"You are the Pounce Hunter Companion, a domain trading expert. Always respond in English.\n\n"
|
||||||
"You help users with: domain analysis, auction hunting, portfolio management, and trading decisions.\n\n"
|
"CRITICAL RULES:\n"
|
||||||
"RESPONSE FORMAT (CRITICAL):\n"
|
"1. NEVER invent or hallucinate data. You do NOT have access to SEMrush, Estibot, GoDaddy sales, or external databases.\n"
|
||||||
"- Write in plain text. NO markdown asterisks, NO ** or *, NO code blocks.\n"
|
"2. If you don't have data, say so honestly. Only use data from tools you actually called.\n"
|
||||||
"- Use simple dashes (-) for bullet points.\n"
|
"3. Keep responses SHORT: 2-3 sentences max, then bullets if needed.\n"
|
||||||
"- Keep responses concise: 2-4 sentences intro, then bullets if needed.\n"
|
"4. NO markdown: no ** or *, no code blocks, no headers with #.\n"
|
||||||
"- Never show tool outputs, JSON, or internal data to the user.\n\n"
|
"5. Use dashes (-) for bullet points.\n\n"
|
||||||
"BEHAVIOR:\n"
|
"WHAT YOU CAN DO:\n"
|
||||||
"- Be helpful, direct, and conversational like a knowledgeable colleague.\n"
|
"- Analyze domains using the analyze_domain tool (gives Pounce Score, risk, value estimate)\n"
|
||||||
"- For domain questions: give a clear BUY / CONSIDER / SKIP recommendation with 3-5 reasons.\n"
|
"- Show user's watchlist, portfolio, listings, inbox, yield data\n"
|
||||||
"- Do NOT invent user preferences, keywords, or data. Ask if unclear.\n"
|
"- Search auctions and drops\n"
|
||||||
"- For greetings: respond naturally and ask how you can help.\n\n"
|
"- Generate brandable names\n\n"
|
||||||
|
"WHAT YOU CANNOT DO:\n"
|
||||||
|
"- Access external sales databases or SEO tools\n"
|
||||||
|
"- Look up real-time WHOIS or DNS (unless via tool)\n"
|
||||||
|
"- Make up sales history or traffic stats\n\n"
|
||||||
"TOOL USAGE:\n"
|
"TOOL USAGE:\n"
|
||||||
"- Use tools when user asks about their data (watchlist, portfolio, listings, inbox, yield) or a specific domain.\n"
|
"- To call a tool, respond with ONLY: {\"tool_calls\":[{\"name\":\"...\",\"args\":{...}}]}\n"
|
||||||
"- To call tools, respond with ONLY: {\"tool_calls\":[{\"name\":\"...\",\"args\":{...}}]}\n"
|
"- After tool results, summarize briefly without mentioning tools.\n\n"
|
||||||
"- After receiving tool results, answer naturally without mentioning tools.\n\n"
|
f"TOOLS:\n{json.dumps(tools, ensure_ascii=False)}\n"
|
||||||
f"AVAILABLE TOOLS:\n{json.dumps(tools, ensure_ascii=False)}\n"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -143,13 +146,8 @@ async def run_agent(
|
|||||||
{
|
{
|
||||||
"role": "assistant",
|
"role": "assistant",
|
||||||
"content": (
|
"content": (
|
||||||
"Hey! How can I help you today?\n\n"
|
"Hey! What can I help you with?\n\n"
|
||||||
"I can help with:\n"
|
"Give me a domain to analyze, or ask about your watchlist, portfolio, or current auctions."
|
||||||
"- Analyzing a specific domain\n"
|
|
||||||
"- Finding auction deals or drops\n"
|
|
||||||
"- Reviewing your portfolio or watchlist\n"
|
|
||||||
"- Checking your listings and leads\n\n"
|
|
||||||
"Just tell me what you need."
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -206,11 +204,11 @@ async def stream_final_answer(convo: list[dict[str, Any]], *, model: Optional[st
|
|||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": (
|
"content": (
|
||||||
"Final step: respond to the user in plain text.\n"
|
"Respond now. Rules:\n"
|
||||||
"- NO markdown: no ** or * for bold/italic, no code blocks.\n"
|
"- NEVER invent data. Only use data from tools you called.\n"
|
||||||
"- Use dashes (-) for bullets.\n"
|
"- Keep it SHORT: 2-3 sentences, then bullet points if needed.\n"
|
||||||
"- Do NOT output JSON or mention tools.\n"
|
"- NO markdown (no ** or *), just plain text with dashes for bullets.\n"
|
||||||
"- Be concise and helpful."
|
"- Do NOT mention tools or JSON."
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@ -98,27 +98,52 @@ function getTier(subscription: any): 'scout' | 'trader' | 'tycoon' {
|
|||||||
return 'scout'
|
return 'scout'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple markdown-like formatting to clean HTML
|
// Format message text to clean HTML with proper spacing
|
||||||
function formatMessage(text: string): string {
|
function formatMessage(text: string): string {
|
||||||
if (!text) return ''
|
if (!text) return ''
|
||||||
|
|
||||||
|
// Escape HTML first
|
||||||
let html = text
|
let html = text
|
||||||
// Escape HTML
|
|
||||||
.replace(/&/g, '&')
|
.replace(/&/g, '&')
|
||||||
.replace(/</g, '<')
|
.replace(/</g, '<')
|
||||||
.replace(/>/g, '>')
|
.replace(/>/g, '>')
|
||||||
// Bold: **text** or __text__
|
|
||||||
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
// Remove markdown formatting (** and * for bold/italic)
|
||||||
.replace(/__(.+?)__/g, '<strong>$1</strong>')
|
html = html
|
||||||
// Italic: *text* or _text_ (but not inside words)
|
.replace(/\*\*(.+?)\*\*/g, '$1')
|
||||||
.replace(/(?<!\w)\*([^*]+)\*(?!\w)/g, '<em>$1</em>')
|
.replace(/\*([^*]+)\*/g, '$1')
|
||||||
.replace(/(?<!\w)_([^_]+)_(?!\w)/g, '<em>$1</em>')
|
.replace(/__(.+?)__/g, '$1')
|
||||||
// Inline code: `code`
|
.replace(/_([^_]+)_/g, '$1')
|
||||||
.replace(/`([^`]+)`/g, '<code class="bg-white/10 px-1 py-0.5 text-accent">$1</code>')
|
|
||||||
// Line breaks
|
// Split into paragraphs (double newline = paragraph break)
|
||||||
.replace(/\n/g, '<br />')
|
const paragraphs = html.split(/\n\n+/)
|
||||||
// Bullet points: - item or • item
|
|
||||||
.replace(/<br \/>[-•]\s+/g, '<br />• ')
|
const formatted = paragraphs.map(para => {
|
||||||
return html
|
// Check if this paragraph is a list (starts with - or number.)
|
||||||
|
const lines = para.split('\n')
|
||||||
|
const isList = lines.every(line => {
|
||||||
|
const trimmed = line.trim()
|
||||||
|
return trimmed === '' || trimmed.startsWith('-') || trimmed.startsWith('•') || /^\d+\./.test(trimmed)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isList) {
|
||||||
|
// Format as list
|
||||||
|
const items = lines
|
||||||
|
.map(line => line.trim())
|
||||||
|
.filter(line => line)
|
||||||
|
.map(line => {
|
||||||
|
// Remove leading dash, bullet, or number
|
||||||
|
const content = line.replace(/^[-•]\s*/, '').replace(/^\d+\.\s*/, '')
|
||||||
|
return `<div class="flex gap-2 py-0.5"><span class="text-accent/60 shrink-0">•</span><span>${content}</span></div>`
|
||||||
|
})
|
||||||
|
return `<div class="space-y-0.5">${items.join('')}</div>`
|
||||||
|
} else {
|
||||||
|
// Regular paragraph - convert single newlines to line breaks
|
||||||
|
return `<p class="mb-2 last:mb-0">${para.replace(/\n/g, '<br />')}</p>`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return formatted.join('')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suggestion chips based on current page
|
// Suggestion chips based on current page
|
||||||
@ -514,21 +539,17 @@ export function HunterCompanion() {
|
|||||||
.prose-chat {
|
.prose-chat {
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 1.6;
|
line-height: 1.7;
|
||||||
}
|
}
|
||||||
.prose-chat strong {
|
.prose-chat p {
|
||||||
color: rgba(255, 255, 255, 0.95);
|
margin-bottom: 0.75rem;
|
||||||
font-weight: 600;
|
|
||||||
}
|
}
|
||||||
.prose-chat em {
|
.prose-chat p:last-child {
|
||||||
color: rgba(255, 255, 255, 0.7);
|
margin-bottom: 0;
|
||||||
font-style: italic;
|
|
||||||
}
|
}
|
||||||
.prose-chat code {
|
.prose-chat .space-y-0\.5 > div {
|
||||||
background: rgba(255, 255, 255, 0.08);
|
padding-top: 0.125rem;
|
||||||
padding: 1px 4px;
|
padding-bottom: 0.125rem;
|
||||||
border-radius: 2px;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user