mirror of
https://github.com/whekin/household-bot.git
synced 2026-03-31 21:14:02 +00:00
fix(bot): localize topic processor responses and allow optional payment amounts
This commit is contained in:
@@ -769,12 +769,21 @@ export function registerConfiguredPaymentTopicIngestion(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create payment proposal using the parsed data from topic processor
|
// Create payment proposal using the parsed data from topic processor
|
||||||
const amountMajor = Money.fromMinor(
|
const amountMajor =
|
||||||
|
processorResult.amountMinor && processorResult.currency
|
||||||
|
? Money.fromMinor(
|
||||||
BigInt(processorResult.amountMinor),
|
BigInt(processorResult.amountMinor),
|
||||||
processorResult.currency
|
processorResult.currency
|
||||||
).toMajorString()
|
).toMajorString()
|
||||||
|
: null
|
||||||
|
|
||||||
|
const synthesizedText =
|
||||||
|
amountMajor && processorResult.currency
|
||||||
|
? `paid ${processorResult.kind} ${amountMajor} ${processorResult.currency}`
|
||||||
|
: `paid ${processorResult.kind}`
|
||||||
|
|
||||||
const proposal = await maybeCreatePaymentProposal({
|
const proposal = await maybeCreatePaymentProposal({
|
||||||
rawText: `paid ${processorResult.kind} ${amountMajor} ${processorResult.currency}`,
|
rawText: synthesizedText,
|
||||||
householdId: record.householdId,
|
householdId: record.householdId,
|
||||||
memberId: member.id,
|
memberId: member.id,
|
||||||
financeService,
|
financeService,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import type {
|
|||||||
} from '@household/ports'
|
} from '@household/ports'
|
||||||
|
|
||||||
import { createDbClient, schema } from '@household/db'
|
import { createDbClient, schema } from '@household/db'
|
||||||
import { getBotTranslations, type BotLocale } from './i18n'
|
import { getBotTranslations, botLocaleFromContext, type BotLocale } from './i18n'
|
||||||
import type { AssistantConversationMemoryStore } from './assistant-state'
|
import type { AssistantConversationMemoryStore } from './assistant-state'
|
||||||
import { buildConversationContext } from './conversation-orchestrator'
|
import { buildConversationContext } from './conversation-orchestrator'
|
||||||
import type {
|
import type {
|
||||||
@@ -2298,9 +2298,10 @@ export function registerPurchaseTopicIngestion(
|
|||||||
rememberUserTurn(options.memoryStore, record)
|
rememberUserTurn(options.memoryStore, record)
|
||||||
typingIndicator =
|
typingIndicator =
|
||||||
options.interpreter && route.shouldStartTyping ? startTypingIndicator(ctx) : null
|
options.interpreter && route.shouldStartTyping ? startTypingIndicator(ctx) : null
|
||||||
|
const locale = botLocaleFromContext(ctx)
|
||||||
const pendingReply =
|
const pendingReply =
|
||||||
options.interpreter && shouldShowProcessingReply(ctx, record, route)
|
options.interpreter && shouldShowProcessingReply(ctx, record, route)
|
||||||
? await sendPurchaseProcessingReply(ctx, getBotTranslations('en').purchase.processing)
|
? await sendPurchaseProcessingReply(ctx, getBotTranslations(locale).purchase.processing)
|
||||||
: null
|
: null
|
||||||
const result = await repository.save(record, options.interpreter, 'GEL')
|
const result = await repository.save(record, options.interpreter, 'GEL')
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { extractOpenAiResponseText, parseJsonFromResponseText } from './openai-responses'
|
import { extractOpenAiResponseText, parseJsonFromResponseText } from './openai-responses'
|
||||||
import type { TopicWorkflowState } from './topic-message-router'
|
import type { TopicWorkflowState } from './topic-message-router'
|
||||||
import type { EngagementAssessment } from './conversation-orchestrator'
|
import type { EngagementAssessment } from './conversation-orchestrator'
|
||||||
|
import { getBotTranslations } from './i18n'
|
||||||
|
|
||||||
export type TopicProcessorRoute =
|
export type TopicProcessorRoute =
|
||||||
| 'silent'
|
| 'silent'
|
||||||
@@ -27,8 +28,8 @@ export interface TopicProcessorPurchaseResult {
|
|||||||
export interface TopicProcessorPaymentResult {
|
export interface TopicProcessorPaymentResult {
|
||||||
route: 'payment'
|
route: 'payment'
|
||||||
kind: 'rent' | 'utilities'
|
kind: 'rent' | 'utilities'
|
||||||
amountMinor: string
|
amountMinor: string | null
|
||||||
currency: 'GEL' | 'USD'
|
currency: 'GEL' | 'USD' | null
|
||||||
confidence: number
|
confidence: number
|
||||||
reason: string
|
reason: string
|
||||||
}
|
}
|
||||||
@@ -327,6 +328,10 @@ For bare summons ("бот?", "bot", "@kojori_bot"), use topic_helper to let the
|
|||||||
For small talk or jokes directed at the bot, use chat_reply with a short playful response.
|
For small talk or jokes directed at the bot, use chat_reply with a short playful response.
|
||||||
For questions that need household knowledge, use topic_helper.
|
For questions that need household knowledge, use topic_helper.
|
||||||
|
|
||||||
|
=== LANGUAGE ===
|
||||||
|
- Always use the user's locale (locale=${input.locale}) for clarificationQuestion and replyText.
|
||||||
|
- If locale=ru, respond in Russian. If locale=en, respond in English.
|
||||||
|
|
||||||
=== WORKFLOWS ===
|
=== WORKFLOWS ===
|
||||||
If there is an active clarification workflow and the user's message answers it, combine with context.
|
If there is an active clarification workflow and the user's message answers it, combine with context.
|
||||||
If user dismisses ("не, забей", "cancel"), use dismiss_workflow.`
|
If user dismisses ("не, забей", "cancel"), use dismiss_workflow.`
|
||||||
@@ -487,9 +492,10 @@ If user dismisses ("не, забей", "cancel"), use dismiss_workflow.`
|
|||||||
},
|
},
|
||||||
'Topic processor missing purchase fields'
|
'Topic processor missing purchase fields'
|
||||||
)
|
)
|
||||||
|
const t = getBotTranslations(input.locale).purchase
|
||||||
return {
|
return {
|
||||||
route: 'purchase_clarification',
|
route: 'purchase_clarification',
|
||||||
clarificationQuestion: 'Could you clarify the purchase details?',
|
clarificationQuestion: t.clarificationLowConfidence,
|
||||||
reason: 'missing_required_fields'
|
reason: 'missing_required_fields'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -518,11 +524,16 @@ If user dismisses ("не, забей", "cancel"), use dismiss_workflow.`
|
|||||||
|
|
||||||
case 'purchase_clarification':
|
case 'purchase_clarification':
|
||||||
case 'payment_clarification': {
|
case 'payment_clarification': {
|
||||||
|
const t = getBotTranslations(input.locale)
|
||||||
|
const defaultQuestion =
|
||||||
|
route === 'purchase_clarification'
|
||||||
|
? t.purchase.clarificationLowConfidence
|
||||||
|
: t.assistant.paymentClarification
|
||||||
const clarificationQuestion =
|
const clarificationQuestion =
|
||||||
typeof parsed.clarificationQuestion === 'string' &&
|
typeof parsed.clarificationQuestion === 'string' &&
|
||||||
parsed.clarificationQuestion.trim().length > 0
|
parsed.clarificationQuestion.trim().length > 0
|
||||||
? parsed.clarificationQuestion.trim()
|
? parsed.clarificationQuestion.trim()
|
||||||
: 'Could you clarify?'
|
: defaultQuestion
|
||||||
return { route, clarificationQuestion, reason }
|
return { route, clarificationQuestion, reason }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,7 +542,7 @@ If user dismisses ("не, забей", "cancel"), use dismiss_workflow.`
|
|||||||
const currency = normalizeCurrency(parsed.currency ?? null)
|
const currency = normalizeCurrency(parsed.currency ?? null)
|
||||||
const kind = parsed.kind === 'rent' || parsed.kind === 'utilities' ? parsed.kind : null
|
const kind = parsed.kind === 'rent' || parsed.kind === 'utilities' ? parsed.kind : null
|
||||||
|
|
||||||
if (!amountMinor || !currency || !kind) {
|
if (!kind) {
|
||||||
logger?.warn(
|
logger?.warn(
|
||||||
{
|
{
|
||||||
event: 'topic_processor.missing_payment_fields',
|
event: 'topic_processor.missing_payment_fields',
|
||||||
@@ -541,9 +552,10 @@ If user dismisses ("не, забей", "cancel"), use dismiss_workflow.`
|
|||||||
},
|
},
|
||||||
'Topic processor missing payment fields'
|
'Topic processor missing payment fields'
|
||||||
)
|
)
|
||||||
|
const t = getBotTranslations(input.locale).assistant
|
||||||
return {
|
return {
|
||||||
route: 'payment_clarification',
|
route: 'payment_clarification',
|
||||||
clarificationQuestion: 'Could you clarify the payment details?',
|
clarificationQuestion: t.paymentClarification,
|
||||||
reason: 'missing_required_fields'
|
reason: 'missing_required_fields'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -551,7 +563,7 @@ If user dismisses ("не, забей", "cancel"), use dismiss_workflow.`
|
|||||||
return {
|
return {
|
||||||
route,
|
route,
|
||||||
kind,
|
kind,
|
||||||
amountMinor: amountMinor.toString(),
|
amountMinor: amountMinor?.toString() ?? null,
|
||||||
currency,
|
currency,
|
||||||
confidence,
|
confidence,
|
||||||
reason
|
reason
|
||||||
|
|||||||
Reference in New Issue
Block a user