vectorgov 0.1.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/README.md +222 -0
- package/dist/index.d.mts +217 -0
- package/dist/index.d.ts +217 -0
- package/dist/index.js +286 -0
- package/dist/index.mjs +256 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# VectorGov SDK for TypeScript/JavaScript
|
|
2
|
+
|
|
3
|
+
SDK oficial para a API VectorGov - Busca semântica em legislação brasileira.
|
|
4
|
+
|
|
5
|
+
## Instalação
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install vectorgov
|
|
9
|
+
# ou
|
|
10
|
+
yarn add vectorgov
|
|
11
|
+
# ou
|
|
12
|
+
pnpm add vectorgov
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Uso Rápido
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { VectorGov } from 'vectorgov';
|
|
19
|
+
|
|
20
|
+
const vg = new VectorGov({ apiKey: 'vg_sua_chave' });
|
|
21
|
+
|
|
22
|
+
// Busca simples
|
|
23
|
+
const results = await vg.search('O que é ETP?');
|
|
24
|
+
|
|
25
|
+
for (const hit of results.hits) {
|
|
26
|
+
console.log(`${hit.source}: ${hit.text.slice(0, 100)}...`);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Integração com LLMs
|
|
31
|
+
|
|
32
|
+
### OpenAI
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { VectorGov } from 'vectorgov';
|
|
36
|
+
import OpenAI from 'openai';
|
|
37
|
+
|
|
38
|
+
const vg = new VectorGov({ apiKey: 'vg_sua_chave' });
|
|
39
|
+
const openai = new OpenAI();
|
|
40
|
+
|
|
41
|
+
// Busca contexto relevante
|
|
42
|
+
const results = await vg.search('Quais os critérios de julgamento?');
|
|
43
|
+
|
|
44
|
+
// Gera resposta com GPT-4
|
|
45
|
+
const response = await openai.chat.completions.create({
|
|
46
|
+
model: 'gpt-4',
|
|
47
|
+
messages: results.toMessages('Quais os critérios de julgamento?')
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
console.log(response.choices[0].message.content);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Anthropic Claude
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { VectorGov } from 'vectorgov';
|
|
57
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
58
|
+
|
|
59
|
+
const vg = new VectorGov({ apiKey: 'vg_sua_chave' });
|
|
60
|
+
const anthropic = new Anthropic();
|
|
61
|
+
|
|
62
|
+
const results = await vg.search('Quando o ETP pode ser dispensado?');
|
|
63
|
+
const messages = results.toMessages('Quando o ETP pode ser dispensado?');
|
|
64
|
+
|
|
65
|
+
const response = await anthropic.messages.create({
|
|
66
|
+
model: 'claude-sonnet-4-20250514',
|
|
67
|
+
max_tokens: 1024,
|
|
68
|
+
system: messages[0].content,
|
|
69
|
+
messages: [{ role: 'user', content: messages[1].content }]
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
console.log(response.content[0].text);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Google Gemini
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { VectorGov } from 'vectorgov';
|
|
79
|
+
import { GoogleGenerativeAI } from '@google/generative-ai';
|
|
80
|
+
|
|
81
|
+
const vg = new VectorGov({ apiKey: 'vg_sua_chave' });
|
|
82
|
+
const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY!);
|
|
83
|
+
|
|
84
|
+
const results = await vg.search('O que é pesquisa de preços?');
|
|
85
|
+
const messages = results.toMessages('O que é pesquisa de preços?');
|
|
86
|
+
|
|
87
|
+
const model = genAI.getGenerativeModel({
|
|
88
|
+
model: 'gemini-2.0-flash',
|
|
89
|
+
systemInstruction: messages[0].content
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const response = await model.generateContent(messages[1].content);
|
|
93
|
+
console.log(response.response.text());
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Modos de Busca
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// Fast (~2s) - Busca direta, sem reranking
|
|
100
|
+
const fast = await vg.search('O que é ETP?', { mode: 'fast' });
|
|
101
|
+
|
|
102
|
+
// Balanced (~5s) - Com reranking (padrão)
|
|
103
|
+
const balanced = await vg.search('O que é ETP?', { mode: 'balanced' });
|
|
104
|
+
|
|
105
|
+
// Precise (~15s) - HyDE + reranking para máxima precisão
|
|
106
|
+
const precise = await vg.search('O que é ETP?', { mode: 'precise' });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Filtros
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// Filtrar por tipo de documento
|
|
113
|
+
const results = await vg.search('dispensa de licitação', {
|
|
114
|
+
tipoDocumento: 'lei',
|
|
115
|
+
ano: 2021
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Perguntas com Resposta
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Usa o LLM do VectorGov para gerar resposta
|
|
123
|
+
const response = await vg.ask('O que é ETP?');
|
|
124
|
+
|
|
125
|
+
console.log(response.answer);
|
|
126
|
+
console.log(`Confiança: ${(response.confidence * 100).toFixed(1)}%`);
|
|
127
|
+
|
|
128
|
+
for (const citation of response.citations) {
|
|
129
|
+
console.log(` - ${citation.short}`);
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Feedback
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const results = await vg.search('O que é ETP?');
|
|
137
|
+
|
|
138
|
+
// Enviar feedback positivo
|
|
139
|
+
await vg.feedback(results.metadata.queryId, true);
|
|
140
|
+
|
|
141
|
+
// Ou negativo
|
|
142
|
+
await vg.feedback(results.metadata.queryId, false);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Tratamento de Erros
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import { VectorGov, AuthenticationError, RateLimitError, VectorGovError } from 'vectorgov';
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const results = await vg.search('query');
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (error instanceof AuthenticationError) {
|
|
154
|
+
console.error('API key inválida');
|
|
155
|
+
} else if (error instanceof RateLimitError) {
|
|
156
|
+
console.error(`Rate limit. Retry após ${error.retryAfter}s`);
|
|
157
|
+
} else if (error instanceof VectorGovError) {
|
|
158
|
+
console.error(`Erro: ${error.message} (${error.statusCode})`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Configuração
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
const vg = new VectorGov({
|
|
167
|
+
apiKey: 'vg_sua_chave',
|
|
168
|
+
baseUrl: 'https://vectorgov.io/api/v1', // opcional
|
|
169
|
+
timeout: 30000 // opcional, em ms
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Variáveis de Ambiente
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
export VECTORGOV_API_KEY="vg_sua_chave"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
const vg = new VectorGov({
|
|
181
|
+
apiKey: process.env.VECTORGOV_API_KEY!
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## API Reference
|
|
186
|
+
|
|
187
|
+
### VectorGov
|
|
188
|
+
|
|
189
|
+
| Método | Descrição |
|
|
190
|
+
|--------|-----------|
|
|
191
|
+
| `search(query, options?)` | Busca semântica |
|
|
192
|
+
| `ask(query, options?)` | Pergunta com resposta IA |
|
|
193
|
+
| `feedback(queryId, like)` | Envia feedback |
|
|
194
|
+
|
|
195
|
+
### SearchOptions
|
|
196
|
+
|
|
197
|
+
| Campo | Tipo | Padrão | Descrição |
|
|
198
|
+
|-------|------|--------|-----------|
|
|
199
|
+
| `topK` | number | 5 | Número de resultados |
|
|
200
|
+
| `mode` | 'fast' \| 'balanced' \| 'precise' | 'balanced' | Modo de busca |
|
|
201
|
+
| `tipoDocumento` | string | - | Filtro por tipo |
|
|
202
|
+
| `ano` | number | - | Filtro por ano |
|
|
203
|
+
|
|
204
|
+
### SearchResult
|
|
205
|
+
|
|
206
|
+
| Campo | Tipo | Descrição |
|
|
207
|
+
|-------|------|-----------|
|
|
208
|
+
| `hits` | SearchHit[] | Resultados |
|
|
209
|
+
| `total` | number | Total encontrado |
|
|
210
|
+
| `metadata` | SearchMetadata | Metadados |
|
|
211
|
+
| `toMessages(query)` | ChatMessage[] | Formato para LLMs |
|
|
212
|
+
| `toContext()` | string | Contexto em texto |
|
|
213
|
+
|
|
214
|
+
## Links
|
|
215
|
+
|
|
216
|
+
- [Documentação](https://vectorgov.io/documentacao)
|
|
217
|
+
- [GitHub](https://github.com/euteajudo/vectorgov-sdk-ts)
|
|
218
|
+
- [npm](https://www.npmjs.com/package/vectorgov)
|
|
219
|
+
|
|
220
|
+
## Licença
|
|
221
|
+
|
|
222
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VectorGov SDK Types
|
|
3
|
+
*/
|
|
4
|
+
/** Modos de busca disponíveis */
|
|
5
|
+
type SearchMode = 'fast' | 'balanced' | 'precise';
|
|
6
|
+
/** Configuração do cliente VectorGov */
|
|
7
|
+
interface VectorGovConfig {
|
|
8
|
+
/** API key para autenticação */
|
|
9
|
+
apiKey: string;
|
|
10
|
+
/** URL base da API (padrão: https://vectorgov.io/api/v1) */
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
/** Timeout em milissegundos (padrão: 30000) */
|
|
13
|
+
timeout?: number;
|
|
14
|
+
}
|
|
15
|
+
/** Opções de busca */
|
|
16
|
+
interface SearchOptions {
|
|
17
|
+
/** Número de resultados (padrão: 5) */
|
|
18
|
+
topK?: number;
|
|
19
|
+
/** Modo de busca (padrão: 'balanced') */
|
|
20
|
+
mode?: SearchMode;
|
|
21
|
+
/** Tipo de documento para filtrar */
|
|
22
|
+
tipoDocumento?: string;
|
|
23
|
+
/** Ano do documento para filtrar */
|
|
24
|
+
ano?: number;
|
|
25
|
+
}
|
|
26
|
+
/** Hit individual de busca */
|
|
27
|
+
interface SearchHit {
|
|
28
|
+
/** Texto do trecho encontrado */
|
|
29
|
+
text: string;
|
|
30
|
+
/** Número do artigo */
|
|
31
|
+
articleNumber?: string;
|
|
32
|
+
/** ID do documento */
|
|
33
|
+
documentId?: string;
|
|
34
|
+
/** Tipo do documento (LEI, DECRETO, IN, etc) */
|
|
35
|
+
documentType?: string;
|
|
36
|
+
/** Número do documento */
|
|
37
|
+
documentNumber?: string;
|
|
38
|
+
/** Ano do documento */
|
|
39
|
+
year?: number;
|
|
40
|
+
/** Score de relevância */
|
|
41
|
+
score: number;
|
|
42
|
+
/** Score final após reranking */
|
|
43
|
+
finalScore?: number;
|
|
44
|
+
/** Fonte formatada */
|
|
45
|
+
source?: string;
|
|
46
|
+
}
|
|
47
|
+
/** Metadados da busca */
|
|
48
|
+
interface SearchMetadata {
|
|
49
|
+
/** Tempo total em milissegundos */
|
|
50
|
+
latencyMs: number;
|
|
51
|
+
/** Se a resposta veio do cache */
|
|
52
|
+
cached: boolean;
|
|
53
|
+
/** ID único da query (para feedback) */
|
|
54
|
+
queryId: string;
|
|
55
|
+
}
|
|
56
|
+
/** Resultado da busca */
|
|
57
|
+
interface SearchResult {
|
|
58
|
+
/** Lista de resultados */
|
|
59
|
+
hits: SearchHit[];
|
|
60
|
+
/** Total de resultados encontrados */
|
|
61
|
+
total: number;
|
|
62
|
+
/** Metadados da busca */
|
|
63
|
+
metadata: SearchMetadata;
|
|
64
|
+
/** Converte para formato de mensagens (OpenAI, Claude, etc) */
|
|
65
|
+
toMessages(query: string): ChatMessage[];
|
|
66
|
+
/** Converte para contexto de texto */
|
|
67
|
+
toContext(): string;
|
|
68
|
+
}
|
|
69
|
+
/** Mensagem de chat (formato OpenAI) */
|
|
70
|
+
interface ChatMessage {
|
|
71
|
+
role: 'system' | 'user' | 'assistant';
|
|
72
|
+
content: string;
|
|
73
|
+
}
|
|
74
|
+
/** Citação em uma resposta */
|
|
75
|
+
interface Citation {
|
|
76
|
+
/** Texto completo da citação */
|
|
77
|
+
text: string;
|
|
78
|
+
/** Versão curta (ex: "Art. 5, I") */
|
|
79
|
+
short: string;
|
|
80
|
+
/** Tipo do documento */
|
|
81
|
+
documentType?: string;
|
|
82
|
+
/** Número do documento */
|
|
83
|
+
documentNumber?: string;
|
|
84
|
+
/** Ano */
|
|
85
|
+
year?: number;
|
|
86
|
+
/** Número do artigo */
|
|
87
|
+
article?: string;
|
|
88
|
+
}
|
|
89
|
+
/** Metadados da resposta de pergunta */
|
|
90
|
+
interface AskMetadata {
|
|
91
|
+
/** Modelo usado */
|
|
92
|
+
model: string;
|
|
93
|
+
/** Tempo total em ms */
|
|
94
|
+
latencyMs: number;
|
|
95
|
+
/** Tempo de busca em ms */
|
|
96
|
+
retrievalMs?: number;
|
|
97
|
+
/** Tempo de geração em ms */
|
|
98
|
+
generationMs?: number;
|
|
99
|
+
/** Chunks usados */
|
|
100
|
+
chunksUsed: number;
|
|
101
|
+
/** Tokens usados */
|
|
102
|
+
tokens?: number;
|
|
103
|
+
/** Hash da query (para feedback) */
|
|
104
|
+
queryHash?: string;
|
|
105
|
+
}
|
|
106
|
+
/** Resposta de uma pergunta */
|
|
107
|
+
interface AskResponse {
|
|
108
|
+
/** Resposta gerada */
|
|
109
|
+
answer: string;
|
|
110
|
+
/** Citações */
|
|
111
|
+
citations: Citation[];
|
|
112
|
+
/** Confiança (0-1) */
|
|
113
|
+
confidence: number;
|
|
114
|
+
/** Metadados */
|
|
115
|
+
metadata: AskMetadata;
|
|
116
|
+
}
|
|
117
|
+
/** Opções para perguntar */
|
|
118
|
+
interface AskOptions extends SearchOptions {
|
|
119
|
+
/** Usar cache semântico */
|
|
120
|
+
useCache?: boolean;
|
|
121
|
+
}
|
|
122
|
+
/** Resposta de feedback */
|
|
123
|
+
interface FeedbackResponse {
|
|
124
|
+
success: boolean;
|
|
125
|
+
message: string;
|
|
126
|
+
newLikes?: number;
|
|
127
|
+
newDislikes?: number;
|
|
128
|
+
}
|
|
129
|
+
/** Erros da API */
|
|
130
|
+
declare class VectorGovError extends Error {
|
|
131
|
+
statusCode?: number | undefined;
|
|
132
|
+
code?: string | undefined;
|
|
133
|
+
constructor(message: string, statusCode?: number | undefined, code?: string | undefined);
|
|
134
|
+
}
|
|
135
|
+
declare class AuthenticationError extends VectorGovError {
|
|
136
|
+
constructor(message?: string);
|
|
137
|
+
}
|
|
138
|
+
declare class RateLimitError extends VectorGovError {
|
|
139
|
+
retryAfter?: number | undefined;
|
|
140
|
+
constructor(message?: string, retryAfter?: number | undefined);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* VectorGov SDK Client
|
|
145
|
+
*/
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Cliente para a API VectorGov
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* import { VectorGov } from 'vectorgov';
|
|
153
|
+
*
|
|
154
|
+
* const vg = new VectorGov({ apiKey: 'vg_sua_chave' });
|
|
155
|
+
*
|
|
156
|
+
* // Busca
|
|
157
|
+
* const results = await vg.search('O que é ETP?');
|
|
158
|
+
*
|
|
159
|
+
* // Para usar com OpenAI
|
|
160
|
+
* const messages = results.toMessages('O que é ETP?');
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
declare class VectorGov {
|
|
164
|
+
private readonly apiKey;
|
|
165
|
+
private readonly baseUrl;
|
|
166
|
+
private readonly timeout;
|
|
167
|
+
constructor(config: VectorGovConfig);
|
|
168
|
+
/**
|
|
169
|
+
* Faz uma requisição para a API
|
|
170
|
+
*/
|
|
171
|
+
private request;
|
|
172
|
+
/**
|
|
173
|
+
* Busca semântica em documentos legais
|
|
174
|
+
*
|
|
175
|
+
* @param query - Pergunta ou texto para buscar
|
|
176
|
+
* @param options - Opções de busca
|
|
177
|
+
* @returns Resultado da busca com métodos auxiliares
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```typescript
|
|
181
|
+
* const results = await vg.search('O que é ETP?', {
|
|
182
|
+
* topK: 5,
|
|
183
|
+
* mode: 'balanced'
|
|
184
|
+
* });
|
|
185
|
+
*
|
|
186
|
+
* for (const hit of results.hits) {
|
|
187
|
+
* console.log(`${hit.source}: ${hit.text.slice(0, 100)}...`);
|
|
188
|
+
* }
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
search(query: string, options?: SearchOptions): Promise<SearchResult>;
|
|
192
|
+
/**
|
|
193
|
+
* Faz uma pergunta e recebe uma resposta gerada por IA
|
|
194
|
+
*
|
|
195
|
+
* @param query - Pergunta
|
|
196
|
+
* @param options - Opções
|
|
197
|
+
* @returns Resposta com citações
|
|
198
|
+
*/
|
|
199
|
+
ask(query: string, options?: AskOptions): Promise<AskResponse>;
|
|
200
|
+
/**
|
|
201
|
+
* Envia feedback (like/dislike) para uma resposta
|
|
202
|
+
*
|
|
203
|
+
* @param queryId - ID da query (de SearchMetadata ou AskMetadata)
|
|
204
|
+
* @param like - true para like, false para dislike
|
|
205
|
+
*/
|
|
206
|
+
feedback(queryId: string, like: boolean): Promise<FeedbackResponse>;
|
|
207
|
+
/**
|
|
208
|
+
* Converte hits para formato de mensagens de chat
|
|
209
|
+
*/
|
|
210
|
+
private hitsToMessages;
|
|
211
|
+
/**
|
|
212
|
+
* Converte hits para texto de contexto
|
|
213
|
+
*/
|
|
214
|
+
private hitsToContext;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export { type AskMetadata, type AskOptions, type AskResponse, AuthenticationError, type ChatMessage, type Citation, type FeedbackResponse, RateLimitError, type SearchHit, type SearchMetadata, type SearchMode, type SearchOptions, type SearchResult, VectorGov, type VectorGovConfig, VectorGovError };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VectorGov SDK Types
|
|
3
|
+
*/
|
|
4
|
+
/** Modos de busca disponíveis */
|
|
5
|
+
type SearchMode = 'fast' | 'balanced' | 'precise';
|
|
6
|
+
/** Configuração do cliente VectorGov */
|
|
7
|
+
interface VectorGovConfig {
|
|
8
|
+
/** API key para autenticação */
|
|
9
|
+
apiKey: string;
|
|
10
|
+
/** URL base da API (padrão: https://vectorgov.io/api/v1) */
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
/** Timeout em milissegundos (padrão: 30000) */
|
|
13
|
+
timeout?: number;
|
|
14
|
+
}
|
|
15
|
+
/** Opções de busca */
|
|
16
|
+
interface SearchOptions {
|
|
17
|
+
/** Número de resultados (padrão: 5) */
|
|
18
|
+
topK?: number;
|
|
19
|
+
/** Modo de busca (padrão: 'balanced') */
|
|
20
|
+
mode?: SearchMode;
|
|
21
|
+
/** Tipo de documento para filtrar */
|
|
22
|
+
tipoDocumento?: string;
|
|
23
|
+
/** Ano do documento para filtrar */
|
|
24
|
+
ano?: number;
|
|
25
|
+
}
|
|
26
|
+
/** Hit individual de busca */
|
|
27
|
+
interface SearchHit {
|
|
28
|
+
/** Texto do trecho encontrado */
|
|
29
|
+
text: string;
|
|
30
|
+
/** Número do artigo */
|
|
31
|
+
articleNumber?: string;
|
|
32
|
+
/** ID do documento */
|
|
33
|
+
documentId?: string;
|
|
34
|
+
/** Tipo do documento (LEI, DECRETO, IN, etc) */
|
|
35
|
+
documentType?: string;
|
|
36
|
+
/** Número do documento */
|
|
37
|
+
documentNumber?: string;
|
|
38
|
+
/** Ano do documento */
|
|
39
|
+
year?: number;
|
|
40
|
+
/** Score de relevância */
|
|
41
|
+
score: number;
|
|
42
|
+
/** Score final após reranking */
|
|
43
|
+
finalScore?: number;
|
|
44
|
+
/** Fonte formatada */
|
|
45
|
+
source?: string;
|
|
46
|
+
}
|
|
47
|
+
/** Metadados da busca */
|
|
48
|
+
interface SearchMetadata {
|
|
49
|
+
/** Tempo total em milissegundos */
|
|
50
|
+
latencyMs: number;
|
|
51
|
+
/** Se a resposta veio do cache */
|
|
52
|
+
cached: boolean;
|
|
53
|
+
/** ID único da query (para feedback) */
|
|
54
|
+
queryId: string;
|
|
55
|
+
}
|
|
56
|
+
/** Resultado da busca */
|
|
57
|
+
interface SearchResult {
|
|
58
|
+
/** Lista de resultados */
|
|
59
|
+
hits: SearchHit[];
|
|
60
|
+
/** Total de resultados encontrados */
|
|
61
|
+
total: number;
|
|
62
|
+
/** Metadados da busca */
|
|
63
|
+
metadata: SearchMetadata;
|
|
64
|
+
/** Converte para formato de mensagens (OpenAI, Claude, etc) */
|
|
65
|
+
toMessages(query: string): ChatMessage[];
|
|
66
|
+
/** Converte para contexto de texto */
|
|
67
|
+
toContext(): string;
|
|
68
|
+
}
|
|
69
|
+
/** Mensagem de chat (formato OpenAI) */
|
|
70
|
+
interface ChatMessage {
|
|
71
|
+
role: 'system' | 'user' | 'assistant';
|
|
72
|
+
content: string;
|
|
73
|
+
}
|
|
74
|
+
/** Citação em uma resposta */
|
|
75
|
+
interface Citation {
|
|
76
|
+
/** Texto completo da citação */
|
|
77
|
+
text: string;
|
|
78
|
+
/** Versão curta (ex: "Art. 5, I") */
|
|
79
|
+
short: string;
|
|
80
|
+
/** Tipo do documento */
|
|
81
|
+
documentType?: string;
|
|
82
|
+
/** Número do documento */
|
|
83
|
+
documentNumber?: string;
|
|
84
|
+
/** Ano */
|
|
85
|
+
year?: number;
|
|
86
|
+
/** Número do artigo */
|
|
87
|
+
article?: string;
|
|
88
|
+
}
|
|
89
|
+
/** Metadados da resposta de pergunta */
|
|
90
|
+
interface AskMetadata {
|
|
91
|
+
/** Modelo usado */
|
|
92
|
+
model: string;
|
|
93
|
+
/** Tempo total em ms */
|
|
94
|
+
latencyMs: number;
|
|
95
|
+
/** Tempo de busca em ms */
|
|
96
|
+
retrievalMs?: number;
|
|
97
|
+
/** Tempo de geração em ms */
|
|
98
|
+
generationMs?: number;
|
|
99
|
+
/** Chunks usados */
|
|
100
|
+
chunksUsed: number;
|
|
101
|
+
/** Tokens usados */
|
|
102
|
+
tokens?: number;
|
|
103
|
+
/** Hash da query (para feedback) */
|
|
104
|
+
queryHash?: string;
|
|
105
|
+
}
|
|
106
|
+
/** Resposta de uma pergunta */
|
|
107
|
+
interface AskResponse {
|
|
108
|
+
/** Resposta gerada */
|
|
109
|
+
answer: string;
|
|
110
|
+
/** Citações */
|
|
111
|
+
citations: Citation[];
|
|
112
|
+
/** Confiança (0-1) */
|
|
113
|
+
confidence: number;
|
|
114
|
+
/** Metadados */
|
|
115
|
+
metadata: AskMetadata;
|
|
116
|
+
}
|
|
117
|
+
/** Opções para perguntar */
|
|
118
|
+
interface AskOptions extends SearchOptions {
|
|
119
|
+
/** Usar cache semântico */
|
|
120
|
+
useCache?: boolean;
|
|
121
|
+
}
|
|
122
|
+
/** Resposta de feedback */
|
|
123
|
+
interface FeedbackResponse {
|
|
124
|
+
success: boolean;
|
|
125
|
+
message: string;
|
|
126
|
+
newLikes?: number;
|
|
127
|
+
newDislikes?: number;
|
|
128
|
+
}
|
|
129
|
+
/** Erros da API */
|
|
130
|
+
declare class VectorGovError extends Error {
|
|
131
|
+
statusCode?: number | undefined;
|
|
132
|
+
code?: string | undefined;
|
|
133
|
+
constructor(message: string, statusCode?: number | undefined, code?: string | undefined);
|
|
134
|
+
}
|
|
135
|
+
declare class AuthenticationError extends VectorGovError {
|
|
136
|
+
constructor(message?: string);
|
|
137
|
+
}
|
|
138
|
+
declare class RateLimitError extends VectorGovError {
|
|
139
|
+
retryAfter?: number | undefined;
|
|
140
|
+
constructor(message?: string, retryAfter?: number | undefined);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* VectorGov SDK Client
|
|
145
|
+
*/
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Cliente para a API VectorGov
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* import { VectorGov } from 'vectorgov';
|
|
153
|
+
*
|
|
154
|
+
* const vg = new VectorGov({ apiKey: 'vg_sua_chave' });
|
|
155
|
+
*
|
|
156
|
+
* // Busca
|
|
157
|
+
* const results = await vg.search('O que é ETP?');
|
|
158
|
+
*
|
|
159
|
+
* // Para usar com OpenAI
|
|
160
|
+
* const messages = results.toMessages('O que é ETP?');
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
declare class VectorGov {
|
|
164
|
+
private readonly apiKey;
|
|
165
|
+
private readonly baseUrl;
|
|
166
|
+
private readonly timeout;
|
|
167
|
+
constructor(config: VectorGovConfig);
|
|
168
|
+
/**
|
|
169
|
+
* Faz uma requisição para a API
|
|
170
|
+
*/
|
|
171
|
+
private request;
|
|
172
|
+
/**
|
|
173
|
+
* Busca semântica em documentos legais
|
|
174
|
+
*
|
|
175
|
+
* @param query - Pergunta ou texto para buscar
|
|
176
|
+
* @param options - Opções de busca
|
|
177
|
+
* @returns Resultado da busca com métodos auxiliares
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```typescript
|
|
181
|
+
* const results = await vg.search('O que é ETP?', {
|
|
182
|
+
* topK: 5,
|
|
183
|
+
* mode: 'balanced'
|
|
184
|
+
* });
|
|
185
|
+
*
|
|
186
|
+
* for (const hit of results.hits) {
|
|
187
|
+
* console.log(`${hit.source}: ${hit.text.slice(0, 100)}...`);
|
|
188
|
+
* }
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
search(query: string, options?: SearchOptions): Promise<SearchResult>;
|
|
192
|
+
/**
|
|
193
|
+
* Faz uma pergunta e recebe uma resposta gerada por IA
|
|
194
|
+
*
|
|
195
|
+
* @param query - Pergunta
|
|
196
|
+
* @param options - Opções
|
|
197
|
+
* @returns Resposta com citações
|
|
198
|
+
*/
|
|
199
|
+
ask(query: string, options?: AskOptions): Promise<AskResponse>;
|
|
200
|
+
/**
|
|
201
|
+
* Envia feedback (like/dislike) para uma resposta
|
|
202
|
+
*
|
|
203
|
+
* @param queryId - ID da query (de SearchMetadata ou AskMetadata)
|
|
204
|
+
* @param like - true para like, false para dislike
|
|
205
|
+
*/
|
|
206
|
+
feedback(queryId: string, like: boolean): Promise<FeedbackResponse>;
|
|
207
|
+
/**
|
|
208
|
+
* Converte hits para formato de mensagens de chat
|
|
209
|
+
*/
|
|
210
|
+
private hitsToMessages;
|
|
211
|
+
/**
|
|
212
|
+
* Converte hits para texto de contexto
|
|
213
|
+
*/
|
|
214
|
+
private hitsToContext;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export { type AskMetadata, type AskOptions, type AskResponse, AuthenticationError, type ChatMessage, type Citation, type FeedbackResponse, RateLimitError, type SearchHit, type SearchMetadata, type SearchMode, type SearchOptions, type SearchResult, VectorGov, type VectorGovConfig, VectorGovError };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AuthenticationError: () => AuthenticationError,
|
|
24
|
+
RateLimitError: () => RateLimitError,
|
|
25
|
+
VectorGov: () => VectorGov,
|
|
26
|
+
VectorGovError: () => VectorGovError
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
|
|
30
|
+
// src/types.ts
|
|
31
|
+
var VectorGovError = class extends Error {
|
|
32
|
+
constructor(message, statusCode, code) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.statusCode = statusCode;
|
|
35
|
+
this.code = code;
|
|
36
|
+
this.name = "VectorGovError";
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var AuthenticationError = class extends VectorGovError {
|
|
40
|
+
constructor(message = "Invalid or missing API key") {
|
|
41
|
+
super(message, 401, "AUTHENTICATION_ERROR");
|
|
42
|
+
this.name = "AuthenticationError";
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var RateLimitError = class extends VectorGovError {
|
|
46
|
+
constructor(message = "Rate limit exceeded", retryAfter) {
|
|
47
|
+
super(message, 429, "RATE_LIMIT_ERROR");
|
|
48
|
+
this.retryAfter = retryAfter;
|
|
49
|
+
this.name = "RateLimitError";
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// src/client.ts
|
|
54
|
+
var DEFAULT_BASE_URL = "https://vectorgov.io/api/v1";
|
|
55
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
56
|
+
var SYSTEM_PROMPT = `Voc\xEA \xE9 um assistente jur\xEDdico especializado em legisla\xE7\xE3o brasileira de licita\xE7\xF5es e contratos administrativos.
|
|
57
|
+
|
|
58
|
+
Use APENAS as informa\xE7\xF5es fornecidas no contexto para responder. Se a informa\xE7\xE3o n\xE3o estiver no contexto, diga que n\xE3o encontrou.
|
|
59
|
+
|
|
60
|
+
Ao citar artigos, use o formato: [Art. X] ou [Art. X, \xA7Y] ou [Art. X, inciso Y].
|
|
61
|
+
|
|
62
|
+
Responda de forma clara, objetiva e em portugu\xEAs brasileiro.`;
|
|
63
|
+
var VectorGov = class {
|
|
64
|
+
constructor(config) {
|
|
65
|
+
if (!config.apiKey) {
|
|
66
|
+
throw new AuthenticationError("API key is required");
|
|
67
|
+
}
|
|
68
|
+
this.apiKey = config.apiKey;
|
|
69
|
+
this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
|
|
70
|
+
this.timeout = config.timeout || DEFAULT_TIMEOUT;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Faz uma requisição para a API
|
|
74
|
+
*/
|
|
75
|
+
async request(endpoint, options = {}) {
|
|
76
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
77
|
+
const controller = new AbortController();
|
|
78
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
79
|
+
try {
|
|
80
|
+
const response = await fetch(url, {
|
|
81
|
+
...options,
|
|
82
|
+
headers: {
|
|
83
|
+
"Content-Type": "application/json",
|
|
84
|
+
"X-API-Key": this.apiKey,
|
|
85
|
+
...options.headers
|
|
86
|
+
},
|
|
87
|
+
signal: controller.signal
|
|
88
|
+
});
|
|
89
|
+
clearTimeout(timeoutId);
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
const errorData = await response.json().catch(() => ({}));
|
|
92
|
+
if (response.status === 401) {
|
|
93
|
+
throw new AuthenticationError(errorData.detail || "Invalid API key");
|
|
94
|
+
}
|
|
95
|
+
if (response.status === 429) {
|
|
96
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
97
|
+
throw new RateLimitError(
|
|
98
|
+
errorData.detail || "Rate limit exceeded",
|
|
99
|
+
retryAfter ? parseInt(retryAfter, 10) : void 0
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
throw new VectorGovError(
|
|
103
|
+
errorData.detail || `Request failed with status ${response.status}`,
|
|
104
|
+
response.status
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return response.json();
|
|
108
|
+
} catch (error) {
|
|
109
|
+
clearTimeout(timeoutId);
|
|
110
|
+
if (error instanceof VectorGovError) {
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
114
|
+
throw new VectorGovError("Request timeout", 408, "TIMEOUT");
|
|
115
|
+
}
|
|
116
|
+
throw new VectorGovError(
|
|
117
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Busca semântica em documentos legais
|
|
123
|
+
*
|
|
124
|
+
* @param query - Pergunta ou texto para buscar
|
|
125
|
+
* @param options - Opções de busca
|
|
126
|
+
* @returns Resultado da busca com métodos auxiliares
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```typescript
|
|
130
|
+
* const results = await vg.search('O que é ETP?', {
|
|
131
|
+
* topK: 5,
|
|
132
|
+
* mode: 'balanced'
|
|
133
|
+
* });
|
|
134
|
+
*
|
|
135
|
+
* for (const hit of results.hits) {
|
|
136
|
+
* console.log(`${hit.source}: ${hit.text.slice(0, 100)}...`);
|
|
137
|
+
* }
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
async search(query, options = {}) {
|
|
141
|
+
const {
|
|
142
|
+
topK = 5,
|
|
143
|
+
mode = "balanced",
|
|
144
|
+
tipoDocumento,
|
|
145
|
+
ano
|
|
146
|
+
} = options;
|
|
147
|
+
const response = await this.request("/sdk/search", {
|
|
148
|
+
method: "POST",
|
|
149
|
+
body: JSON.stringify({
|
|
150
|
+
query,
|
|
151
|
+
top_k: topK,
|
|
152
|
+
mode,
|
|
153
|
+
tipo_documento: tipoDocumento,
|
|
154
|
+
ano
|
|
155
|
+
})
|
|
156
|
+
});
|
|
157
|
+
const hits = response.hits.map((hit) => ({
|
|
158
|
+
text: hit.text,
|
|
159
|
+
articleNumber: hit.article_number,
|
|
160
|
+
documentId: hit.document_id,
|
|
161
|
+
documentType: hit.document_type,
|
|
162
|
+
documentNumber: hit.document_number,
|
|
163
|
+
year: hit.year,
|
|
164
|
+
score: hit.score,
|
|
165
|
+
finalScore: hit.final_score,
|
|
166
|
+
source: hit.document_type && hit.document_number && hit.year ? `${hit.document_type} ${hit.document_number}/${hit.year}, Art. ${hit.article_number || "N/A"}` : void 0
|
|
167
|
+
}));
|
|
168
|
+
const metadata = {
|
|
169
|
+
latencyMs: response.latency_ms,
|
|
170
|
+
cached: response.cached,
|
|
171
|
+
queryId: response.query_id
|
|
172
|
+
};
|
|
173
|
+
return {
|
|
174
|
+
hits,
|
|
175
|
+
total: response.total,
|
|
176
|
+
metadata,
|
|
177
|
+
toMessages: (q) => this.hitsToMessages(hits, q),
|
|
178
|
+
toContext: () => this.hitsToContext(hits)
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Faz uma pergunta e recebe uma resposta gerada por IA
|
|
183
|
+
*
|
|
184
|
+
* @param query - Pergunta
|
|
185
|
+
* @param options - Opções
|
|
186
|
+
* @returns Resposta com citações
|
|
187
|
+
*/
|
|
188
|
+
async ask(query, options = {}) {
|
|
189
|
+
const {
|
|
190
|
+
topK = 5,
|
|
191
|
+
mode = "balanced",
|
|
192
|
+
useCache = true,
|
|
193
|
+
tipoDocumento,
|
|
194
|
+
ano
|
|
195
|
+
} = options;
|
|
196
|
+
const response = await this.request("/sdk/ask", {
|
|
197
|
+
method: "POST",
|
|
198
|
+
body: JSON.stringify({
|
|
199
|
+
query,
|
|
200
|
+
top_k: topK,
|
|
201
|
+
mode,
|
|
202
|
+
use_cache: useCache,
|
|
203
|
+
tipo_documento: tipoDocumento,
|
|
204
|
+
ano
|
|
205
|
+
})
|
|
206
|
+
});
|
|
207
|
+
return {
|
|
208
|
+
answer: response.data.answer,
|
|
209
|
+
citations: response.data.citations.map((c) => ({
|
|
210
|
+
text: c.text,
|
|
211
|
+
short: c.short,
|
|
212
|
+
documentType: c.document_type,
|
|
213
|
+
documentNumber: c.document_number,
|
|
214
|
+
year: c.year,
|
|
215
|
+
article: c.article
|
|
216
|
+
})),
|
|
217
|
+
confidence: response.data.confidence,
|
|
218
|
+
metadata: {
|
|
219
|
+
model: response.metadata.model,
|
|
220
|
+
latencyMs: response.metadata.latency_ms,
|
|
221
|
+
retrievalMs: response.metadata.retrieval_ms,
|
|
222
|
+
generationMs: response.metadata.generation_ms,
|
|
223
|
+
chunksUsed: response.metadata.chunks_used,
|
|
224
|
+
tokens: response.metadata.tokens,
|
|
225
|
+
queryHash: response.metadata.query_hash
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Envia feedback (like/dislike) para uma resposta
|
|
231
|
+
*
|
|
232
|
+
* @param queryId - ID da query (de SearchMetadata ou AskMetadata)
|
|
233
|
+
* @param like - true para like, false para dislike
|
|
234
|
+
*/
|
|
235
|
+
async feedback(queryId, like) {
|
|
236
|
+
const response = await this.request("/cache/feedback", {
|
|
237
|
+
method: "POST",
|
|
238
|
+
body: JSON.stringify({
|
|
239
|
+
query_hash: queryId,
|
|
240
|
+
is_like: like
|
|
241
|
+
})
|
|
242
|
+
});
|
|
243
|
+
return {
|
|
244
|
+
success: response.success,
|
|
245
|
+
message: response.message,
|
|
246
|
+
newLikes: response.new_likes,
|
|
247
|
+
newDislikes: response.new_dislikes
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Converte hits para formato de mensagens de chat
|
|
252
|
+
*/
|
|
253
|
+
hitsToMessages(hits, query) {
|
|
254
|
+
const context = this.hitsToContext(hits);
|
|
255
|
+
return [
|
|
256
|
+
{
|
|
257
|
+
role: "system",
|
|
258
|
+
content: SYSTEM_PROMPT
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
role: "user",
|
|
262
|
+
content: `Contexto:
|
|
263
|
+
${context}
|
|
264
|
+
|
|
265
|
+
Pergunta: ${query}`
|
|
266
|
+
}
|
|
267
|
+
];
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Converte hits para texto de contexto
|
|
271
|
+
*/
|
|
272
|
+
hitsToContext(hits) {
|
|
273
|
+
return hits.map((hit, i) => {
|
|
274
|
+
const source = hit.source || `Resultado ${i + 1}`;
|
|
275
|
+
return `[${source}]
|
|
276
|
+
${hit.text}`;
|
|
277
|
+
}).join("\n\n---\n\n");
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
281
|
+
0 && (module.exports = {
|
|
282
|
+
AuthenticationError,
|
|
283
|
+
RateLimitError,
|
|
284
|
+
VectorGov,
|
|
285
|
+
VectorGovError
|
|
286
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var VectorGovError = class extends Error {
|
|
3
|
+
constructor(message, statusCode, code) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.statusCode = statusCode;
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.name = "VectorGovError";
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var AuthenticationError = class extends VectorGovError {
|
|
11
|
+
constructor(message = "Invalid or missing API key") {
|
|
12
|
+
super(message, 401, "AUTHENTICATION_ERROR");
|
|
13
|
+
this.name = "AuthenticationError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var RateLimitError = class extends VectorGovError {
|
|
17
|
+
constructor(message = "Rate limit exceeded", retryAfter) {
|
|
18
|
+
super(message, 429, "RATE_LIMIT_ERROR");
|
|
19
|
+
this.retryAfter = retryAfter;
|
|
20
|
+
this.name = "RateLimitError";
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// src/client.ts
|
|
25
|
+
var DEFAULT_BASE_URL = "https://vectorgov.io/api/v1";
|
|
26
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
27
|
+
var SYSTEM_PROMPT = `Voc\xEA \xE9 um assistente jur\xEDdico especializado em legisla\xE7\xE3o brasileira de licita\xE7\xF5es e contratos administrativos.
|
|
28
|
+
|
|
29
|
+
Use APENAS as informa\xE7\xF5es fornecidas no contexto para responder. Se a informa\xE7\xE3o n\xE3o estiver no contexto, diga que n\xE3o encontrou.
|
|
30
|
+
|
|
31
|
+
Ao citar artigos, use o formato: [Art. X] ou [Art. X, \xA7Y] ou [Art. X, inciso Y].
|
|
32
|
+
|
|
33
|
+
Responda de forma clara, objetiva e em portugu\xEAs brasileiro.`;
|
|
34
|
+
var VectorGov = class {
|
|
35
|
+
constructor(config) {
|
|
36
|
+
if (!config.apiKey) {
|
|
37
|
+
throw new AuthenticationError("API key is required");
|
|
38
|
+
}
|
|
39
|
+
this.apiKey = config.apiKey;
|
|
40
|
+
this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
|
|
41
|
+
this.timeout = config.timeout || DEFAULT_TIMEOUT;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Faz uma requisição para a API
|
|
45
|
+
*/
|
|
46
|
+
async request(endpoint, options = {}) {
|
|
47
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
48
|
+
const controller = new AbortController();
|
|
49
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
50
|
+
try {
|
|
51
|
+
const response = await fetch(url, {
|
|
52
|
+
...options,
|
|
53
|
+
headers: {
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
"X-API-Key": this.apiKey,
|
|
56
|
+
...options.headers
|
|
57
|
+
},
|
|
58
|
+
signal: controller.signal
|
|
59
|
+
});
|
|
60
|
+
clearTimeout(timeoutId);
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
const errorData = await response.json().catch(() => ({}));
|
|
63
|
+
if (response.status === 401) {
|
|
64
|
+
throw new AuthenticationError(errorData.detail || "Invalid API key");
|
|
65
|
+
}
|
|
66
|
+
if (response.status === 429) {
|
|
67
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
68
|
+
throw new RateLimitError(
|
|
69
|
+
errorData.detail || "Rate limit exceeded",
|
|
70
|
+
retryAfter ? parseInt(retryAfter, 10) : void 0
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
throw new VectorGovError(
|
|
74
|
+
errorData.detail || `Request failed with status ${response.status}`,
|
|
75
|
+
response.status
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return response.json();
|
|
79
|
+
} catch (error) {
|
|
80
|
+
clearTimeout(timeoutId);
|
|
81
|
+
if (error instanceof VectorGovError) {
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
85
|
+
throw new VectorGovError("Request timeout", 408, "TIMEOUT");
|
|
86
|
+
}
|
|
87
|
+
throw new VectorGovError(
|
|
88
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Busca semântica em documentos legais
|
|
94
|
+
*
|
|
95
|
+
* @param query - Pergunta ou texto para buscar
|
|
96
|
+
* @param options - Opções de busca
|
|
97
|
+
* @returns Resultado da busca com métodos auxiliares
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* const results = await vg.search('O que é ETP?', {
|
|
102
|
+
* topK: 5,
|
|
103
|
+
* mode: 'balanced'
|
|
104
|
+
* });
|
|
105
|
+
*
|
|
106
|
+
* for (const hit of results.hits) {
|
|
107
|
+
* console.log(`${hit.source}: ${hit.text.slice(0, 100)}...`);
|
|
108
|
+
* }
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
async search(query, options = {}) {
|
|
112
|
+
const {
|
|
113
|
+
topK = 5,
|
|
114
|
+
mode = "balanced",
|
|
115
|
+
tipoDocumento,
|
|
116
|
+
ano
|
|
117
|
+
} = options;
|
|
118
|
+
const response = await this.request("/sdk/search", {
|
|
119
|
+
method: "POST",
|
|
120
|
+
body: JSON.stringify({
|
|
121
|
+
query,
|
|
122
|
+
top_k: topK,
|
|
123
|
+
mode,
|
|
124
|
+
tipo_documento: tipoDocumento,
|
|
125
|
+
ano
|
|
126
|
+
})
|
|
127
|
+
});
|
|
128
|
+
const hits = response.hits.map((hit) => ({
|
|
129
|
+
text: hit.text,
|
|
130
|
+
articleNumber: hit.article_number,
|
|
131
|
+
documentId: hit.document_id,
|
|
132
|
+
documentType: hit.document_type,
|
|
133
|
+
documentNumber: hit.document_number,
|
|
134
|
+
year: hit.year,
|
|
135
|
+
score: hit.score,
|
|
136
|
+
finalScore: hit.final_score,
|
|
137
|
+
source: hit.document_type && hit.document_number && hit.year ? `${hit.document_type} ${hit.document_number}/${hit.year}, Art. ${hit.article_number || "N/A"}` : void 0
|
|
138
|
+
}));
|
|
139
|
+
const metadata = {
|
|
140
|
+
latencyMs: response.latency_ms,
|
|
141
|
+
cached: response.cached,
|
|
142
|
+
queryId: response.query_id
|
|
143
|
+
};
|
|
144
|
+
return {
|
|
145
|
+
hits,
|
|
146
|
+
total: response.total,
|
|
147
|
+
metadata,
|
|
148
|
+
toMessages: (q) => this.hitsToMessages(hits, q),
|
|
149
|
+
toContext: () => this.hitsToContext(hits)
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Faz uma pergunta e recebe uma resposta gerada por IA
|
|
154
|
+
*
|
|
155
|
+
* @param query - Pergunta
|
|
156
|
+
* @param options - Opções
|
|
157
|
+
* @returns Resposta com citações
|
|
158
|
+
*/
|
|
159
|
+
async ask(query, options = {}) {
|
|
160
|
+
const {
|
|
161
|
+
topK = 5,
|
|
162
|
+
mode = "balanced",
|
|
163
|
+
useCache = true,
|
|
164
|
+
tipoDocumento,
|
|
165
|
+
ano
|
|
166
|
+
} = options;
|
|
167
|
+
const response = await this.request("/sdk/ask", {
|
|
168
|
+
method: "POST",
|
|
169
|
+
body: JSON.stringify({
|
|
170
|
+
query,
|
|
171
|
+
top_k: topK,
|
|
172
|
+
mode,
|
|
173
|
+
use_cache: useCache,
|
|
174
|
+
tipo_documento: tipoDocumento,
|
|
175
|
+
ano
|
|
176
|
+
})
|
|
177
|
+
});
|
|
178
|
+
return {
|
|
179
|
+
answer: response.data.answer,
|
|
180
|
+
citations: response.data.citations.map((c) => ({
|
|
181
|
+
text: c.text,
|
|
182
|
+
short: c.short,
|
|
183
|
+
documentType: c.document_type,
|
|
184
|
+
documentNumber: c.document_number,
|
|
185
|
+
year: c.year,
|
|
186
|
+
article: c.article
|
|
187
|
+
})),
|
|
188
|
+
confidence: response.data.confidence,
|
|
189
|
+
metadata: {
|
|
190
|
+
model: response.metadata.model,
|
|
191
|
+
latencyMs: response.metadata.latency_ms,
|
|
192
|
+
retrievalMs: response.metadata.retrieval_ms,
|
|
193
|
+
generationMs: response.metadata.generation_ms,
|
|
194
|
+
chunksUsed: response.metadata.chunks_used,
|
|
195
|
+
tokens: response.metadata.tokens,
|
|
196
|
+
queryHash: response.metadata.query_hash
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Envia feedback (like/dislike) para uma resposta
|
|
202
|
+
*
|
|
203
|
+
* @param queryId - ID da query (de SearchMetadata ou AskMetadata)
|
|
204
|
+
* @param like - true para like, false para dislike
|
|
205
|
+
*/
|
|
206
|
+
async feedback(queryId, like) {
|
|
207
|
+
const response = await this.request("/cache/feedback", {
|
|
208
|
+
method: "POST",
|
|
209
|
+
body: JSON.stringify({
|
|
210
|
+
query_hash: queryId,
|
|
211
|
+
is_like: like
|
|
212
|
+
})
|
|
213
|
+
});
|
|
214
|
+
return {
|
|
215
|
+
success: response.success,
|
|
216
|
+
message: response.message,
|
|
217
|
+
newLikes: response.new_likes,
|
|
218
|
+
newDislikes: response.new_dislikes
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Converte hits para formato de mensagens de chat
|
|
223
|
+
*/
|
|
224
|
+
hitsToMessages(hits, query) {
|
|
225
|
+
const context = this.hitsToContext(hits);
|
|
226
|
+
return [
|
|
227
|
+
{
|
|
228
|
+
role: "system",
|
|
229
|
+
content: SYSTEM_PROMPT
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
role: "user",
|
|
233
|
+
content: `Contexto:
|
|
234
|
+
${context}
|
|
235
|
+
|
|
236
|
+
Pergunta: ${query}`
|
|
237
|
+
}
|
|
238
|
+
];
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Converte hits para texto de contexto
|
|
242
|
+
*/
|
|
243
|
+
hitsToContext(hits) {
|
|
244
|
+
return hits.map((hit, i) => {
|
|
245
|
+
const source = hit.source || `Resultado ${i + 1}`;
|
|
246
|
+
return `[${source}]
|
|
247
|
+
${hit.text}`;
|
|
248
|
+
}).join("\n\n---\n\n");
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
export {
|
|
252
|
+
AuthenticationError,
|
|
253
|
+
RateLimitError,
|
|
254
|
+
VectorGov,
|
|
255
|
+
VectorGovError
|
|
256
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vectorgov",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "SDK TypeScript para a API VectorGov - Busca semântica em legislação brasileira",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
20
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
21
|
+
"lint": "eslint src/",
|
|
22
|
+
"test": "vitest",
|
|
23
|
+
"prepublishOnly": "npm run build"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"vectorgov",
|
|
27
|
+
"legal",
|
|
28
|
+
"legislation",
|
|
29
|
+
"semantic-search",
|
|
30
|
+
"rag",
|
|
31
|
+
"licitacao",
|
|
32
|
+
"brazil",
|
|
33
|
+
"sdk"
|
|
34
|
+
],
|
|
35
|
+
"author": "VectorGov <contato@vectorgov.io>",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/euteajudo/vectorgov-sdk-ts"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/euteajudo/vectorgov-sdk-ts/issues"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://vectorgov.io",
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^20.0.0",
|
|
47
|
+
"tsup": "^8.0.0",
|
|
48
|
+
"typescript": "^5.0.0",
|
|
49
|
+
"vitest": "^1.0.0"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=18.0.0"
|
|
53
|
+
}
|
|
54
|
+
}
|