@nabeh/chat-widget-angular
v0.0.11
Published
- Uses stream metadata IDs immediately, so feedback sends `message_id` without waiting for chat history reload. - Sends feedback payloads as `{ "message_id": "...", "isLike": true }`. - Shows user initials when no avatar URL is configured. - Displays `No
Readme
@nabeh/chat-widget-angular
Release Notes
Latest
- Uses stream metadata IDs immediately, so feedback sends
message_idwithout waiting for chat history reload. - Sends feedback payloads as
{ "message_id": "...", "isLike": true }. - Shows user initials when no avatar URL is configured.
- Displays
No Contentwhen a completed assistant response has no answer text. - Improves chat-list overflow handling and active like/dislike visual states.
Install
npm install @nabeh/chat-widget-angularPeer dependencies:
{
"@angular/common": ">=17.3.0",
"@angular/core": ">=17.3.0",
"rxjs": "6.6.7"
}Import the module where the widget is used:
import { ChatWidgetModule } from '@nabeh/chat-widget-angular';Basic Usage
<chat-widget [config]="chatConfig"></chat-widget>import { ChatWidgetConfig } from '@nabeh/chat-widget-angular';
chatConfig: ChatWidgetConfig = {
apiBaseUrl: 'https://customer-proxy.example.com',
displayMode: 'widget',
rag: {
loadHistoryOnOpen: true
},
getUserContext: () => ({
userId: 'demo-user-10',
email: '[email protected]'
})
};By default the library uses these backend paths:
{
ask: '/my-chats/:chatId/messages',
askStream: '/my-chats/:chatId/messages/stream',
history: '/my-chats/:chatId/messages',
listChats: '/my-chats',
createChat: '/my-chats',
updateChat: '/my-chats/:chatId',
deleteChat: '/my-chats/:chatId',
feedback: '/my-chats/:chatId/feedback',
upload: '/my-chats/upload',
docs: '/my-chats/docs/:filename'
}Recommended Auth Architecture
For customer deployments, prefer a customer proxy backend:
chat-widget -> customer proxy -> NestJS backend -> AI backendThe widget should call the customer proxy. The proxy can generate or refresh AI access tokens and forward requests to NestJS or AI services. In that setup, the widget does not need getAccessToken; auth stays server-side.
Use getAccessToken only for local testing or apps that intentionally attach a browser-side bearer token:
getAccessToken: () => localStorage.getItem('ACCESS_TOKEN')The widget sends that value as:
Authorization: Bearer <token>Use getUserContext to send user context:
getUserContext: () => ({
userId: 'demo-user-10',
email: '[email protected]'
})The widget sends that value as:
X-Chat-User-Context: {"userId":"demo-user-10","email":"[email protected]"}Streaming Responses
Streaming uses fetch() and ReadableStream.getReader() because Angular HttpClient does not expose token-by-token response chunks.
Streaming is enabled by default. Configure endpoints.askStream for the customer proxy or AI streaming route:
chatConfig: ChatWidgetConfig = {
apiBaseUrl: 'https://customer-proxy.example.com',
endpoints: {
askStream: '/my-chats/:chatId/messages/stream'
},
rag: {
sourceUuid: '6f8d2ef4-75f4-4b17-9f7f-92d4d0cf52c9',
enableThink: false
}
};endpoints.askStream should normally point to the customer proxy or NestJS backend. The backend should forward the request to the AI server stream endpoint. A full AI URL can be used only for isolated local testing when CORS and auth allow it:
endpoints: {
askStream: 'http://183.82.145.33:7777/ai-server/smart_docs/ask_your_doc/stream'
}The streaming request body is:
{
"chat_id": "smart-docs-session-001",
"query": "What are the key findings in this document?",
"enable_think": false,
"source_uuid": "6f8d2ef4-75f4-4b17-9f7f-92d4d0cf52c9"
}The stream parser supports concatenated JSON objects and objects split across chunks:
{"type":"metadata","content":""}
{"type":"answer","content":"The"}
{"type":"answer","content":" document"}
{"type":"references","content":{"citations":[]}}type: "answer" appends content to the current assistant message in real time. type: "references" attaches citations to that assistant message and displays source cards.
Document Preview
Citations are displayed below assistant answers and in the right sources panel. Clicking a source opens a document preview overlay.
The preview uses:
endpoints: {
docs: '/my-chats/docs/:filename'
}For a citation with knowledgeName: "labor-law" and pageNumber: 28, the iframe opens:
/my-chats/docs/labor-law#page=28The backend can resolve the real file extension by matching files that start with the requested filename.
Chat Actions
The sidebar supports:
Edit: callsupdateChatwith{ title }.Pin Chat/Unpin Chat: callsupdateChatwith{ title, pinned }and moves the chat between Recent Activity and Pinned Collections immediately.Delete: callsdeleteChatand removes the chat from the UI.
Configuration Reference
type ChatWidgetConfig = {
apiBaseUrl: string;
endpoints?: Partial<ChatWidgetEndpoints>;
displayMode?: 'widget' | 'embedded';
position?: 'bottom-right' | 'bottom-left';
title?: string;
subtitle?: string;
welcomeMessage?: string;
inputPlaceholder?: string;
launcherAriaLabel?: string;
closeAriaLabel?: string;
initialSuggestions?: string[];
sourceApp?: string;
locale?: string;
customHeaders?: Record<string, string>;
rag?: KnowledgeRagConfig;
getAccessToken?: () => Promise<string | null> | string | null;
getUserContext?: () => Promise<UserContext | null> | UserContext | null;
userInfo?: () => Promise<UserInfo | null> | UserInfo | null;
onOpen?: () => void;
onClose?: () => void;
onError?: (error: Error) => void;
onOpenAssistantPage?: () => Promise<void> | void;
assistantPageUrl?: string;
assistantAvatarUrl?: string;
embedded?: {
showHeader?: boolean;
};
};apiBaseUrl
Base URL for the customer proxy or backend.
endpoints
Override any backend path. Relative paths are resolved against apiBaseUrl. Full URLs are supported for askStream and other endpoints.
customHeaders
Static headers added to every request. Prefer proxy-side auth for production.
getAccessToken
Optional browser-side bearer token provider. Useful for testing; not required when the customer proxy adds tokens server-side.
getUserContext
Provides user context for backend user mapping. Sent as X-Chat-User-Context.
rag
type KnowledgeRagConfig = {
chatId?: string;
chatIdFactory?: () => string;
knowledgeNames?: string[];
sourceUuid?: string;
enableThink?: boolean;
useStreaming?: boolean;
enableReferences?: boolean;
loadHistoryOnOpen?: boolean;
};sourceUuid is used by the AI streaming endpoint as source_uuid. Chat responses use endpoints.askStream by default.
