@meller/tokentalos 1.0.0 → 1.0.4
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/api/setup.js +330 -0
- package/bin/tokentalos.js +1 -1
- package/lib/engine/llm_clients.js +5 -2
- package/package.json +2 -1
package/api/setup.js
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
|
|
7
|
+
const CONFIG_PATH = path.join(os.homedir(), '.tokentalosrc');
|
|
8
|
+
|
|
9
|
+
export async function runSetup() {
|
|
10
|
+
if (!process.stdout.isTTY) {
|
|
11
|
+
console.log(chalk.gray('Non-interactive environment detected. Using default configuration.'));
|
|
12
|
+
const defaults = {
|
|
13
|
+
databaseType: 'sqlite',
|
|
14
|
+
sqlitePath: path.join(os.homedir(), '.tokentalos', 'data.db'),
|
|
15
|
+
enableCollector: true,
|
|
16
|
+
gatewayPort: 8060,
|
|
17
|
+
enableDashboard: true,
|
|
18
|
+
dashboardPort: 8060,
|
|
19
|
+
llmProvider: 'gemini',
|
|
20
|
+
defaultModel: 'gemini-3-flash-preview',
|
|
21
|
+
location: 'global',
|
|
22
|
+
formattingFeatures: ['compress', 'pii', 'neutralize'],
|
|
23
|
+
intelligenceFeatures: ['cache', 'explain'],
|
|
24
|
+
securityFeatures: ['injection', 'secrets'],
|
|
25
|
+
securityAction: 'warn',
|
|
26
|
+
piiAction: 'mask',
|
|
27
|
+
databaseSchema: 'tokentalos',
|
|
28
|
+
maxTokens: 12000,
|
|
29
|
+
thresholdAction: 'warning'
|
|
30
|
+
};
|
|
31
|
+
const dbDir = path.dirname(defaults.sqlitePath);
|
|
32
|
+
await fs.ensureDir(dbDir);
|
|
33
|
+
await fs.writeJson(CONFIG_PATH, defaults, { spaces: 2 });
|
|
34
|
+
return defaults;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log(chalk.blue.bold('\n--- Token Talos Setup ---\n'));
|
|
38
|
+
console.log(chalk.gray('This wizard will configure your Database, Collector (API), and Dashboard.'));
|
|
39
|
+
console.log(chalk.white('\n [Collector]: ') + chalk.gray('The ingestion endpoint that receives and analyzes LLM data.'));
|
|
40
|
+
console.log(chalk.white(' [Dashboard]: ') + chalk.gray('The visual interface for monitoring your AI performance.\n'));
|
|
41
|
+
|
|
42
|
+
// Load previous configuration if it exists to use as defaults
|
|
43
|
+
let previousAnswers = {};
|
|
44
|
+
if (await fs.pathExists(CONFIG_PATH)) {
|
|
45
|
+
try {
|
|
46
|
+
previousAnswers = await fs.readJson(CONFIG_PATH);
|
|
47
|
+
} catch (e) { /* ignore */ }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const answers = await inquirer.prompt([
|
|
51
|
+
// --- DATABASE SECTION ---
|
|
52
|
+
{
|
|
53
|
+
type: 'list',
|
|
54
|
+
name: 'databaseType',
|
|
55
|
+
message: chalk.cyan('DATABASE: ') + 'Select storage engine:',
|
|
56
|
+
choices: [
|
|
57
|
+
{ name: 'SQLite (Local file, zero-install)', value: 'sqlite' },
|
|
58
|
+
{ name: 'PostgreSQL (External server)', value: 'postgres' },
|
|
59
|
+
],
|
|
60
|
+
default: 'sqlite',
|
|
61
|
+
},
|
|
62
|
+
// ... rest of database questions ...
|
|
63
|
+
{
|
|
64
|
+
type: 'input',
|
|
65
|
+
name: 'sqlitePath',
|
|
66
|
+
message: ' Where should the SQLite database be stored?',
|
|
67
|
+
default: path.join(os.homedir(), '.tokentalos', 'data.db'),
|
|
68
|
+
when: (ans) => ans.databaseType === 'sqlite',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
type: 'input',
|
|
72
|
+
name: 'pgHost',
|
|
73
|
+
message: ' Postgres Host:',
|
|
74
|
+
default: 'localhost',
|
|
75
|
+
when: (ans) => ans.databaseType === 'postgres',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
type: 'input',
|
|
79
|
+
name: 'pgPort',
|
|
80
|
+
message: ' Postgres Port:',
|
|
81
|
+
default: 5432,
|
|
82
|
+
when: (ans) => ans.databaseType === 'postgres',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: 'input',
|
|
86
|
+
name: 'pgUser',
|
|
87
|
+
message: ' Postgres User:',
|
|
88
|
+
default: 'postgres',
|
|
89
|
+
when: (ans) => ans.databaseType === 'postgres',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
type: 'password',
|
|
93
|
+
name: 'pgPassword',
|
|
94
|
+
message: ' Postgres Password:',
|
|
95
|
+
when: (ans) => ans.databaseType === 'postgres',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
type: 'input',
|
|
99
|
+
name: 'pgDatabase',
|
|
100
|
+
message: ' Postgres Database Name:',
|
|
101
|
+
default: 'tokentalos',
|
|
102
|
+
when: (ans) => ans.databaseType === 'postgres',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
type: 'input',
|
|
106
|
+
name: 'databaseSchema',
|
|
107
|
+
message: ' Database schema name:',
|
|
108
|
+
default: 'tokentalos',
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
// --- COLLECTOR (API) SECTION ---
|
|
112
|
+
{
|
|
113
|
+
type: 'confirm',
|
|
114
|
+
name: 'enableCollector',
|
|
115
|
+
message: chalk.green('COLLECTOR: ') + 'Enable API ingestion endpoint?',
|
|
116
|
+
default: true,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
type: 'input',
|
|
120
|
+
name: 'gatewayPort',
|
|
121
|
+
message: ' Collector Port:',
|
|
122
|
+
default: 8060,
|
|
123
|
+
when: (ans) => ans.enableCollector,
|
|
124
|
+
validate: (input) => !isNaN(parseInt(input)) || 'Please enter a valid port number',
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
// --- DASHBOARD SECTION ---
|
|
128
|
+
{
|
|
129
|
+
type: 'confirm',
|
|
130
|
+
name: 'enableDashboard',
|
|
131
|
+
message: chalk.magenta('DASHBOARD: ') + 'Enable web interface?',
|
|
132
|
+
default: true,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
type: 'input',
|
|
136
|
+
name: 'dashboardPort',
|
|
137
|
+
message: ' Dashboard Port:',
|
|
138
|
+
default: 8060,
|
|
139
|
+
when: (ans) => ans.enableDashboard,
|
|
140
|
+
validate: (input) => !isNaN(parseInt(input)) || 'Please enter a valid port number',
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
// --- LLM CONFIGURATION (Common) ---
|
|
144
|
+
{
|
|
145
|
+
type: 'list',
|
|
146
|
+
name: 'llmProvider',
|
|
147
|
+
message: chalk.yellow('LLM CONFIG: ') + 'Select provider for OPV/Analysis/Ingest:',
|
|
148
|
+
choices: [
|
|
149
|
+
{ name: 'Gemini (via ADC/Vertex)', value: 'gemini' },
|
|
150
|
+
{ name: 'Anthropic', value: 'anthropic' },
|
|
151
|
+
{ name: 'OpenAI', value: 'openai' },
|
|
152
|
+
{ name: 'Skip / Configure Later', value: 'none' },
|
|
153
|
+
],
|
|
154
|
+
default: 'none',
|
|
155
|
+
when: (ans) => ans.enableCollector || ans.enableDashboard,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
type: 'input',
|
|
159
|
+
name: 'defaultModel',
|
|
160
|
+
message: ' Default model name:',
|
|
161
|
+
default: (ans) => {
|
|
162
|
+
if (ans.llmProvider === 'gemini') return 'gemini-3-flash-preview';
|
|
163
|
+
if (ans.llmProvider === 'anthropic') return 'claude-3-5-sonnet-latest';
|
|
164
|
+
if (ans.llmProvider === 'openai') return 'gpt-4o-mini';
|
|
165
|
+
return 'none';
|
|
166
|
+
},
|
|
167
|
+
when: (ans) => (ans.enableCollector || ans.enableDashboard) && ans.llmProvider !== 'none',
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
type: 'input',
|
|
171
|
+
name: 'location',
|
|
172
|
+
message: ' LLM Model Location:',
|
|
173
|
+
default: (ans) => ans.llmProvider === 'gemini' ? 'global' : 'us-central1',
|
|
174
|
+
when: (ans) => (ans.enableCollector || ans.enableDashboard) && ans.llmProvider !== 'none',
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
type: 'list',
|
|
178
|
+
name: 'geminiAuthType',
|
|
179
|
+
message: ' Gemini Authentication Method:',
|
|
180
|
+
choices: [
|
|
181
|
+
{ name: 'Application Default Credentials (ADC)', value: 'adc' },
|
|
182
|
+
{ name: 'API Key', value: 'apikey' }
|
|
183
|
+
],
|
|
184
|
+
when: (ans) => (ans.enableCollector || ans.enableDashboard) && ans.llmProvider === 'gemini',
|
|
185
|
+
default: (ans) => previousAnswers?.geminiAuthType || 'adc',
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
type: 'input',
|
|
189
|
+
name: 'gcpProjectId',
|
|
190
|
+
message: ' Google Cloud Project ID (required for ADC):',
|
|
191
|
+
when: (ans) => (ans.enableCollector || ans.enableDashboard) && ans.llmProvider === 'gemini' && ans.geminiAuthType === 'adc',
|
|
192
|
+
default: (ans) => previousAnswers?.gcpProjectId || '',
|
|
193
|
+
validate: (input) => input.length > 0 || 'Project ID is required for Vertex AI',
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
type: 'input',
|
|
197
|
+
name: 'geminiApiKey',
|
|
198
|
+
message: ' Gemini API Key:',
|
|
199
|
+
when: (ans) => (ans.enableCollector || ans.enableDashboard) && ans.llmProvider === 'gemini' && ans.geminiAuthType === 'apikey',
|
|
200
|
+
default: (ans) => previousAnswers?.geminiApiKey || '',
|
|
201
|
+
validate: (input) => input.length > 0 || 'API Key is required',
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
// --- SAFETY FEATURES (Collector Only) ---
|
|
205
|
+
{
|
|
206
|
+
type: 'checkbox',
|
|
207
|
+
name: 'formattingFeatures',
|
|
208
|
+
message: chalk.green('SAFETY: ') + 'Select active guard features:',
|
|
209
|
+
choices: [
|
|
210
|
+
{ name: 'Compress (Lossless compression)', value: 'compress', checked: true },
|
|
211
|
+
{ name: 'Neutralize (XML wrapping)', value: 'neutralize', checked: true },
|
|
212
|
+
{ name: 'PII Redaction (Masking)', value: 'pii', checked: true },
|
|
213
|
+
],
|
|
214
|
+
when: (ans) => ans.enableCollector,
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
type: 'checkbox',
|
|
218
|
+
name: 'securityFeatures',
|
|
219
|
+
message: ' Select Security scanning:',
|
|
220
|
+
choices: [
|
|
221
|
+
{ name: 'Injection Scanning (Jailbreak detection)', value: 'injection', checked: true },
|
|
222
|
+
{ name: 'Secret Detection (API Keys/Secrets)', value: 'secrets', checked: true },
|
|
223
|
+
],
|
|
224
|
+
when: (ans) => ans.enableCollector,
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
type: 'list',
|
|
228
|
+
name: 'securityAction',
|
|
229
|
+
message: ' Default action for security threats:',
|
|
230
|
+
choices: [
|
|
231
|
+
{ name: 'Warn (Log to dashboard, proceed)', value: 'warn' },
|
|
232
|
+
{ name: 'Reject (Fail request with error)', value: 'reject' },
|
|
233
|
+
],
|
|
234
|
+
default: 'warn',
|
|
235
|
+
when: (ans) => ans.enableCollector,
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
type: 'input',
|
|
239
|
+
name: 'maxTokens',
|
|
240
|
+
message: ' Max token threshold (per prompt):',
|
|
241
|
+
default: 12000,
|
|
242
|
+
when: (ans) => ans.enableCollector,
|
|
243
|
+
validate: (input) => !isNaN(parseInt(input)) || 'Please enter a valid number',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
type: 'list',
|
|
247
|
+
name: 'thresholdAction',
|
|
248
|
+
message: ' Action when threshold exceeded:',
|
|
249
|
+
choices: [
|
|
250
|
+
{ name: 'Warning (Flag in dashboard)', value: 'warning' },
|
|
251
|
+
{ name: 'Reject (Fail request)', value: 'reject' },
|
|
252
|
+
],
|
|
253
|
+
default: 'warning',
|
|
254
|
+
when: (ans) => ans.enableCollector,
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
// --- ANALYTICS FEATURES (Dashboard Only) ---
|
|
258
|
+
{
|
|
259
|
+
type: 'checkbox',
|
|
260
|
+
name: 'intelligenceFeatures',
|
|
261
|
+
message: chalk.magenta('ANALYTICS: ') + 'Select Dashboard features:',
|
|
262
|
+
choices: [
|
|
263
|
+
{ name: 'Semantic Caching', value: 'cache', checked: true },
|
|
264
|
+
{ name: 'OPV (Reasoning Analysis)', value: 'opv', checked: true },
|
|
265
|
+
{ name: 'Explain Plan (Heuristic Detection)', value: 'explain', checked: true },
|
|
266
|
+
],
|
|
267
|
+
when: (ans) => ans.enableDashboard,
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
type: 'checkbox',
|
|
271
|
+
name: 'comparisonProviders',
|
|
272
|
+
message: ' Include in cost-comparison:',
|
|
273
|
+
choices: [
|
|
274
|
+
{ name: 'OpenAI', value: 'openai', checked: true },
|
|
275
|
+
{ name: 'Anthropic', value: 'anthropic', checked: true },
|
|
276
|
+
{ name: 'Gemini', value: 'gemini', checked: true },
|
|
277
|
+
{ name: 'DeepSeek', value: 'deepseek', checked: true },
|
|
278
|
+
{ name: 'Mistral', value: 'mistral', checked: true },
|
|
279
|
+
{ name: 'Meta', value: 'meta', checked: false },
|
|
280
|
+
{ name: 'Amazon', value: 'amazon', checked: false },
|
|
281
|
+
{ name: 'Alibaba', value: 'alibaba', checked: false },
|
|
282
|
+
{ name: 'xAI', value: 'xai', checked: false },
|
|
283
|
+
{ name: 'Cohere', value: 'cohere', checked: false },
|
|
284
|
+
],
|
|
285
|
+
when: (ans) => ans.enableDashboard,
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
type: 'list',
|
|
289
|
+
name: 'exportTarget',
|
|
290
|
+
message: ' Default export format:',
|
|
291
|
+
choices: ['jsonl', 'langsmith', 'none'],
|
|
292
|
+
default: 'jsonl',
|
|
293
|
+
when: (ans) => ans.enableDashboard,
|
|
294
|
+
}
|
|
295
|
+
]);
|
|
296
|
+
|
|
297
|
+
// Create directory for database if using sqlite
|
|
298
|
+
if (answers.databaseType === 'sqlite') {
|
|
299
|
+
const dbDir = path.dirname(answers.sqlitePath);
|
|
300
|
+
await fs.ensureDir(dbDir);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Save config
|
|
304
|
+
await fs.writeJson(CONFIG_PATH, answers, { spaces: 2 });
|
|
305
|
+
|
|
306
|
+
console.log(chalk.green.bold(`\n✅ Setup complete! Configuration saved to ${CONFIG_PATH}`));
|
|
307
|
+
|
|
308
|
+
if (answers.enableCollector) {
|
|
309
|
+
console.log(chalk.white(`\n Collector (API) will run on port: `) + chalk.green.bold(answers.gatewayPort));
|
|
310
|
+
console.log(chalk.gray(` Endpoint: http://localhost:${answers.gatewayPort}/api/v1`));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (answers.enableDashboard) {
|
|
314
|
+
console.log(chalk.white(`\n Dashboard will run on port: `) + chalk.magenta.bold(answers.dashboardPort));
|
|
315
|
+
console.log(chalk.gray(` URL: http://localhost:${answers.dashboardPort}`));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (answers.llmProvider === 'none') {
|
|
319
|
+
console.log(chalk.yellow(`\nNotice: OPV and AI Analysis are currently disabled.\n`));
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return answers;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export async function loadConfig() {
|
|
326
|
+
if (await fs.pathExists(CONFIG_PATH)) {
|
|
327
|
+
return await fs.readJson(CONFIG_PATH);
|
|
328
|
+
}
|
|
329
|
+
return null;
|
|
330
|
+
}
|
package/bin/tokentalos.js
CHANGED
|
@@ -24,14 +24,17 @@ export class LLMGateway {
|
|
|
24
24
|
async getVertex() {
|
|
25
25
|
if (!this.clients.vertex) {
|
|
26
26
|
const location = this.config.location || process.env.GCP_REGION || 'us-central1';
|
|
27
|
-
let project =
|
|
27
|
+
let project = this.config.gcpProjectId || this.config.project || process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT;
|
|
28
28
|
|
|
29
29
|
if (!project) {
|
|
30
30
|
try {
|
|
31
31
|
// Try to auto-detect project ID from ADC
|
|
32
32
|
project = await this.auth.getProjectId();
|
|
33
|
+
if (project) {
|
|
34
|
+
console.log(`[TokenTalos] Auto-detected Project ID from ADC: ${project}`);
|
|
35
|
+
}
|
|
33
36
|
} catch (e) {
|
|
34
|
-
|
|
37
|
+
console.warn(`[TokenTalos] Failed to auto-detect Project ID: ${e.message}`);
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
40
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meller/tokentalos",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Token Talos: The ORM for LLMs. A standalone gateway and library for cost-optimized, secure, and tracked prompt orchestration.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"bin/tokentalos.js",
|
|
21
21
|
"lib/engine/",
|
|
22
22
|
"api/index.js",
|
|
23
|
+
"api/setup.js",
|
|
23
24
|
"api/api/",
|
|
24
25
|
"api/middleware/",
|
|
25
26
|
"package.json"
|