@allanfsouza/aether-sdk 2.4.10 → 2.4.11

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/ai.d.ts ADDED
@@ -0,0 +1,300 @@
1
+ import type { AxiosInstance } from "axios";
2
+ import type { PlataformaClient } from "./index.js";
3
+ /**
4
+ * Opções para o método chat()
5
+ */
6
+ export interface ChatOptions {
7
+ /** ID da conversa para manter contexto entre mensagens */
8
+ conversationId?: string;
9
+ /** Contexto adicional (ex: "vendas", "suporte", "produtos") */
10
+ context?: string;
11
+ /** Callback chamado a cada chunk de texto recebido (streaming) */
12
+ onChunk?: (chunk: string) => void;
13
+ /** Callback chamado quando a resposta completa é recebida */
14
+ onComplete?: (fullResponse: string, metadata: ChatMetadata) => void;
15
+ /** Callback chamado em caso de erro */
16
+ onError?: (error: Error) => void;
17
+ /** Timeout em ms (padrão: 30000) */
18
+ timeout?: number;
19
+ }
20
+ /**
21
+ * Metadados retornados após uma resposta completa
22
+ */
23
+ export interface ChatMetadata {
24
+ conversationId: string;
25
+ messageId?: string;
26
+ tokensUsed?: number;
27
+ sources?: RetrievedSource[];
28
+ }
29
+ /**
30
+ * Fonte de dados usada pelo RAG
31
+ */
32
+ export interface RetrievedSource {
33
+ type: string;
34
+ content: string;
35
+ similarity: number;
36
+ }
37
+ /**
38
+ * Resposta do método ask()
39
+ */
40
+ export interface AskResponse {
41
+ text: string;
42
+ conversationId: string;
43
+ sources?: RetrievedSource[];
44
+ }
45
+ /**
46
+ * Conversa persistida
47
+ */
48
+ export interface Conversation {
49
+ id: string;
50
+ title: string;
51
+ context: string;
52
+ createdAt: string;
53
+ updatedAt: string;
54
+ messageCount?: number;
55
+ }
56
+ /**
57
+ * Mensagem de uma conversa
58
+ */
59
+ export interface Message {
60
+ id: string;
61
+ conversationId: string;
62
+ role: 'user' | 'assistant';
63
+ content: string;
64
+ metadata?: Record<string, any>;
65
+ createdAt: string;
66
+ }
67
+ /**
68
+ * Tipo de feedback
69
+ */
70
+ export type FeedbackType = 'thumbs_up' | 'thumbs_down';
71
+ /**
72
+ * Opções para busca semântica
73
+ */
74
+ export interface SemanticSearchOptions {
75
+ /** Número máximo de resultados (padrão: 10) */
76
+ limit?: number;
77
+ /** Score mínimo de similaridade 0-1 (padrão: 0.5) */
78
+ minScore?: number;
79
+ /** Filtrar por collection específica */
80
+ collection?: string;
81
+ }
82
+ /**
83
+ * Resultado de busca semântica
84
+ */
85
+ export interface SemanticSearchResult {
86
+ id: string;
87
+ content: string;
88
+ collection: string;
89
+ similarity: number;
90
+ metadata?: Record<string, any>;
91
+ }
92
+ /**
93
+ * Opções para geração de conteúdo
94
+ */
95
+ export interface GenerateOptions {
96
+ /** Tom do texto (formal, casual, técnico, etc) */
97
+ tone?: 'formal' | 'casual' | 'technical' | 'friendly';
98
+ /** Tamanho aproximado (short, medium, long) */
99
+ length?: 'short' | 'medium' | 'long';
100
+ /** Idioma (padrão: pt-BR) */
101
+ language?: string;
102
+ /** Contexto adicional para a geração */
103
+ context?: string;
104
+ }
105
+ export declare class AIModule {
106
+ private client;
107
+ private http;
108
+ /** Sub-módulo para gerenciar conversas */
109
+ conversations: ConversationsAPI;
110
+ /** Sub-módulo para funções administrativas */
111
+ admin: AIAdminAPI;
112
+ constructor(client: PlataformaClient, http: AxiosInstance);
113
+ /**
114
+ * Faz uma pergunta simples e aguarda a resposta completa.
115
+ * Ideal para quando não precisa de streaming.
116
+ *
117
+ * @example
118
+ * const { text } = await aether.ai.ask("Quais produtos estão em promoção?");
119
+ * console.log(text);
120
+ */
121
+ ask(message: string, conversationId?: string): Promise<AskResponse>;
122
+ /**
123
+ * Inicia um chat com streaming de resposta.
124
+ * A resposta chega em chunks conforme a IA gera.
125
+ *
126
+ * @example
127
+ * await aether.ai.chat("Explique como funciona o sistema de pagamentos", {
128
+ * onChunk: (chunk) => {
129
+ * // Atualiza UI em tempo real
130
+ * setResponse(prev => prev + chunk);
131
+ * },
132
+ * onComplete: (full, meta) => {
133
+ * console.log("Conversa ID:", meta.conversationId);
134
+ * }
135
+ * });
136
+ */
137
+ chat(message: string, options?: ChatOptions): Promise<void>;
138
+ /**
139
+ * Envia feedback sobre uma resposta da IA.
140
+ * Ajuda a melhorar as respostas futuras.
141
+ *
142
+ * @example
143
+ * await aether.ai.feedback(messageId, 'thumbs_up');
144
+ * await aether.ai.feedback(messageId, 'thumbs_down', 'Resposta incorreta');
145
+ */
146
+ feedback(messageId: string, type: FeedbackType, comment?: string): Promise<{
147
+ success: boolean;
148
+ }>;
149
+ /**
150
+ * Busca semântica nos dados do projeto.
151
+ * Encontra documentos similares usando embeddings.
152
+ *
153
+ * @example
154
+ * // Busca produtos similares a uma descrição
155
+ * const results = await aether.ai.search("tênis confortável para corrida");
156
+ *
157
+ * // Com filtros
158
+ * const results = await aether.ai.search("problema no login", {
159
+ * collection: "support_tickets",
160
+ * limit: 5,
161
+ * minScore: 0.7
162
+ * });
163
+ */
164
+ search(query: string, options?: SemanticSearchOptions): Promise<SemanticSearchResult[]>;
165
+ /**
166
+ * Gera texto baseado em um prompt e contexto.
167
+ * Útil para descrições de produtos, resumos, etc.
168
+ *
169
+ * @example
170
+ * // Gerar descrição de produto
171
+ * const descricao = await aether.ai.generate(
172
+ * "Escreva uma descrição para o produto",
173
+ * { productName: "iPhone 15", price: 5999, features: ["5G", "48MP"] },
174
+ * { tone: 'friendly', length: 'medium' }
175
+ * );
176
+ */
177
+ generate(prompt: string, data: Record<string, any>, options?: GenerateOptions): Promise<string>;
178
+ /**
179
+ * Analisa dados e retorna insights em linguagem natural.
180
+ *
181
+ * @example
182
+ * const insights = await aether.ai.analyze("orders", {
183
+ * question: "Como foram as vendas essa semana?",
184
+ * period: "7d"
185
+ * });
186
+ */
187
+ analyze(collection: string, options: {
188
+ question: string;
189
+ period?: string;
190
+ }): Promise<{
191
+ insights: string;
192
+ data?: any;
193
+ }>;
194
+ /**
195
+ * Processa linguagem natural e converte em query estruturada.
196
+ * Útil para criar filtros de busca baseados em texto do usuário.
197
+ *
198
+ * @example
199
+ * const query = await aether.ai.parseQuery(
200
+ * "produtos vermelhos acima de 100 reais ordenados por preço"
201
+ * );
202
+ * // Retorna: { filter: { color: 'vermelho', price: { $gt: 100 } }, sort: { price: 'ASC' } }
203
+ */
204
+ parseQuery(naturalLanguage: string, collection?: string): Promise<{
205
+ filter?: Record<string, any>;
206
+ sort?: Record<string, any>;
207
+ }>;
208
+ }
209
+ declare class ConversationsAPI {
210
+ private client;
211
+ private http;
212
+ constructor(client: PlataformaClient, http: AxiosInstance);
213
+ /**
214
+ * Lista todas as conversas do usuário no projeto.
215
+ *
216
+ * @example
217
+ * const conversas = await aether.ai.conversations.list();
218
+ */
219
+ list(): Promise<Conversation[]>;
220
+ /**
221
+ * Busca o histórico de mensagens de uma conversa.
222
+ *
223
+ * @example
224
+ * const mensagens = await aether.ai.conversations.getMessages(conversationId);
225
+ */
226
+ getMessages(conversationId: string): Promise<Message[]>;
227
+ /**
228
+ * Deleta uma conversa e todo seu histórico.
229
+ *
230
+ * @example
231
+ * await aether.ai.conversations.delete(conversationId);
232
+ */
233
+ delete(conversationId: string): Promise<{
234
+ success: boolean;
235
+ }>;
236
+ /**
237
+ * Renomeia uma conversa.
238
+ *
239
+ * @example
240
+ * await aether.ai.conversations.rename(conversationId, "Suporte Técnico");
241
+ */
242
+ rename(conversationId: string, title: string): Promise<Conversation>;
243
+ }
244
+ declare class AIAdminAPI {
245
+ private client;
246
+ private http;
247
+ constructor(client: PlataformaClient, http: AxiosInstance);
248
+ /**
249
+ * Reindexa todos os dados do projeto para RAG.
250
+ * Use após adicionar/modificar grandes volumes de dados.
251
+ * Executa em background.
252
+ *
253
+ * @example
254
+ * await aether.ai.admin.reindex();
255
+ */
256
+ reindex(): Promise<{
257
+ message: string;
258
+ }>;
259
+ /**
260
+ * Limpa todos os embeddings do projeto.
261
+ * CUIDADO: A IA "esquece" todo o contexto aprendido.
262
+ *
263
+ * @example
264
+ * const { deletedCount } = await aether.ai.admin.clear();
265
+ */
266
+ clear(): Promise<{
267
+ deletedCount: number;
268
+ }>;
269
+ /**
270
+ * Retorna estatísticas de uso da IA.
271
+ *
272
+ * @example
273
+ * const stats = await aether.ai.admin.stats();
274
+ * console.log(stats.totalConversations, stats.totalMessages);
275
+ */
276
+ stats(): Promise<{
277
+ totalConversations: number;
278
+ totalMessages: number;
279
+ totalEmbeddings: number;
280
+ storageUsedMB: number;
281
+ }>;
282
+ /**
283
+ * Configura quais collections são indexadas automaticamente.
284
+ *
285
+ * @example
286
+ * await aether.ai.admin.setIndexedCollections(['products', 'articles', 'faq']);
287
+ */
288
+ setIndexedCollections(collections: string[]): Promise<{
289
+ success: boolean;
290
+ }>;
291
+ /**
292
+ * Busca configurações atuais de IA do projeto.
293
+ */
294
+ getConfig(): Promise<{
295
+ indexedCollections: string[];
296
+ autoIndex: boolean;
297
+ embeddingModel: string;
298
+ }>;
299
+ }
300
+ export {};
package/dist/ai.js ADDED
@@ -0,0 +1,370 @@
1
+ // src/ai.ts
2
+ // [NOVO] Módulo de IA do Aether SDK
3
+ // Permite que clientes adicionem IA aos seus apps com poucas linhas de código
4
+ // Data: Dezembro 2025
5
+ // =============================================================================
6
+ // MÓDULO PRINCIPAL
7
+ // =============================================================================
8
+ export class AIModule {
9
+ constructor(client, http) {
10
+ this.client = client;
11
+ this.http = http;
12
+ this.conversations = new ConversationsAPI(client, http);
13
+ this.admin = new AIAdminAPI(client, http);
14
+ }
15
+ // ===========================================================================
16
+ // MÉTODOS PRINCIPAIS
17
+ // ===========================================================================
18
+ /**
19
+ * Faz uma pergunta simples e aguarda a resposta completa.
20
+ * Ideal para quando não precisa de streaming.
21
+ *
22
+ * @example
23
+ * const { text } = await aether.ai.ask("Quais produtos estão em promoção?");
24
+ * console.log(text);
25
+ */
26
+ async ask(message, conversationId) {
27
+ return new Promise((resolve, reject) => {
28
+ let fullText = '';
29
+ let metadata = { conversationId: conversationId || '' };
30
+ this.chat(message, {
31
+ conversationId,
32
+ onChunk: (chunk) => {
33
+ fullText += chunk;
34
+ },
35
+ onComplete: (response, meta) => {
36
+ metadata = meta;
37
+ resolve({
38
+ text: fullText,
39
+ conversationId: meta.conversationId,
40
+ sources: meta.sources
41
+ });
42
+ },
43
+ onError: reject
44
+ }).catch(reject);
45
+ });
46
+ }
47
+ /**
48
+ * Inicia um chat com streaming de resposta.
49
+ * A resposta chega em chunks conforme a IA gera.
50
+ *
51
+ * @example
52
+ * await aether.ai.chat("Explique como funciona o sistema de pagamentos", {
53
+ * onChunk: (chunk) => {
54
+ * // Atualiza UI em tempo real
55
+ * setResponse(prev => prev + chunk);
56
+ * },
57
+ * onComplete: (full, meta) => {
58
+ * console.log("Conversa ID:", meta.conversationId);
59
+ * }
60
+ * });
61
+ */
62
+ async chat(message, options = {}) {
63
+ const projectId = this.client.projectId;
64
+ const token = this.client.getToken();
65
+ if (!token) {
66
+ const error = new Error('Usuário não autenticado. Faça login primeiro.');
67
+ if (options.onError) {
68
+ options.onError(error);
69
+ return;
70
+ }
71
+ throw error;
72
+ }
73
+ const timeout = options.timeout || 30000;
74
+ const controller = new AbortController();
75
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
76
+ try {
77
+ const response = await fetch(`${this.client.apiUrl}/v1/ai/chat`, {
78
+ method: 'POST',
79
+ headers: {
80
+ 'Content-Type': 'application/json',
81
+ 'Authorization': `Bearer ${token}`,
82
+ 'X-Project-ID': projectId
83
+ },
84
+ body: JSON.stringify({
85
+ message,
86
+ projectId,
87
+ conversationId: options.conversationId,
88
+ context: options.context || 'general'
89
+ }),
90
+ signal: controller.signal
91
+ });
92
+ clearTimeout(timeoutId);
93
+ if (!response.ok) {
94
+ const errorText = await response.text();
95
+ throw new Error(`Erro na API: ${response.status} - ${errorText}`);
96
+ }
97
+ // Captura conversation ID do header (se disponível)
98
+ const convIdFromHeader = response.headers.get('X-Conversation-ID');
99
+ // Processa o stream
100
+ const reader = response.body?.getReader();
101
+ const decoder = new TextDecoder();
102
+ let fullResponse = '';
103
+ if (!reader) {
104
+ throw new Error('Stream não disponível');
105
+ }
106
+ while (true) {
107
+ const { done, value } = await reader.read();
108
+ if (done)
109
+ break;
110
+ const chunk = decoder.decode(value, { stream: true });
111
+ fullResponse += chunk;
112
+ if (options.onChunk) {
113
+ options.onChunk(chunk);
114
+ }
115
+ }
116
+ // Callback de conclusão
117
+ if (options.onComplete) {
118
+ options.onComplete(fullResponse, {
119
+ conversationId: convIdFromHeader || options.conversationId || '',
120
+ });
121
+ }
122
+ }
123
+ catch (error) {
124
+ clearTimeout(timeoutId);
125
+ if (error.name === 'AbortError') {
126
+ error = new Error(`Timeout: A requisição excedeu ${timeout}ms`);
127
+ }
128
+ if (options.onError) {
129
+ options.onError(error);
130
+ }
131
+ else {
132
+ throw error;
133
+ }
134
+ }
135
+ }
136
+ /**
137
+ * Envia feedback sobre uma resposta da IA.
138
+ * Ajuda a melhorar as respostas futuras.
139
+ *
140
+ * @example
141
+ * await aether.ai.feedback(messageId, 'thumbs_up');
142
+ * await aether.ai.feedback(messageId, 'thumbs_down', 'Resposta incorreta');
143
+ */
144
+ async feedback(messageId, type, comment) {
145
+ const { data } = await this.http.post('/ai/feedback', {
146
+ messageId,
147
+ feedbackType: type,
148
+ comment
149
+ });
150
+ return data;
151
+ }
152
+ // ===========================================================================
153
+ // BUSCA SEMÂNTICA
154
+ // ===========================================================================
155
+ /**
156
+ * Busca semântica nos dados do projeto.
157
+ * Encontra documentos similares usando embeddings.
158
+ *
159
+ * @example
160
+ * // Busca produtos similares a uma descrição
161
+ * const results = await aether.ai.search("tênis confortável para corrida");
162
+ *
163
+ * // Com filtros
164
+ * const results = await aether.ai.search("problema no login", {
165
+ * collection: "support_tickets",
166
+ * limit: 5,
167
+ * minScore: 0.7
168
+ * });
169
+ */
170
+ async search(query, options = {}) {
171
+ const { data } = await this.http.post('/ai/search', {
172
+ query,
173
+ projectId: this.client.projectId,
174
+ limit: options.limit || 10,
175
+ minScore: options.minScore || 0.5,
176
+ collection: options.collection
177
+ });
178
+ return data.results;
179
+ }
180
+ // ===========================================================================
181
+ // GERAÇÃO DE CONTEÚDO
182
+ // ===========================================================================
183
+ /**
184
+ * Gera texto baseado em um prompt e contexto.
185
+ * Útil para descrições de produtos, resumos, etc.
186
+ *
187
+ * @example
188
+ * // Gerar descrição de produto
189
+ * const descricao = await aether.ai.generate(
190
+ * "Escreva uma descrição para o produto",
191
+ * { productName: "iPhone 15", price: 5999, features: ["5G", "48MP"] },
192
+ * { tone: 'friendly', length: 'medium' }
193
+ * );
194
+ */
195
+ async generate(prompt, data, options = {}) {
196
+ const { data: response } = await this.http.post('/ai/generate', {
197
+ prompt,
198
+ data,
199
+ projectId: this.client.projectId,
200
+ tone: options.tone || 'friendly',
201
+ length: options.length || 'medium',
202
+ language: options.language || 'pt-BR',
203
+ context: options.context
204
+ });
205
+ return response.text;
206
+ }
207
+ // ===========================================================================
208
+ // ANÁLISE DE DADOS
209
+ // ===========================================================================
210
+ /**
211
+ * Analisa dados e retorna insights em linguagem natural.
212
+ *
213
+ * @example
214
+ * const insights = await aether.ai.analyze("orders", {
215
+ * question: "Como foram as vendas essa semana?",
216
+ * period: "7d"
217
+ * });
218
+ */
219
+ async analyze(collection, options) {
220
+ const { data } = await this.http.post('/ai/analyze', {
221
+ collection,
222
+ projectId: this.client.projectId,
223
+ question: options.question,
224
+ period: options.period || '7d'
225
+ });
226
+ return data;
227
+ }
228
+ /**
229
+ * Processa linguagem natural e converte em query estruturada.
230
+ * Útil para criar filtros de busca baseados em texto do usuário.
231
+ *
232
+ * @example
233
+ * const query = await aether.ai.parseQuery(
234
+ * "produtos vermelhos acima de 100 reais ordenados por preço"
235
+ * );
236
+ * // Retorna: { filter: { color: 'vermelho', price: { $gt: 100 } }, sort: { price: 'ASC' } }
237
+ */
238
+ async parseQuery(naturalLanguage, collection) {
239
+ const { data } = await this.http.post('/ai/parse-query', {
240
+ query: naturalLanguage,
241
+ collection,
242
+ projectId: this.client.projectId
243
+ });
244
+ return data;
245
+ }
246
+ }
247
+ // =============================================================================
248
+ // SUB-MÓDULO: CONVERSAS
249
+ // =============================================================================
250
+ class ConversationsAPI {
251
+ constructor(client, http) {
252
+ this.client = client;
253
+ this.http = http;
254
+ }
255
+ /**
256
+ * Lista todas as conversas do usuário no projeto.
257
+ *
258
+ * @example
259
+ * const conversas = await aether.ai.conversations.list();
260
+ */
261
+ async list() {
262
+ const { data } = await this.http.get('/ai/conversations', {
263
+ params: { projectId: this.client.projectId }
264
+ });
265
+ return data;
266
+ }
267
+ /**
268
+ * Busca o histórico de mensagens de uma conversa.
269
+ *
270
+ * @example
271
+ * const mensagens = await aether.ai.conversations.getMessages(conversationId);
272
+ */
273
+ async getMessages(conversationId) {
274
+ const { data } = await this.http.get(`/ai/messages/${conversationId}`);
275
+ return data;
276
+ }
277
+ /**
278
+ * Deleta uma conversa e todo seu histórico.
279
+ *
280
+ * @example
281
+ * await aether.ai.conversations.delete(conversationId);
282
+ */
283
+ async delete(conversationId) {
284
+ const { data } = await this.http.delete(`/ai/conversations/${conversationId}`);
285
+ return data;
286
+ }
287
+ /**
288
+ * Renomeia uma conversa.
289
+ *
290
+ * @example
291
+ * await aether.ai.conversations.rename(conversationId, "Suporte Técnico");
292
+ */
293
+ async rename(conversationId, title) {
294
+ const { data } = await this.http.patch(`/ai/conversations/${conversationId}`, {
295
+ title
296
+ });
297
+ return data;
298
+ }
299
+ }
300
+ // =============================================================================
301
+ // SUB-MÓDULO: ADMIN
302
+ // =============================================================================
303
+ class AIAdminAPI {
304
+ constructor(client, http) {
305
+ this.client = client;
306
+ this.http = http;
307
+ }
308
+ /**
309
+ * Reindexa todos os dados do projeto para RAG.
310
+ * Use após adicionar/modificar grandes volumes de dados.
311
+ * Executa em background.
312
+ *
313
+ * @example
314
+ * await aether.ai.admin.reindex();
315
+ */
316
+ async reindex() {
317
+ const { data } = await this.http.post('/ai/reindex', {
318
+ projectId: this.client.projectId
319
+ });
320
+ return data;
321
+ }
322
+ /**
323
+ * Limpa todos os embeddings do projeto.
324
+ * CUIDADO: A IA "esquece" todo o contexto aprendido.
325
+ *
326
+ * @example
327
+ * const { deletedCount } = await aether.ai.admin.clear();
328
+ */
329
+ async clear() {
330
+ const { data } = await this.http.post('/ai/clear', {
331
+ projectId: this.client.projectId
332
+ });
333
+ return data;
334
+ }
335
+ /**
336
+ * Retorna estatísticas de uso da IA.
337
+ *
338
+ * @example
339
+ * const stats = await aether.ai.admin.stats();
340
+ * console.log(stats.totalConversations, stats.totalMessages);
341
+ */
342
+ async stats() {
343
+ const { data } = await this.http.get('/ai/stats', {
344
+ params: { projectId: this.client.projectId }
345
+ });
346
+ return data;
347
+ }
348
+ /**
349
+ * Configura quais collections são indexadas automaticamente.
350
+ *
351
+ * @example
352
+ * await aether.ai.admin.setIndexedCollections(['products', 'articles', 'faq']);
353
+ */
354
+ async setIndexedCollections(collections) {
355
+ const { data } = await this.http.post('/ai/config/collections', {
356
+ projectId: this.client.projectId,
357
+ collections
358
+ });
359
+ return data;
360
+ }
361
+ /**
362
+ * Busca configurações atuais de IA do projeto.
363
+ */
364
+ async getConfig() {
365
+ const { data } = await this.http.get('/ai/config', {
366
+ params: { projectId: this.client.projectId }
367
+ });
368
+ return data;
369
+ }
370
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allanfsouza/aether-sdk",
3
- "version": "2.4.10",
3
+ "version": "2.4.11",
4
4
  "description": "SDK do Cliente para a Plataforma Aether",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/ai.ts ADDED
@@ -0,0 +1,580 @@
1
+ // src/ai.ts
2
+ // [NOVO] Módulo de IA do Aether SDK
3
+ // Permite que clientes adicionem IA aos seus apps com poucas linhas de código
4
+ // Data: Dezembro 2025
5
+
6
+ import type { AxiosInstance } from "axios";
7
+ import type { PlataformaClient } from "./index.js";
8
+
9
+ // =============================================================================
10
+ // TIPOS
11
+ // =============================================================================
12
+
13
+ /**
14
+ * Opções para o método chat()
15
+ */
16
+ export interface ChatOptions {
17
+ /** ID da conversa para manter contexto entre mensagens */
18
+ conversationId?: string;
19
+
20
+ /** Contexto adicional (ex: "vendas", "suporte", "produtos") */
21
+ context?: string;
22
+
23
+ /** Callback chamado a cada chunk de texto recebido (streaming) */
24
+ onChunk?: (chunk: string) => void;
25
+
26
+ /** Callback chamado quando a resposta completa é recebida */
27
+ onComplete?: (fullResponse: string, metadata: ChatMetadata) => void;
28
+
29
+ /** Callback chamado em caso de erro */
30
+ onError?: (error: Error) => void;
31
+
32
+ /** Timeout em ms (padrão: 30000) */
33
+ timeout?: number;
34
+ }
35
+
36
+ /**
37
+ * Metadados retornados após uma resposta completa
38
+ */
39
+ export interface ChatMetadata {
40
+ conversationId: string;
41
+ messageId?: string;
42
+ tokensUsed?: number;
43
+ sources?: RetrievedSource[];
44
+ }
45
+
46
+ /**
47
+ * Fonte de dados usada pelo RAG
48
+ */
49
+ export interface RetrievedSource {
50
+ type: string;
51
+ content: string;
52
+ similarity: number;
53
+ }
54
+
55
+ /**
56
+ * Resposta do método ask()
57
+ */
58
+ export interface AskResponse {
59
+ text: string;
60
+ conversationId: string;
61
+ sources?: RetrievedSource[];
62
+ }
63
+
64
+ /**
65
+ * Conversa persistida
66
+ */
67
+ export interface Conversation {
68
+ id: string;
69
+ title: string;
70
+ context: string;
71
+ createdAt: string;
72
+ updatedAt: string;
73
+ messageCount?: number;
74
+ }
75
+
76
+ /**
77
+ * Mensagem de uma conversa
78
+ */
79
+ export interface Message {
80
+ id: string;
81
+ conversationId: string;
82
+ role: 'user' | 'assistant';
83
+ content: string;
84
+ metadata?: Record<string, any>;
85
+ createdAt: string;
86
+ }
87
+
88
+ /**
89
+ * Tipo de feedback
90
+ */
91
+ export type FeedbackType = 'thumbs_up' | 'thumbs_down';
92
+
93
+ /**
94
+ * Opções para busca semântica
95
+ */
96
+ export interface SemanticSearchOptions {
97
+ /** Número máximo de resultados (padrão: 10) */
98
+ limit?: number;
99
+
100
+ /** Score mínimo de similaridade 0-1 (padrão: 0.5) */
101
+ minScore?: number;
102
+
103
+ /** Filtrar por collection específica */
104
+ collection?: string;
105
+ }
106
+
107
+ /**
108
+ * Resultado de busca semântica
109
+ */
110
+ export interface SemanticSearchResult {
111
+ id: string;
112
+ content: string;
113
+ collection: string;
114
+ similarity: number;
115
+ metadata?: Record<string, any>;
116
+ }
117
+
118
+ /**
119
+ * Opções para geração de conteúdo
120
+ */
121
+ export interface GenerateOptions {
122
+ /** Tom do texto (formal, casual, técnico, etc) */
123
+ tone?: 'formal' | 'casual' | 'technical' | 'friendly';
124
+
125
+ /** Tamanho aproximado (short, medium, long) */
126
+ length?: 'short' | 'medium' | 'long';
127
+
128
+ /** Idioma (padrão: pt-BR) */
129
+ language?: string;
130
+
131
+ /** Contexto adicional para a geração */
132
+ context?: string;
133
+ }
134
+
135
+ // =============================================================================
136
+ // MÓDULO PRINCIPAL
137
+ // =============================================================================
138
+
139
+ export class AIModule {
140
+ private client: PlataformaClient;
141
+ private http: AxiosInstance;
142
+
143
+ /** Sub-módulo para gerenciar conversas */
144
+ public conversations: ConversationsAPI;
145
+
146
+ /** Sub-módulo para funções administrativas */
147
+ public admin: AIAdminAPI;
148
+
149
+ constructor(client: PlataformaClient, http: AxiosInstance) {
150
+ this.client = client;
151
+ this.http = http;
152
+ this.conversations = new ConversationsAPI(client, http);
153
+ this.admin = new AIAdminAPI(client, http);
154
+ }
155
+
156
+ // ===========================================================================
157
+ // MÉTODOS PRINCIPAIS
158
+ // ===========================================================================
159
+
160
+ /**
161
+ * Faz uma pergunta simples e aguarda a resposta completa.
162
+ * Ideal para quando não precisa de streaming.
163
+ *
164
+ * @example
165
+ * const { text } = await aether.ai.ask("Quais produtos estão em promoção?");
166
+ * console.log(text);
167
+ */
168
+ async ask(message: string, conversationId?: string): Promise<AskResponse> {
169
+ return new Promise((resolve, reject) => {
170
+ let fullText = '';
171
+ let metadata: ChatMetadata = { conversationId: conversationId || '' };
172
+
173
+ this.chat(message, {
174
+ conversationId,
175
+ onChunk: (chunk) => {
176
+ fullText += chunk;
177
+ },
178
+ onComplete: (response, meta) => {
179
+ metadata = meta;
180
+ resolve({
181
+ text: fullText,
182
+ conversationId: meta.conversationId,
183
+ sources: meta.sources
184
+ });
185
+ },
186
+ onError: reject
187
+ }).catch(reject);
188
+ });
189
+ }
190
+
191
+ /**
192
+ * Inicia um chat com streaming de resposta.
193
+ * A resposta chega em chunks conforme a IA gera.
194
+ *
195
+ * @example
196
+ * await aether.ai.chat("Explique como funciona o sistema de pagamentos", {
197
+ * onChunk: (chunk) => {
198
+ * // Atualiza UI em tempo real
199
+ * setResponse(prev => prev + chunk);
200
+ * },
201
+ * onComplete: (full, meta) => {
202
+ * console.log("Conversa ID:", meta.conversationId);
203
+ * }
204
+ * });
205
+ */
206
+ async chat(message: string, options: ChatOptions = {}): Promise<void> {
207
+ const projectId = this.client.projectId;
208
+ const token = this.client.getToken();
209
+
210
+ if (!token) {
211
+ const error = new Error('Usuário não autenticado. Faça login primeiro.');
212
+ if (options.onError) {
213
+ options.onError(error);
214
+ return;
215
+ }
216
+ throw error;
217
+ }
218
+
219
+ const timeout = options.timeout || 30000;
220
+ const controller = new AbortController();
221
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
222
+
223
+ try {
224
+ const response = await fetch(`${this.client.apiUrl}/v1/ai/chat`, {
225
+ method: 'POST',
226
+ headers: {
227
+ 'Content-Type': 'application/json',
228
+ 'Authorization': `Bearer ${token}`,
229
+ 'X-Project-ID': projectId
230
+ },
231
+ body: JSON.stringify({
232
+ message,
233
+ projectId,
234
+ conversationId: options.conversationId,
235
+ context: options.context || 'general'
236
+ }),
237
+ signal: controller.signal
238
+ });
239
+
240
+ clearTimeout(timeoutId);
241
+
242
+ if (!response.ok) {
243
+ const errorText = await response.text();
244
+ throw new Error(`Erro na API: ${response.status} - ${errorText}`);
245
+ }
246
+
247
+ // Captura conversation ID do header (se disponível)
248
+ const convIdFromHeader = response.headers.get('X-Conversation-ID');
249
+
250
+ // Processa o stream
251
+ const reader = response.body?.getReader();
252
+ const decoder = new TextDecoder();
253
+ let fullResponse = '';
254
+
255
+ if (!reader) {
256
+ throw new Error('Stream não disponível');
257
+ }
258
+
259
+ while (true) {
260
+ const { done, value } = await reader.read();
261
+
262
+ if (done) break;
263
+
264
+ const chunk = decoder.decode(value, { stream: true });
265
+ fullResponse += chunk;
266
+
267
+ if (options.onChunk) {
268
+ options.onChunk(chunk);
269
+ }
270
+ }
271
+
272
+ // Callback de conclusão
273
+ if (options.onComplete) {
274
+ options.onComplete(fullResponse, {
275
+ conversationId: convIdFromHeader || options.conversationId || '',
276
+ });
277
+ }
278
+
279
+ } catch (error: any) {
280
+ clearTimeout(timeoutId);
281
+
282
+ if (error.name === 'AbortError') {
283
+ error = new Error(`Timeout: A requisição excedeu ${timeout}ms`);
284
+ }
285
+
286
+ if (options.onError) {
287
+ options.onError(error);
288
+ } else {
289
+ throw error;
290
+ }
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Envia feedback sobre uma resposta da IA.
296
+ * Ajuda a melhorar as respostas futuras.
297
+ *
298
+ * @example
299
+ * await aether.ai.feedback(messageId, 'thumbs_up');
300
+ * await aether.ai.feedback(messageId, 'thumbs_down', 'Resposta incorreta');
301
+ */
302
+ async feedback(
303
+ messageId: string,
304
+ type: FeedbackType,
305
+ comment?: string
306
+ ): Promise<{ success: boolean }> {
307
+ const { data } = await this.http.post('/ai/feedback', {
308
+ messageId,
309
+ feedbackType: type,
310
+ comment
311
+ });
312
+ return data;
313
+ }
314
+
315
+ // ===========================================================================
316
+ // BUSCA SEMÂNTICA
317
+ // ===========================================================================
318
+
319
+ /**
320
+ * Busca semântica nos dados do projeto.
321
+ * Encontra documentos similares usando embeddings.
322
+ *
323
+ * @example
324
+ * // Busca produtos similares a uma descrição
325
+ * const results = await aether.ai.search("tênis confortável para corrida");
326
+ *
327
+ * // Com filtros
328
+ * const results = await aether.ai.search("problema no login", {
329
+ * collection: "support_tickets",
330
+ * limit: 5,
331
+ * minScore: 0.7
332
+ * });
333
+ */
334
+ async search(
335
+ query: string,
336
+ options: SemanticSearchOptions = {}
337
+ ): Promise<SemanticSearchResult[]> {
338
+ const { data } = await this.http.post('/ai/search', {
339
+ query,
340
+ projectId: this.client.projectId,
341
+ limit: options.limit || 10,
342
+ minScore: options.minScore || 0.5,
343
+ collection: options.collection
344
+ });
345
+ return data.results;
346
+ }
347
+
348
+ // ===========================================================================
349
+ // GERAÇÃO DE CONTEÚDO
350
+ // ===========================================================================
351
+
352
+ /**
353
+ * Gera texto baseado em um prompt e contexto.
354
+ * Útil para descrições de produtos, resumos, etc.
355
+ *
356
+ * @example
357
+ * // Gerar descrição de produto
358
+ * const descricao = await aether.ai.generate(
359
+ * "Escreva uma descrição para o produto",
360
+ * { productName: "iPhone 15", price: 5999, features: ["5G", "48MP"] },
361
+ * { tone: 'friendly', length: 'medium' }
362
+ * );
363
+ */
364
+ async generate(
365
+ prompt: string,
366
+ data: Record<string, any>,
367
+ options: GenerateOptions = {}
368
+ ): Promise<string> {
369
+ const { data: response } = await this.http.post('/ai/generate', {
370
+ prompt,
371
+ data,
372
+ projectId: this.client.projectId,
373
+ tone: options.tone || 'friendly',
374
+ length: options.length || 'medium',
375
+ language: options.language || 'pt-BR',
376
+ context: options.context
377
+ });
378
+ return response.text;
379
+ }
380
+
381
+ // ===========================================================================
382
+ // ANÁLISE DE DADOS
383
+ // ===========================================================================
384
+
385
+ /**
386
+ * Analisa dados e retorna insights em linguagem natural.
387
+ *
388
+ * @example
389
+ * const insights = await aether.ai.analyze("orders", {
390
+ * question: "Como foram as vendas essa semana?",
391
+ * period: "7d"
392
+ * });
393
+ */
394
+ async analyze(
395
+ collection: string,
396
+ options: { question: string; period?: string }
397
+ ): Promise<{ insights: string; data?: any }> {
398
+ const { data } = await this.http.post('/ai/analyze', {
399
+ collection,
400
+ projectId: this.client.projectId,
401
+ question: options.question,
402
+ period: options.period || '7d'
403
+ });
404
+ return data;
405
+ }
406
+
407
+ /**
408
+ * Processa linguagem natural e converte em query estruturada.
409
+ * Útil para criar filtros de busca baseados em texto do usuário.
410
+ *
411
+ * @example
412
+ * const query = await aether.ai.parseQuery(
413
+ * "produtos vermelhos acima de 100 reais ordenados por preço"
414
+ * );
415
+ * // Retorna: { filter: { color: 'vermelho', price: { $gt: 100 } }, sort: { price: 'ASC' } }
416
+ */
417
+ async parseQuery(
418
+ naturalLanguage: string,
419
+ collection?: string
420
+ ): Promise<{ filter?: Record<string, any>; sort?: Record<string, any> }> {
421
+ const { data } = await this.http.post('/ai/parse-query', {
422
+ query: naturalLanguage,
423
+ collection,
424
+ projectId: this.client.projectId
425
+ });
426
+ return data;
427
+ }
428
+ }
429
+
430
+ // =============================================================================
431
+ // SUB-MÓDULO: CONVERSAS
432
+ // =============================================================================
433
+
434
+ class ConversationsAPI {
435
+ private client: PlataformaClient;
436
+ private http: AxiosInstance;
437
+
438
+ constructor(client: PlataformaClient, http: AxiosInstance) {
439
+ this.client = client;
440
+ this.http = http;
441
+ }
442
+
443
+ /**
444
+ * Lista todas as conversas do usuário no projeto.
445
+ *
446
+ * @example
447
+ * const conversas = await aether.ai.conversations.list();
448
+ */
449
+ async list(): Promise<Conversation[]> {
450
+ const { data } = await this.http.get('/ai/conversations', {
451
+ params: { projectId: this.client.projectId }
452
+ });
453
+ return data;
454
+ }
455
+
456
+ /**
457
+ * Busca o histórico de mensagens de uma conversa.
458
+ *
459
+ * @example
460
+ * const mensagens = await aether.ai.conversations.getMessages(conversationId);
461
+ */
462
+ async getMessages(conversationId: string): Promise<Message[]> {
463
+ const { data } = await this.http.get(`/ai/messages/${conversationId}`);
464
+ return data;
465
+ }
466
+
467
+ /**
468
+ * Deleta uma conversa e todo seu histórico.
469
+ *
470
+ * @example
471
+ * await aether.ai.conversations.delete(conversationId);
472
+ */
473
+ async delete(conversationId: string): Promise<{ success: boolean }> {
474
+ const { data } = await this.http.delete(`/ai/conversations/${conversationId}`);
475
+ return data;
476
+ }
477
+
478
+ /**
479
+ * Renomeia uma conversa.
480
+ *
481
+ * @example
482
+ * await aether.ai.conversations.rename(conversationId, "Suporte Técnico");
483
+ */
484
+ async rename(conversationId: string, title: string): Promise<Conversation> {
485
+ const { data } = await this.http.patch(`/ai/conversations/${conversationId}`, {
486
+ title
487
+ });
488
+ return data;
489
+ }
490
+ }
491
+
492
+ // =============================================================================
493
+ // SUB-MÓDULO: ADMIN
494
+ // =============================================================================
495
+
496
+ class AIAdminAPI {
497
+ private client: PlataformaClient;
498
+ private http: AxiosInstance;
499
+
500
+ constructor(client: PlataformaClient, http: AxiosInstance) {
501
+ this.client = client;
502
+ this.http = http;
503
+ }
504
+
505
+ /**
506
+ * Reindexa todos os dados do projeto para RAG.
507
+ * Use após adicionar/modificar grandes volumes de dados.
508
+ * Executa em background.
509
+ *
510
+ * @example
511
+ * await aether.ai.admin.reindex();
512
+ */
513
+ async reindex(): Promise<{ message: string }> {
514
+ const { data } = await this.http.post('/ai/reindex', {
515
+ projectId: this.client.projectId
516
+ });
517
+ return data;
518
+ }
519
+
520
+ /**
521
+ * Limpa todos os embeddings do projeto.
522
+ * CUIDADO: A IA "esquece" todo o contexto aprendido.
523
+ *
524
+ * @example
525
+ * const { deletedCount } = await aether.ai.admin.clear();
526
+ */
527
+ async clear(): Promise<{ deletedCount: number }> {
528
+ const { data } = await this.http.post('/ai/clear', {
529
+ projectId: this.client.projectId
530
+ });
531
+ return data;
532
+ }
533
+
534
+ /**
535
+ * Retorna estatísticas de uso da IA.
536
+ *
537
+ * @example
538
+ * const stats = await aether.ai.admin.stats();
539
+ * console.log(stats.totalConversations, stats.totalMessages);
540
+ */
541
+ async stats(): Promise<{
542
+ totalConversations: number;
543
+ totalMessages: number;
544
+ totalEmbeddings: number;
545
+ storageUsedMB: number;
546
+ }> {
547
+ const { data } = await this.http.get('/ai/stats', {
548
+ params: { projectId: this.client.projectId }
549
+ });
550
+ return data;
551
+ }
552
+
553
+ /**
554
+ * Configura quais collections são indexadas automaticamente.
555
+ *
556
+ * @example
557
+ * await aether.ai.admin.setIndexedCollections(['products', 'articles', 'faq']);
558
+ */
559
+ async setIndexedCollections(collections: string[]): Promise<{ success: boolean }> {
560
+ const { data } = await this.http.post('/ai/config/collections', {
561
+ projectId: this.client.projectId,
562
+ collections
563
+ });
564
+ return data;
565
+ }
566
+
567
+ /**
568
+ * Busca configurações atuais de IA do projeto.
569
+ */
570
+ async getConfig(): Promise<{
571
+ indexedCollections: string[];
572
+ autoIndex: boolean;
573
+ embeddingModel: string;
574
+ }> {
575
+ const { data } = await this.http.get('/ai/config', {
576
+ params: { projectId: this.client.projectId }
577
+ });
578
+ return data;
579
+ }
580
+ }