@orbe-agro/client-core 5.6.239 → 5.6.241

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 (93) 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/@types/base/@types/models/transportadora/irpf.d.ts +1 -0
  62. package/dist/@types/base/@types/models/transportadora/irpf.d.ts.map +1 -1
  63. package/dist/@types/base/@types/models/transportadora/irpfFaixaDesconto.d.ts +0 -1
  64. package/dist/@types/base/@types/models/transportadora/irpfFaixaDesconto.d.ts.map +1 -1
  65. package/dist/base/assets/styles/init.css +128 -3
  66. package/lib/@ecme/components/template/OrbeAiWidget/OrbeAiWidget.tsx +0 -2
  67. package/lib/@ecme/components/view/ChatBox/ChatBox.tsx +2 -0
  68. package/lib/@ecme/components/view/ChatBox/components/MessageList.tsx +8 -4
  69. package/lib/@ecme/services/AiService.ts +89 -44
  70. package/lib/@ecme/services/aiAuthHeaders.ts +58 -0
  71. package/lib/@ecme/services/axios/AxiosRequestIntrceptorConfigCallback.ts +10 -39
  72. package/lib/@ecme/services/sseParser.ts +75 -0
  73. package/lib/@ecme/views/concepts/ai/Chat/components/AiErrorMessage.tsx +28 -0
  74. package/lib/@ecme/views/concepts/ai/Chat/components/ApprovalCard.tsx +103 -0
  75. package/lib/@ecme/views/concepts/ai/Chat/components/ChatView.tsx +74 -10
  76. package/lib/@ecme/views/concepts/ai/Chat/components/ToolCallChip.tsx +218 -0
  77. package/lib/@ecme/views/concepts/ai/Chat/components/toolPresentation.ts +137 -0
  78. package/lib/@ecme/views/concepts/ai/Chat/hooks/useChatSend.ts +175 -23
  79. package/lib/@ecme/views/concepts/ai/Chat/store/generativeChatStore.ts +41 -13
  80. package/lib/@ecme/views/concepts/ai/Chat/types.ts +82 -29
  81. package/lib/base/@types/models/transportadora/irpf.ts +1 -0
  82. package/lib/base/@types/models/transportadora/irpfFaixaDesconto.ts +0 -1
  83. package/package.json +1 -1
  84. package/dist/@ecme/views/concepts/ai/Chat/components/ModelSelect.js +0 -48
  85. package/dist/@ecme/views/concepts/ai/Chat/components/ModelSelect.js.map +0 -1
  86. package/dist/@ecme/views/concepts/ai/Chat/store/aiModelStore.js +0 -15
  87. package/dist/@ecme/views/concepts/ai/Chat/store/aiModelStore.js.map +0 -1
  88. package/dist/@types/@ecme/views/concepts/ai/Chat/components/ModelSelect.d.ts +0 -8
  89. package/dist/@types/@ecme/views/concepts/ai/Chat/components/ModelSelect.d.ts.map +0 -1
  90. package/dist/@types/@ecme/views/concepts/ai/Chat/store/aiModelStore.d.ts +0 -25
  91. package/dist/@types/@ecme/views/concepts/ai/Chat/store/aiModelStore.d.ts.map +0 -1
  92. package/lib/@ecme/views/concepts/ai/Chat/components/ModelSelect.tsx +0 -66
  93. package/lib/@ecme/views/concepts/ai/Chat/store/aiModelStore.ts +0 -23
@@ -5481,6 +5481,10 @@ video {
5481
5481
  width: 0.375rem;
5482
5482
  height: 0.375rem;
5483
5483
  }
5484
+ .size-8{
5485
+ width: 2rem;
5486
+ height: 2rem;
5487
+ }
5484
5488
  .h-0{
5485
5489
  height: 0px;
5486
5490
  }
@@ -5613,6 +5617,9 @@ video {
5613
5617
  .max-h-20{
5614
5618
  max-height: 5rem;
5615
5619
  }
5620
+ .max-h-40{
5621
+ max-height: 10rem;
5622
+ }
5616
5623
  .max-h-8{
5617
5624
  max-height: 2rem;
5618
5625
  }
@@ -5827,6 +5834,10 @@ video {
5827
5834
  .w-\[950px\]{
5828
5835
  width: 950px;
5829
5836
  }
5837
+ .w-fit{
5838
+ width: -moz-fit-content;
5839
+ width: fit-content;
5840
+ }
5830
5841
  .w-full{
5831
5842
  width: 100%;
5832
5843
  }
@@ -5954,9 +5965,6 @@ video {
5954
5965
  .max-w-\[900px\]{
5955
5966
  max-width: 900px;
5956
5967
  }
5957
- .max-w-\[9rem\]{
5958
- max-width: 9rem;
5959
- }
5960
5968
  .max-w-full{
5961
5969
  max-width: 100%;
5962
5970
  }
@@ -6003,6 +6011,10 @@ video {
6003
6011
  --tw-translate-y: -50%;
6004
6012
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
6005
6013
  }
6014
+ .rotate-180{
6015
+ --tw-rotate: 180deg;
6016
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
6017
+ }
6006
6018
  .transform{
6007
6019
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
6008
6020
  }
@@ -6108,6 +6120,9 @@ video {
6108
6120
  .grid-cols-6{
6109
6121
  grid-template-columns: repeat(6, minmax(0, 1fr));
6110
6122
  }
6123
+ .grid-cols-\[minmax\(0\2c auto\)_minmax\(0\2c 1fr\)\]{
6124
+ grid-template-columns: minmax(0,auto) minmax(0,1fr);
6125
+ }
6111
6126
  .grid-rows-3{
6112
6127
  grid-template-rows: repeat(3, minmax(0, 1fr));
6113
6128
  }
@@ -6138,6 +6153,9 @@ video {
6138
6153
  .items-center{
6139
6154
  align-items: center;
6140
6155
  }
6156
+ .items-baseline{
6157
+ align-items: baseline;
6158
+ }
6141
6159
  .\!justify-start{
6142
6160
  justify-content: flex-start !important;
6143
6161
  }
@@ -6174,6 +6192,9 @@ video {
6174
6192
  .gap-2{
6175
6193
  gap: 0.5rem;
6176
6194
  }
6195
+ .gap-2\.5{
6196
+ gap: 0.625rem;
6197
+ }
6177
6198
  .gap-3{
6178
6199
  gap: 0.75rem;
6179
6200
  }
@@ -6197,6 +6218,10 @@ video {
6197
6218
  -moz-column-gap: 0.5rem;
6198
6219
  column-gap: 0.5rem;
6199
6220
  }
6221
+ .gap-x-3{
6222
+ -moz-column-gap: 0.75rem;
6223
+ column-gap: 0.75rem;
6224
+ }
6200
6225
  .gap-x-4{
6201
6226
  -moz-column-gap: 1rem;
6202
6227
  column-gap: 1rem;
@@ -6212,6 +6237,9 @@ video {
6212
6237
  .gap-y-0\.5{
6213
6238
  row-gap: 0.125rem;
6214
6239
  }
6240
+ .gap-y-1{
6241
+ row-gap: 0.25rem;
6242
+ }
6215
6243
  .gap-y-10{
6216
6244
  row-gap: 2.5rem;
6217
6245
  }
@@ -6232,6 +6260,14 @@ video {
6232
6260
  margin-right: calc(0.5rem * var(--tw-space-x-reverse));
6233
6261
  margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
6234
6262
  }
6263
+ .divide-y > :not([hidden]) ~ :not([hidden]){
6264
+ --tw-divide-y-reverse: 0;
6265
+ border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
6266
+ border-bottom-width: calc(1px * var(--tw-divide-y-reverse));
6267
+ }
6268
+ .divide-gray-100 > :not([hidden]) ~ :not([hidden]){
6269
+ border-color: var(--gray-100);
6270
+ }
6235
6271
  .overflow-auto{
6236
6272
  overflow: auto;
6237
6273
  }
@@ -6264,9 +6300,15 @@ video {
6264
6300
  .whitespace-nowrap{
6265
6301
  white-space: nowrap;
6266
6302
  }
6303
+ .whitespace-pre-wrap{
6304
+ white-space: pre-wrap;
6305
+ }
6267
6306
  .text-nowrap{
6268
6307
  text-wrap: nowrap;
6269
6308
  }
6309
+ .break-words{
6310
+ overflow-wrap: break-word;
6311
+ }
6270
6312
  .break-all{
6271
6313
  word-break: break-all;
6272
6314
  }
@@ -6393,6 +6435,10 @@ video {
6393
6435
  .border-primary{
6394
6436
  border-color: var(--primary);
6395
6437
  }
6438
+ .border-red-200{
6439
+ --tw-border-opacity: 1;
6440
+ border-color: rgb(254 202 202 / var(--tw-border-opacity, 1));
6441
+ }
6396
6442
  .border-success{
6397
6443
  border-color: var(--success);
6398
6444
  }
@@ -6930,6 +6976,10 @@ video {
6930
6976
  padding-left: 0.5rem;
6931
6977
  padding-right: 0.5rem;
6932
6978
  }
6979
+ .px-2\.5{
6980
+ padding-left: 0.625rem;
6981
+ padding-right: 0.625rem;
6982
+ }
6933
6983
  .px-3{
6934
6984
  padding-left: 0.75rem;
6935
6985
  padding-right: 0.75rem;
@@ -6986,6 +7036,10 @@ video {
6986
7036
  padding-top: 0.75rem;
6987
7037
  padding-bottom: 0.75rem;
6988
7038
  }
7039
+ .py-3\.5{
7040
+ padding-top: 0.875rem;
7041
+ padding-bottom: 0.875rem;
7042
+ }
6989
7043
  .py-4{
6990
7044
  padding-top: 1rem;
6991
7045
  padding-bottom: 1rem;
@@ -7272,6 +7326,9 @@ video {
7272
7326
  --tw-text-opacity: 1;
7273
7327
  color: rgb(239 68 68 / var(--tw-text-opacity, 1));
7274
7328
  }
7329
+ .text-red-500\/90{
7330
+ color: rgb(239 68 68 / 0.9);
7331
+ }
7275
7332
  .text-red-600{
7276
7333
  --tw-text-opacity: 1;
7277
7334
  color: rgb(220 38 38 / var(--tw-text-opacity, 1));
@@ -7329,6 +7386,9 @@ video {
7329
7386
  .no-underline{
7330
7387
  text-decoration-line: none;
7331
7388
  }
7389
+ .underline-offset-2{
7390
+ text-underline-offset: 2px;
7391
+ }
7332
7392
  .opacity-0{
7333
7393
  opacity: 0;
7334
7394
  }
@@ -7476,6 +7536,11 @@ video {
7476
7536
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
7477
7537
  transition-duration: 150ms;
7478
7538
  }
7539
+ .transition-transform{
7540
+ transition-property: transform;
7541
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
7542
+ transition-duration: 150ms;
7543
+ }
7479
7544
  .duration-150{
7480
7545
  transition-duration: 150ms;
7481
7546
  }
@@ -7613,6 +7678,10 @@ video {
7613
7678
  content: var(--tw-content);
7614
7679
  }
7615
7680
 
7681
+ .last\:mb-0:last-child{
7682
+ margin-bottom: 0px;
7683
+ }
7684
+
7616
7685
  .focus-within\:border-primary:focus-within{
7617
7686
  border-color: var(--primary);
7618
7687
  }
@@ -7980,6 +8049,18 @@ video {
7980
8049
  .motion-safe\:animate-pulse{
7981
8050
  animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
7982
8051
  }
8052
+ @keyframes spin{
8053
+ to{
8054
+ transform: rotate(360deg);
8055
+ }
8056
+ }
8057
+ .motion-safe\:animate-spin{
8058
+ animation: spin 1s linear infinite;
8059
+ }
8060
+ }
8061
+
8062
+ .dark\:divide-gray-700:is(.dark *) > :not([hidden]) ~ :not([hidden]){
8063
+ border-color: var(--gray-700);
7983
8064
  }
7984
8065
 
7985
8066
  .dark\:border:is(.dark *){
@@ -8019,6 +8100,10 @@ video {
8019
8100
  border-color: var(--primary);
8020
8101
  }
8021
8102
 
8103
+ .dark\:border-red-500\/20:is(.dark *){
8104
+ border-color: rgb(239 68 68 / 0.2);
8105
+ }
8106
+
8022
8107
  .dark\:border-transparent:is(.dark *){
8023
8108
  border-color: transparent;
8024
8109
  }
@@ -8166,6 +8251,14 @@ video {
8166
8251
  background-color: rgb(254 202 202 / var(--tw-bg-opacity, 1));
8167
8252
  }
8168
8253
 
8254
+ .dark\:bg-red-500\/10:is(.dark *){
8255
+ background-color: rgb(239 68 68 / 0.1);
8256
+ }
8257
+
8258
+ .dark\:bg-red-500\/15:is(.dark *){
8259
+ background-color: rgb(239 68 68 / 0.15);
8260
+ }
8261
+
8169
8262
  .dark\:bg-red-500\/20:is(.dark *){
8170
8263
  background-color: rgb(239 68 68 / 0.2);
8171
8264
  }
@@ -8274,6 +8367,11 @@ video {
8274
8367
  color: rgb(254 243 199 / var(--tw-text-opacity, 1));
8275
8368
  }
8276
8369
 
8370
+ .dark\:text-amber-400:is(.dark *){
8371
+ --tw-text-opacity: 1;
8372
+ color: rgb(251 191 36 / var(--tw-text-opacity, 1));
8373
+ }
8374
+
8277
8375
  .dark\:text-black:is(.dark *){
8278
8376
  --tw-text-opacity: 1;
8279
8377
  color: rgb(0 0 0 / var(--tw-text-opacity, 1));
@@ -8284,11 +8382,21 @@ video {
8284
8382
  color: rgb(219 234 254 / var(--tw-text-opacity, 1));
8285
8383
  }
8286
8384
 
8385
+ .dark\:text-blue-400:is(.dark *){
8386
+ --tw-text-opacity: 1;
8387
+ color: rgb(96 165 250 / var(--tw-text-opacity, 1));
8388
+ }
8389
+
8287
8390
  .dark\:text-emerald-100:is(.dark *){
8288
8391
  --tw-text-opacity: 1;
8289
8392
  color: rgb(209 250 229 / var(--tw-text-opacity, 1));
8290
8393
  }
8291
8394
 
8395
+ .dark\:text-emerald-400:is(.dark *){
8396
+ --tw-text-opacity: 1;
8397
+ color: rgb(52 211 153 / var(--tw-text-opacity, 1));
8398
+ }
8399
+
8292
8400
  .dark\:text-fuchsia-400:is(.dark *){
8293
8401
  --tw-text-opacity: 1;
8294
8402
  color: rgb(232 121 249 / var(--tw-text-opacity, 1));
@@ -8314,6 +8422,10 @@ video {
8314
8422
  color: var(--gray-50);
8315
8423
  }
8316
8424
 
8425
+ .dark\:text-gray-500:is(.dark *){
8426
+ color: var(--gray-500);
8427
+ }
8428
+
8317
8429
  .dark\:text-gray-900:is(.dark *){
8318
8430
  color: var(--gray-900);
8319
8431
  }
@@ -8351,6 +8463,15 @@ video {
8351
8463
  color: rgb(254 226 226 / var(--tw-text-opacity, 1));
8352
8464
  }
8353
8465
 
8466
+ .dark\:text-red-300\/90:is(.dark *){
8467
+ color: rgb(252 165 165 / 0.9);
8468
+ }
8469
+
8470
+ .dark\:text-red-400:is(.dark *){
8471
+ --tw-text-opacity: 1;
8472
+ color: rgb(248 113 113 / var(--tw-text-opacity, 1));
8473
+ }
8474
+
8354
8475
  .dark\:text-slate-400:is(.dark *){
8355
8476
  --tw-text-opacity: 1;
8356
8477
  color: rgb(148 163 184 / var(--tw-text-opacity, 1));
@@ -8379,6 +8500,10 @@ video {
8379
8500
  --tw-ring-color: var(--primary);
8380
8501
  }
8381
8502
 
8503
+ .dark\:hover\:border-gray-600:hover:is(.dark *){
8504
+ border-color: var(--gray-600);
8505
+ }
8506
+
8382
8507
  .dark\:hover\:border-primary:hover:is(.dark *){
8383
8508
  border-color: var(--primary);
8384
8509
  }
@@ -8,7 +8,6 @@ import {
8
8
  import { HiX } from 'react-icons/hi'
9
9
  import ChatView from '@/views/concepts/ai/Chat/components/ChatView'
10
10
  import ChatHistoryRenameDialog from '@/views/concepts/ai/Chat/components/ChatHistoryRenameDialog'
11
- import ModelSelect from '@/views/concepts/ai/Chat/components/ModelSelect'
12
11
  import useResizablePanel from './useResizablePanel'
13
12
  import ResizeHandles from './ResizeHandles'
14
13
 
@@ -71,7 +70,6 @@ const OrbeAiWidget = () => {
71
70
  </span>
72
71
  </div>
73
72
  <div className="flex items-center gap-2">
74
- <ModelSelect />
75
73
  <button
76
74
  type="button"
77
75
  className={iconButtonClass}
@@ -34,6 +34,7 @@ const ChatBox = forwardRef<ScrollBarRef, ChatBoxProps>((props, ref) => {
34
34
  bubbleClass,
35
35
  myBubbleClass,
36
36
  botBubbleClass,
37
+ errorBubbleClass,
37
38
  renderTyping,
38
39
  typing,
39
40
  messageListClass,
@@ -64,6 +65,7 @@ const ChatBox = forwardRef<ScrollBarRef, ChatBoxProps>((props, ref) => {
64
65
  bubbleClass={bubbleClass}
65
66
  myBubbleClass={myBubbleClass}
66
67
  botBubbleClass={botBubbleClass}
68
+ errorBubbleClass={errorBubbleClass}
67
69
  renderTyping={renderTyping}
68
70
  /> )}
69
71
  {children}
@@ -6,7 +6,7 @@ import { forwardRef } from 'react'
6
6
  import type { ScrollBarRef } from '@/components/ui/ScrollBar'
7
7
  import type { MessageProps } from './Message'
8
8
 
9
- type List = MessageProps & { fresh?: boolean }
9
+ type List = MessageProps & { fresh?: boolean; isError?: boolean }
10
10
 
11
11
  export type MessageListProps = {
12
12
  list: List[]
@@ -24,6 +24,7 @@ export type MessageListProps = {
24
24
  bubbleClass?: string
25
25
  myBubbleClass?: string
26
26
  botBubbleClass?: string
27
+ errorBubbleClass?: string
27
28
  renderTyping?: () => ReactNode
28
29
  messageListClass?: string
29
30
  }
@@ -39,6 +40,7 @@ const MessageList = forwardRef<ScrollBarRef, MessageListProps>((props, ref) => {
39
40
  bubbleClass,
40
41
  myBubbleClass,
41
42
  botBubbleClass,
43
+ errorBubbleClass,
42
44
  renderTyping,
43
45
  } = props
44
46
 
@@ -56,9 +58,11 @@ const MessageList = forwardRef<ScrollBarRef, MessageListProps>((props, ref) => {
56
58
  showAvatar={showAvatar}
57
59
  avatarGap={avatarGap}
58
60
  bubbleClass={
59
- message.isMyMessage
60
- ? myBubbleClass ?? bubbleClass
61
- : botBubbleClass ?? bubbleClass
61
+ message.isError
62
+ ? errorBubbleClass ?? bubbleClass
63
+ : message.isMyMessage
64
+ ? myBubbleClass ?? bubbleClass
65
+ : botBubbleClass ?? bubbleClass
62
66
  }
63
67
  {...message}
64
68
  {...(customRenderer
@@ -1,10 +1,9 @@
1
+ import { authHeaders } from './aiAuthHeaders'
1
2
  import ApiService from './ApiService'
2
- import { useAiModelStore } from '@/views/concepts/ai/Chat/store/aiModelStore'
3
+ import { readSse } from './sseParser'
4
+ import type { ChatEvent } from '@/views/concepts/ai/Chat/types'
3
5
  import type { AxiosRequestConfig } from 'axios'
4
6
 
5
- /** Informa ao backend qual modelo usar. Sem o header, ele aplica o default. */
6
- export const AI_MODEL_HEADER = 'X-Ai-Model'
7
-
8
7
  const AI_DEV_URL = 'http://localhost:8000'
9
8
  const AI_BUILD_URL = 'https://orbe-ai-homolog.apps.qas.orbeagro.com.br'
10
9
 
@@ -30,29 +29,6 @@ function getAiApiUrl() {
30
29
 
31
30
  export const AI_API_URL = getAiApiUrl()
32
31
 
33
- export type AiModel = {
34
- id: string
35
- label: string
36
- default: boolean
37
- cloud: boolean
38
- }
39
-
40
- export type GetAiModelsResponse = {
41
- models: AiModel[]
42
- }
43
-
44
- /**
45
- * Header do modelo escolhido, montado por chamada.
46
- *
47
- * Deliberadamente NAO e um interceptor global do AxiosBase: o chat vive no
48
- * layout de todos os MFEs, e um interceptor faria toda requisicao de todo
49
- * modulo passar por essa checagem so para servir ao /ai/*.
50
- */
51
- const aiModelHeader = () => {
52
- const selectedModel = useAiModelStore.getState().selectedModel
53
- return selectedModel ? { [AI_MODEL_HEADER]: selectedModel } : undefined
54
- }
55
-
56
32
  /**
57
33
  * Toda chamada de IA sai pelo AI_API_URL (o baseURL por requisicao vence o da
58
34
  * instancia do AxiosBase) e fica fora do loading global — o chat mostra o
@@ -65,29 +41,98 @@ const aiRequest = <T>(config: AxiosRequestConfig) =>
65
41
  skipGlobalLoading: true,
66
42
  })
67
43
 
68
- export async function apiPostChat<T>(data: {
69
- prompt: string
70
- attachments?: File[]
71
- }) {
72
- return aiRequest<T>({
73
- url: '/ai/chat',
74
- method: 'post',
75
- data,
76
- headers: aiModelHeader(),
77
- })
78
- }
79
-
80
- export async function apiGetChatHistory<T>() {
44
+ export async function apiGetChatHistory<T>(conversationId?: string) {
81
45
  return aiRequest<T>({
82
46
  url: '/ai/chat/history',
83
47
  method: 'get',
48
+ params: conversationId ? { conversation_id: conversationId } : undefined,
84
49
  })
85
50
  }
86
51
 
87
- export async function apiGetAiModels<T>() {
88
- return aiRequest<T>({
89
- url: '/ai/models',
90
- method: 'get',
52
+ export type StreamChatParams = {
53
+ prompt: string
54
+ /** Identifica a conversa; o backend usa como thread da memoria. */
55
+ conversationId?: string
56
+ signal?: AbortSignal
57
+ onEvent: (evento: ChatEvent) => void
58
+ }
59
+
60
+ export type ResumeChatParams = {
61
+ /** true = aprovar a operacao pendente, false = negar. */
62
+ approved: boolean
63
+ conversationId?: string
64
+ signal?: AbortSignal
65
+ onEvent: (evento: ChatEvent) => void
66
+ }
67
+
68
+ /**
69
+ * Envia a mensagem e consome a resposta em streaming (SSE).
70
+ *
71
+ * Usa `fetch`, e nao o Axios, porque o Axios no browser so resolve a Promise
72
+ * com a resposta inteira — nao ha como ler o corpo enquanto ele chega.
73
+ */
74
+ export async function streamChat({
75
+ prompt,
76
+ conversationId,
77
+ signal,
78
+ onEvent,
79
+ }: StreamChatParams): Promise<void> {
80
+ await postarEConsumir(
81
+ '/ai/chat/stream',
82
+ { prompt, conversation_id: conversationId },
83
+ onEvent,
84
+ signal,
85
+ )
86
+ }
87
+
88
+ /**
89
+ * Retoma uma conversa parada num pedido de aprovacao.
90
+ *
91
+ * Devolve o mesmo tipo de stream do `streamChat`, e a resposta continua na
92
+ * MESMA mensagem do chat — o backend so volta do ponto onde parou.
93
+ */
94
+ export async function resumeChat({
95
+ approved,
96
+ conversationId,
97
+ signal,
98
+ onEvent,
99
+ }: ResumeChatParams): Promise<void> {
100
+ await postarEConsumir(
101
+ '/ai/chat/resume',
102
+ { approved, conversation_id: conversationId },
103
+ onEvent,
104
+ signal,
105
+ )
106
+ }
107
+
108
+ /** POST + leitura do SSE, comum ao envio e a retomada. */
109
+ async function postarEConsumir(
110
+ caminho: string,
111
+ corpo: Record<string, unknown>,
112
+ onEvent: (evento: ChatEvent) => void,
113
+ signal?: AbortSignal,
114
+ ): Promise<void> {
115
+ const resposta = await fetch(`${AI_API_URL}${caminho}`, {
116
+ method: 'POST',
117
+ headers: {
118
+ ...authHeaders(),
119
+ 'Content-Type': 'application/json',
120
+ },
121
+ body: JSON.stringify(corpo),
122
+ signal,
123
+ })
124
+
125
+ if (!resposta.ok || !resposta.body) {
126
+ throw new Error(`Falha ao falar com a IA (HTTP ${resposta.status})`)
127
+ }
128
+
129
+ await readSse(resposta.body, ({ event, data }) => {
130
+ try {
131
+ onEvent({ type: event, data: JSON.parse(data) } as ChatEvent)
132
+ } catch (error) {
133
+ // Evento malformado nao pode derrubar o stream inteiro.
134
+ console.debug('Evento SSE ignorado:', event, error)
135
+ }
91
136
  })
92
137
  }
93
138
 
@@ -0,0 +1,58 @@
1
+ import appConfig from '@/configs/app.config'
2
+ import {
3
+ REQUEST_HEADER_AUTH_KEY,
4
+ TOKEN_NAME_IN_STORAGE,
5
+ TOKEN_TYPE,
6
+ } from '@/constants/api.constant'
7
+
8
+ /**
9
+ * Headers de autenticacao lidos do storage.
10
+ *
11
+ * Existe porque o chat de IA usa `fetch` (o Axios nao faz streaming no
12
+ * browser) e portanto nao passa pelo interceptor. Em vez de duplicar as regras
13
+ * de onde mora o token, o interceptor e o fetch chamam esta funcao — uma
14
+ * verdade so.
15
+ *
16
+ * Ver AxiosRequestIntrceptorConfigCallback, que a consome.
17
+ */
18
+ export const authHeaders = (): Record<string, string> => {
19
+ const storage = appConfig.accessTokenPersistStrategy
20
+ const headers: Record<string, string> = {}
21
+
22
+ if (storage !== 'localStorage' && storage !== 'sessionStorage') {
23
+ return headers
24
+ }
25
+
26
+ const source = storage === 'localStorage' ? localStorage : sessionStorage
27
+ const accessToken = source.getItem(TOKEN_NAME_IN_STORAGE) || ''
28
+
29
+ if (accessToken) {
30
+ headers[REQUEST_HEADER_AUTH_KEY] = `${TOKEN_TYPE}${accessToken}`
31
+ }
32
+
33
+ const userId = readUserId()
34
+ if (userId) {
35
+ headers['X-User-Id'] = userId
36
+ }
37
+
38
+ return headers
39
+ }
40
+
41
+ /**
42
+ * userId da sessao persistida pelo zustand.
43
+ *
44
+ * Silencioso em caso de erro: um storage corrompido nao pode derrubar a
45
+ * requisicao — sem o header, o backend apenas trata o usuario como desconhecido.
46
+ */
47
+ const readUserId = (): string | undefined => {
48
+ try {
49
+ const raw = localStorage.getItem('SessionUserPageStore')
50
+ if (!raw) {
51
+ return undefined
52
+ }
53
+ return JSON.parse(raw)?.state?.user?.userId
54
+ } catch (error) {
55
+ console.debug('Could not read userId from session store:', error)
56
+ return undefined
57
+ }
58
+ }
@@ -1,47 +1,18 @@
1
- import appConfig from '@/configs/app.config'
2
- import {
3
- TOKEN_TYPE,
4
- REQUEST_HEADER_AUTH_KEY,
5
- TOKEN_NAME_IN_STORAGE,
6
- } from '@/constants/api.constant'
1
+ import { authHeaders } from '../aiAuthHeaders'
7
2
  import type { InternalAxiosRequestConfig } from 'axios'
8
3
 
4
+ /**
5
+ * Injeta Authorization e X-User-Id em toda requisicao do Axios.
6
+ *
7
+ * As regras de onde o token mora vivem em `authHeaders` — compartilhadas com o
8
+ * chat de IA, que usa `fetch` e nao passa por aqui.
9
+ */
9
10
  const AxiosRequestIntrceptorConfigCallback = (
10
11
  config: InternalAxiosRequestConfig,
11
12
  ) => {
12
- const storage = appConfig.accessTokenPersistStrategy
13
-
14
- if (storage === 'localStorage' || storage === 'sessionStorage') {
15
- let accessToken = ''
16
-
17
- if (storage === 'localStorage') {
18
- accessToken = localStorage.getItem(TOKEN_NAME_IN_STORAGE) || ''
19
- }
20
-
21
- if (storage === 'sessionStorage') {
22
- accessToken = sessionStorage.getItem(TOKEN_NAME_IN_STORAGE) || ''
23
- }
24
-
25
- if (accessToken) {
26
- config.headers[REQUEST_HEADER_AUTH_KEY] =
27
- `${TOKEN_TYPE}${accessToken}`
28
- }
29
-
30
- // Adicionar X-User-Id header se o usuário estiver autenticado
31
- try {
32
- const sessionUserStore = localStorage.getItem('SessionUserPageStore')
33
- if (sessionUserStore) {
34
- const parsedStore = JSON.parse(sessionUserStore)
35
- const userId = parsedStore?.state?.user?.userId
36
- if (userId) {
37
- config.headers['X-User-Id'] = userId
38
- }
39
- }
40
- } catch (error) {
41
- // Silently ignore errors reading user session
42
- console.debug('Could not read userId from session store:', error)
43
- }
44
- }
13
+ Object.entries(authHeaders()).forEach(([chave, valor]) => {
14
+ config.headers[chave] = valor
15
+ })
45
16
 
46
17
  return config
47
18
  }