@orbe-agro/client-core 5.6.239 → 5.6.240

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.
Files changed (87) hide show
  1. package/dist/@ecme/assets/styles/app.css +116 -3
  2. package/dist/@ecme/assets/styles/tailwind/index.css +119 -3
  3. package/dist/@ecme/components/template/Footer.js +1 -1
  4. package/dist/@ecme/components/template/OrbeAiWidget/OrbeAiWidget.js +0 -2
  5. package/dist/@ecme/components/template/OrbeAiWidget/OrbeAiWidget.js.map +1 -1
  6. package/dist/@ecme/components/view/ChatBox/ChatBox.js +2 -0
  7. package/dist/@ecme/components/view/ChatBox/ChatBox.js.map +1 -1
  8. package/dist/@ecme/components/view/ChatBox/components/MessageList.js +2 -1
  9. package/dist/@ecme/components/view/ChatBox/components/MessageList.js.map +1 -1
  10. package/dist/@ecme/index.js +3 -4
  11. package/dist/@ecme/services/AiService.js +53 -24
  12. package/dist/@ecme/services/AiService.js.map +1 -1
  13. package/dist/@ecme/services/aiAuthHeaders.js +35 -0
  14. package/dist/@ecme/services/aiAuthHeaders.js.map +1 -0
  15. package/dist/@ecme/services/axios/AxiosRequestIntrceptorConfigCallback.js +4 -27
  16. package/dist/@ecme/services/axios/AxiosRequestIntrceptorConfigCallback.js.map +1 -1
  17. package/dist/@ecme/services/index.js +4 -5
  18. package/dist/@ecme/services/sseParser.js +42 -0
  19. package/dist/@ecme/services/sseParser.js.map +1 -0
  20. package/dist/@ecme/views/concepts/ai/Chat/components/AiErrorMessage.js +15 -0
  21. package/dist/@ecme/views/concepts/ai/Chat/components/AiErrorMessage.js.map +1 -0
  22. package/dist/@ecme/views/concepts/ai/Chat/components/ApprovalCard.js +73 -0
  23. package/dist/@ecme/views/concepts/ai/Chat/components/ApprovalCard.js.map +1 -0
  24. package/dist/@ecme/views/concepts/ai/Chat/components/ChatView.js +37 -10
  25. package/dist/@ecme/views/concepts/ai/Chat/components/ChatView.js.map +1 -1
  26. package/dist/@ecme/views/concepts/ai/Chat/components/ToolCallChip.js +153 -0
  27. package/dist/@ecme/views/concepts/ai/Chat/components/ToolCallChip.js.map +1 -0
  28. package/dist/@ecme/views/concepts/ai/Chat/components/toolPresentation.js +99 -0
  29. package/dist/@ecme/views/concepts/ai/Chat/components/toolPresentation.js.map +1 -0
  30. package/dist/@ecme/views/concepts/ai/Chat/hooks/useChatSend.js +111 -17
  31. package/dist/@ecme/views/concepts/ai/Chat/hooks/useChatSend.js.map +1 -1
  32. package/dist/@ecme/views/concepts/ai/Chat/store/generativeChatStore.js +29 -13
  33. package/dist/@ecme/views/concepts/ai/Chat/store/generativeChatStore.js.map +1 -1
  34. package/dist/@types/@ecme/components/template/OrbeAiWidget/OrbeAiWidget.d.ts.map +1 -1
  35. package/dist/@types/@ecme/components/view/ChatBox/ChatBox.d.ts.map +1 -1
  36. package/dist/@types/@ecme/components/view/ChatBox/components/MessageList.d.ts +2 -0
  37. package/dist/@types/@ecme/components/view/ChatBox/components/MessageList.d.ts.map +1 -1
  38. package/dist/@types/@ecme/services/AiService.d.ts +28 -15
  39. package/dist/@types/@ecme/services/AiService.d.ts.map +1 -1
  40. package/dist/@types/@ecme/services/aiAuthHeaders.d.ts +12 -0
  41. package/dist/@types/@ecme/services/aiAuthHeaders.d.ts.map +1 -0
  42. package/dist/@types/@ecme/services/axios/AxiosRequestIntrceptorConfigCallback.d.ts +6 -0
  43. package/dist/@types/@ecme/services/axios/AxiosRequestIntrceptorConfigCallback.d.ts.map +1 -1
  44. package/dist/@types/@ecme/services/sseParser.d.ts +20 -0
  45. package/dist/@types/@ecme/services/sseParser.d.ts.map +1 -0
  46. package/dist/@types/@ecme/views/concepts/ai/Chat/components/AiErrorMessage.d.ts +8 -0
  47. package/dist/@types/@ecme/views/concepts/ai/Chat/components/AiErrorMessage.d.ts.map +1 -0
  48. package/dist/@types/@ecme/views/concepts/ai/Chat/components/ApprovalCard.d.ts +15 -0
  49. package/dist/@types/@ecme/views/concepts/ai/Chat/components/ApprovalCard.d.ts.map +1 -0
  50. package/dist/@types/@ecme/views/concepts/ai/Chat/components/ChatView.d.ts.map +1 -1
  51. package/dist/@types/@ecme/views/concepts/ai/Chat/components/ToolCallChip.d.ts +7 -0
  52. package/dist/@types/@ecme/views/concepts/ai/Chat/components/ToolCallChip.d.ts.map +1 -0
  53. package/dist/@types/@ecme/views/concepts/ai/Chat/components/toolPresentation.d.ts +14 -0
  54. package/dist/@types/@ecme/views/concepts/ai/Chat/components/toolPresentation.d.ts.map +1 -0
  55. package/dist/@types/@ecme/views/concepts/ai/Chat/hooks/useChatSend.d.ts +2 -1
  56. package/dist/@types/@ecme/views/concepts/ai/Chat/hooks/useChatSend.d.ts.map +1 -1
  57. package/dist/@types/@ecme/views/concepts/ai/Chat/store/generativeChatStore.d.ts +1 -0
  58. package/dist/@types/@ecme/views/concepts/ai/Chat/store/generativeChatStore.d.ts.map +1 -1
  59. package/dist/@types/@ecme/views/concepts/ai/Chat/types.d.ts +96 -23
  60. package/dist/@types/@ecme/views/concepts/ai/Chat/types.d.ts.map +1 -1
  61. package/dist/base/assets/styles/init.css +128 -3
  62. package/lib/@ecme/components/template/OrbeAiWidget/OrbeAiWidget.tsx +0 -2
  63. package/lib/@ecme/components/view/ChatBox/ChatBox.tsx +2 -0
  64. package/lib/@ecme/components/view/ChatBox/components/MessageList.tsx +8 -4
  65. package/lib/@ecme/services/AiService.ts +89 -44
  66. package/lib/@ecme/services/aiAuthHeaders.ts +58 -0
  67. package/lib/@ecme/services/axios/AxiosRequestIntrceptorConfigCallback.ts +10 -39
  68. package/lib/@ecme/services/sseParser.ts +75 -0
  69. package/lib/@ecme/views/concepts/ai/Chat/components/AiErrorMessage.tsx +28 -0
  70. package/lib/@ecme/views/concepts/ai/Chat/components/ApprovalCard.tsx +103 -0
  71. package/lib/@ecme/views/concepts/ai/Chat/components/ChatView.tsx +74 -10
  72. package/lib/@ecme/views/concepts/ai/Chat/components/ToolCallChip.tsx +218 -0
  73. package/lib/@ecme/views/concepts/ai/Chat/components/toolPresentation.ts +137 -0
  74. package/lib/@ecme/views/concepts/ai/Chat/hooks/useChatSend.ts +175 -23
  75. package/lib/@ecme/views/concepts/ai/Chat/store/generativeChatStore.ts +41 -13
  76. package/lib/@ecme/views/concepts/ai/Chat/types.ts +82 -29
  77. package/package.json +1 -1
  78. package/dist/@ecme/views/concepts/ai/Chat/components/ModelSelect.js +0 -48
  79. package/dist/@ecme/views/concepts/ai/Chat/components/ModelSelect.js.map +0 -1
  80. package/dist/@ecme/views/concepts/ai/Chat/store/aiModelStore.js +0 -15
  81. package/dist/@ecme/views/concepts/ai/Chat/store/aiModelStore.js.map +0 -1
  82. package/dist/@types/@ecme/views/concepts/ai/Chat/components/ModelSelect.d.ts +0 -8
  83. package/dist/@types/@ecme/views/concepts/ai/Chat/components/ModelSelect.d.ts.map +0 -1
  84. package/dist/@types/@ecme/views/concepts/ai/Chat/store/aiModelStore.d.ts +0 -25
  85. package/dist/@types/@ecme/views/concepts/ai/Chat/store/aiModelStore.d.ts.map +0 -1
  86. package/lib/@ecme/views/concepts/ai/Chat/components/ModelSelect.tsx +0 -66
  87. package/lib/@ecme/views/concepts/ai/Chat/store/aiModelStore.ts +0 -23
@@ -1,9 +1,13 @@
1
1
  import { usGenerativeChatStore } from '../store/generativeChatStore'
2
- import { apiPostChat } from '@/services/AiService'
2
+ import { resumeChat, streamChat } from '@/services/AiService'
3
3
  import { useAuth } from '@/auth'
4
4
  import dayjs from 'dayjs'
5
5
  import uniqueId from 'lodash/uniqueId'
6
- import type { PostAiChatResponse } from '../types'
6
+ import type { ChatEvent, Conversation, ToolCall } from '../types'
7
+
8
+ /** Resposta genérica exibida no chat quando a comunicação com a IA falha. */
9
+ const AI_ERROR_MESSAGE =
10
+ 'Desculpe, estou com um pequeno problema de comunicação e não consigo te atender no momento. Tente novamente em instantes.'
7
11
 
8
12
  const useChatSend = () => {
9
13
  const { user } = useAuth()
@@ -12,6 +16,7 @@ const useChatSend = () => {
12
16
  setSelectedConversation,
13
17
  pushChatHistory,
14
18
  pushConversation,
19
+ updateConversation,
15
20
  setIsTyping,
16
21
  } = usGenerativeChatStore()
17
22
 
@@ -30,41 +35,153 @@ const useChatSend = () => {
30
35
  })
31
36
  }
32
37
 
33
- const sendMessage = async (
34
- id: string,
35
- prompt: string,
36
- attachments?: File[],
38
+ /**
39
+ * Cria a mensagem da IA vazia e devolve o id, para o streaming preenchê-la.
40
+ *
41
+ * `fresh: false` é obrigatório: `fresh` liga a animação de máquina de
42
+ * escrever do ChatCustomContent, que reinicia do zero a cada mudança de
43
+ * conteúdo — com streaming, o texto ficaria piscando e voltando ao início.
44
+ * A animação agora vem do servidor.
45
+ */
46
+ const createAiMessage = (
47
+ chatId: string,
48
+ options?: Partial<Conversation>,
37
49
  ) => {
38
- const resp = await apiPostChat<PostAiChatResponse>({
39
- prompt,
40
- attachments,
41
- })
42
- pushConversation(id, {
43
- id: uniqueId('ai-conversation-'),
50
+ const messageId = uniqueId('ai-conversation-')
51
+ pushConversation(chatId, {
52
+ id: messageId,
44
53
  sender: {
45
54
  id: 'ai',
46
55
  name: 'Chat AI',
47
56
  avatarImageUrl: '/img/thumbs/ai.jpg',
48
57
  },
49
- content: resp.choices[0].message.content || '',
58
+ content: '',
50
59
  timestamp: dayjs().toDate(),
51
60
  type: 'regular',
52
61
  isMyMessage: false,
53
- fresh: true,
62
+ fresh: false,
63
+ ...options,
54
64
  })
55
- setIsTyping(false)
65
+ return messageId
56
66
  }
57
67
 
58
- const createConversation = async (
59
- id: string,
60
- prompt: string,
61
- attachments?: File[],
68
+ /**
69
+ * Consome um stream escrevendo numa mensagem que já existe.
70
+ *
71
+ * Compartilhado pelo envio e pela retomada de aprovação: quando o usuário
72
+ * aprova, a resposta continua na MESMA mensagem, então o acumulador de
73
+ * texto e de ferramentas precisa partir do que já está lá.
74
+ */
75
+ const consumirStream = async (
76
+ chatId: string,
77
+ messageId: string,
78
+ executar: (onEvent: (evento: ChatEvent) => void) => Promise<void>,
79
+ inicial: { texto: string; tools: ToolCall[] },
62
80
  ) => {
63
- setSelectedConversation(id)
64
- await sendMessage(id, prompt, attachments)
81
+ // Acumulados localmente: o store guarda o resultado, mas ler de volta a
82
+ // cada token para concatenar seria uma corrida com o próprio set().
83
+ let texto = inicial.texto
84
+ let tools = inicial.tools
85
+
86
+ const aplicar = (evento: ChatEvent) => {
87
+ switch (evento.type) {
88
+ case 'token':
89
+ texto += evento.data.content
90
+ updateConversation(chatId, messageId, { content: texto })
91
+ break
92
+
93
+ case 'tool_start':
94
+ tools = [
95
+ ...tools,
96
+ {
97
+ id: evento.data.id,
98
+ name: evento.data.name,
99
+ args: evento.data.args,
100
+ running: true,
101
+ },
102
+ ]
103
+ updateConversation(chatId, messageId, { tools })
104
+ break
105
+
106
+ case 'tool_end':
107
+ tools = tools.map((tool) =>
108
+ tool.id === evento.data.id
109
+ ? {
110
+ ...tool,
111
+ running: false,
112
+ awaitingApproval: false,
113
+ result: evento.data.content,
114
+ isError: evento.data.is_error,
115
+ }
116
+ : tool,
117
+ )
118
+ updateConversation(chatId, messageId, { tools })
119
+ break
120
+
121
+ case 'approval_required': {
122
+ // O tool_start já marcou estas ferramentas como "running".
123
+ // Elas não estão executando: estão paradas esperando o
124
+ // clique — senão o chip giraria para sempre.
125
+ const pausadas = new Set(
126
+ evento.data.calls.map((call) => call.id),
127
+ )
128
+ tools = tools.map((tool) =>
129
+ pausadas.has(tool.id)
130
+ ? { ...tool, running: false, awaitingApproval: true }
131
+ : tool,
132
+ )
133
+ updateConversation(chatId, messageId, {
134
+ tools,
135
+ pendingApproval: evento.data,
136
+ })
137
+ break
138
+ }
139
+
140
+ case 'done':
141
+ // O done traz a resposta final completa e é a fonte
142
+ // autoritativa — se algum token se perdeu, ele corrige.
143
+ updateConversation(chatId, messageId, {
144
+ content: evento.data.content,
145
+ })
146
+ break
147
+
148
+ case 'error':
149
+ updateConversation(chatId, messageId, {
150
+ content: evento.data.message || AI_ERROR_MESSAGE,
151
+ isError: true,
152
+ })
153
+ break
154
+ }
155
+ }
156
+
157
+ try {
158
+ await executar(aplicar)
159
+ } catch {
160
+ updateConversation(chatId, messageId, {
161
+ content: texto || AI_ERROR_MESSAGE,
162
+ isError: !texto,
163
+ })
164
+ } finally {
165
+ setIsTyping(false)
166
+ }
167
+ }
168
+
169
+ const sendMessage = async (chatId: string, prompt: string) => {
170
+ const messageId = createAiMessage(chatId)
171
+ await consumirStream(
172
+ chatId,
173
+ messageId,
174
+ (onEvent) => streamChat({ prompt, conversationId: chatId, onEvent }),
175
+ { texto: '', tools: [] },
176
+ )
65
177
  }
66
178
 
67
- const handleSend = async (prompt: string, attachments?: File[]) => {
179
+ const createConversation = async (chatId: string, prompt: string) => {
180
+ setSelectedConversation(chatId)
181
+ await sendMessage(chatId, prompt)
182
+ }
183
+
184
+ const handleSend = async (prompt: string) => {
68
185
  setIsTyping(true)
69
186
  if (selectedConversation) {
70
187
  creteMyMessage(selectedConversation, prompt)
@@ -80,12 +197,47 @@ const useChatSend = () => {
80
197
  enable: false,
81
198
  })
82
199
  creteMyMessage(newId, prompt)
83
- await createConversation(newId, prompt, attachments)
200
+ await createConversation(newId, prompt)
84
201
  }
85
202
  }
86
203
 
204
+ /**
205
+ * Aprova ou nega a operação pendente e retoma a conversa.
206
+ *
207
+ * Continua escrevendo na mesma mensagem: por isso o acumulador parte do
208
+ * conteúdo e das ferramentas que já estão lá.
209
+ */
210
+ const handleApproval = async (
211
+ chatId: string,
212
+ messageId: string,
213
+ approved: boolean,
214
+ ) => {
215
+ const mensagem = usGenerativeChatStore
216
+ .getState()
217
+ .chatHistory.find((chat) => chat.id === chatId)
218
+ ?.conversation?.find((item) => item.id === messageId)
219
+
220
+ if (!mensagem?.pendingApproval) {
221
+ return
222
+ }
223
+
224
+ setIsTyping(true)
225
+ // Some com o card antes da chamada: dois cliques rápidos enviariam duas
226
+ // retomadas, e a segunda falha (o interrupt já foi consumido).
227
+ updateConversation(chatId, messageId, { pendingApproval: undefined })
228
+
229
+ await consumirStream(
230
+ chatId,
231
+ messageId,
232
+ (onEvent) =>
233
+ resumeChat({ approved, conversationId: chatId, onEvent }),
234
+ { texto: mensagem.content ?? '', tools: mensagem.tools ?? [] },
235
+ )
236
+ }
237
+
87
238
  return {
88
239
  handleSend,
240
+ handleApproval,
89
241
  }
90
242
  }
91
243
 
@@ -24,6 +24,11 @@ type GenerativeChatAction = {
24
24
  setIsTyping: (payload: boolean) => void
25
25
  pushChatHistory: (payload: ChatHistory) => void
26
26
  pushConversation: (id: string, conversation: Conversation) => void
27
+ updateConversation: (
28
+ chatId: string,
29
+ messageId: string,
30
+ patch: Partial<Conversation>,
31
+ ) => void
27
32
  disabledChatFresh: (id: string) => void
28
33
  }
29
34
 
@@ -70,20 +75,43 @@ export const usGenerativeChatStore = create<
70
75
  return { selectedConversationRecord: record }
71
76
  }),
72
77
  pushChatHistory: (payload) =>
73
- set(() => ({ chatHistory: [...[payload], ...get().chatHistory] })),
78
+ set(() => ({ chatHistory: [payload, ...get().chatHistory] })),
74
79
  pushConversation: (id, conversation) =>
75
- set(() => {
76
- const chatHistory = get().chatHistory.map((chat) => {
77
- if (chat.id === id) {
78
- if (!chat.conversation) {
79
- chat.conversation = []
80
- }
81
- chat.conversation?.push(conversation)
82
- }
83
- return chat
84
- })
85
- return { chatHistory }
86
- }),
80
+ set(() => ({
81
+ chatHistory: get().chatHistory.map((chat) =>
82
+ chat.id === id
83
+ ? {
84
+ ...chat,
85
+ conversation: [
86
+ ...(chat.conversation ?? []),
87
+ conversation,
88
+ ],
89
+ }
90
+ : chat,
91
+ ),
92
+ })),
93
+ /**
94
+ * Atualiza UMA mensagem de uma conversa.
95
+ *
96
+ * Imutavel de proposito: o streaming chama isto a cada token, e mutar o
97
+ * objeto no lugar faria o React perder atualizacoes — a re-renderizacao
98
+ * dependeria da identidade do array externo mudar por acaso.
99
+ */
100
+ updateConversation: (chatId, messageId, patch) =>
101
+ set(() => ({
102
+ chatHistory: get().chatHistory.map((chat) =>
103
+ chat.id === chatId
104
+ ? {
105
+ ...chat,
106
+ conversation: chat.conversation?.map((mensagem) =>
107
+ mensagem.id === messageId
108
+ ? { ...mensagem, ...patch }
109
+ : mensagem,
110
+ ),
111
+ }
112
+ : chat,
113
+ ),
114
+ })),
87
115
  setIsTyping: (payload) => set(() => ({ isTyping: payload })),
88
116
  disabledChatFresh: () =>
89
117
  set(() => {
@@ -1,3 +1,29 @@
1
+ /** Uma ferramenta que o agente chamou durante a resposta. */
2
+ export type ToolCall = {
3
+ id: string
4
+ name: string
5
+ args: Record<string, unknown>
6
+ /** Retorno da ferramenta; so existe depois do tool_end. */
7
+ result?: string
8
+ isError?: boolean
9
+ /** true enquanto a ferramenta esta executando. */
10
+ running: boolean
11
+ /**
12
+ * true quando a ferramenta esta parada esperando o usuario aprovar.
13
+ *
14
+ * O backend emite `tool_start` assim que o modelo DECIDE chamar a
15
+ * ferramenta — antes de saber se ela precisa de aprovacao. Sem esta flag, o
16
+ * chip ficaria girando "executando" enquanto na verdade espera um clique.
17
+ */
18
+ awaitingApproval?: boolean
19
+ }
20
+
21
+ /** Operacao de escrita parada, esperando o usuario aprovar ou negar. */
22
+ export type PendingApproval = {
23
+ id: string
24
+ calls: { id: string; name: string; args: Record<string, unknown> }[]
25
+ }
26
+
1
27
  export type Conversation = {
2
28
  id: string
3
29
  sender: {
@@ -14,38 +40,65 @@ export type Conversation = {
14
40
  mediaUrl: string
15
41
  }>
16
42
  isMyMessage?: boolean
43
+ /**
44
+ * Liga a animacao de maquina de escrever do ChatCustomContent.
45
+ *
46
+ * Mensagens vindas do streaming devem usar `false`: o texto ja chega
47
+ * progressivamente do servidor, e a animacao reinicia do zero a cada token
48
+ * novo (ela reseta a posicao quando o conteudo muda).
49
+ */
17
50
  fresh?: boolean
51
+ isError?: boolean
52
+ /** Ferramentas chamadas pelo agente nesta resposta. */
53
+ tools?: ToolCall[]
54
+ /** Preenchido quando o agente pausou esperando aprovacao humana. */
55
+ pendingApproval?: PendingApproval
18
56
  }
19
57
 
20
- export type PostAiChatResponse = {
21
- id: string
22
- choices: {
23
- finish_reason:
24
- | 'stop'
25
- | 'length'
26
- | 'tool_calls'
27
- | 'content_filter'
28
- | 'function_call'
29
- index: number
30
- logprobs: {
31
- content: Array<{
32
- token: string
33
- bytes: Array<number> | null
34
- top_logprobs: Array<{
35
- token: string
36
- bytes: Array<number> | null
37
- logprob: number
38
- }>
39
- }>
40
- } | null
41
- message: {
42
- content: string | null
43
- role: string
44
- }
45
- }[]
46
- created: number
47
- model: string
48
- }
58
+ /**
59
+ * Eventos do stream de /ai/chat/stream.
60
+ *
61
+ * Ordem tipica de uma resposta que consulta o ERP:
62
+ *
63
+ * token* tool_start tool_end token* done
64
+ *
65
+ * `done` sempre fecha o stream e traz a resposta final completa. Concatenar os
66
+ * `token` da o mesmo texto — o front pode usar qualquer um dos dois caminhos.
67
+ */
68
+ export type ChatEvent =
69
+ | { type: 'token'; data: { content: string } }
70
+ | {
71
+ type: 'tool_start'
72
+ data: { id: string; name: string; args: Record<string, unknown> }
73
+ }
74
+ | {
75
+ type: 'tool_end'
76
+ data: {
77
+ id: string
78
+ name: string
79
+ content: string
80
+ is_error: boolean
81
+ }
82
+ }
83
+ | {
84
+ /**
85
+ * O agente parou antes de uma operacao de escrita e espera decisao.
86
+ *
87
+ * Quando este evento chega, o `done` NAO vem: o turno nao terminou,
88
+ * esta pausado. Retomar e chamar `resumeChat`.
89
+ */
90
+ type: 'approval_required'
91
+ data: {
92
+ id: string
93
+ calls: {
94
+ id: string
95
+ name: string
96
+ args: Record<string, unknown>
97
+ }[]
98
+ }
99
+ }
100
+ | { type: 'done'; data: { content: string } }
101
+ | { type: 'error'; data: { message: string } }
49
102
 
50
103
  export type ChatHistory = {
51
104
  id: string
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orbe-agro/client-core",
3
- "version": "5.6.239",
3
+ "version": "5.6.240",
4
4
  "description": "Biblioteca principal de componentes e utilidades para os microfrontends do Orbe Agro.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,48 +0,0 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import { useState, useEffect } from "react";
3
- import { apiGetAiModels } from "../../../../../services/AiService.js";
4
- import { useAiModelStore } from "../store/aiModelStore.js";
5
- const ModelSelect = () => {
6
- const [models, setModels] = useState([]);
7
- const selectedModel = useAiModelStore((state) => state.selectedModel);
8
- const setSelectedModel = useAiModelStore((state) => state.setSelectedModel);
9
- useEffect(() => {
10
- let active = true;
11
- apiGetAiModels().then((resp) => {
12
- if (!active) {
13
- return;
14
- }
15
- setModels(resp.models);
16
- const current = useAiModelStore.getState().selectedModel;
17
- const exists = resp.models.some((model) => model.id === current);
18
- if (!current || !exists) {
19
- const fallback = resp.models.find((model) => model.default) ?? resp.models[0];
20
- if (fallback) {
21
- useAiModelStore.getState().setSelectedModel(fallback.id);
22
- }
23
- }
24
- }).catch(() => {
25
- });
26
- return () => {
27
- active = false;
28
- };
29
- }, []);
30
- if (models.length === 0) {
31
- return null;
32
- }
33
- return /* @__PURE__ */ jsx(
34
- "select",
35
- {
36
- "aria-label": "Modelo de IA",
37
- title: "Modelo de IA",
38
- className: "max-w-[9rem] truncate rounded-lg border border-gray-300 bg-white px-2 py-1 text-xs dark:border-gray-600 dark:bg-gray-800",
39
- value: selectedModel ?? "",
40
- onChange: (event) => setSelectedModel(event.target.value),
41
- children: models.map((model) => /* @__PURE__ */ jsx("option", { value: model.id, children: model.label }, model.id))
42
- }
43
- );
44
- };
45
- export {
46
- ModelSelect as default
47
- };
48
- //# sourceMappingURL=ModelSelect.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ModelSelect.js","sources":["../../../../../../../lib/@ecme/views/concepts/ai/Chat/components/ModelSelect.tsx"],"sourcesContent":["import { useEffect, useState } from 'react'\nimport { apiGetAiModels } from '@/services/AiService'\nimport { useAiModelStore } from '../store/aiModelStore'\nimport type { AiModel, GetAiModelsResponse } from '@/services/AiService'\n\n/**\n * Seletor do modelo de IA do chat (GET /ai/models). Se o backend estiver\n * indisponivel ou nao expuser modelos, o seletor nao aparece e o backend usa o\n * modelo default — por isso ele pode ser montado sem medo em qualquer MFE.\n */\nconst ModelSelect = () => {\n const [models, setModels] = useState<AiModel[]>([])\n const selectedModel = useAiModelStore((state) => state.selectedModel)\n const setSelectedModel = useAiModelStore((state) => state.setSelectedModel)\n\n useEffect(() => {\n let active = true\n apiGetAiModels<GetAiModelsResponse>()\n .then((resp) => {\n if (!active) {\n return\n }\n setModels(resp.models)\n const current = useAiModelStore.getState().selectedModel\n const exists = resp.models.some((model) => model.id === current)\n if (!current || !exists) {\n const fallback =\n resp.models.find((model) => model.default) ??\n resp.models[0]\n if (fallback) {\n useAiModelStore\n .getState()\n .setSelectedModel(fallback.id)\n }\n }\n })\n .catch(() => {\n // Sem lista de modelos: o backend responde com o default.\n })\n return () => {\n active = false\n }\n }, [])\n\n if (models.length === 0) {\n return null\n }\n\n return (\n <select\n aria-label=\"Modelo de IA\"\n title=\"Modelo de IA\"\n className=\"max-w-[9rem] truncate rounded-lg border border-gray-300 bg-white px-2 py-1 text-xs dark:border-gray-600 dark:bg-gray-800\"\n value={selectedModel ?? ''}\n onChange={(event) => setSelectedModel(event.target.value)}\n >\n {models.map((model) => (\n <option key={model.id} value={model.id}>\n {model.label}\n </option>\n ))}\n </select>\n )\n}\n\nexport default ModelSelect\n"],"names":[],"mappings":";;;;AAUA,MAAM,cAAc,MAAM;AACtB,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAoB,CAAA,CAAE;AAClD,QAAM,gBAAgB,gBAAgB,CAAC,UAAU,MAAM,aAAa;AACpE,QAAM,mBAAmB,gBAAgB,CAAC,UAAU,MAAM,gBAAgB;AAE1E,YAAU,MAAM;AACZ,QAAI,SAAS;AACb,mBAAA,EACK,KAAK,CAAC,SAAS;AACZ,UAAI,CAAC,QAAQ;AACT;AAAA,MACJ;AACA,gBAAU,KAAK,MAAM;AACrB,YAAM,UAAU,gBAAgB,SAAA,EAAW;AAC3C,YAAM,SAAS,KAAK,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,OAAO;AAC/D,UAAI,CAAC,WAAW,CAAC,QAAQ;AACrB,cAAM,WACF,KAAK,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,KACzC,KAAK,OAAO,CAAC;AACjB,YAAI,UAAU;AACV,0BACK,SAAA,EACA,iBAAiB,SAAS,EAAE;AAAA,QACrC;AAAA,MACJ;AAAA,IACJ,CAAC,EACA,MAAM,MAAM;AAAA,IAEb,CAAC;AACL,WAAO,MAAM;AACT,eAAS;AAAA,IACb;AAAA,EACJ,GAAG,CAAA,CAAE;AAEL,MAAI,OAAO,WAAW,GAAG;AACrB,WAAO;AAAA,EACX;AAEA,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MACG,cAAW;AAAA,MACX,OAAM;AAAA,MACN,WAAU;AAAA,MACV,OAAO,iBAAiB;AAAA,MACxB,UAAU,CAAC,UAAU,iBAAiB,MAAM,OAAO,KAAK;AAAA,MAEvD,UAAA,OAAO,IAAI,CAAC,UACT,oBAAC,UAAA,EAAsB,OAAO,MAAM,IAC/B,UAAA,MAAM,MAAA,GADE,MAAM,EAEnB,CACH;AAAA,IAAA;AAAA,EAAA;AAGb;"}
@@ -1,15 +0,0 @@
1
- import { create } from "zustand";
2
- import { persist } from "zustand/middleware";
3
- const useAiModelStore = create()(
4
- persist(
5
- (set) => ({
6
- selectedModel: null,
7
- setSelectedModel: (model) => set({ selectedModel: model })
8
- }),
9
- { name: "orbe-ai-model" }
10
- )
11
- );
12
- export {
13
- useAiModelStore
14
- };
15
- //# sourceMappingURL=aiModelStore.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"aiModelStore.js","sources":["../../../../../../../lib/@ecme/views/concepts/ai/Chat/store/aiModelStore.ts"],"sourcesContent":["import { create } from 'zustand'\nimport { persist } from 'zustand/middleware'\n\ntype AiModelState = {\n selectedModel: string | null\n setSelectedModel: (model: string) => void\n}\n\n/**\n * Modelo de IA escolhido pelo usuario para o chat do Orbe AI.\n *\n * Persistido em localStorage: como todos os MFEs vivem na mesma origem, a\n * escolha acompanha o usuario entre os modulos e entre sessoes.\n */\nexport const useAiModelStore = create<AiModelState>()(\n persist(\n (set) => ({\n selectedModel: null,\n setSelectedModel: (model) => set({ selectedModel: model }),\n }),\n { name: 'orbe-ai-model' },\n ),\n)\n"],"names":[],"mappings":";;AAcO,MAAM,kBAAkB,OAAA;AAAA,EAC3B;AAAA,IACI,CAAC,SAAS;AAAA,MACN,eAAe;AAAA,MACf,kBAAkB,CAAC,UAAU,IAAI,EAAE,eAAe,OAAO;AAAA,IAAA;AAAA,IAE7D,EAAE,MAAM,gBAAA;AAAA,EAAgB;AAEhC;"}
@@ -1,8 +0,0 @@
1
- /**
2
- * Seletor do modelo de IA do chat (GET /ai/models). Se o backend estiver
3
- * indisponivel ou nao expuser modelos, o seletor nao aparece e o backend usa o
4
- * modelo default — por isso ele pode ser montado sem medo em qualquer MFE.
5
- */
6
- declare const ModelSelect: () => import("react/jsx-runtime").JSX.Element | null;
7
- export default ModelSelect;
8
- //# sourceMappingURL=ModelSelect.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ModelSelect.d.ts","sourceRoot":"","sources":["../../../../../../../../lib/@ecme/views/concepts/ai/Chat/components/ModelSelect.tsx"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,QAAA,MAAM,WAAW,sDAqDhB,CAAA;AAED,eAAe,WAAW,CAAA"}
@@ -1,25 +0,0 @@
1
- type AiModelState = {
2
- selectedModel: string | null;
3
- setSelectedModel: (model: string) => void;
4
- };
5
- /**
6
- * Modelo de IA escolhido pelo usuario para o chat do Orbe AI.
7
- *
8
- * Persistido em localStorage: como todos os MFEs vivem na mesma origem, a
9
- * escolha acompanha o usuario entre os modulos e entre sessoes.
10
- */
11
- export declare const useAiModelStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<AiModelState>, "setState" | "persist"> & {
12
- setState(partial: AiModelState | Partial<AiModelState> | ((state: AiModelState) => AiModelState | Partial<AiModelState>), replace?: false | undefined): unknown;
13
- setState(state: AiModelState | ((state: AiModelState) => AiModelState), replace: true): unknown;
14
- persist: {
15
- setOptions: (options: Partial<import("zustand/middleware").PersistOptions<AiModelState, AiModelState, unknown>>) => void;
16
- clearStorage: () => void;
17
- rehydrate: () => Promise<void> | void;
18
- hasHydrated: () => boolean;
19
- onHydrate: (fn: (state: AiModelState) => void) => () => void;
20
- onFinishHydration: (fn: (state: AiModelState) => void) => () => void;
21
- getOptions: () => Partial<import("zustand/middleware").PersistOptions<AiModelState, AiModelState, unknown>>;
22
- };
23
- }>;
24
- export {};
25
- //# sourceMappingURL=aiModelStore.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"aiModelStore.d.ts","sourceRoot":"","sources":["../../../../../../../../lib/@ecme/views/concepts/ai/Chat/store/aiModelStore.ts"],"names":[],"mappings":"AAGA,KAAK,YAAY,GAAG;IAChB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAC5C,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;EAQ3B,CAAA"}
@@ -1,66 +0,0 @@
1
- import { useEffect, useState } from 'react'
2
- import { apiGetAiModels } from '@/services/AiService'
3
- import { useAiModelStore } from '../store/aiModelStore'
4
- import type { AiModel, GetAiModelsResponse } from '@/services/AiService'
5
-
6
- /**
7
- * Seletor do modelo de IA do chat (GET /ai/models). Se o backend estiver
8
- * indisponivel ou nao expuser modelos, o seletor nao aparece e o backend usa o
9
- * modelo default — por isso ele pode ser montado sem medo em qualquer MFE.
10
- */
11
- const ModelSelect = () => {
12
- const [models, setModels] = useState<AiModel[]>([])
13
- const selectedModel = useAiModelStore((state) => state.selectedModel)
14
- const setSelectedModel = useAiModelStore((state) => state.setSelectedModel)
15
-
16
- useEffect(() => {
17
- let active = true
18
- apiGetAiModels<GetAiModelsResponse>()
19
- .then((resp) => {
20
- if (!active) {
21
- return
22
- }
23
- setModels(resp.models)
24
- const current = useAiModelStore.getState().selectedModel
25
- const exists = resp.models.some((model) => model.id === current)
26
- if (!current || !exists) {
27
- const fallback =
28
- resp.models.find((model) => model.default) ??
29
- resp.models[0]
30
- if (fallback) {
31
- useAiModelStore
32
- .getState()
33
- .setSelectedModel(fallback.id)
34
- }
35
- }
36
- })
37
- .catch(() => {
38
- // Sem lista de modelos: o backend responde com o default.
39
- })
40
- return () => {
41
- active = false
42
- }
43
- }, [])
44
-
45
- if (models.length === 0) {
46
- return null
47
- }
48
-
49
- return (
50
- <select
51
- aria-label="Modelo de IA"
52
- title="Modelo de IA"
53
- className="max-w-[9rem] truncate rounded-lg border border-gray-300 bg-white px-2 py-1 text-xs dark:border-gray-600 dark:bg-gray-800"
54
- value={selectedModel ?? ''}
55
- onChange={(event) => setSelectedModel(event.target.value)}
56
- >
57
- {models.map((model) => (
58
- <option key={model.id} value={model.id}>
59
- {model.label}
60
- </option>
61
- ))}
62
- </select>
63
- )
64
- }
65
-
66
- export default ModelSelect
@@ -1,23 +0,0 @@
1
- import { create } from 'zustand'
2
- import { persist } from 'zustand/middleware'
3
-
4
- type AiModelState = {
5
- selectedModel: string | null
6
- setSelectedModel: (model: string) => void
7
- }
8
-
9
- /**
10
- * Modelo de IA escolhido pelo usuario para o chat do Orbe AI.
11
- *
12
- * Persistido em localStorage: como todos os MFEs vivem na mesma origem, a
13
- * escolha acompanha o usuario entre os modulos e entre sessoes.
14
- */
15
- export const useAiModelStore = create<AiModelState>()(
16
- persist(
17
- (set) => ({
18
- selectedModel: null,
19
- setSelectedModel: (model) => set({ selectedModel: model }),
20
- }),
21
- { name: 'orbe-ai-model' },
22
- ),
23
- )