wovvmap-ai-assistant
v1.0.3
Published
Wovv AI Assistant Cross-platform Package
Readme
@wovvmap/ai-assistant
A headless React SDK for integrating the WovVMall AI Navigation Assistant (PrismOne) into web and mobile maps.
This package is designed as a headless core, meaning it provides all the API services, state management, automatic kiosk session timeouts, and parsing logic, but leaves the UI rendering entirely up to the developer.
Installation
Install the package via npm or yarn:
npm install @wovvmap/ai-assistant
# or
yarn add @wovvmap/ai-assistantMake sure react is installed in your host project as a peer dependency.
Core Concepts
useAIAssistant React Hook
The primary entry point is the useAIAssistant hook. It handles:
- Session initiation and auto-renewals.
- Idle timeouts for kiosk deployments (auto-clears chat history after 2 minutes of inactivity).
- Sending user queries and parsing response formats (cards, suggestions, route directions, clarifications).
- Error boundaries and typing loaders.
Configuration Options
When calling useAIAssistant(options), you can configure the hook with:
| Option | Type | Required | Default | Description |
| :--- | :--- | :--- | :--- | :--- |
| mapId | string | Yes | - | The ID of the mall or map. |
| channel | 'web' \| 'mobile' \| 'kiosk' | Yes | - | Deployment channel. Kiosk auto-clears on 2m inactivity. |
| baseUrl | string | No | 'https://mapai.wovvtech.site' | The base URL of the Wovv AI backend. |
| maxHistory | number | No | 6 | Maximum messages context history limit. |
| initialGreeting | string | No | - | Optional greeting message to populate chat. |
| onNavigate | (toKey: string) => void | No | - | Triggered when the user clicks a card navigation button. |
| onShowDirection | (fromKey: string, toKey: string) => void | No | - | Triggered when the user requests route directions. |
| onClearRoute | () => void | No | - | Triggered if routing states need to be reset. |
Hook Return Values
The hook returns:
const {
messages, // Array of ChatMessage objects (contains user/assistant/error items)
isTyping, // Boolean indicating if assistant is typing
error, // Error string or null if successful
sendMessage, // Function to send a text query: (text: string) => void
clearChat // Function to reset chat history: () => void
} = useAIAssistant(options);Usage Examples
1. React Web Implementation
Here is a basic implementation of a customized Chat Modal on the Web (using Tailwind CSS):
import React, { useState, useRef, useEffect } from 'react';
import { useAIAssistant } from '@wovvmap/ai-assistant';
export function WebAIChatModal({ mapId, onClose }) {
const [input, setInput] = useState('');
const messagesEndRef = useRef<HTMLDivElement>(null);
const { messages, isTyping, error, sendMessage } = useAIAssistant({
mapId,
channel: 'kiosk',
onNavigate: (toKey) => {
console.log('Navigate to store key:', toKey);
// Implement map navigation logic
},
onShowDirection: (fromKey, toKey) => {
console.log(`Draw path from ${fromKey} to ${toKey}`);
// Implement map routing path logic
}
});
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages, isTyping]);
const handleSend = () => {
if (!input.trim() || isTyping) return;
sendMessage(input.trim());
setInput('');
};
return (
<div className="flex h-96 w-80 flex-col bg-white border rounded-xl shadow-lg">
{/* Header */}
<div className="p-3 border-b flex justify-between">
<span className="font-bold">PrismOne Assistant</span>
<button onClick={onClose}>×</button>
</div>
{/* Messages */}
<div className="flex-1 overflow-y-auto p-3 space-y-2">
{messages.map((msg) => (
<div
key={msg.id}
className={`p-2 rounded-lg text-sm max-w-[85%] ${
msg.role === 'user'
? 'bg-blue-600 text-white ml-auto'
: msg.role === 'error'
? 'bg-red-100 text-red-700'
: 'bg-gray-100 text-gray-800'
}`}
>
{msg.text}
{/* Render Store Cards */}
{msg.cards && msg.cards.map((card) => (
<div key={card.key} className="mt-2 p-2 bg-white border rounded">
<p className="font-semibold text-xs">{card.name}</p>
<button
onClick={() => onNavigate && onNavigate(card.key)}
className="mt-1 text-xs text-blue-600 underline"
>
Show on Map
</button>
</div>
))}
</div>
))}
{isTyping && <div className="text-xs text-gray-400 italic">Thinking...</div>}
{error && <div className="text-xs text-red-500">Error: {error}</div>}
<div ref={messagesEndRef} />
</div>
{/* Input */}
<div className="p-2 border-t flex gap-1">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Ask me anything..."
className="flex-1 border rounded px-2 text-sm"
/>
<button onClick={handleSend} className="bg-blue-600 text-white px-3 py-1 rounded">
Send
</button>
</div>
</div>
);
}2. React Native (Mobile) Implementation
import React, { useState } from 'react';
import { View, Text, TextInput, TouchableOpacity, ScrollView, StyleSheet } from 'react-native';
import { useAIAssistant } from '@wovvmap/ai-assistant';
export function MobileAIChat({ mapId }) {
const [input, setInput] = useState('');
const { messages, isTyping, sendMessage } = useAIAssistant({
mapId,
channel: 'mobile',
onNavigate: (key) => {
// Handle mobile navigation events
}
});
const handleSend = () => {
if (!input.trim()) return;
sendMessage(input.trim());
setInput('');
};
return (
<View style={styles.container}>
<ScrollView style={styles.chatArea}>
{messages.map((msg) => (
<View
key={msg.id}
style={[
styles.bubble,
msg.role === 'user' ? styles.userBubble : styles.assistantBubble,
]}
>
<Text style={msg.role === 'user' ? styles.userText : styles.assistantText}>
{msg.text}
</Text>
</View>
))}
</ScrollView>
<View style={styles.inputRow}>
<TextInput
value={input}
onChangeText={setInput}
style={styles.input}
placeholder="Ask PrismOne..."
/>
<TouchableOpacity onPress={handleSend} style={styles.sendButton}>
<Text style={styles.sendText}>Send</Text>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#fff' },
chatArea: { flex: 1, padding: 12 },
bubble: { padding: 10, borderRadius: 12, marginVertical: 4, maxWidth: '80%' },
userBubble: { backgroundColor: '#A2298C', alignSelf: 'flex-end' },
assistantBubble: { backgroundColor: '#f0f0f0', alignSelf: 'flex-start' },
userText: { color: '#fff' },
assistantText: { color: '#333' },
inputRow: { flexDirection: 'row', padding: 8, borderTopWidth: 1, borderColor: '#eee' },
input: { flex: 1, height: 40, borderWidth: 1, borderColor: '#ddd', borderRadius: 8, paddingHorizontal: 10 },
sendButton: { backgroundColor: '#A2298C', justifyContent: 'center', paddingHorizontal: 16, borderRadius: 8, marginLeft: 6 },
sendText: { color: '#fff', fontWeight: 'bold' },
});License
MIT
