@utilia-os/sdk-js 1.1.0 → 1.2.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/dist/index.d.mts +198 -2
- package/dist/index.d.ts +198 -2
- package/dist/index.js +275 -13
- package/dist/index.mjs +275 -13
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -43,10 +43,22 @@ declare class UtiliaClient {
|
|
|
43
43
|
* Ejecuta una funcion con reintentos y backoff exponencial
|
|
44
44
|
*/
|
|
45
45
|
private executeWithRetry;
|
|
46
|
+
/**
|
|
47
|
+
* Genera un ID simple como fallback cuando crypto.randomUUID no esta disponible
|
|
48
|
+
*/
|
|
49
|
+
private generateSimpleId;
|
|
46
50
|
/**
|
|
47
51
|
* Helper para esperar un tiempo
|
|
48
52
|
*/
|
|
49
53
|
private sleep;
|
|
54
|
+
/**
|
|
55
|
+
* Construye una URL completa para conexiones SSE con la API key como query param.
|
|
56
|
+
* Util para EventSource que no soporta headers personalizados.
|
|
57
|
+
*
|
|
58
|
+
* @param path - Ruta relativa de la API (ej: /external/v1/tickets/ai-suggest/123/stream)
|
|
59
|
+
* @returns URL completa con apiKey como parametro de consulta
|
|
60
|
+
*/
|
|
61
|
+
buildSseUrl(path: string): string;
|
|
50
62
|
/**
|
|
51
63
|
* Realiza una peticion GET
|
|
52
64
|
*/
|
|
@@ -672,6 +684,65 @@ interface GetSuggestionsOptions {
|
|
|
672
684
|
/** Callback para reportar progreso */
|
|
673
685
|
onProgress?: (progress: number) => void;
|
|
674
686
|
}
|
|
687
|
+
/**
|
|
688
|
+
* Evento SSE recibido durante el streaming de un job
|
|
689
|
+
*/
|
|
690
|
+
interface SseEvent {
|
|
691
|
+
/** Tipo de evento */
|
|
692
|
+
event: 'progress' | 'completed' | 'failed' | 'heartbeat';
|
|
693
|
+
/** Progreso actual (0-100) */
|
|
694
|
+
progress?: number;
|
|
695
|
+
/** Estado descriptivo */
|
|
696
|
+
status?: string;
|
|
697
|
+
/** Resultado del job (disponible en evento 'completed') */
|
|
698
|
+
result?: AiJobStatus['result'];
|
|
699
|
+
/** Mensaje de error (disponible en evento 'failed') */
|
|
700
|
+
error?: string;
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Opciones para el metodo streamSuggestions
|
|
704
|
+
*/
|
|
705
|
+
interface StreamSuggestionsOptions {
|
|
706
|
+
/** Callback para reportar progreso (0-100) */
|
|
707
|
+
onProgress?: (progress: number) => void;
|
|
708
|
+
/** Callback para errores no fatales */
|
|
709
|
+
onError?: (error: Error) => void;
|
|
710
|
+
/** Timeout en ms (default: 120000) */
|
|
711
|
+
timeout?: number;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Handle retornado por streamSuggestions para controlar la conexion SSE
|
|
715
|
+
*/
|
|
716
|
+
interface StreamSuggestionsHandle {
|
|
717
|
+
/** Promesa que resuelve con el resultado cuando el job completa */
|
|
718
|
+
result: Promise<AiJobStatus['result']>;
|
|
719
|
+
/** Funcion para abortar la conexion SSE */
|
|
720
|
+
abort: () => void;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Estado del job de transcripcion asincrona
|
|
724
|
+
*/
|
|
725
|
+
interface TranscriptionJobStatus {
|
|
726
|
+
/** Estado actual del job */
|
|
727
|
+
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
728
|
+
/** Progreso del job (0-100) */
|
|
729
|
+
progress?: number;
|
|
730
|
+
/** Texto transcrito (disponible cuando status es 'completed') */
|
|
731
|
+
transcription?: string;
|
|
732
|
+
/** Mensaje de error (si status es 'failed') */
|
|
733
|
+
error?: string;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Opciones para el metodo streamTranscription
|
|
737
|
+
*/
|
|
738
|
+
interface StreamTranscriptionOptions {
|
|
739
|
+
/** Callback para reportar progreso (0-100) */
|
|
740
|
+
onProgress?: (progress: number) => void;
|
|
741
|
+
/** Callback para errores no fatales */
|
|
742
|
+
onError?: (error: Error) => void;
|
|
743
|
+
/** Timeout en ms (default: 120000) */
|
|
744
|
+
timeout?: number;
|
|
745
|
+
}
|
|
675
746
|
declare class AiService {
|
|
676
747
|
private readonly client;
|
|
677
748
|
private readonly basePath;
|
|
@@ -794,6 +865,122 @@ declare class AiService {
|
|
|
794
865
|
trackAiAction(data: TrackAiActionInput): Promise<{
|
|
795
866
|
success: boolean;
|
|
796
867
|
}>;
|
|
868
|
+
/**
|
|
869
|
+
* Solicita sugerencias de IA y recibe el resultado via SSE (Server-Sent Events).
|
|
870
|
+
* Alternativa al polling que ofrece actualizaciones en tiempo real.
|
|
871
|
+
*
|
|
872
|
+
* Requiere un entorno con EventSource nativo (navegador).
|
|
873
|
+
* Para Node.js se necesita un polyfill como 'eventsource'.
|
|
874
|
+
*
|
|
875
|
+
* @param data - Datos para generar sugerencias
|
|
876
|
+
* @param options - Opciones de streaming
|
|
877
|
+
* @returns Handle con promesa de resultado y funcion de aborto
|
|
878
|
+
*
|
|
879
|
+
* @example
|
|
880
|
+
* ```typescript
|
|
881
|
+
* const handle = await sdk.ai.streamSuggestions(
|
|
882
|
+
* { userId: 'user-123', text: 'No puedo iniciar sesion' },
|
|
883
|
+
* { onProgress: (p) => console.log(`Progreso: ${p}%`) },
|
|
884
|
+
* );
|
|
885
|
+
*
|
|
886
|
+
* // Esperar resultado
|
|
887
|
+
* const result = await handle.result;
|
|
888
|
+
* console.log(result?.suggestions);
|
|
889
|
+
*
|
|
890
|
+
* // O abortar si el usuario cancela
|
|
891
|
+
* handle.abort();
|
|
892
|
+
* ```
|
|
893
|
+
*/
|
|
894
|
+
streamSuggestions(data: AiSuggestInput, options?: StreamSuggestionsOptions): Promise<StreamSuggestionsHandle>;
|
|
895
|
+
/**
|
|
896
|
+
* Solicita una transcripcion de audio de forma asincrona.
|
|
897
|
+
* Retorna un jobId para consultar el estado o usar con streamTranscription.
|
|
898
|
+
*
|
|
899
|
+
* @param audioBase64 - Audio codificado en base64
|
|
900
|
+
* @param audioFilename - Nombre del archivo con extension
|
|
901
|
+
* @returns ID del job para consultar estado
|
|
902
|
+
*
|
|
903
|
+
* @example
|
|
904
|
+
* ```typescript
|
|
905
|
+
* const { jobId } = await sdk.ai.requestTranscription(
|
|
906
|
+
* audioBase64,
|
|
907
|
+
* 'recording.webm',
|
|
908
|
+
* );
|
|
909
|
+
* ```
|
|
910
|
+
*/
|
|
911
|
+
requestTranscription(audioBase64: string, audioFilename: string): Promise<{
|
|
912
|
+
jobId: string;
|
|
913
|
+
}>;
|
|
914
|
+
/**
|
|
915
|
+
* Obtiene el estado de un job de transcripcion asincrona.
|
|
916
|
+
*
|
|
917
|
+
* @param jobId - ID del job obtenido de requestTranscription
|
|
918
|
+
* @returns Estado actual del job de transcripcion
|
|
919
|
+
*
|
|
920
|
+
* @example
|
|
921
|
+
* ```typescript
|
|
922
|
+
* const status = await sdk.ai.getTranscriptionStatus(jobId);
|
|
923
|
+
*
|
|
924
|
+
* if (status.status === 'completed') {
|
|
925
|
+
* console.log(status.transcription);
|
|
926
|
+
* }
|
|
927
|
+
* ```
|
|
928
|
+
*/
|
|
929
|
+
getTranscriptionStatus(jobId: string): Promise<TranscriptionJobStatus>;
|
|
930
|
+
/**
|
|
931
|
+
* Solicita una transcripcion de audio y recibe el resultado via SSE.
|
|
932
|
+
* Alternativa al polling para transcripciones asincronas.
|
|
933
|
+
*
|
|
934
|
+
* Requiere un entorno con EventSource nativo (navegador).
|
|
935
|
+
* Para Node.js se necesita un polyfill como 'eventsource'.
|
|
936
|
+
*
|
|
937
|
+
* @param audioBase64 - Audio codificado en base64
|
|
938
|
+
* @param audioFilename - Nombre del archivo con extension
|
|
939
|
+
* @param options - Opciones de streaming
|
|
940
|
+
* @returns Promesa que resuelve con el texto transcrito
|
|
941
|
+
*
|
|
942
|
+
* @example
|
|
943
|
+
* ```typescript
|
|
944
|
+
* const { transcription } = await sdk.ai.streamTranscription(
|
|
945
|
+
* audioBase64,
|
|
946
|
+
* 'recording.webm',
|
|
947
|
+
* { onProgress: (p) => console.log(`Progreso: ${p}%`) },
|
|
948
|
+
* );
|
|
949
|
+
* console.log(transcription);
|
|
950
|
+
* ```
|
|
951
|
+
*/
|
|
952
|
+
streamTranscription(audioBase64: string, audioFilename: string, options?: StreamTranscriptionOptions): Promise<{
|
|
953
|
+
transcription: string;
|
|
954
|
+
}>;
|
|
955
|
+
/**
|
|
956
|
+
* Reporta un error del cliente al servidor para monitoreo.
|
|
957
|
+
* Este metodo nunca lanza excepciones; los errores de reporte se ignoran silenciosamente.
|
|
958
|
+
*
|
|
959
|
+
* @param params - Datos del error a reportar
|
|
960
|
+
*
|
|
961
|
+
* @example
|
|
962
|
+
* ```typescript
|
|
963
|
+
* await sdk.ai.reportError({
|
|
964
|
+
* message: 'No se pudo cargar el widget',
|
|
965
|
+
* code: 'WIDGET_LOAD_ERROR',
|
|
966
|
+
* component: 'widget',
|
|
967
|
+
* url: window.location.href,
|
|
968
|
+
* userAgent: navigator.userAgent,
|
|
969
|
+
* });
|
|
970
|
+
* ```
|
|
971
|
+
*/
|
|
972
|
+
reportError(params: {
|
|
973
|
+
message: string;
|
|
974
|
+
stack?: string;
|
|
975
|
+
code?: string;
|
|
976
|
+
component?: string;
|
|
977
|
+
context?: string;
|
|
978
|
+
endpoint?: string;
|
|
979
|
+
method?: string;
|
|
980
|
+
requestId?: string;
|
|
981
|
+
url?: string;
|
|
982
|
+
userAgent?: string;
|
|
983
|
+
}): Promise<void>;
|
|
797
984
|
/**
|
|
798
985
|
* Helper para esperar un tiempo
|
|
799
986
|
*/
|
|
@@ -828,7 +1015,14 @@ declare class UtiliaSDKError extends Error {
|
|
|
828
1015
|
readonly code: ErrorCode;
|
|
829
1016
|
/** Timestamp de cuando ocurrio el error */
|
|
830
1017
|
readonly timestamp: Date;
|
|
831
|
-
|
|
1018
|
+
/** ID de la peticion que genero el error */
|
|
1019
|
+
readonly requestId?: string;
|
|
1020
|
+
/** Codigo de estado HTTP de la respuesta */
|
|
1021
|
+
readonly statusCode?: number;
|
|
1022
|
+
constructor(code: ErrorCode, message: string, options?: {
|
|
1023
|
+
requestId?: string;
|
|
1024
|
+
statusCode?: number;
|
|
1025
|
+
});
|
|
832
1026
|
/**
|
|
833
1027
|
* Serializa el error a un objeto JSON
|
|
834
1028
|
*/
|
|
@@ -837,6 +1031,8 @@ declare class UtiliaSDKError extends Error {
|
|
|
837
1031
|
code: ErrorCode;
|
|
838
1032
|
message: string;
|
|
839
1033
|
timestamp: string;
|
|
1034
|
+
requestId: string | undefined;
|
|
1035
|
+
statusCode: number | undefined;
|
|
840
1036
|
};
|
|
841
1037
|
/**
|
|
842
1038
|
* Verifica si el error es de tipo rate limit
|
|
@@ -943,4 +1139,4 @@ declare class UtiliaSDK {
|
|
|
943
1139
|
constructor(config: UtiliaSDKConfig);
|
|
944
1140
|
}
|
|
945
1141
|
|
|
946
|
-
export { type AddMessageInput, type AiJobStatus, type AiSuggestInput, type AiSuggestions, type AiUserAction, type CreateTicketInput, type CreateTicketUser, type CreatedMessage, type CreatedTicket, ErrorCode, type ExternalUser, type FileQuota, type GetSuggestionsOptions, type IdentifyUserInput, type MessageAuthor, type PaginatedResponse, type PaginationMeta, type RequestConfig, SDK_LIMITS, type TicketAttachment, type TicketCategory, type TicketContext, type TicketDetail, type TicketFilters, type TicketListItem, type TicketMessage, type TicketPriority, type TicketReporter, type TicketStatus, type TrackAiActionInput, type UnreadCount, type UploadFileOptions, type UploadedFile, UtiliaSDK, type UtiliaSDKConfig, UtiliaSDKError };
|
|
1142
|
+
export { type AddMessageInput, type AiJobStatus, type AiSuggestInput, type AiSuggestions, type AiUserAction, type CreateTicketInput, type CreateTicketUser, type CreatedMessage, type CreatedTicket, ErrorCode, type ExternalUser, type FileQuota, type GetSuggestionsOptions, type IdentifyUserInput, type MessageAuthor, type PaginatedResponse, type PaginationMeta, type RequestConfig, SDK_LIMITS, type SseEvent, type StreamSuggestionsHandle, type StreamSuggestionsOptions, type StreamTranscriptionOptions, type TicketAttachment, type TicketCategory, type TicketContext, type TicketDetail, type TicketFilters, type TicketListItem, type TicketMessage, type TicketPriority, type TicketReporter, type TicketStatus, type TrackAiActionInput, type TranscriptionJobStatus, type UnreadCount, type UploadFileOptions, type UploadedFile, UtiliaSDK, type UtiliaSDKConfig, UtiliaSDKError };
|
package/dist/index.d.ts
CHANGED
|
@@ -43,10 +43,22 @@ declare class UtiliaClient {
|
|
|
43
43
|
* Ejecuta una funcion con reintentos y backoff exponencial
|
|
44
44
|
*/
|
|
45
45
|
private executeWithRetry;
|
|
46
|
+
/**
|
|
47
|
+
* Genera un ID simple como fallback cuando crypto.randomUUID no esta disponible
|
|
48
|
+
*/
|
|
49
|
+
private generateSimpleId;
|
|
46
50
|
/**
|
|
47
51
|
* Helper para esperar un tiempo
|
|
48
52
|
*/
|
|
49
53
|
private sleep;
|
|
54
|
+
/**
|
|
55
|
+
* Construye una URL completa para conexiones SSE con la API key como query param.
|
|
56
|
+
* Util para EventSource que no soporta headers personalizados.
|
|
57
|
+
*
|
|
58
|
+
* @param path - Ruta relativa de la API (ej: /external/v1/tickets/ai-suggest/123/stream)
|
|
59
|
+
* @returns URL completa con apiKey como parametro de consulta
|
|
60
|
+
*/
|
|
61
|
+
buildSseUrl(path: string): string;
|
|
50
62
|
/**
|
|
51
63
|
* Realiza una peticion GET
|
|
52
64
|
*/
|
|
@@ -672,6 +684,65 @@ interface GetSuggestionsOptions {
|
|
|
672
684
|
/** Callback para reportar progreso */
|
|
673
685
|
onProgress?: (progress: number) => void;
|
|
674
686
|
}
|
|
687
|
+
/**
|
|
688
|
+
* Evento SSE recibido durante el streaming de un job
|
|
689
|
+
*/
|
|
690
|
+
interface SseEvent {
|
|
691
|
+
/** Tipo de evento */
|
|
692
|
+
event: 'progress' | 'completed' | 'failed' | 'heartbeat';
|
|
693
|
+
/** Progreso actual (0-100) */
|
|
694
|
+
progress?: number;
|
|
695
|
+
/** Estado descriptivo */
|
|
696
|
+
status?: string;
|
|
697
|
+
/** Resultado del job (disponible en evento 'completed') */
|
|
698
|
+
result?: AiJobStatus['result'];
|
|
699
|
+
/** Mensaje de error (disponible en evento 'failed') */
|
|
700
|
+
error?: string;
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Opciones para el metodo streamSuggestions
|
|
704
|
+
*/
|
|
705
|
+
interface StreamSuggestionsOptions {
|
|
706
|
+
/** Callback para reportar progreso (0-100) */
|
|
707
|
+
onProgress?: (progress: number) => void;
|
|
708
|
+
/** Callback para errores no fatales */
|
|
709
|
+
onError?: (error: Error) => void;
|
|
710
|
+
/** Timeout en ms (default: 120000) */
|
|
711
|
+
timeout?: number;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Handle retornado por streamSuggestions para controlar la conexion SSE
|
|
715
|
+
*/
|
|
716
|
+
interface StreamSuggestionsHandle {
|
|
717
|
+
/** Promesa que resuelve con el resultado cuando el job completa */
|
|
718
|
+
result: Promise<AiJobStatus['result']>;
|
|
719
|
+
/** Funcion para abortar la conexion SSE */
|
|
720
|
+
abort: () => void;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Estado del job de transcripcion asincrona
|
|
724
|
+
*/
|
|
725
|
+
interface TranscriptionJobStatus {
|
|
726
|
+
/** Estado actual del job */
|
|
727
|
+
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
728
|
+
/** Progreso del job (0-100) */
|
|
729
|
+
progress?: number;
|
|
730
|
+
/** Texto transcrito (disponible cuando status es 'completed') */
|
|
731
|
+
transcription?: string;
|
|
732
|
+
/** Mensaje de error (si status es 'failed') */
|
|
733
|
+
error?: string;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Opciones para el metodo streamTranscription
|
|
737
|
+
*/
|
|
738
|
+
interface StreamTranscriptionOptions {
|
|
739
|
+
/** Callback para reportar progreso (0-100) */
|
|
740
|
+
onProgress?: (progress: number) => void;
|
|
741
|
+
/** Callback para errores no fatales */
|
|
742
|
+
onError?: (error: Error) => void;
|
|
743
|
+
/** Timeout en ms (default: 120000) */
|
|
744
|
+
timeout?: number;
|
|
745
|
+
}
|
|
675
746
|
declare class AiService {
|
|
676
747
|
private readonly client;
|
|
677
748
|
private readonly basePath;
|
|
@@ -794,6 +865,122 @@ declare class AiService {
|
|
|
794
865
|
trackAiAction(data: TrackAiActionInput): Promise<{
|
|
795
866
|
success: boolean;
|
|
796
867
|
}>;
|
|
868
|
+
/**
|
|
869
|
+
* Solicita sugerencias de IA y recibe el resultado via SSE (Server-Sent Events).
|
|
870
|
+
* Alternativa al polling que ofrece actualizaciones en tiempo real.
|
|
871
|
+
*
|
|
872
|
+
* Requiere un entorno con EventSource nativo (navegador).
|
|
873
|
+
* Para Node.js se necesita un polyfill como 'eventsource'.
|
|
874
|
+
*
|
|
875
|
+
* @param data - Datos para generar sugerencias
|
|
876
|
+
* @param options - Opciones de streaming
|
|
877
|
+
* @returns Handle con promesa de resultado y funcion de aborto
|
|
878
|
+
*
|
|
879
|
+
* @example
|
|
880
|
+
* ```typescript
|
|
881
|
+
* const handle = await sdk.ai.streamSuggestions(
|
|
882
|
+
* { userId: 'user-123', text: 'No puedo iniciar sesion' },
|
|
883
|
+
* { onProgress: (p) => console.log(`Progreso: ${p}%`) },
|
|
884
|
+
* );
|
|
885
|
+
*
|
|
886
|
+
* // Esperar resultado
|
|
887
|
+
* const result = await handle.result;
|
|
888
|
+
* console.log(result?.suggestions);
|
|
889
|
+
*
|
|
890
|
+
* // O abortar si el usuario cancela
|
|
891
|
+
* handle.abort();
|
|
892
|
+
* ```
|
|
893
|
+
*/
|
|
894
|
+
streamSuggestions(data: AiSuggestInput, options?: StreamSuggestionsOptions): Promise<StreamSuggestionsHandle>;
|
|
895
|
+
/**
|
|
896
|
+
* Solicita una transcripcion de audio de forma asincrona.
|
|
897
|
+
* Retorna un jobId para consultar el estado o usar con streamTranscription.
|
|
898
|
+
*
|
|
899
|
+
* @param audioBase64 - Audio codificado en base64
|
|
900
|
+
* @param audioFilename - Nombre del archivo con extension
|
|
901
|
+
* @returns ID del job para consultar estado
|
|
902
|
+
*
|
|
903
|
+
* @example
|
|
904
|
+
* ```typescript
|
|
905
|
+
* const { jobId } = await sdk.ai.requestTranscription(
|
|
906
|
+
* audioBase64,
|
|
907
|
+
* 'recording.webm',
|
|
908
|
+
* );
|
|
909
|
+
* ```
|
|
910
|
+
*/
|
|
911
|
+
requestTranscription(audioBase64: string, audioFilename: string): Promise<{
|
|
912
|
+
jobId: string;
|
|
913
|
+
}>;
|
|
914
|
+
/**
|
|
915
|
+
* Obtiene el estado de un job de transcripcion asincrona.
|
|
916
|
+
*
|
|
917
|
+
* @param jobId - ID del job obtenido de requestTranscription
|
|
918
|
+
* @returns Estado actual del job de transcripcion
|
|
919
|
+
*
|
|
920
|
+
* @example
|
|
921
|
+
* ```typescript
|
|
922
|
+
* const status = await sdk.ai.getTranscriptionStatus(jobId);
|
|
923
|
+
*
|
|
924
|
+
* if (status.status === 'completed') {
|
|
925
|
+
* console.log(status.transcription);
|
|
926
|
+
* }
|
|
927
|
+
* ```
|
|
928
|
+
*/
|
|
929
|
+
getTranscriptionStatus(jobId: string): Promise<TranscriptionJobStatus>;
|
|
930
|
+
/**
|
|
931
|
+
* Solicita una transcripcion de audio y recibe el resultado via SSE.
|
|
932
|
+
* Alternativa al polling para transcripciones asincronas.
|
|
933
|
+
*
|
|
934
|
+
* Requiere un entorno con EventSource nativo (navegador).
|
|
935
|
+
* Para Node.js se necesita un polyfill como 'eventsource'.
|
|
936
|
+
*
|
|
937
|
+
* @param audioBase64 - Audio codificado en base64
|
|
938
|
+
* @param audioFilename - Nombre del archivo con extension
|
|
939
|
+
* @param options - Opciones de streaming
|
|
940
|
+
* @returns Promesa que resuelve con el texto transcrito
|
|
941
|
+
*
|
|
942
|
+
* @example
|
|
943
|
+
* ```typescript
|
|
944
|
+
* const { transcription } = await sdk.ai.streamTranscription(
|
|
945
|
+
* audioBase64,
|
|
946
|
+
* 'recording.webm',
|
|
947
|
+
* { onProgress: (p) => console.log(`Progreso: ${p}%`) },
|
|
948
|
+
* );
|
|
949
|
+
* console.log(transcription);
|
|
950
|
+
* ```
|
|
951
|
+
*/
|
|
952
|
+
streamTranscription(audioBase64: string, audioFilename: string, options?: StreamTranscriptionOptions): Promise<{
|
|
953
|
+
transcription: string;
|
|
954
|
+
}>;
|
|
955
|
+
/**
|
|
956
|
+
* Reporta un error del cliente al servidor para monitoreo.
|
|
957
|
+
* Este metodo nunca lanza excepciones; los errores de reporte se ignoran silenciosamente.
|
|
958
|
+
*
|
|
959
|
+
* @param params - Datos del error a reportar
|
|
960
|
+
*
|
|
961
|
+
* @example
|
|
962
|
+
* ```typescript
|
|
963
|
+
* await sdk.ai.reportError({
|
|
964
|
+
* message: 'No se pudo cargar el widget',
|
|
965
|
+
* code: 'WIDGET_LOAD_ERROR',
|
|
966
|
+
* component: 'widget',
|
|
967
|
+
* url: window.location.href,
|
|
968
|
+
* userAgent: navigator.userAgent,
|
|
969
|
+
* });
|
|
970
|
+
* ```
|
|
971
|
+
*/
|
|
972
|
+
reportError(params: {
|
|
973
|
+
message: string;
|
|
974
|
+
stack?: string;
|
|
975
|
+
code?: string;
|
|
976
|
+
component?: string;
|
|
977
|
+
context?: string;
|
|
978
|
+
endpoint?: string;
|
|
979
|
+
method?: string;
|
|
980
|
+
requestId?: string;
|
|
981
|
+
url?: string;
|
|
982
|
+
userAgent?: string;
|
|
983
|
+
}): Promise<void>;
|
|
797
984
|
/**
|
|
798
985
|
* Helper para esperar un tiempo
|
|
799
986
|
*/
|
|
@@ -828,7 +1015,14 @@ declare class UtiliaSDKError extends Error {
|
|
|
828
1015
|
readonly code: ErrorCode;
|
|
829
1016
|
/** Timestamp de cuando ocurrio el error */
|
|
830
1017
|
readonly timestamp: Date;
|
|
831
|
-
|
|
1018
|
+
/** ID de la peticion que genero el error */
|
|
1019
|
+
readonly requestId?: string;
|
|
1020
|
+
/** Codigo de estado HTTP de la respuesta */
|
|
1021
|
+
readonly statusCode?: number;
|
|
1022
|
+
constructor(code: ErrorCode, message: string, options?: {
|
|
1023
|
+
requestId?: string;
|
|
1024
|
+
statusCode?: number;
|
|
1025
|
+
});
|
|
832
1026
|
/**
|
|
833
1027
|
* Serializa el error a un objeto JSON
|
|
834
1028
|
*/
|
|
@@ -837,6 +1031,8 @@ declare class UtiliaSDKError extends Error {
|
|
|
837
1031
|
code: ErrorCode;
|
|
838
1032
|
message: string;
|
|
839
1033
|
timestamp: string;
|
|
1034
|
+
requestId: string | undefined;
|
|
1035
|
+
statusCode: number | undefined;
|
|
840
1036
|
};
|
|
841
1037
|
/**
|
|
842
1038
|
* Verifica si el error es de tipo rate limit
|
|
@@ -943,4 +1139,4 @@ declare class UtiliaSDK {
|
|
|
943
1139
|
constructor(config: UtiliaSDKConfig);
|
|
944
1140
|
}
|
|
945
1141
|
|
|
946
|
-
export { type AddMessageInput, type AiJobStatus, type AiSuggestInput, type AiSuggestions, type AiUserAction, type CreateTicketInput, type CreateTicketUser, type CreatedMessage, type CreatedTicket, ErrorCode, type ExternalUser, type FileQuota, type GetSuggestionsOptions, type IdentifyUserInput, type MessageAuthor, type PaginatedResponse, type PaginationMeta, type RequestConfig, SDK_LIMITS, type TicketAttachment, type TicketCategory, type TicketContext, type TicketDetail, type TicketFilters, type TicketListItem, type TicketMessage, type TicketPriority, type TicketReporter, type TicketStatus, type TrackAiActionInput, type UnreadCount, type UploadFileOptions, type UploadedFile, UtiliaSDK, type UtiliaSDKConfig, UtiliaSDKError };
|
|
1142
|
+
export { type AddMessageInput, type AiJobStatus, type AiSuggestInput, type AiSuggestions, type AiUserAction, type CreateTicketInput, type CreateTicketUser, type CreatedMessage, type CreatedTicket, ErrorCode, type ExternalUser, type FileQuota, type GetSuggestionsOptions, type IdentifyUserInput, type MessageAuthor, type PaginatedResponse, type PaginationMeta, type RequestConfig, SDK_LIMITS, type SseEvent, type StreamSuggestionsHandle, type StreamSuggestionsOptions, type StreamTranscriptionOptions, type TicketAttachment, type TicketCategory, type TicketContext, type TicketDetail, type TicketFilters, type TicketListItem, type TicketMessage, type TicketPriority, type TicketReporter, type TicketStatus, type TrackAiActionInput, type TranscriptionJobStatus, type UnreadCount, type UploadFileOptions, type UploadedFile, UtiliaSDK, type UtiliaSDKConfig, UtiliaSDKError };
|
package/dist/index.js
CHANGED
|
@@ -52,11 +52,13 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
|
52
52
|
return ErrorCode2;
|
|
53
53
|
})(ErrorCode || {});
|
|
54
54
|
var UtiliaSDKError = class _UtiliaSDKError extends Error {
|
|
55
|
-
constructor(code, message) {
|
|
55
|
+
constructor(code, message, options) {
|
|
56
56
|
super(message);
|
|
57
57
|
this.name = "UtiliaSDKError";
|
|
58
58
|
this.code = code;
|
|
59
59
|
this.timestamp = /* @__PURE__ */ new Date();
|
|
60
|
+
this.requestId = options?.requestId;
|
|
61
|
+
this.statusCode = options?.statusCode;
|
|
60
62
|
if (Error.captureStackTrace) {
|
|
61
63
|
Error.captureStackTrace(this, _UtiliaSDKError);
|
|
62
64
|
}
|
|
@@ -69,7 +71,9 @@ var UtiliaSDKError = class _UtiliaSDKError extends Error {
|
|
|
69
71
|
name: this.name,
|
|
70
72
|
code: this.code,
|
|
71
73
|
message: this.message,
|
|
72
|
-
timestamp: this.timestamp.toISOString()
|
|
74
|
+
timestamp: this.timestamp.toISOString(),
|
|
75
|
+
requestId: this.requestId,
|
|
76
|
+
statusCode: this.statusCode
|
|
73
77
|
};
|
|
74
78
|
}
|
|
75
79
|
/**
|
|
@@ -109,6 +113,12 @@ var UtiliaClient = class {
|
|
|
109
113
|
"X-Api-Key": this.config.apiKey
|
|
110
114
|
}
|
|
111
115
|
});
|
|
116
|
+
this.axios.interceptors.request.use((config2) => {
|
|
117
|
+
const requestId = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : this.generateSimpleId();
|
|
118
|
+
config2.headers["x-request-id"] = requestId;
|
|
119
|
+
config2._requestId = requestId;
|
|
120
|
+
return config2;
|
|
121
|
+
});
|
|
112
122
|
this.setupInterceptors();
|
|
113
123
|
}
|
|
114
124
|
/**
|
|
@@ -134,40 +144,43 @@ var UtiliaClient = class {
|
|
|
134
144
|
* Convierte un error de Axios en un UtiliaSDKError
|
|
135
145
|
*/
|
|
136
146
|
toSDKError(error) {
|
|
147
|
+
const requestId = error.response?.headers?.["x-request-id"] || error.config?._requestId;
|
|
148
|
+
const statusCode = error.response?.status;
|
|
149
|
+
const errorOptions = { requestId, statusCode };
|
|
137
150
|
if (!error.response) {
|
|
138
151
|
if (error.code === "ECONNABORTED") {
|
|
139
|
-
return new UtiliaSDKError("NETWORK_ERROR" /* NETWORK_ERROR */, "Timeout: la solicitud excedio el tiempo limite");
|
|
152
|
+
return new UtiliaSDKError("NETWORK_ERROR" /* NETWORK_ERROR */, "Timeout: la solicitud excedio el tiempo limite", errorOptions);
|
|
140
153
|
}
|
|
141
|
-
return new UtiliaSDKError("NETWORK_ERROR" /* NETWORK_ERROR */, "Error de conexion: no se pudo conectar con el servidor");
|
|
154
|
+
return new UtiliaSDKError("NETWORK_ERROR" /* NETWORK_ERROR */, "Error de conexion: no se pudo conectar con el servidor", errorOptions);
|
|
142
155
|
}
|
|
143
156
|
const status = error.response.status;
|
|
144
157
|
const data = error.response.data;
|
|
145
158
|
const message = data?.message || data?.error || error.message;
|
|
146
159
|
switch (status) {
|
|
147
160
|
case 401:
|
|
148
|
-
return new UtiliaSDKError("UNAUTHORIZED" /* UNAUTHORIZED */, message || "API Key invalida o faltante");
|
|
161
|
+
return new UtiliaSDKError("UNAUTHORIZED" /* UNAUTHORIZED */, message || "API Key invalida o faltante", errorOptions);
|
|
149
162
|
case 403:
|
|
150
163
|
if (message?.toLowerCase().includes("rate limit")) {
|
|
151
|
-
return new UtiliaSDKError("RATE_LIMITED" /* RATE_LIMITED */, "Rate limit excedido. Intenta de nuevo mas tarde.");
|
|
164
|
+
return new UtiliaSDKError("RATE_LIMITED" /* RATE_LIMITED */, "Rate limit excedido. Intenta de nuevo mas tarde.", errorOptions);
|
|
152
165
|
}
|
|
153
166
|
if (message?.toLowerCase().includes("origen")) {
|
|
154
|
-
return new UtiliaSDKError("FORBIDDEN" /* FORBIDDEN */, "Origen no permitido por CORS");
|
|
167
|
+
return new UtiliaSDKError("FORBIDDEN" /* FORBIDDEN */, "Origen no permitido por CORS", errorOptions);
|
|
155
168
|
}
|
|
156
|
-
return new UtiliaSDKError("FORBIDDEN" /* FORBIDDEN */, message || "Acceso denegado");
|
|
169
|
+
return new UtiliaSDKError("FORBIDDEN" /* FORBIDDEN */, message || "Acceso denegado", errorOptions);
|
|
157
170
|
case 404:
|
|
158
|
-
return new UtiliaSDKError("NOT_FOUND" /* NOT_FOUND */, message || "Recurso no encontrado");
|
|
171
|
+
return new UtiliaSDKError("NOT_FOUND" /* NOT_FOUND */, message || "Recurso no encontrado", errorOptions);
|
|
159
172
|
case 400:
|
|
160
173
|
case 422:
|
|
161
|
-
return new UtiliaSDKError("VALIDATION_ERROR" /* VALIDATION_ERROR */, message || "Error de validacion en los datos enviados");
|
|
174
|
+
return new UtiliaSDKError("VALIDATION_ERROR" /* VALIDATION_ERROR */, message || "Error de validacion en los datos enviados", errorOptions);
|
|
162
175
|
case 429:
|
|
163
|
-
return new UtiliaSDKError("RATE_LIMITED" /* RATE_LIMITED */, message || "Rate limit excedido. Intenta de nuevo mas tarde.");
|
|
176
|
+
return new UtiliaSDKError("RATE_LIMITED" /* RATE_LIMITED */, message || "Rate limit excedido. Intenta de nuevo mas tarde.", errorOptions);
|
|
164
177
|
case 500:
|
|
165
178
|
case 502:
|
|
166
179
|
case 503:
|
|
167
180
|
case 504:
|
|
168
|
-
return new UtiliaSDKError("UNKNOWN" /* UNKNOWN */, message || "Error interno del servidor");
|
|
181
|
+
return new UtiliaSDKError("UNKNOWN" /* UNKNOWN */, message || "Error interno del servidor", errorOptions);
|
|
169
182
|
default:
|
|
170
|
-
return new UtiliaSDKError("UNKNOWN" /* UNKNOWN */, message || `Error desconocido (HTTP ${status})
|
|
183
|
+
return new UtiliaSDKError("UNKNOWN" /* UNKNOWN */, message || `Error desconocido (HTTP ${status})`, errorOptions);
|
|
171
184
|
}
|
|
172
185
|
}
|
|
173
186
|
/**
|
|
@@ -214,12 +227,30 @@ var UtiliaClient = class {
|
|
|
214
227
|
}
|
|
215
228
|
throw lastError;
|
|
216
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* Genera un ID simple como fallback cuando crypto.randomUUID no esta disponible
|
|
232
|
+
*/
|
|
233
|
+
generateSimpleId() {
|
|
234
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
235
|
+
}
|
|
217
236
|
/**
|
|
218
237
|
* Helper para esperar un tiempo
|
|
219
238
|
*/
|
|
220
239
|
sleep(ms) {
|
|
221
240
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
222
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* Construye una URL completa para conexiones SSE con la API key como query param.
|
|
244
|
+
* Util para EventSource que no soporta headers personalizados.
|
|
245
|
+
*
|
|
246
|
+
* @param path - Ruta relativa de la API (ej: /external/v1/tickets/ai-suggest/123/stream)
|
|
247
|
+
* @returns URL completa con apiKey como parametro de consulta
|
|
248
|
+
*/
|
|
249
|
+
buildSseUrl(path) {
|
|
250
|
+
const base = this.config.baseURL.replace(/\/$/, "");
|
|
251
|
+
const separator = path.includes("?") ? "&" : "?";
|
|
252
|
+
return `${base}${path}${separator}apiKey=${this.config.apiKey}`;
|
|
253
|
+
}
|
|
223
254
|
/**
|
|
224
255
|
* Realiza una peticion GET
|
|
225
256
|
*/
|
|
@@ -792,6 +823,237 @@ var AiService = class {
|
|
|
792
823
|
async trackAiAction(data) {
|
|
793
824
|
return this.client.post(`${this.basePath}/ai-track-action`, data);
|
|
794
825
|
}
|
|
826
|
+
/**
|
|
827
|
+
* Solicita sugerencias de IA y recibe el resultado via SSE (Server-Sent Events).
|
|
828
|
+
* Alternativa al polling que ofrece actualizaciones en tiempo real.
|
|
829
|
+
*
|
|
830
|
+
* Requiere un entorno con EventSource nativo (navegador).
|
|
831
|
+
* Para Node.js se necesita un polyfill como 'eventsource'.
|
|
832
|
+
*
|
|
833
|
+
* @param data - Datos para generar sugerencias
|
|
834
|
+
* @param options - Opciones de streaming
|
|
835
|
+
* @returns Handle con promesa de resultado y funcion de aborto
|
|
836
|
+
*
|
|
837
|
+
* @example
|
|
838
|
+
* ```typescript
|
|
839
|
+
* const handle = await sdk.ai.streamSuggestions(
|
|
840
|
+
* { userId: 'user-123', text: 'No puedo iniciar sesion' },
|
|
841
|
+
* { onProgress: (p) => console.log(`Progreso: ${p}%`) },
|
|
842
|
+
* );
|
|
843
|
+
*
|
|
844
|
+
* // Esperar resultado
|
|
845
|
+
* const result = await handle.result;
|
|
846
|
+
* console.log(result?.suggestions);
|
|
847
|
+
*
|
|
848
|
+
* // O abortar si el usuario cancela
|
|
849
|
+
* handle.abort();
|
|
850
|
+
* ```
|
|
851
|
+
*/
|
|
852
|
+
async streamSuggestions(data, options = {}) {
|
|
853
|
+
const { onProgress, onError, timeout = 12e4 } = options;
|
|
854
|
+
if (typeof EventSource === "undefined") {
|
|
855
|
+
throw new Error(
|
|
856
|
+
'EventSource no esta disponible en este entorno. Para Node.js, instala un polyfill como "eventsource" y asignalo a globalThis.EventSource.'
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
const { jobId } = await this.requestSuggestions(data);
|
|
860
|
+
const sseUrl = this.client.buildSseUrl(`${this.basePath}/ai-suggest/${jobId}/stream`);
|
|
861
|
+
let eventSource;
|
|
862
|
+
let timeoutId;
|
|
863
|
+
const result = new Promise((resolve, reject) => {
|
|
864
|
+
eventSource = new EventSource(sseUrl);
|
|
865
|
+
timeoutId = setTimeout(() => {
|
|
866
|
+
eventSource.close();
|
|
867
|
+
reject(new Error("Timeout: la conexion SSE excedio el tiempo limite"));
|
|
868
|
+
}, timeout);
|
|
869
|
+
eventSource.onmessage = (event) => {
|
|
870
|
+
try {
|
|
871
|
+
const data2 = JSON.parse(event.data);
|
|
872
|
+
switch (data2.event) {
|
|
873
|
+
case "progress":
|
|
874
|
+
if (onProgress && data2.progress !== void 0) {
|
|
875
|
+
onProgress(data2.progress);
|
|
876
|
+
}
|
|
877
|
+
break;
|
|
878
|
+
case "completed":
|
|
879
|
+
clearTimeout(timeoutId);
|
|
880
|
+
eventSource.close();
|
|
881
|
+
resolve(data2.result);
|
|
882
|
+
break;
|
|
883
|
+
case "failed":
|
|
884
|
+
clearTimeout(timeoutId);
|
|
885
|
+
eventSource.close();
|
|
886
|
+
reject(new Error(data2.error || "Error al generar sugerencias"));
|
|
887
|
+
break;
|
|
888
|
+
case "heartbeat":
|
|
889
|
+
break;
|
|
890
|
+
}
|
|
891
|
+
} catch (parseError) {
|
|
892
|
+
if (onError) {
|
|
893
|
+
onError(new Error("Error al parsear evento SSE"));
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
eventSource.onerror = () => {
|
|
898
|
+
clearTimeout(timeoutId);
|
|
899
|
+
eventSource.close();
|
|
900
|
+
reject(new Error("Error en la conexion SSE"));
|
|
901
|
+
};
|
|
902
|
+
});
|
|
903
|
+
const abort = () => {
|
|
904
|
+
clearTimeout(timeoutId);
|
|
905
|
+
eventSource?.close();
|
|
906
|
+
};
|
|
907
|
+
return { result, abort };
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Solicita una transcripcion de audio de forma asincrona.
|
|
911
|
+
* Retorna un jobId para consultar el estado o usar con streamTranscription.
|
|
912
|
+
*
|
|
913
|
+
* @param audioBase64 - Audio codificado en base64
|
|
914
|
+
* @param audioFilename - Nombre del archivo con extension
|
|
915
|
+
* @returns ID del job para consultar estado
|
|
916
|
+
*
|
|
917
|
+
* @example
|
|
918
|
+
* ```typescript
|
|
919
|
+
* const { jobId } = await sdk.ai.requestTranscription(
|
|
920
|
+
* audioBase64,
|
|
921
|
+
* 'recording.webm',
|
|
922
|
+
* );
|
|
923
|
+
* ```
|
|
924
|
+
*/
|
|
925
|
+
async requestTranscription(audioBase64, audioFilename) {
|
|
926
|
+
return this.client.post(`${this.basePath}/ai-transcribe-async`, {
|
|
927
|
+
audioBase64,
|
|
928
|
+
audioFilename
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Obtiene el estado de un job de transcripcion asincrona.
|
|
933
|
+
*
|
|
934
|
+
* @param jobId - ID del job obtenido de requestTranscription
|
|
935
|
+
* @returns Estado actual del job de transcripcion
|
|
936
|
+
*
|
|
937
|
+
* @example
|
|
938
|
+
* ```typescript
|
|
939
|
+
* const status = await sdk.ai.getTranscriptionStatus(jobId);
|
|
940
|
+
*
|
|
941
|
+
* if (status.status === 'completed') {
|
|
942
|
+
* console.log(status.transcription);
|
|
943
|
+
* }
|
|
944
|
+
* ```
|
|
945
|
+
*/
|
|
946
|
+
async getTranscriptionStatus(jobId) {
|
|
947
|
+
return this.client.get(`${this.basePath}/ai-transcribe/${jobId}`);
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Solicita una transcripcion de audio y recibe el resultado via SSE.
|
|
951
|
+
* Alternativa al polling para transcripciones asincronas.
|
|
952
|
+
*
|
|
953
|
+
* Requiere un entorno con EventSource nativo (navegador).
|
|
954
|
+
* Para Node.js se necesita un polyfill como 'eventsource'.
|
|
955
|
+
*
|
|
956
|
+
* @param audioBase64 - Audio codificado en base64
|
|
957
|
+
* @param audioFilename - Nombre del archivo con extension
|
|
958
|
+
* @param options - Opciones de streaming
|
|
959
|
+
* @returns Promesa que resuelve con el texto transcrito
|
|
960
|
+
*
|
|
961
|
+
* @example
|
|
962
|
+
* ```typescript
|
|
963
|
+
* const { transcription } = await sdk.ai.streamTranscription(
|
|
964
|
+
* audioBase64,
|
|
965
|
+
* 'recording.webm',
|
|
966
|
+
* { onProgress: (p) => console.log(`Progreso: ${p}%`) },
|
|
967
|
+
* );
|
|
968
|
+
* console.log(transcription);
|
|
969
|
+
* ```
|
|
970
|
+
*/
|
|
971
|
+
async streamTranscription(audioBase64, audioFilename, options = {}) {
|
|
972
|
+
const { onProgress, onError, timeout = 12e4 } = options;
|
|
973
|
+
if (typeof EventSource === "undefined") {
|
|
974
|
+
throw new Error(
|
|
975
|
+
'EventSource no esta disponible en este entorno. Para Node.js, instala un polyfill como "eventsource" y asignalo a globalThis.EventSource.'
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
const { jobId } = await this.requestTranscription(audioBase64, audioFilename);
|
|
979
|
+
const sseUrl = this.client.buildSseUrl(`${this.basePath}/ai-transcribe/${jobId}/stream`);
|
|
980
|
+
return new Promise((resolve, reject) => {
|
|
981
|
+
const eventSource = new EventSource(sseUrl);
|
|
982
|
+
const timeoutId = setTimeout(() => {
|
|
983
|
+
eventSource.close();
|
|
984
|
+
reject(new Error("Timeout: la conexion SSE excedio el tiempo limite"));
|
|
985
|
+
}, timeout);
|
|
986
|
+
eventSource.onmessage = (event) => {
|
|
987
|
+
try {
|
|
988
|
+
const data = JSON.parse(event.data);
|
|
989
|
+
switch (data.event) {
|
|
990
|
+
case "progress":
|
|
991
|
+
if (onProgress && data.progress !== void 0) {
|
|
992
|
+
onProgress(data.progress);
|
|
993
|
+
}
|
|
994
|
+
break;
|
|
995
|
+
case "completed":
|
|
996
|
+
clearTimeout(timeoutId);
|
|
997
|
+
eventSource.close();
|
|
998
|
+
resolve({ transcription: data.result?.transcription || "" });
|
|
999
|
+
break;
|
|
1000
|
+
case "failed":
|
|
1001
|
+
clearTimeout(timeoutId);
|
|
1002
|
+
eventSource.close();
|
|
1003
|
+
reject(new Error(data.error || "Error en la transcripcion"));
|
|
1004
|
+
break;
|
|
1005
|
+
case "heartbeat":
|
|
1006
|
+
break;
|
|
1007
|
+
}
|
|
1008
|
+
} catch (parseError) {
|
|
1009
|
+
if (onError) {
|
|
1010
|
+
onError(new Error("Error al parsear evento SSE"));
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
eventSource.onerror = () => {
|
|
1015
|
+
clearTimeout(timeoutId);
|
|
1016
|
+
eventSource.close();
|
|
1017
|
+
reject(new Error("Error en la conexion SSE"));
|
|
1018
|
+
};
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Reporta un error del cliente al servidor para monitoreo.
|
|
1023
|
+
* Este metodo nunca lanza excepciones; los errores de reporte se ignoran silenciosamente.
|
|
1024
|
+
*
|
|
1025
|
+
* @param params - Datos del error a reportar
|
|
1026
|
+
*
|
|
1027
|
+
* @example
|
|
1028
|
+
* ```typescript
|
|
1029
|
+
* await sdk.ai.reportError({
|
|
1030
|
+
* message: 'No se pudo cargar el widget',
|
|
1031
|
+
* code: 'WIDGET_LOAD_ERROR',
|
|
1032
|
+
* component: 'widget',
|
|
1033
|
+
* url: window.location.href,
|
|
1034
|
+
* userAgent: navigator.userAgent,
|
|
1035
|
+
* });
|
|
1036
|
+
* ```
|
|
1037
|
+
*/
|
|
1038
|
+
async reportError(params) {
|
|
1039
|
+
try {
|
|
1040
|
+
await this.client.post(`${this.basePath}/client-errors`, {
|
|
1041
|
+
errors: [{
|
|
1042
|
+
message: params.message,
|
|
1043
|
+
stack: params.stack,
|
|
1044
|
+
code: params.code,
|
|
1045
|
+
severity: "low",
|
|
1046
|
+
component: params.component || "sdk",
|
|
1047
|
+
endpoint: params.endpoint,
|
|
1048
|
+
method: params.method,
|
|
1049
|
+
requestId: params.requestId,
|
|
1050
|
+
url: params.url,
|
|
1051
|
+
userAgent: params.userAgent
|
|
1052
|
+
}]
|
|
1053
|
+
});
|
|
1054
|
+
} catch {
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
795
1057
|
/**
|
|
796
1058
|
* Helper para esperar un tiempo
|
|
797
1059
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -13,11 +13,13 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
|
13
13
|
return ErrorCode2;
|
|
14
14
|
})(ErrorCode || {});
|
|
15
15
|
var UtiliaSDKError = class _UtiliaSDKError extends Error {
|
|
16
|
-
constructor(code, message) {
|
|
16
|
+
constructor(code, message, options) {
|
|
17
17
|
super(message);
|
|
18
18
|
this.name = "UtiliaSDKError";
|
|
19
19
|
this.code = code;
|
|
20
20
|
this.timestamp = /* @__PURE__ */ new Date();
|
|
21
|
+
this.requestId = options?.requestId;
|
|
22
|
+
this.statusCode = options?.statusCode;
|
|
21
23
|
if (Error.captureStackTrace) {
|
|
22
24
|
Error.captureStackTrace(this, _UtiliaSDKError);
|
|
23
25
|
}
|
|
@@ -30,7 +32,9 @@ var UtiliaSDKError = class _UtiliaSDKError extends Error {
|
|
|
30
32
|
name: this.name,
|
|
31
33
|
code: this.code,
|
|
32
34
|
message: this.message,
|
|
33
|
-
timestamp: this.timestamp.toISOString()
|
|
35
|
+
timestamp: this.timestamp.toISOString(),
|
|
36
|
+
requestId: this.requestId,
|
|
37
|
+
statusCode: this.statusCode
|
|
34
38
|
};
|
|
35
39
|
}
|
|
36
40
|
/**
|
|
@@ -70,6 +74,12 @@ var UtiliaClient = class {
|
|
|
70
74
|
"X-Api-Key": this.config.apiKey
|
|
71
75
|
}
|
|
72
76
|
});
|
|
77
|
+
this.axios.interceptors.request.use((config2) => {
|
|
78
|
+
const requestId = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : this.generateSimpleId();
|
|
79
|
+
config2.headers["x-request-id"] = requestId;
|
|
80
|
+
config2._requestId = requestId;
|
|
81
|
+
return config2;
|
|
82
|
+
});
|
|
73
83
|
this.setupInterceptors();
|
|
74
84
|
}
|
|
75
85
|
/**
|
|
@@ -95,40 +105,43 @@ var UtiliaClient = class {
|
|
|
95
105
|
* Convierte un error de Axios en un UtiliaSDKError
|
|
96
106
|
*/
|
|
97
107
|
toSDKError(error) {
|
|
108
|
+
const requestId = error.response?.headers?.["x-request-id"] || error.config?._requestId;
|
|
109
|
+
const statusCode = error.response?.status;
|
|
110
|
+
const errorOptions = { requestId, statusCode };
|
|
98
111
|
if (!error.response) {
|
|
99
112
|
if (error.code === "ECONNABORTED") {
|
|
100
|
-
return new UtiliaSDKError("NETWORK_ERROR" /* NETWORK_ERROR */, "Timeout: la solicitud excedio el tiempo limite");
|
|
113
|
+
return new UtiliaSDKError("NETWORK_ERROR" /* NETWORK_ERROR */, "Timeout: la solicitud excedio el tiempo limite", errorOptions);
|
|
101
114
|
}
|
|
102
|
-
return new UtiliaSDKError("NETWORK_ERROR" /* NETWORK_ERROR */, "Error de conexion: no se pudo conectar con el servidor");
|
|
115
|
+
return new UtiliaSDKError("NETWORK_ERROR" /* NETWORK_ERROR */, "Error de conexion: no se pudo conectar con el servidor", errorOptions);
|
|
103
116
|
}
|
|
104
117
|
const status = error.response.status;
|
|
105
118
|
const data = error.response.data;
|
|
106
119
|
const message = data?.message || data?.error || error.message;
|
|
107
120
|
switch (status) {
|
|
108
121
|
case 401:
|
|
109
|
-
return new UtiliaSDKError("UNAUTHORIZED" /* UNAUTHORIZED */, message || "API Key invalida o faltante");
|
|
122
|
+
return new UtiliaSDKError("UNAUTHORIZED" /* UNAUTHORIZED */, message || "API Key invalida o faltante", errorOptions);
|
|
110
123
|
case 403:
|
|
111
124
|
if (message?.toLowerCase().includes("rate limit")) {
|
|
112
|
-
return new UtiliaSDKError("RATE_LIMITED" /* RATE_LIMITED */, "Rate limit excedido. Intenta de nuevo mas tarde.");
|
|
125
|
+
return new UtiliaSDKError("RATE_LIMITED" /* RATE_LIMITED */, "Rate limit excedido. Intenta de nuevo mas tarde.", errorOptions);
|
|
113
126
|
}
|
|
114
127
|
if (message?.toLowerCase().includes("origen")) {
|
|
115
|
-
return new UtiliaSDKError("FORBIDDEN" /* FORBIDDEN */, "Origen no permitido por CORS");
|
|
128
|
+
return new UtiliaSDKError("FORBIDDEN" /* FORBIDDEN */, "Origen no permitido por CORS", errorOptions);
|
|
116
129
|
}
|
|
117
|
-
return new UtiliaSDKError("FORBIDDEN" /* FORBIDDEN */, message || "Acceso denegado");
|
|
130
|
+
return new UtiliaSDKError("FORBIDDEN" /* FORBIDDEN */, message || "Acceso denegado", errorOptions);
|
|
118
131
|
case 404:
|
|
119
|
-
return new UtiliaSDKError("NOT_FOUND" /* NOT_FOUND */, message || "Recurso no encontrado");
|
|
132
|
+
return new UtiliaSDKError("NOT_FOUND" /* NOT_FOUND */, message || "Recurso no encontrado", errorOptions);
|
|
120
133
|
case 400:
|
|
121
134
|
case 422:
|
|
122
|
-
return new UtiliaSDKError("VALIDATION_ERROR" /* VALIDATION_ERROR */, message || "Error de validacion en los datos enviados");
|
|
135
|
+
return new UtiliaSDKError("VALIDATION_ERROR" /* VALIDATION_ERROR */, message || "Error de validacion en los datos enviados", errorOptions);
|
|
123
136
|
case 429:
|
|
124
|
-
return new UtiliaSDKError("RATE_LIMITED" /* RATE_LIMITED */, message || "Rate limit excedido. Intenta de nuevo mas tarde.");
|
|
137
|
+
return new UtiliaSDKError("RATE_LIMITED" /* RATE_LIMITED */, message || "Rate limit excedido. Intenta de nuevo mas tarde.", errorOptions);
|
|
125
138
|
case 500:
|
|
126
139
|
case 502:
|
|
127
140
|
case 503:
|
|
128
141
|
case 504:
|
|
129
|
-
return new UtiliaSDKError("UNKNOWN" /* UNKNOWN */, message || "Error interno del servidor");
|
|
142
|
+
return new UtiliaSDKError("UNKNOWN" /* UNKNOWN */, message || "Error interno del servidor", errorOptions);
|
|
130
143
|
default:
|
|
131
|
-
return new UtiliaSDKError("UNKNOWN" /* UNKNOWN */, message || `Error desconocido (HTTP ${status})
|
|
144
|
+
return new UtiliaSDKError("UNKNOWN" /* UNKNOWN */, message || `Error desconocido (HTTP ${status})`, errorOptions);
|
|
132
145
|
}
|
|
133
146
|
}
|
|
134
147
|
/**
|
|
@@ -175,12 +188,30 @@ var UtiliaClient = class {
|
|
|
175
188
|
}
|
|
176
189
|
throw lastError;
|
|
177
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Genera un ID simple como fallback cuando crypto.randomUUID no esta disponible
|
|
193
|
+
*/
|
|
194
|
+
generateSimpleId() {
|
|
195
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
196
|
+
}
|
|
178
197
|
/**
|
|
179
198
|
* Helper para esperar un tiempo
|
|
180
199
|
*/
|
|
181
200
|
sleep(ms) {
|
|
182
201
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
183
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Construye una URL completa para conexiones SSE con la API key como query param.
|
|
205
|
+
* Util para EventSource que no soporta headers personalizados.
|
|
206
|
+
*
|
|
207
|
+
* @param path - Ruta relativa de la API (ej: /external/v1/tickets/ai-suggest/123/stream)
|
|
208
|
+
* @returns URL completa con apiKey como parametro de consulta
|
|
209
|
+
*/
|
|
210
|
+
buildSseUrl(path) {
|
|
211
|
+
const base = this.config.baseURL.replace(/\/$/, "");
|
|
212
|
+
const separator = path.includes("?") ? "&" : "?";
|
|
213
|
+
return `${base}${path}${separator}apiKey=${this.config.apiKey}`;
|
|
214
|
+
}
|
|
184
215
|
/**
|
|
185
216
|
* Realiza una peticion GET
|
|
186
217
|
*/
|
|
@@ -753,6 +784,237 @@ var AiService = class {
|
|
|
753
784
|
async trackAiAction(data) {
|
|
754
785
|
return this.client.post(`${this.basePath}/ai-track-action`, data);
|
|
755
786
|
}
|
|
787
|
+
/**
|
|
788
|
+
* Solicita sugerencias de IA y recibe el resultado via SSE (Server-Sent Events).
|
|
789
|
+
* Alternativa al polling que ofrece actualizaciones en tiempo real.
|
|
790
|
+
*
|
|
791
|
+
* Requiere un entorno con EventSource nativo (navegador).
|
|
792
|
+
* Para Node.js se necesita un polyfill como 'eventsource'.
|
|
793
|
+
*
|
|
794
|
+
* @param data - Datos para generar sugerencias
|
|
795
|
+
* @param options - Opciones de streaming
|
|
796
|
+
* @returns Handle con promesa de resultado y funcion de aborto
|
|
797
|
+
*
|
|
798
|
+
* @example
|
|
799
|
+
* ```typescript
|
|
800
|
+
* const handle = await sdk.ai.streamSuggestions(
|
|
801
|
+
* { userId: 'user-123', text: 'No puedo iniciar sesion' },
|
|
802
|
+
* { onProgress: (p) => console.log(`Progreso: ${p}%`) },
|
|
803
|
+
* );
|
|
804
|
+
*
|
|
805
|
+
* // Esperar resultado
|
|
806
|
+
* const result = await handle.result;
|
|
807
|
+
* console.log(result?.suggestions);
|
|
808
|
+
*
|
|
809
|
+
* // O abortar si el usuario cancela
|
|
810
|
+
* handle.abort();
|
|
811
|
+
* ```
|
|
812
|
+
*/
|
|
813
|
+
async streamSuggestions(data, options = {}) {
|
|
814
|
+
const { onProgress, onError, timeout = 12e4 } = options;
|
|
815
|
+
if (typeof EventSource === "undefined") {
|
|
816
|
+
throw new Error(
|
|
817
|
+
'EventSource no esta disponible en este entorno. Para Node.js, instala un polyfill como "eventsource" y asignalo a globalThis.EventSource.'
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
const { jobId } = await this.requestSuggestions(data);
|
|
821
|
+
const sseUrl = this.client.buildSseUrl(`${this.basePath}/ai-suggest/${jobId}/stream`);
|
|
822
|
+
let eventSource;
|
|
823
|
+
let timeoutId;
|
|
824
|
+
const result = new Promise((resolve, reject) => {
|
|
825
|
+
eventSource = new EventSource(sseUrl);
|
|
826
|
+
timeoutId = setTimeout(() => {
|
|
827
|
+
eventSource.close();
|
|
828
|
+
reject(new Error("Timeout: la conexion SSE excedio el tiempo limite"));
|
|
829
|
+
}, timeout);
|
|
830
|
+
eventSource.onmessage = (event) => {
|
|
831
|
+
try {
|
|
832
|
+
const data2 = JSON.parse(event.data);
|
|
833
|
+
switch (data2.event) {
|
|
834
|
+
case "progress":
|
|
835
|
+
if (onProgress && data2.progress !== void 0) {
|
|
836
|
+
onProgress(data2.progress);
|
|
837
|
+
}
|
|
838
|
+
break;
|
|
839
|
+
case "completed":
|
|
840
|
+
clearTimeout(timeoutId);
|
|
841
|
+
eventSource.close();
|
|
842
|
+
resolve(data2.result);
|
|
843
|
+
break;
|
|
844
|
+
case "failed":
|
|
845
|
+
clearTimeout(timeoutId);
|
|
846
|
+
eventSource.close();
|
|
847
|
+
reject(new Error(data2.error || "Error al generar sugerencias"));
|
|
848
|
+
break;
|
|
849
|
+
case "heartbeat":
|
|
850
|
+
break;
|
|
851
|
+
}
|
|
852
|
+
} catch (parseError) {
|
|
853
|
+
if (onError) {
|
|
854
|
+
onError(new Error("Error al parsear evento SSE"));
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
eventSource.onerror = () => {
|
|
859
|
+
clearTimeout(timeoutId);
|
|
860
|
+
eventSource.close();
|
|
861
|
+
reject(new Error("Error en la conexion SSE"));
|
|
862
|
+
};
|
|
863
|
+
});
|
|
864
|
+
const abort = () => {
|
|
865
|
+
clearTimeout(timeoutId);
|
|
866
|
+
eventSource?.close();
|
|
867
|
+
};
|
|
868
|
+
return { result, abort };
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Solicita una transcripcion de audio de forma asincrona.
|
|
872
|
+
* Retorna un jobId para consultar el estado o usar con streamTranscription.
|
|
873
|
+
*
|
|
874
|
+
* @param audioBase64 - Audio codificado en base64
|
|
875
|
+
* @param audioFilename - Nombre del archivo con extension
|
|
876
|
+
* @returns ID del job para consultar estado
|
|
877
|
+
*
|
|
878
|
+
* @example
|
|
879
|
+
* ```typescript
|
|
880
|
+
* const { jobId } = await sdk.ai.requestTranscription(
|
|
881
|
+
* audioBase64,
|
|
882
|
+
* 'recording.webm',
|
|
883
|
+
* );
|
|
884
|
+
* ```
|
|
885
|
+
*/
|
|
886
|
+
async requestTranscription(audioBase64, audioFilename) {
|
|
887
|
+
return this.client.post(`${this.basePath}/ai-transcribe-async`, {
|
|
888
|
+
audioBase64,
|
|
889
|
+
audioFilename
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Obtiene el estado de un job de transcripcion asincrona.
|
|
894
|
+
*
|
|
895
|
+
* @param jobId - ID del job obtenido de requestTranscription
|
|
896
|
+
* @returns Estado actual del job de transcripcion
|
|
897
|
+
*
|
|
898
|
+
* @example
|
|
899
|
+
* ```typescript
|
|
900
|
+
* const status = await sdk.ai.getTranscriptionStatus(jobId);
|
|
901
|
+
*
|
|
902
|
+
* if (status.status === 'completed') {
|
|
903
|
+
* console.log(status.transcription);
|
|
904
|
+
* }
|
|
905
|
+
* ```
|
|
906
|
+
*/
|
|
907
|
+
async getTranscriptionStatus(jobId) {
|
|
908
|
+
return this.client.get(`${this.basePath}/ai-transcribe/${jobId}`);
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Solicita una transcripcion de audio y recibe el resultado via SSE.
|
|
912
|
+
* Alternativa al polling para transcripciones asincronas.
|
|
913
|
+
*
|
|
914
|
+
* Requiere un entorno con EventSource nativo (navegador).
|
|
915
|
+
* Para Node.js se necesita un polyfill como 'eventsource'.
|
|
916
|
+
*
|
|
917
|
+
* @param audioBase64 - Audio codificado en base64
|
|
918
|
+
* @param audioFilename - Nombre del archivo con extension
|
|
919
|
+
* @param options - Opciones de streaming
|
|
920
|
+
* @returns Promesa que resuelve con el texto transcrito
|
|
921
|
+
*
|
|
922
|
+
* @example
|
|
923
|
+
* ```typescript
|
|
924
|
+
* const { transcription } = await sdk.ai.streamTranscription(
|
|
925
|
+
* audioBase64,
|
|
926
|
+
* 'recording.webm',
|
|
927
|
+
* { onProgress: (p) => console.log(`Progreso: ${p}%`) },
|
|
928
|
+
* );
|
|
929
|
+
* console.log(transcription);
|
|
930
|
+
* ```
|
|
931
|
+
*/
|
|
932
|
+
async streamTranscription(audioBase64, audioFilename, options = {}) {
|
|
933
|
+
const { onProgress, onError, timeout = 12e4 } = options;
|
|
934
|
+
if (typeof EventSource === "undefined") {
|
|
935
|
+
throw new Error(
|
|
936
|
+
'EventSource no esta disponible en este entorno. Para Node.js, instala un polyfill como "eventsource" y asignalo a globalThis.EventSource.'
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
const { jobId } = await this.requestTranscription(audioBase64, audioFilename);
|
|
940
|
+
const sseUrl = this.client.buildSseUrl(`${this.basePath}/ai-transcribe/${jobId}/stream`);
|
|
941
|
+
return new Promise((resolve, reject) => {
|
|
942
|
+
const eventSource = new EventSource(sseUrl);
|
|
943
|
+
const timeoutId = setTimeout(() => {
|
|
944
|
+
eventSource.close();
|
|
945
|
+
reject(new Error("Timeout: la conexion SSE excedio el tiempo limite"));
|
|
946
|
+
}, timeout);
|
|
947
|
+
eventSource.onmessage = (event) => {
|
|
948
|
+
try {
|
|
949
|
+
const data = JSON.parse(event.data);
|
|
950
|
+
switch (data.event) {
|
|
951
|
+
case "progress":
|
|
952
|
+
if (onProgress && data.progress !== void 0) {
|
|
953
|
+
onProgress(data.progress);
|
|
954
|
+
}
|
|
955
|
+
break;
|
|
956
|
+
case "completed":
|
|
957
|
+
clearTimeout(timeoutId);
|
|
958
|
+
eventSource.close();
|
|
959
|
+
resolve({ transcription: data.result?.transcription || "" });
|
|
960
|
+
break;
|
|
961
|
+
case "failed":
|
|
962
|
+
clearTimeout(timeoutId);
|
|
963
|
+
eventSource.close();
|
|
964
|
+
reject(new Error(data.error || "Error en la transcripcion"));
|
|
965
|
+
break;
|
|
966
|
+
case "heartbeat":
|
|
967
|
+
break;
|
|
968
|
+
}
|
|
969
|
+
} catch (parseError) {
|
|
970
|
+
if (onError) {
|
|
971
|
+
onError(new Error("Error al parsear evento SSE"));
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
eventSource.onerror = () => {
|
|
976
|
+
clearTimeout(timeoutId);
|
|
977
|
+
eventSource.close();
|
|
978
|
+
reject(new Error("Error en la conexion SSE"));
|
|
979
|
+
};
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* Reporta un error del cliente al servidor para monitoreo.
|
|
984
|
+
* Este metodo nunca lanza excepciones; los errores de reporte se ignoran silenciosamente.
|
|
985
|
+
*
|
|
986
|
+
* @param params - Datos del error a reportar
|
|
987
|
+
*
|
|
988
|
+
* @example
|
|
989
|
+
* ```typescript
|
|
990
|
+
* await sdk.ai.reportError({
|
|
991
|
+
* message: 'No se pudo cargar el widget',
|
|
992
|
+
* code: 'WIDGET_LOAD_ERROR',
|
|
993
|
+
* component: 'widget',
|
|
994
|
+
* url: window.location.href,
|
|
995
|
+
* userAgent: navigator.userAgent,
|
|
996
|
+
* });
|
|
997
|
+
* ```
|
|
998
|
+
*/
|
|
999
|
+
async reportError(params) {
|
|
1000
|
+
try {
|
|
1001
|
+
await this.client.post(`${this.basePath}/client-errors`, {
|
|
1002
|
+
errors: [{
|
|
1003
|
+
message: params.message,
|
|
1004
|
+
stack: params.stack,
|
|
1005
|
+
code: params.code,
|
|
1006
|
+
severity: "low",
|
|
1007
|
+
component: params.component || "sdk",
|
|
1008
|
+
endpoint: params.endpoint,
|
|
1009
|
+
method: params.method,
|
|
1010
|
+
requestId: params.requestId,
|
|
1011
|
+
url: params.url,
|
|
1012
|
+
userAgent: params.userAgent
|
|
1013
|
+
}]
|
|
1014
|
+
});
|
|
1015
|
+
} catch {
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
756
1018
|
/**
|
|
757
1019
|
* Helper para esperar un tiempo
|
|
758
1020
|
*/
|