brasil-ceps-offline 1.0.2 → 1.0.3
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.
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* AI Updater: Agente de IA para descobrir e inserir novos CEPs
|
|
5
|
+
*
|
|
6
|
+
* Uso:
|
|
7
|
+
* npx ts-node src/scripts/ai-updater.ts
|
|
8
|
+
* OPENAI_API_KEY=sk-xxx npx ts-node src/scripts/ai-updater.ts
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
44
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
45
|
+
};
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
const fs = __importStar(require("fs"));
|
|
48
|
+
const path = __importStar(require("path"));
|
|
49
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
50
|
+
const openai_1 = __importDefault(require("openai"));
|
|
51
|
+
class AiUpdater {
|
|
52
|
+
constructor() {
|
|
53
|
+
this.dbPath = path.join(__dirname, '../../.db/ceps.sqlite');
|
|
54
|
+
this.db = null;
|
|
55
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
56
|
+
if (!apiKey) {
|
|
57
|
+
throw new Error('OPENAI_API_KEY not configured');
|
|
58
|
+
}
|
|
59
|
+
this.client = new openai_1.default({ apiKey });
|
|
60
|
+
this.log('AI Updater started');
|
|
61
|
+
}
|
|
62
|
+
log(message) {
|
|
63
|
+
console.log(`[ai-updater] ${message}`);
|
|
64
|
+
}
|
|
65
|
+
error(message, err) {
|
|
66
|
+
console.error(`[ai-updater] ERROR: ${message}`);
|
|
67
|
+
if (err)
|
|
68
|
+
console.error(`[ai-updater] ${err.message}`);
|
|
69
|
+
}
|
|
70
|
+
success(message) {
|
|
71
|
+
console.log(`[ai-updater] SUCCESS: ${message}`);
|
|
72
|
+
}
|
|
73
|
+
validateDatabase() {
|
|
74
|
+
if (!fs.existsSync(this.dbPath)) {
|
|
75
|
+
this.error(`Database not found: ${this.dbPath}`);
|
|
76
|
+
throw new Error('Database not found');
|
|
77
|
+
}
|
|
78
|
+
this.log(`Database found: ${this.dbPath}`);
|
|
79
|
+
}
|
|
80
|
+
connectDatabase() {
|
|
81
|
+
try {
|
|
82
|
+
this.db = new better_sqlite3_1.default(this.dbPath);
|
|
83
|
+
this.success('Connected to database');
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
this.error('Error connecting to database', err);
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
closeDatabase() {
|
|
91
|
+
if (this.db) {
|
|
92
|
+
this.db.close();
|
|
93
|
+
this.log('Connection closed');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async extractCepsWithAI(inputText) {
|
|
97
|
+
try {
|
|
98
|
+
this.log('Sending text to OpenAI...');
|
|
99
|
+
const systemPrompt = 'You are an expert in Brazilian geographic data. Extract CEP information from unstructured text. ' +
|
|
100
|
+
'Return ONLY a JSON array in this format: [{cep, state, city, neighborhood, street}]. ' +
|
|
101
|
+
'CEP must be 8 digits (no formatting). State must be 2 uppercase letters. Validate all CEPs.';
|
|
102
|
+
const response = await this.client.chat.completions.create({
|
|
103
|
+
model: 'gpt-4o-mini',
|
|
104
|
+
messages: [
|
|
105
|
+
{
|
|
106
|
+
role: 'system',
|
|
107
|
+
content: systemPrompt,
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
role: 'user',
|
|
111
|
+
content: `Extract CEPs from this text:\n\n${inputText}`,
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
temperature: 0.3,
|
|
115
|
+
});
|
|
116
|
+
const content = response.choices[0]?.message?.content;
|
|
117
|
+
if (!content) {
|
|
118
|
+
throw new Error('No response from OpenAI');
|
|
119
|
+
}
|
|
120
|
+
this.log(`Response received: ${content.substring(0, 100)}...`);
|
|
121
|
+
// Extract JSON array from response
|
|
122
|
+
const jsonMatch = content.match(/\[[\s\S]*\]/);
|
|
123
|
+
if (!jsonMatch) {
|
|
124
|
+
throw new Error('Could not extract JSON from response');
|
|
125
|
+
}
|
|
126
|
+
const ceps = JSON.parse(jsonMatch[0]);
|
|
127
|
+
// Validate CEPs
|
|
128
|
+
const validatedCeps = ceps.filter((cep) => {
|
|
129
|
+
if (!/^\d{8}$/.test(cep.cep)) {
|
|
130
|
+
this.log(`Skipping invalid CEP: ${cep.cep}`);
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
return true;
|
|
134
|
+
});
|
|
135
|
+
this.success(`Extracted and validated ${validatedCeps.length} CEPs`);
|
|
136
|
+
return validatedCeps;
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
this.error('Error extracting CEPs with AI', err);
|
|
140
|
+
throw err;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
insertCeps(ceps) {
|
|
144
|
+
if (!this.db || ceps.length === 0)
|
|
145
|
+
return;
|
|
146
|
+
try {
|
|
147
|
+
const transaction = this.db.transaction((records) => {
|
|
148
|
+
for (const record of records) {
|
|
149
|
+
this.db.prepare('INSERT OR IGNORE INTO addresses (cep, state, city, neighborhood, street) VALUES (?, ?, ?, ?, ?)').run(record.cep, record.state, record.city, record.neighborhood, record.street);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
transaction(ceps);
|
|
153
|
+
this.success(`${ceps.length} CEPs inserted into database`);
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
this.error('Error inserting CEPs', err);
|
|
157
|
+
throw err;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async fetchNewCepsText() {
|
|
161
|
+
// In production, this would fetch from external API or system
|
|
162
|
+
// For now, return example data
|
|
163
|
+
return ('New CEPs discovered:\n\n' +
|
|
164
|
+
'1. Lins, SP - Residential Florescer neighborhood\n' +
|
|
165
|
+
' - Rua das Rosas: CEP 16400500\n' +
|
|
166
|
+
' - Rua dos Lirios: CEP 16400501\n' +
|
|
167
|
+
' - Avenida Central: CEP 16400510\n\n' +
|
|
168
|
+
'2. Ribeirao Preto, SP - Urban expansion\n' +
|
|
169
|
+
' - Rua das Flores: CEP 14025500\n' +
|
|
170
|
+
' - Avenida Brasil: CEP 14025600');
|
|
171
|
+
}
|
|
172
|
+
async run() {
|
|
173
|
+
try {
|
|
174
|
+
this.validateDatabase();
|
|
175
|
+
this.connectDatabase();
|
|
176
|
+
this.log('Fetching new CEPs...');
|
|
177
|
+
const inputText = await this.fetchNewCepsText();
|
|
178
|
+
this.log(`Text received: ${inputText.substring(0, 50)}...`);
|
|
179
|
+
const extractedCeps = await this.extractCepsWithAI(inputText);
|
|
180
|
+
if (extractedCeps.length > 0) {
|
|
181
|
+
this.insertCeps(extractedCeps);
|
|
182
|
+
this.success('Pipeline completed successfully');
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
this.log('No new CEPs to insert');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
this.error('Fatal error in pipeline', err);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
finally {
|
|
193
|
+
this.closeDatabase();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Execute
|
|
198
|
+
const updater = new AiUpdater();
|
|
199
|
+
updater.run().catch((err) => {
|
|
200
|
+
console.error('Critical error:', err);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brasil-ceps-offline",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Busca rápida de CEPs brasileiros com zero latência de rede - dados off-line usando SQLite",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
"build": "tsc",
|
|
12
12
|
"build:db": "ts-node scripts/builder/builder.ts",
|
|
13
13
|
"builder": "ts-node src/builder.ts",
|
|
14
|
-
"postinstall": "node dist/scripts/download-db.js",
|
|
15
14
|
"dev": "tsc --watch",
|
|
16
15
|
"test": "jest",
|
|
17
16
|
"prepare": "npm run build",
|
|
18
17
|
"sync": "node dist/bin/sync.js",
|
|
19
18
|
"sync:status": "node dist/bin/sync.js status",
|
|
20
|
-
"example": "ts-node examples/usage.ts"
|
|
19
|
+
"example": "ts-node examples/usage.ts",
|
|
20
|
+
"ai:update": "ts-node src/scripts/ai-updater.ts"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"cep",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"ts-node": "^10.9.2",
|
|
41
41
|
"jest": "^29.7.0",
|
|
42
42
|
"ts-jest": "^29.1.1",
|
|
43
|
-
"csv-parse": "^5.5.6"
|
|
43
|
+
"csv-parse": "^5.5.6",
|
|
44
|
+
"openai": "^4.52.0"
|
|
44
45
|
},
|
|
45
46
|
"files": [
|
|
46
47
|
"dist"
|