Fix Julia AI voice: use SINGLE_DEPLOYMENT_MODE for Lite

- livekitService.ts: send empty beneficiaryNamesDict in Lite mode
- agent.py: handle None beneficiary_names_dict correctly
- chat.tsx: align text chat with same SINGLE_DEPLOYMENT_MODE flag

This fixes Julia saying "I didn't get the name of beneficiary"
by letting WellNuo API use the default beneficiary for deployment_id.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Sergei 2026-01-22 16:49:55 -08:00
parent 8d98bab3cf
commit 5ecb5f9683
5 changed files with 76 additions and 19 deletions

View File

@ -49,12 +49,14 @@ export default function LoginScreen() {
return ( return (
<KeyboardAvoidingView <KeyboardAvoidingView
style={styles.container} style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'} behavior="padding"
keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 20}
> >
<ScrollView <ScrollView
contentContainerStyle={styles.scrollContent} contentContainerStyle={styles.scrollContent}
keyboardShouldPersistTaps="handled" keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
bounces={false}
> >
{/* Logo / Header */} {/* Logo / Header */}
<View style={styles.header}> <View style={styles.header}>
@ -132,8 +134,9 @@ const styles = StyleSheet.create({
}, },
scrollContent: { scrollContent: {
flexGrow: 1, flexGrow: 1,
justifyContent: 'center',
paddingHorizontal: Spacing.lg, paddingHorizontal: Spacing.lg,
paddingTop: Spacing.xxl + Spacing.xl, paddingTop: Spacing.xl,
paddingBottom: Spacing.xl, paddingBottom: Spacing.xl,
}, },
header: { header: {

View File

@ -34,6 +34,16 @@ const API_URL = 'https://eluxnetworks.net/function/well-api/api';
const WELLNUO_USER = 'anandk'; const WELLNUO_USER = 'anandk';
const WELLNUO_PASSWORD = 'anandk_8'; const WELLNUO_PASSWORD = 'anandk_8';
// ============================================================================
// SINGLE_DEPLOYMENT_MODE
// When true: sends only deployment_id (no beneficiary_names_dict)
// When false: sends both deployment_id AND beneficiary_names_dict
//
// Use true for WellNuo Lite (single beneficiary per user)
// Use false for full WellNuo app (multiple beneficiaries)
// ============================================================================
const SINGLE_DEPLOYMENT_MODE = true;
// Keywords for question normalization (same as julia-agent/julia-ai/src/agent.py) // Keywords for question normalization (same as julia-agent/julia-ai/src/agent.py)
const STATUS_KEYWORDS = [ const STATUS_KEYWORDS = [
/\bhow\s+is\b/i, /\bhow\s+is\b/i,
@ -271,19 +281,25 @@ export default function ChatScreen() {
const deploymentId = currentBeneficiary?.id?.toString() || beneficiaries[0]?.id?.toString() || '21'; const deploymentId = currentBeneficiary?.id?.toString() || beneficiaries[0]?.id?.toString() || '21';
// Call API with EXACT same params as voice agent // Call API with EXACT same params as voice agent
// Using ask_wellnuo_ai with new beneficiary_names_dict parameter // SINGLE_DEPLOYMENT_MODE: sends only deployment_id (no beneficiary_names_dict)
const response = await fetch(API_URL, { const requestParams: Record<string, string> = {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
function: 'ask_wellnuo_ai', function: 'ask_wellnuo_ai',
clientId: 'MA_001', clientId: 'MA_001',
user_name: WELLNUO_USER, user_name: WELLNUO_USER,
token: token, token: token,
question: normalizedQuestion, question: normalizedQuestion,
deployment_id: deploymentId, deployment_id: deploymentId,
beneficiary_names_dict: JSON.stringify(beneficiaryNamesDict), };
}).toString(),
// Only add beneficiary_names_dict if NOT in single deployment mode
if (!SINGLE_DEPLOYMENT_MODE) {
requestParams.beneficiary_names_dict = JSON.stringify(beneficiaryNamesDict);
}
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams(requestParams).toString(),
}); });
const data = await response.json(); const data = await response.json();

View File

@ -27,7 +27,7 @@
"credentialsSource": "remote" "credentialsSource": "remote"
}, },
"android": { "android": {
"buildType": "apk" "buildType": "app-bundle"
} }
} }
}, },

View File

@ -151,7 +151,10 @@ class WellNuoLLM(llm.LLM):
self._model_name = "wellnuo-voice-ask" self._model_name = "wellnuo-voice-ask"
# Dynamic values from participant metadata (or fallback to env/defaults) # Dynamic values from participant metadata (or fallback to env/defaults)
self._deployment_id = deployment_id or DEPLOYMENT_ID self._deployment_id = deployment_id or DEPLOYMENT_ID
self._beneficiary_names_dict = beneficiary_names_dict or {} # SINGLE_DEPLOYMENT_MODE: if beneficiary_names_dict is empty or None,
# WellNuo API will automatically use the beneficiary name for this deployment_id
# This is the Lite mode - we don't need to pass the names dict
self._beneficiary_names_dict = beneficiary_names_dict if beneficiary_names_dict else None
@property @property
def model(self) -> str: def model(self) -> str:
@ -209,13 +212,19 @@ class WellNuoLLM(llm.LLM):
"question": normalized_question, "question": normalized_question,
"deployment_id": self._deployment_id, "deployment_id": self._deployment_id,
} }
# Add beneficiary_names_dict if available # Add beneficiary_names_dict ONLY if it's not empty
# In SINGLE_DEPLOYMENT_MODE (Lite app), we don't send names dict
# WellNuo API will use the beneficiary name for this deployment_id
if self._beneficiary_names_dict: if self._beneficiary_names_dict:
data["beneficiary_names_dict"] = json.dumps( data["beneficiary_names_dict"] = json.dumps(
self._beneficiary_names_dict self._beneficiary_names_dict
) )
logger.info( logger.info(
f"Using beneficiary_names_dict: {self._beneficiary_names_dict}" f"Full mode: Using beneficiary_names_dict: {self._beneficiary_names_dict}"
)
else:
logger.info(
f"Single deployment mode: deployment_id={self._deployment_id}, no beneficiary_names_dict"
) )
async with session.post(WELLNUO_API_URL, data=data) as resp: async with session.post(WELLNUO_API_URL, data=data) as resp:
result = await resp.json() result = await resp.json()

View File

@ -11,6 +11,16 @@ const JULIA_TOKEN_SERVER = 'https://wellnuo.smartlaunchhub.com/julia';
export const VOICE_ID = 'Asteria'; export const VOICE_ID = 'Asteria';
export const VOICE_NAME = 'Asteria'; export const VOICE_NAME = 'Asteria';
// ============================================================================
// SINGLE_DEPLOYMENT_MODE
// When true: sends only deploymentId (no beneficiaryNamesDict)
// When false: sends both deploymentId AND beneficiaryNamesDict
//
// Use true for WellNuo Lite (single beneficiary per user)
// Use false for full WellNuo app (multiple beneficiaries)
// ============================================================================
export const SINGLE_DEPLOYMENT_MODE = true;
// Beneficiary data to pass to voice agent // Beneficiary data to pass to voice agent
export interface BeneficiaryData { export interface BeneficiaryData {
deploymentId: string; deploymentId: string;
@ -40,8 +50,27 @@ export async function getToken(
): Promise<LiveKitTokenResponse> { ): Promise<LiveKitTokenResponse> {
try { try {
console.log('[LiveKit] Getting token for user:', userId); console.log('[LiveKit] Getting token for user:', userId);
console.log('[LiveKit] SINGLE_DEPLOYMENT_MODE:', SINGLE_DEPLOYMENT_MODE);
// Prepare request body based on SINGLE_DEPLOYMENT_MODE
let requestBody: { userId: string; beneficiaryData?: BeneficiaryData };
if (SINGLE_DEPLOYMENT_MODE && beneficiaryData) {
// In single deployment mode: send only deploymentId, no beneficiaryNamesDict
requestBody = {
userId,
beneficiaryData: {
deploymentId: beneficiaryData.deploymentId,
beneficiaryNamesDict: {}, // Empty - no list of names
},
};
console.log('[LiveKit] Single deployment mode - sending only deploymentId:', beneficiaryData.deploymentId);
} else {
// Full mode: send everything
requestBody = { userId, beneficiaryData };
if (beneficiaryData) { if (beneficiaryData) {
console.log('[LiveKit] With beneficiary data:', beneficiaryData); console.log('[LiveKit] Full mode - sending beneficiary data:', beneficiaryData);
}
} }
// Request LiveKit token from Julia Token Server // Request LiveKit token from Julia Token Server
@ -50,7 +79,7 @@ export async function getToken(
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ userId, beneficiaryData }), body: JSON.stringify(requestBody),
}); });
if (!response.ok) { if (!response.ok) {