vantuz 3.2.7 → 3.3.1

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.
@@ -10,6 +10,75 @@ import os from 'os';
10
10
 
11
11
  const LOG_FILE = path.join(os.homedir(), '.vantuz', 'vantuz.log');
12
12
 
13
+ const PROVIDER_CONFIG = {
14
+ gemini: {
15
+ url: (apiKey) => `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${apiKey}`,
16
+ body: (systemPrompt, message) => ({
17
+ contents: [{ parts: [{ text: `${systemPrompt}\n\nKullanıcı: ${message}` }] }]
18
+ }),
19
+ headers: { 'Content-Type': 'application/json' },
20
+ parseResponse: (data) => data?.candidates?.[0]?.content?.parts?.[0]?.text,
21
+ errorMsg: 'Gemini yanıt vermedi'
22
+ },
23
+ groq: {
24
+ url: 'https://api.groq.com/openai/v1/chat/completions',
25
+ body: (systemPrompt, message) => ({
26
+ model: 'llama-3.3-70b-versatile',
27
+ messages: [
28
+ { role: 'system', content: systemPrompt },
29
+ { role: 'user', content: message }
30
+ ],
31
+ max_tokens: 1000,
32
+ temperature: 0.7
33
+ }),
34
+ headers: (apiKey) => ({ 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }),
35
+ parseResponse: (data) => data?.choices?.[0]?.message?.content,
36
+ errorMsg: 'Groq yanıt vermedi'
37
+ },
38
+ openai: {
39
+ url: 'https://api.openai.com/v1/chat/completions',
40
+ body: (systemPrompt, message) => ({
41
+ model: 'gpt-4o-mini',
42
+ messages: [
43
+ { role: 'system', content: systemPrompt },
44
+ { role: 'user', content: message }
45
+ ],
46
+ max_tokens: 1000
47
+ }),
48
+ headers: (apiKey) => ({ 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }),
49
+ parseResponse: (data) => data?.choices?.[0]?.message?.content,
50
+ errorMsg: 'OpenAI yanıt vermedi'
51
+ },
52
+ anthropic: {
53
+ url: 'https://api.anthropic.com/v1/messages',
54
+ body: (systemPrompt, message) => ({
55
+ model: 'claude-3-haiku-20240307',
56
+ max_tokens: 1000,
57
+ messages: [
58
+ { role: 'user', content: message }
59
+ ],
60
+ system: systemPrompt
61
+ }),
62
+ headers: (apiKey) => ({ 'x-api-key': apiKey, 'anthropic-version': '2023-06-01', 'Content-Type': 'application/json' }),
63
+ parseResponse: (data) => data?.content?.[0]?.text,
64
+ errorMsg: 'Anthropic yanıt vermedi'
65
+ },
66
+ deepseek: {
67
+ url: 'https://api.deepseek.com/v1/chat/completions',
68
+ body: (systemPrompt, message) => ({
69
+ model: 'deepseek-chat',
70
+ messages: [
71
+ { role: 'system', content: systemPrompt },
72
+ { role: 'user', content: message }
73
+ ],
74
+ max_tokens: 1000
75
+ }),
76
+ headers: (apiKey) => ({ 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }),
77
+ parseResponse: (data) => data?.choices?.[0]?.message?.content,
78
+ errorMsg: 'DeepSeek yanıt vermedi'
79
+ }
80
+ };
81
+
13
82
  // ═══════════════════════════════════════════════════════════════════════════
14
83
  // LOGGING
15
84
  // ═══════════════════════════════════════════════════════════════════════════
@@ -91,8 +160,28 @@ const VANTUZ_SYSTEM_PROMPT = `Sen Vantuz AI, e-ticaret operasyonlarını yönete
91
160
  // AI CHAT
92
161
  // ═══════════════════════════════════════════════════════════════════════════
93
162
 
163
+ async function _makeApiRequest(providerName, providerConfig, message, apiKey, systemPrompt) {
164
+ if (!apiKey) throw new Error(`${providerName.toUpperCase()}_API_KEY ayarlanmamış`);
165
+
166
+ const url = typeof providerConfig.url === 'function' ? providerConfig.url(apiKey) : providerConfig.url;
167
+ const headers = typeof providerConfig.headers === 'function' ? providerConfig.headers(apiKey) : providerConfig.headers;
168
+ const body = providerConfig.body(systemPrompt, message);
169
+
170
+ const response = await axios.post(url, body, {
171
+ headers,
172
+ timeout: 30000
173
+ });
174
+
175
+ const text = providerConfig.parseResponse(response.data);
176
+ if (!text) throw new Error(providerConfig.errorMsg);
177
+
178
+ log('INFO', `${providerName} yanıtı alındı`, { chars: text.length });
179
+ return text;
180
+ }
181
+
94
182
  export async function chat(message, config, env) {
95
183
  const provider = config.aiProvider || 'gemini';
184
+ const providerConfig = PROVIDER_CONFIG[provider];
96
185
 
97
186
  // Context bilgisi ekle
98
187
  const contextInfo = config.systemContext || '';
@@ -100,154 +189,19 @@ export async function chat(message, config, env) {
100
189
 
101
190
  log('INFO', `AI isteği: ${provider}`, { message: message.slice(0, 100) });
102
191
 
192
+ if (!providerConfig) {
193
+ return 'Desteklenmeyen AI sağlayıcı: ' + provider;
194
+ }
195
+
103
196
  try {
104
- switch (provider) {
105
- case 'gemini':
106
- return await chatGemini(message, env.GEMINI_API_KEY, fullSystemPrompt);
107
- case 'groq':
108
- return await chatGroq(message, env.GROQ_API_KEY, fullSystemPrompt);
109
- case 'openai':
110
- return await chatOpenAI(message, env.OPENAI_API_KEY, fullSystemPrompt);
111
- case 'anthropic':
112
- return await chatAnthropic(message, env.ANTHROPIC_API_KEY, fullSystemPrompt);
113
- case 'deepseek':
114
- return await chatDeepSeek(message, env.DEEPSEEK_API_KEY, fullSystemPrompt);
115
- default:
116
- return 'Desteklenmeyen AI sağlayıcı: ' + provider;
117
- }
197
+ const apiKey = env[`${provider.toUpperCase()}_API_KEY`];
198
+ return await _makeApiRequest(provider, providerConfig, message, apiKey, fullSystemPrompt);
118
199
  } catch (error) {
119
200
  log('ERROR', `AI hatası: ${error.message}`, { provider });
120
201
  return `AI hatası: ${error.message}. /logs komutu ile detay görün.`;
121
202
  }
122
203
  }
123
204
 
124
- // ═══════════════════════════════════════════════════════════════════════════
125
- // PROVIDER IMPLEMENTATIONS
126
- // ═══════════════════════════════════════════════════════════════════════════
127
-
128
- async function chatGemini(message, apiKey, systemPrompt) {
129
- if (!apiKey) throw new Error('GEMINI_API_KEY ayarlanmamış');
130
-
131
- const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${apiKey}`;
132
-
133
- const response = await axios.post(url, {
134
- contents: [{
135
- parts: [{
136
- text: `${systemPrompt}\n\nKullanıcı: ${message}`
137
- }]
138
- }]
139
- }, {
140
- headers: { 'Content-Type': 'application/json' },
141
- timeout: 30000
142
- });
143
-
144
- const text = response.data?.candidates?.[0]?.content?.parts?.[0]?.text;
145
- if (!text) throw new Error('Gemini yanıt vermedi');
146
-
147
- log('INFO', 'Gemini yanıtı alındı', { chars: text.length });
148
- return text;
149
- }
150
-
151
- async function chatGroq(message, apiKey, systemPrompt) {
152
- if (!apiKey) throw new Error('GROQ_API_KEY ayarlanmamış');
153
-
154
- const response = await axios.post('https://api.groq.com/openai/v1/chat/completions', {
155
- model: 'llama-3.3-70b-versatile',
156
- messages: [
157
- { role: 'system', content: systemPrompt },
158
- { role: 'user', content: message }
159
- ],
160
- max_tokens: 1000,
161
- temperature: 0.7
162
- }, {
163
- headers: {
164
- 'Authorization': `Bearer ${apiKey}`,
165
- 'Content-Type': 'application/json'
166
- },
167
- timeout: 30000
168
- });
169
-
170
- const text = response.data?.choices?.[0]?.message?.content;
171
- if (!text) throw new Error('Groq yanıt vermedi');
172
-
173
- log('INFO', 'Groq yanıtı alındı', { chars: text.length });
174
- return text;
175
- }
176
-
177
- async function chatOpenAI(message, apiKey, systemPrompt) {
178
- if (!apiKey) throw new Error('OPENAI_API_KEY ayarlanmamış');
179
-
180
- const response = await axios.post('https://api.openai.com/v1/chat/completions', {
181
- model: 'gpt-4o-mini',
182
- messages: [
183
- { role: 'system', content: systemPrompt },
184
- { role: 'user', content: message }
185
- ],
186
- max_tokens: 1000
187
- }, {
188
- headers: {
189
- 'Authorization': `Bearer ${apiKey}`,
190
- 'Content-Type': 'application/json'
191
- },
192
- timeout: 30000
193
- });
194
-
195
- const text = response.data?.choices?.[0]?.message?.content;
196
- if (!text) throw new Error('OpenAI yanıt vermedi');
197
-
198
- log('INFO', 'OpenAI yanıtı alındı', { chars: text.length });
199
- return text;
200
- }
201
205
 
202
- async function chatAnthropic(message, apiKey, systemPrompt) {
203
- if (!apiKey) throw new Error('ANTHROPIC_API_KEY ayarlanmamış');
204
-
205
- const response = await axios.post('https://api.anthropic.com/v1/messages', {
206
- model: 'claude-3-haiku-20240307',
207
- max_tokens: 1000,
208
- messages: [
209
- { role: 'user', content: message }
210
- ],
211
- system: systemPrompt
212
- }, {
213
- headers: {
214
- 'x-api-key': apiKey,
215
- 'anthropic-version': '2023-06-01',
216
- 'Content-Type': 'application/json'
217
- },
218
- timeout: 30000
219
- });
220
-
221
- const text = response.data?.content?.[0]?.text;
222
- if (!text) throw new Error('Anthropic yanıt vermedi');
223
-
224
- log('INFO', 'Anthropic yanıtı alındı', { chars: text.length });
225
- return text;
226
- }
227
-
228
- async function chatDeepSeek(message, apiKey, systemPrompt) {
229
- if (!apiKey) throw new Error('DEEPSEEK_API_KEY ayarlanmamış');
230
-
231
- const response = await axios.post('https://api.deepseek.com/v1/chat/completions', {
232
- model: 'deepseek-chat',
233
- messages: [
234
- { role: 'system', content: systemPrompt },
235
- { role: 'user', content: message }
236
- ],
237
- max_tokens: 1000
238
- }, {
239
- headers: {
240
- 'Authorization': `Bearer ${apiKey}`,
241
- 'Content-Type': 'application/json'
242
- },
243
- timeout: 30000
244
- });
245
-
246
- const text = response.data?.choices?.[0]?.message?.content;
247
- if (!text) throw new Error('DeepSeek yanıt vermedi');
248
-
249
- log('INFO', 'DeepSeek yanıtı alındı', { chars: text.length });
250
- return text;
251
- }
252
206
 
253
207
  export default { chat, log, getLogs, clearLogs };
package/core/database.js CHANGED
@@ -1,73 +1,83 @@
1
- /**
2
- * 💾 VANTUZ DATABASE v3.2
3
- * SQLite veritabanı - ESM modülü
4
- */
5
-
6
- import { Sequelize, DataTypes } from 'sequelize';
1
+ // core/database.js
2
+ import sqlite3 from 'sqlite3';
3
+ import { open } from 'sqlite';
7
4
  import path from 'path';
8
5
  import os from 'os';
6
+ import { fileURLToPath } from 'url'; // Import fileURLToPath
7
+ import { log } from './ai-provider.js';
9
8
 
10
- const VANTUZ_HOME = path.join(os.homedir(), '.vantuz');
11
- const dbPath = path.join(VANTUZ_HOME, 'vantuz.sqlite');
9
+ const __filename = fileURLToPath(import.meta.url); // Define __filename
10
+ const __dirname = path.dirname(__filename); // Define __dirname
12
11
 
13
- const sequelize = new Sequelize({
14
- dialect: 'sqlite',
15
- storage: dbPath,
16
- logging: false
17
- });
12
+ const DB_PATH = path.join(os.homedir(), '.vantuz', 'eia_data.db');
18
13
 
19
- // --- MODELLER ---
14
+ class Database {
15
+ constructor() {
16
+ this.db = null;
17
+ }
20
18
 
21
- // Mağaza Ayarları (API Keyler vb.)
22
- const Store = sequelize.define('Store', {
23
- name: { type: DataTypes.STRING, allowNull: false },
24
- platform: { type: DataTypes.STRING, allowNull: false },
25
- credentials: { type: DataTypes.JSON, allowNull: false },
26
- isActive: { type: DataTypes.BOOLEAN, defaultValue: true }
27
- });
19
+ async init() {
20
+ if (this.db) return this.db;
28
21
 
29
- // Ürünler
30
- const Product = sequelize.define('Product', {
31
- name: { type: DataTypes.STRING, allowNull: false },
32
- barcode: { type: DataTypes.STRING, unique: true },
33
- sku: { type: DataTypes.STRING },
34
- description: { type: DataTypes.TEXT },
35
- brand: { type: DataTypes.STRING },
36
- images: { type: DataTypes.JSON },
37
- marketData: { type: DataTypes.JSON },
38
- aiAnalysis: { type: DataTypes.TEXT }
39
- });
22
+ try {
23
+ this.db = await open({
24
+ filename: DB_PATH,
25
+ driver: sqlite3.Database
26
+ });
27
+ await this.db.migrate({
28
+ migrationsPath: path.join(__dirname, 'migrations')
29
+ });
30
+ log('INFO', 'SQLite database initialized and migrated');
31
+ return this.db;
32
+ } catch (error) {
33
+ log('ERROR', `Failed to initialize database: ${error.message}`, { error });
34
+ throw error;
35
+ }
36
+ }
40
37
 
41
- // Siparişler
42
- const Order = sequelize.define('Order', {
43
- platform: { type: DataTypes.STRING, allowNull: false },
44
- orderNumber: { type: DataTypes.STRING, unique: true },
45
- customerName: { type: DataTypes.STRING },
46
- totalAmount: { type: DataTypes.FLOAT },
47
- currency: { type: DataTypes.STRING, defaultValue: 'TRY' },
48
- status: { type: DataTypes.STRING },
49
- orderDate: { type: DataTypes.DATE },
50
- items: { type: DataTypes.JSON }
51
- });
38
+ async close() {
39
+ if (this.db) {
40
+ await this.db.close();
41
+ this.db = null;
42
+ log('INFO', 'SQLite database closed');
43
+ }
44
+ }
52
45
 
53
- // AI Önerileri
54
- const Insight = sequelize.define('Insight', {
55
- type: { type: DataTypes.STRING },
56
- message: { type: DataTypes.TEXT },
57
- priority: { type: DataTypes.INTEGER },
58
- isRead: { type: DataTypes.BOOLEAN, defaultValue: false }
59
- });
46
+ // Example: Save product data
47
+ async saveProduct(product) {
48
+ const { id, name, price, cost, platform } = product;
49
+ await this.db.run(
50
+ `INSERT OR REPLACE INTO products (id, name, price, cost, platform, lastUpdated) VALUES (?, ?, ?, ?, ?, ?)`,
51
+ id, name, price, cost, platform, new Date().toISOString()
52
+ );
53
+ log('INFO', `Product ${name} saved/updated`);
54
+ }
60
55
 
61
- // Veritabanını Başlat
62
- export async function initDB() {
63
- try {
64
- await sequelize.sync({ alter: true });
65
- return true;
66
- } catch (error) {
67
- console.error('Veritabanı hatası:', error);
68
- return false;
56
+ // Example: Get product by ID
57
+ async getProduct(id) {
58
+ return this.db.get(`SELECT * FROM products WHERE id = ?`, id);
59
+ }
60
+
61
+ // Example: Save historical price
62
+ async saveHistoricalPrice(productId, price, timestamp) {
63
+ await this.db.run(
64
+ `INSERT INTO historical_prices (productId, price, timestamp) VALUES (?, ?, ?)`,
65
+ productId, price, timestamp
66
+ );
67
+ log('INFO', `Historical price for product ${productId} saved`);
68
+ }
69
+
70
+ // Example: Get historical prices for a product
71
+ async getHistoricalPrices(productId) {
72
+ return this.db.all(`SELECT * FROM historical_prices WHERE productId = ? ORDER BY timestamp ASC`, productId);
69
73
  }
70
74
  }
71
75
 
72
- export { sequelize, Store, Product, Order, Insight };
73
- export default { sequelize, Store, Product, Order, Insight, initDB };
76
+ let dbInstance = null;
77
+
78
+ export function getDatabase() {
79
+ if (!dbInstance) {
80
+ dbInstance = new Database();
81
+ }
82
+ return dbInstance;
83
+ }
@@ -0,0 +1,102 @@
1
+ // core/eia-brain.js
2
+ import { chat as aiChat, log } from './ai-provider.js';
3
+
4
+ class EIABrain {
5
+ constructor(config, env) {
6
+ this.config = config;
7
+ this.env = env;
8
+ this.systemPrompt = this._buildSystemPrompt();
9
+ }
10
+
11
+ _buildSystemPrompt() {
12
+ return `Sen bir E-Ticaret Yönetim Ajansı'nın (EIA) beynisin. Görevin, e-ticaret operasyonlarını 7/24 izlemek, analiz etmek ve **şirketin karlılığını ve büyümesini maksimize edecek proaktif aksiyon önerileri** sunmaktır. Veriye dayalı kararlar alan, sonuç odaklı ve iş stratejilerine hakim bir yönetici kişiliğine sahipsin.
13
+
14
+ Aşağıdaki bilgilerle karar ver:
15
+ - Güncel durum verileri (stok, fiyat, buybox durumu, yorumlar vb.)
16
+ - Şirket kuralları ve hedefleri (bellekten alınacak)
17
+ - Geçmiş stratejik kararlar (bellekten alınacak)
18
+
19
+ Yanıtların kısa, öz, **iş değerine odaklı** ve eyleme yönelik olmalı. Gerekirse ek bilgi veya onay istemelisin.`;
20
+ }
21
+
22
+ async analyzeAndDecide(context) {
23
+ const fullPrompt = `${this.systemPrompt}
24
+
25
+ --- GÜNCEL DURUM ---
26
+ ${context.currentSituation}
27
+
28
+ --- ŞİRKET KURALLARI VE HEDEFLERİ ---
29
+ ${context.companyRules}
30
+
31
+ --- GEÇMİŞ KARARLAR ---
32
+ ${context.pastDecisions}
33
+
34
+ Durumu analiz et ve en uygun aksiyon önerisini veya içgörüyü sun.`;
35
+
36
+ try {
37
+ const response = await aiChat(fullPrompt, this.config, this.env);
38
+ log('INFO', 'EIA Brain analysis complete', { response: response.slice(0, 100) });
39
+ return response;
40
+ } catch (error) {
41
+ log('ERROR', 'EIA Brain analysis failed', { error: error.message });
42
+ return `Analiz sırasında bir hata oluştu: ${error.message}`;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Scraper'lardan gelen ham veriyi işleyip ticari içgörülere dönüştürür.
48
+ * Bu metod, veri modelleme ve iş mantığı kurallarını içerecektir.
49
+ */
50
+ async processRawData(rawData, dataType) {
51
+ log('INFO', `Processing raw data for type: ${dataType}`, { rawData: JSON.stringify(rawData).slice(0, 200) });
52
+
53
+ let insights = {};
54
+ switch (dataType) {
55
+ case 'price_changes':
56
+ insights = {
57
+ type: 'price_change_insight',
58
+ summary: `Fiyat değişikliği verisi işlendi. ${rawData.length} adet kayıt mevcut.`,
59
+ details: rawData.map(item => `Ürün: ${item.productName}, Eski Fiyat: ${item.oldPrice}, Yeni Fiyat: ${item.newPrice}, Platform: ${item.platform}`)
60
+ };
61
+ break;
62
+ case 'buybox_status':
63
+ insights = {
64
+ type: 'buybox_insight',
65
+ summary: `Buybox durumu verisi işlendi.`,
66
+ details: rawData.map(item => `Ürün: ${item.productName}, Durum: ${item.status}, Platform: ${item.platform}`)
67
+ };
68
+ break;
69
+ case 'stock_movements':
70
+ insights = {
71
+ type: 'stock_insight',
72
+ summary: `Stok hareketi verisi işlendi.`,
73
+ details: rawData.map(item => `Ürün: ${item.productName}, Miktar: ${item.quantityChange}, Platform: ${item.platform}`)
74
+ };
75
+ break;
76
+ case 'product_reviews':
77
+ insights = {
78
+ type: 'review_insight',
79
+ summary: `Ürün yorumları verisi işlendi.`,
80
+ details: rawData.map(item => `Ürün: ${item.productName}, Yorum: ${item.reviewText.slice(0, 50)}, Puan: ${item.rating}`)
81
+ };
82
+ break;
83
+ default:
84
+ insights = {
85
+ type: 'general_insight',
86
+ summary: 'Bilinen bir veri tipi değil, ham veri olarak işlendi.',
87
+ rawData: rawData
88
+ };
89
+ break;
90
+ }
91
+ return insights;
92
+ }
93
+ }
94
+
95
+ let eiaBrainInstance = null;
96
+
97
+ export function getEIABrain(config, env) {
98
+ if (!eiaBrainInstance) {
99
+ eiaBrainInstance = new EIABrain(config, env);
100
+ }
101
+ return eiaBrainInstance;
102
+ }