@webority/mobile-chat 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -107,8 +107,8 @@ actions, messageId, rating, ratingNote }]`, oldest first), `conversationId`, `me
107
107
 
108
108
  An answer still arriving is cancelled on `reset`, `newConversation`, `openConversation` and
109
109
  unmount. A failed turn keeps the question, drops the half-streamed text and carries `error`
110
- (`{ code, message, resetsAtUtc }`). After `daily-limit`, `messagesLeftToday` becomes 0; after
111
- `conversation-not-found`, the next question starts a new conversation. A question asked while an
110
+ (`{ code, message, resetsAtUtc }`). After `daily_limit`, `messagesLeftToday` becomes 0; after
111
+ `conversation_not_found`, the next question starts a new conversation. A question asked while an
112
112
  answer is arriving is ignored.
113
113
 
114
114
  ## Components
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.useChat=void 0;var _react=require("react");const toError=e=>{const t=e;return{code:"string"==typeof t?.code&&t.code?t.code:"unknown",message:"string"==typeof t?.message&&t.message?t.message:"Something went wrong. Please try again.",resetsAtUtc:"string"==typeof t?.resetsAtUtc?t.resetsAtUtc:null}},parseActions=e=>{if(!e)return[];const t=JSON.parse(e);return Array.isArray(t)?t:[]},emptyTurn=(e,t)=>({id:e,question:t,answer:"",streaming:!1,error:null,actions:[],messageId:null,rating:null,ratingNote:null}),turnsFromConversation=e=>{const t=[];for(const r of e??[]){if("User"===r.role){t.push(emptyTurn(r.publicId,r.content));continue}const e=t[t.length-1],n={answer:r.content,actions:parseActions(r.actionsJson),messageId:r.publicId,rating:r.rating??null,ratingNote:r.ratingNote??null};e&&null===e.messageId&&""===e.answer?Object.assign(e,n):t.push({...emptyTurn(r.publicId,""),...n})}return t},useChat=({client:e,pageContext:t})=>{const[r,n]=(0,_react.useState)([]),[s,a]=(0,_react.useState)(null),[c,o]=(0,_react.useState)(null),[u,l]=(0,_react.useState)(!1),[i,g]=(0,_react.useState)(null),d=(0,_react.useRef)(null),m=(0,_react.useRef)(0),f=(0,_react.useRef)(0),y=(0,_react.useRef)(t);y.current=t;const p=(0,_react.useRef)(null);p.current=s;const _=(0,_react.useRef)(r);_.current=r;const w=(0,_react.useCallback)(()=>{m.current+=1,d.current?.abort(),d.current=null},[]);(0,_react.useEffect)(()=>w,[w]);const C=(0,_react.useCallback)((e,t)=>{n(r=>r.map(r=>r.id===e?{...r,..."function"==typeof t?t(r):t}:r))},[]),b=(0,_react.useCallback)(async t=>{const r="string"==typeof t?t.trim():"";if(!r||d.current)return;const s=m.current,c=new AbortController;d.current=c,f.current+=1;const u=`turn-${f.current}`;n(e=>[...e,{...emptyTurn(u,r),streaming:!0}]);const live=()=>s===m.current&&!c.signal.aborted;try{const t=await e.sendMessage({message:r,conversationId:p.current,pageContext:y.current},{signal:c.signal,onEvent:(e,t)=>{if(!live())return;const r=t;"delta"===e?C(u,e=>({answer:e.answer+(r?.text??"")})):"reset"===e?C(u,{answer:""}):"actions"===e&&C(u,{actions:Array.isArray(r?.actions)?r.actions:[]})}});if(!live())return;C(u,{answer:t.reply,messageId:t.messageId??null,streaming:!1}),p.current=t.conversationId,a(t.conversationId),o(t.messagesLeftToday??null)}catch(e){if(!live())return;const t=toError(e);C(u,{answer:"",actions:[],streaming:!1,error:t}),"daily-limit"===t.code&&o(0),"conversation-not-found"===t.code&&(p.current=null,a(null))}finally{d.current===c&&(d.current=null)}},[e,C]),I=(0,_react.useCallback)(async t=>{w();const r=m.current;n([]),a(null),g(null),l(!0);try{const s=await e.getConversation(t);if(r!==m.current)return;n(turnsFromConversation(s.messages)),a(s.publicId)}catch(e){if(r!==m.current)return;g(toError(e))}finally{r===m.current&&l(!1)}},[w,e]),h=(0,_react.useCallback)(()=>{w(),n([]),a(null),g(null),l(!1)},[w]),v=(0,_react.useCallback)(()=>{w(),n([]),a(null),o(null),g(null),l(!1)},[w]),A=(0,_react.useCallback)(async()=>{const t=m.current,r=await e.getAllowance();t===m.current&&o(r?.messagesLeftToday??null)},[e]),k=(0,_react.useCallback)(async(t,r,n)=>{const s=_.current.find(e=>e.id===t),a=p.current;if(!s?.messageId||!a)throw new Error("Only a stored reply can be rated.");const c={rating:s.rating,ratingNote:s.ratingNote},o="string"==typeof n&&n.trim()?n.trim():null;C(t,{rating:r,ratingNote:o});try{await e.rateMessage(a,s.messageId,{rating:r,note:o})}catch(e){throw C(t,c),e}},[e,C]);return{turns:r,conversationId:s,messagesLeftToday:c,streaming:r.some(e=>e.streaming),loading:u,loadError:i,submit:b,openConversation:I,newConversation:h,rate:k,refreshAllowance:A,reset:v}};exports.useChat=useChat;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.useChat=void 0;var _react=require("react");const toError=e=>{const t=e;return{code:"string"==typeof t?.code&&t.code?t.code:"unknown",message:"string"==typeof t?.message&&t.message?t.message:"Something went wrong. Please try again.",resetsAtUtc:"string"==typeof t?.resetsAtUtc?t.resetsAtUtc:null}},parseActions=e=>{if(!e)return[];const t=JSON.parse(e);return Array.isArray(t)?t:[]},emptyTurn=(e,t)=>({id:e,question:t,answer:"",streaming:!1,error:null,actions:[],messageId:null,rating:null,ratingNote:null}),turnsFromConversation=e=>{const t=[];for(const r of e??[]){if("User"===r.role){t.push(emptyTurn(r.publicId,r.content));continue}const e=t[t.length-1],n={answer:r.content,actions:parseActions(r.actionsJson),messageId:r.publicId,rating:r.rating??null,ratingNote:r.ratingNote??null};e&&null===e.messageId&&""===e.answer?Object.assign(e,n):t.push({...emptyTurn(r.publicId,""),...n})}return t},useChat=({client:e,pageContext:t})=>{const[r,n]=(0,_react.useState)([]),[s,a]=(0,_react.useState)(null),[c,o]=(0,_react.useState)(null),[u,l]=(0,_react.useState)(!1),[i,g]=(0,_react.useState)(null),d=(0,_react.useRef)(null),m=(0,_react.useRef)(0),f=(0,_react.useRef)(0),y=(0,_react.useRef)(t);y.current=t;const p=(0,_react.useRef)(null);p.current=s;const _=(0,_react.useRef)(r);_.current=r;const w=(0,_react.useCallback)(()=>{m.current+=1,d.current?.abort(),d.current=null},[]);(0,_react.useEffect)(()=>w,[w]);const C=(0,_react.useCallback)((e,t)=>{n(r=>r.map(r=>r.id===e?{...r,..."function"==typeof t?t(r):t}:r))},[]),b=(0,_react.useCallback)(async t=>{const r="string"==typeof t?t.trim():"";if(!r||d.current)return;const s=m.current,c=new AbortController;d.current=c,f.current+=1;const u=`turn-${f.current}`;n(e=>[...e,{...emptyTurn(u,r),streaming:!0}]);const live=()=>s===m.current&&!c.signal.aborted;try{const t=await e.sendMessage({message:r,conversationId:p.current,pageContext:y.current},{signal:c.signal,onEvent:(e,t)=>{if(!live())return;const r=t;"delta"===e?C(u,e=>({answer:e.answer+(r?.text??"")})):"reset"===e?C(u,{answer:""}):"actions"===e&&C(u,{actions:Array.isArray(r?.actions)?r.actions:[]})}});if(!live())return;C(u,{answer:t.reply,messageId:t.messageId??null,streaming:!1}),p.current=t.conversationId,a(t.conversationId),o(t.messagesLeftToday??null)}catch(e){if(!live())return;const t=toError(e);C(u,{answer:"",actions:[],streaming:!1,error:t}),"daily_limit"===t.code&&o(0),"conversation_not_found"===t.code&&(p.current=null,a(null))}finally{d.current===c&&(d.current=null)}},[e,C]),I=(0,_react.useCallback)(async t=>{w();const r=m.current;n([]),a(null),g(null),l(!0);try{const s=await e.getConversation(t);if(r!==m.current)return;n(turnsFromConversation(s.messages)),a(s.publicId)}catch(e){if(r!==m.current)return;g(toError(e))}finally{r===m.current&&l(!1)}},[w,e]),h=(0,_react.useCallback)(()=>{w(),n([]),a(null),g(null),l(!1)},[w]),v=(0,_react.useCallback)(()=>{w(),n([]),a(null),o(null),g(null),l(!1)},[w]),A=(0,_react.useCallback)(async()=>{const t=m.current,r=await e.getAllowance();t===m.current&&o(r?.messagesLeftToday??null)},[e]),k=(0,_react.useCallback)(async(t,r,n)=>{const s=_.current.find(e=>e.id===t),a=p.current;if(!s?.messageId||!a)throw new Error("Only a stored reply can be rated.");const c={rating:s.rating,ratingNote:s.ratingNote},o="string"==typeof n&&n.trim()?n.trim():null;C(t,{rating:r,ratingNote:o});try{await e.rateMessage(a,s.messageId,{rating:r,note:o})}catch(e){throw C(t,c),e}},[e,C]);return{turns:r,conversationId:s,messagesLeftToday:c,streaming:r.some(e=>e.streaming),loading:u,loadError:i,submit:b,openConversation:I,newConversation:h,rate:k,refreshAllowance:A,reset:v}};exports.useChat=useChat;
@@ -1 +1 @@
1
- "use strict";import{useCallback,useEffect,useRef,useState}from"react";const toError=e=>{const t=e;return{code:"string"==typeof t?.code&&t.code?t.code:"unknown",message:"string"==typeof t?.message&&t.message?t.message:"Something went wrong. Please try again.",resetsAtUtc:"string"==typeof t?.resetsAtUtc?t.resetsAtUtc:null}},parseActions=e=>{if(!e)return[];const t=JSON.parse(e);return Array.isArray(t)?t:[]},emptyTurn=(e,t)=>({id:e,question:t,answer:"",streaming:!1,error:null,actions:[],messageId:null,rating:null,ratingNote:null}),turnsFromConversation=e=>{const t=[];for(const n of e??[]){if("User"===n.role){t.push(emptyTurn(n.publicId,n.content));continue}const e=t[t.length-1],r={answer:n.content,actions:parseActions(n.actionsJson),messageId:n.publicId,rating:n.rating??null,ratingNote:n.ratingNote??null};e&&null===e.messageId&&""===e.answer?Object.assign(e,r):t.push({...emptyTurn(n.publicId,""),...r})}return t};export const useChat=({client:e,pageContext:t})=>{const[n,r]=useState([]),[s,a]=useState(null),[o,u]=useState(null),[c,l]=useState(!1),[i,g]=useState(null),f=useRef(null),m=useRef(0),d=useRef(0),y=useRef(t);y.current=t;const p=useRef(null);p.current=s;const w=useRef(n);w.current=n;const b=useCallback(()=>{m.current+=1,f.current?.abort(),f.current=null},[]);useEffect(()=>b,[b]);const C=useCallback((e,t)=>{r(n=>n.map(n=>n.id===e?{...n,..."function"==typeof t?t(n):t}:n))},[]),I=useCallback(async t=>{const n="string"==typeof t?t.trim():"";if(!n||f.current)return;const s=m.current,o=new AbortController;f.current=o,d.current+=1;const c=`turn-${d.current}`;r(e=>[...e,{...emptyTurn(c,n),streaming:!0}]);const live=()=>s===m.current&&!o.signal.aborted;try{const t=await e.sendMessage({message:n,conversationId:p.current,pageContext:y.current},{signal:o.signal,onEvent:(e,t)=>{if(!live())return;const n=t;"delta"===e?C(c,e=>({answer:e.answer+(n?.text??"")})):"reset"===e?C(c,{answer:""}):"actions"===e&&C(c,{actions:Array.isArray(n?.actions)?n.actions:[]})}});if(!live())return;C(c,{answer:t.reply,messageId:t.messageId??null,streaming:!1}),p.current=t.conversationId,a(t.conversationId),u(t.messagesLeftToday??null)}catch(e){if(!live())return;const t=toError(e);C(c,{answer:"",actions:[],streaming:!1,error:t}),"daily-limit"===t.code&&u(0),"conversation-not-found"===t.code&&(p.current=null,a(null))}finally{f.current===o&&(f.current=null)}},[e,C]),A=useCallback(async t=>{b();const n=m.current;r([]),a(null),g(null),l(!0);try{const s=await e.getConversation(t);if(n!==m.current)return;r(turnsFromConversation(s.messages)),a(s.publicId)}catch(e){if(n!==m.current)return;g(toError(e))}finally{n===m.current&&l(!1)}},[b,e]),h=useCallback(()=>{b(),r([]),a(null),g(null),l(!1)},[b]),v=useCallback(()=>{b(),r([]),a(null),u(null),g(null),l(!1)},[b]),k=useCallback(async()=>{const t=m.current,n=await e.getAllowance();t===m.current&&u(n?.messagesLeftToday??null)},[e]),E=useCallback(async(t,n,r)=>{const s=w.current.find(e=>e.id===t),a=p.current;if(!s?.messageId||!a)throw new Error("Only a stored reply can be rated.");const o={rating:s.rating,ratingNote:s.ratingNote},u="string"==typeof r&&r.trim()?r.trim():null;C(t,{rating:n,ratingNote:u});try{await e.rateMessage(a,s.messageId,{rating:n,note:u})}catch(e){throw C(t,o),e}},[e,C]);return{turns:n,conversationId:s,messagesLeftToday:o,streaming:n.some(e=>e.streaming),loading:c,loadError:i,submit:I,openConversation:A,newConversation:h,rate:E,refreshAllowance:k,reset:v}};
1
+ "use strict";import{useCallback,useEffect,useRef,useState}from"react";const toError=e=>{const t=e;return{code:"string"==typeof t?.code&&t.code?t.code:"unknown",message:"string"==typeof t?.message&&t.message?t.message:"Something went wrong. Please try again.",resetsAtUtc:"string"==typeof t?.resetsAtUtc?t.resetsAtUtc:null}},parseActions=e=>{if(!e)return[];const t=JSON.parse(e);return Array.isArray(t)?t:[]},emptyTurn=(e,t)=>({id:e,question:t,answer:"",streaming:!1,error:null,actions:[],messageId:null,rating:null,ratingNote:null}),turnsFromConversation=e=>{const t=[];for(const n of e??[]){if("User"===n.role){t.push(emptyTurn(n.publicId,n.content));continue}const e=t[t.length-1],r={answer:n.content,actions:parseActions(n.actionsJson),messageId:n.publicId,rating:n.rating??null,ratingNote:n.ratingNote??null};e&&null===e.messageId&&""===e.answer?Object.assign(e,r):t.push({...emptyTurn(n.publicId,""),...r})}return t};export const useChat=({client:e,pageContext:t})=>{const[n,r]=useState([]),[s,a]=useState(null),[o,u]=useState(null),[c,l]=useState(!1),[i,g]=useState(null),f=useRef(null),m=useRef(0),d=useRef(0),y=useRef(t);y.current=t;const p=useRef(null);p.current=s;const w=useRef(n);w.current=n;const b=useCallback(()=>{m.current+=1,f.current?.abort(),f.current=null},[]);useEffect(()=>b,[b]);const C=useCallback((e,t)=>{r(n=>n.map(n=>n.id===e?{...n,..."function"==typeof t?t(n):t}:n))},[]),I=useCallback(async t=>{const n="string"==typeof t?t.trim():"";if(!n||f.current)return;const s=m.current,o=new AbortController;f.current=o,d.current+=1;const c=`turn-${d.current}`;r(e=>[...e,{...emptyTurn(c,n),streaming:!0}]);const live=()=>s===m.current&&!o.signal.aborted;try{const t=await e.sendMessage({message:n,conversationId:p.current,pageContext:y.current},{signal:o.signal,onEvent:(e,t)=>{if(!live())return;const n=t;"delta"===e?C(c,e=>({answer:e.answer+(n?.text??"")})):"reset"===e?C(c,{answer:""}):"actions"===e&&C(c,{actions:Array.isArray(n?.actions)?n.actions:[]})}});if(!live())return;C(c,{answer:t.reply,messageId:t.messageId??null,streaming:!1}),p.current=t.conversationId,a(t.conversationId),u(t.messagesLeftToday??null)}catch(e){if(!live())return;const t=toError(e);C(c,{answer:"",actions:[],streaming:!1,error:t}),"daily_limit"===t.code&&u(0),"conversation_not_found"===t.code&&(p.current=null,a(null))}finally{f.current===o&&(f.current=null)}},[e,C]),A=useCallback(async t=>{b();const n=m.current;r([]),a(null),g(null),l(!0);try{const s=await e.getConversation(t);if(n!==m.current)return;r(turnsFromConversation(s.messages)),a(s.publicId)}catch(e){if(n!==m.current)return;g(toError(e))}finally{n===m.current&&l(!1)}},[b,e]),h=useCallback(()=>{b(),r([]),a(null),g(null),l(!1)},[b]),v=useCallback(()=>{b(),r([]),a(null),u(null),g(null),l(!1)},[b]),k=useCallback(async()=>{const t=m.current,n=await e.getAllowance();t===m.current&&u(n?.messagesLeftToday??null)},[e]),E=useCallback(async(t,n,r)=>{const s=w.current.find(e=>e.id===t),a=p.current;if(!s?.messageId||!a)throw new Error("Only a stored reply can be rated.");const o={rating:s.rating,ratingNote:s.ratingNote},u="string"==typeof r&&r.trim()?r.trim():null;C(t,{rating:n,ratingNote:u});try{await e.rateMessage(a,s.messageId,{rating:n,note:u})}catch(e){throw C(t,o),e}},[e,C]);return{turns:n,conversationId:s,messagesLeftToday:o,streaming:n.some(e=>e.streaming),loading:c,loadError:i,submit:I,openConversation:A,newConversation:h,rate:E,refreshAllowance:k,reset:v}};
@@ -2,13 +2,13 @@ import type { ChatRatingInput, ChatStreamOptions, ChatTurnInput, ChatTurnResult,
2
2
  /** A failed chat request. `message` is written for the person and is safe to show as is. */
3
3
  export declare class ChatApiError extends Error {
4
4
  /**
5
- * The server's code (`daily-limit`, `assistant-unavailable`, ...), `network` when no response
5
+ * The server's code (`daily_limit`, `assistant_unavailable`, ...), `network` when no response
6
6
  * arrived, `aborted` when the request was cancelled, `invalid-response`, or `unknown`.
7
7
  */
8
8
  readonly code: string;
9
9
  /** The HTTP status, when a response arrived. */
10
10
  readonly status: number | undefined;
11
- /** For `daily-limit`: when the allowance comes back, in UTC. */
11
+ /** For `daily_limit`: when the allowance comes back, in UTC. */
12
12
  readonly resetsAtUtc: string | null;
13
13
  constructor(code: string, message: string, status?: number, resetsAtUtc?: string | null);
14
14
  }
@@ -2,13 +2,13 @@ import type { ChatRatingInput, ChatStreamOptions, ChatTurnInput, ChatTurnResult,
2
2
  /** A failed chat request. `message` is written for the person and is safe to show as is. */
3
3
  export declare class ChatApiError extends Error {
4
4
  /**
5
- * The server's code (`daily-limit`, `assistant-unavailable`, ...), `network` when no response
5
+ * The server's code (`daily_limit`, `assistant_unavailable`, ...), `network` when no response
6
6
  * arrived, `aborted` when the request was cancelled, `invalid-response`, or `unknown`.
7
7
  */
8
8
  readonly code: string;
9
9
  /** The HTTP status, when a response arrived. */
10
10
  readonly status: number | undefined;
11
- /** For `daily-limit`: when the allowance comes back, in UTC. */
11
+ /** For `daily_limit`: when the allowance comes back, in UTC. */
12
12
  readonly resetsAtUtc: string | null;
13
13
  constructor(code: string, message: string, status?: number, resetsAtUtc?: string | null);
14
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webority/mobile-chat",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "AI assistant chat for Webority React Native apps: a streaming chat client, a store-agnostic chat hook, and the chat screen, thread, composer, past chats, reply rating and support action cards, built on @webority/mobile-ui.",
5
5
  "keywords": [
6
6
  "react-native",
@@ -42,8 +42,8 @@
42
42
  "!**/*.map"
43
43
  ],
44
44
  "peerDependencies": {
45
- "@webority/mobile-core": ">=0.15.0 <0.16.0",
46
- "@webority/mobile-ui": ">=0.15.0 <0.16.0",
45
+ "@webority/mobile-core": ">=0.16.0 <0.17.0",
46
+ "@webority/mobile-ui": ">=0.16.0 <0.17.0",
47
47
  "react": "^19.1.0",
48
48
  "react-native": ">=0.81.0",
49
49
  "react-native-keyboard-controller": ">=1.21.9",
@@ -81,8 +81,8 @@
81
81
  ]
82
82
  },
83
83
  "devDependencies": {
84
- "@webority/mobile-core": "0.15.0",
85
- "@webority/mobile-ui": "0.15.0"
84
+ "@webority/mobile-core": "0.16.0",
85
+ "@webority/mobile-ui": "0.16.0"
86
86
  },
87
87
  "scripts": {
88
88
  "build": "bob build && node ../../scripts/minify-lib.mjs",