@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 `
|
|
111
|
-
`
|
|
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
|
package/lib/commonjs/useChat.js
CHANGED
|
@@ -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}),"
|
|
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;
|
package/lib/module/useChat.js
CHANGED
|
@@ -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}),"
|
|
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 (`
|
|
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 `
|
|
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 (`
|
|
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 `
|
|
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.
|
|
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.
|
|
46
|
-
"@webority/mobile-ui": ">=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.
|
|
85
|
-
"@webority/mobile-ui": "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",
|