@@ -30,8 +30,43 @@ function detectApiType(chunkElement) {
3030
3131let LLMactive = false ;
3232
33+ function isJSONComplete ( str ) {
34+ try {
35+ JSON . parse ( str ) ;
36+ return true ;
37+ } catch {
38+ return false ;
39+ }
40+ }
41+
42+ function parseChunkSafely ( chunk , incompleteBuffer ) {
43+ incompleteBuffer += chunk ;
44+ if ( ! isJSONComplete ( incompleteBuffer ) ) {
45+ return { parsed : null , incompleteChunkBuffer : incompleteBuffer } ;
46+ }
47+ const parsed = JSON . parse ( incompleteBuffer ) ;
48+ incompleteBuffer = "" ;
49+ return { parsed : parsed , incompleteChunkBuffer : incompleteBuffer } ;
50+ }
51+
52+ function extractCohereText ( chunkObject , version ) {
53+ switch ( version ) {
54+ case "v1" :
55+ return chunkObject . text ? chunkObject . text : "" ;
56+ case "v2" :
57+ return chunkObject . message &&
58+ chunkObject . message . content &&
59+ chunkObject . message . content [ 0 ]
60+ ? chunkObject . message . content [ 0 ] . text
61+ : "" ;
62+ default :
63+ throw new Error ( "Version d'API Cohere non supportée : " + version ) ;
64+ }
65+ }
66+
3367// Pour pouvoir lire le stream diffusé par l'API utilisée pour se connecter à une IA
3468async function readStream ( streamableObject , chatMessage , APItype ) {
69+ let incompleteChunkBuffer = "" ;
3570 const LLM_MAX_PROCESSING_TIME = yaml . useLLM . maxProcessingTime ;
3671 if ( ! streamableObject . getReader ) {
3772 throw new TypeError (
@@ -73,83 +108,57 @@ async function readStream(streamableObject, chatMessage, APItype) {
73108 if ( ! APItype ) {
74109 APItype = detectApiType ( chunkElement ) ;
75110 }
76- if ( APItype === "cohere" ) {
77- // Cas de l'API Cohere
78- // détection de la version utilisée
79- const cohereAPIversion = yaml . useLLM . url . match ( / v \d + / ) ?. [ 0 ] ;
80- function textInChunk ( chunkObject ) {
81- switch ( cohereAPIversion ) {
82- case "v1" :
83- return chunkObject . text ;
84- case "v2" :
85- return chunkObject . message . content [ 0 ] . text ;
86- default :
87- throw new Error (
88- "Version d'API Cohere non supportée : " + cohereAPIversion ,
89- ) ;
90- }
111+
112+ let cleanedChunk = chunkElement . trim ( ) ;
113+
114+ if ( APItype === "openai" ) {
115+ cleanedChunk = cleanedChunk . replace ( "data: " , "" ) ;
116+ if ( cleanedChunk . indexOf ( "[DONE]" ) !== - 1 ) {
117+ LLMactive = false ;
118+ continue ;
91119 }
120+ }
121+
122+ const parsingChunk = parseChunkSafely (
123+ cleanedChunk ,
124+ incompleteChunkBuffer ,
125+ ) ;
126+ const chunkObject = parsingChunk . parsed ;
127+ incompleteChunkBuffer = parsingChunk . incompleteChunkBuffer ;
128+
129+ if ( ! chunkObject ) continue ;
92130
93- const chunkObject = JSON . parse ( chunkElement . trim ( ) ) ;
131+ if ( APItype === "cohere" ) {
132+ const cohereAPIversion =
133+ yaml &&
134+ yaml . useLLM &&
135+ yaml . useLLM . url &&
136+ yaml . useLLM . url . match ( / v \d + / )
137+ ? yaml . useLLM . url . match ( / v \d + / ) [ 0 ]
138+ : "v2" ;
94139 if ( chunkObject . event_type === "text-generation" && LLMactive ) {
95- const chunkMessage = textInChunk ( chunkObject ) || "" ;
140+ const chunkMessage =
141+ extractCohereText ( chunkObject , cohereAPIversion ) || "" ;
96142 accumulatedChunks += chunkMessage ;
97143 chatMessage . innerHTML = markdownToHTML ( accumulatedChunks ) ;
98144 }
99- LLMactive = chunkObject . is_finished ? false : true ;
145+ LLMactive = ! chunkObject . is_finished ;
100146 } else if ( APItype === "ollama" ) {
101147 // Cas de l'API Ollama
102- const chunkObjectString = chunkElement . trim ( ) ;
103- // Vérifie si le flux a fini
104- if ( chunkObjectString . includes ( '"done":true' ) ) {
148+ if ( cleanedChunk . indexOf ( '"done":true' ) !== - 1 ) {
105149 LLMactive = false ;
106150 continue ;
107151 }
108- let chunkObject ;
109- try {
110- chunkObject = JSON . parse ( chunkObjectString ) ;
111- } catch ( jsonError ) {
112- console . warn (
113- "Erreur JSON.parse sur chunkObjectString :" ,
114- chunkObjectString ,
115- jsonError ,
116- ) ;
117- continue ;
118- }
119152 if ( chunkObject . message && chunkObject . message . content ) {
120- const chunkMessage = chunkObject . message . content || "" ;
153+ const chunkMessage = chunkObject . message . content ;
121154 accumulatedChunks += chunkMessage ;
122155 chatMessage . innerHTML = markdownToHTML ( accumulatedChunks ) ;
123156 }
124157 } else if ( APItype === "openai" ) {
125158 // Cas du modèle openAI
126- const chunkObjectString = chunkElement . replace ( "data: " , "" ) . trim ( ) ;
127-
128- // Vérifie si le flux a fini
129- if ( chunkObjectString . includes ( "[DONE]" ) ) {
130- LLMactive = false ;
131- continue ;
132- }
133-
134- let chunkObject ;
135- try {
136- chunkObject = JSON . parse ( chunkObjectString ) ;
137- } catch ( jsonError ) {
138- console . warn (
139- "Erreur JSON.parse sur chunkObjectString :" ,
140- chunkObjectString ,
141- jsonError ,
142- ) ;
143- continue ;
144- }
145-
146- if (
147- chunkObject . choices &&
148- chunkObject . choices [ 0 ] &&
149- chunkObject . choices [ 0 ] . delta &&
150- chunkObject . choices [ 0 ] . delta . content
151- ) {
152- const chunkMessage = chunkObject . choices [ 0 ] . delta . content || "" ;
159+ const choice = chunkObject . choices && chunkObject . choices [ 0 ] ;
160+ if ( choice && choice . delta && choice . delta . content ) {
161+ const chunkMessage = choice . delta . content ;
153162 accumulatedChunks += chunkMessage ;
154163 chatMessage . innerHTML = markdownToHTML ( accumulatedChunks ) ;
155164 }
0 commit comments