@goldensheepai/toknxr-cli 0.2.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/.env +21 -0
- package/.env.example +21 -0
- package/README.md +238 -0
- package/interactions.log +8 -0
- package/lib/ai-analytics.js +296 -0
- package/lib/auth.js +73 -0
- package/lib/cli.js +382 -0
- package/lib/code-analysis.js +304 -0
- package/lib/code-review.js +319 -0
- package/lib/config.js +7 -0
- package/lib/dashboard.js +363 -0
- package/lib/hallucination-detector.js +272 -0
- package/lib/policy.js +49 -0
- package/lib/pricing.js +20 -0
- package/lib/proxy.js +359 -0
- package/lib/sync.js +95 -0
- package/package.json +38 -0
- package/src/ai-analytics.ts +418 -0
- package/src/auth.ts +80 -0
- package/src/cli.ts +447 -0
- package/src/code-analysis.ts +365 -0
- package/src/config.ts +10 -0
- package/src/dashboard.tsx +391 -0
- package/src/hallucination-detector.ts +368 -0
- package/src/policy.ts +55 -0
- package/src/pricing.ts +21 -0
- package/src/proxy.ts +438 -0
- package/src/sync.ts +129 -0
- package/start.sh +56 -0
- package/test-analysis.mjs +77 -0
- package/test-coding.mjs +27 -0
- package/test-generate-sample-data.js +118 -0
- package/test-proxy.mjs +25 -0
- package/toknxr.config.json +63 -0
- package/toknxr.policy.json +18 -0
- package/tsconfig.json +19 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
|
3
|
+
// Direct test of code analysis functions
|
4
|
+
import { analyzeCodeQuality, scoreEffectiveness, extractCodeFromResponse } from './src/code-analysis.ts';
|
5
|
+
|
6
|
+
console.log('๐งช Testing AI Code Quality Analysis Feature\n');
|
7
|
+
|
8
|
+
// Test 1: Code Quality Analysis
|
9
|
+
const testCode = `/**
|
10
|
+
* Calculate fibonacci sequence
|
11
|
+
* @param {number} n - number of terms
|
12
|
+
* @returns {number[]} fibonacci sequence
|
13
|
+
*/
|
14
|
+
function fibonacci(n) {
|
15
|
+
if (typeof n !== 'number' || n < 1 || n > 100) {
|
16
|
+
throw new Error('Invalid input: n must be a number between 1 and 100');
|
17
|
+
}
|
18
|
+
|
19
|
+
const sequence = [0, 1];
|
20
|
+
for (let i = 2; i < n; i++) {
|
21
|
+
sequence.push(sequence[i - 1] + sequence[i - 2]);
|
22
|
+
}
|
23
|
+
|
24
|
+
return sequence.slice(0, n);
|
25
|
+
}`;
|
26
|
+
|
27
|
+
console.log('1. Testing Code Quality Analysis:');
|
28
|
+
console.log('๐ Input code length:', testCode.length, 'characters');
|
29
|
+
console.log('๐ Code:');
|
30
|
+
console.log(testCode.substring(0, 100) + '...\n');
|
31
|
+
|
32
|
+
// Analyze code quality
|
33
|
+
const qualityMetrics = analyzeCodeQuality(testCode, 'javascript');
|
34
|
+
|
35
|
+
console.log('๐ Analysis Results:');
|
36
|
+
console.log('โ
Syntax Valid:', qualityMetrics.syntaxValid);
|
37
|
+
console.log('๐ Lines of Code:', qualityMetrics.linesOfCode);
|
38
|
+
console.log('๐ฏ Has Functions:', qualityMetrics.hasFunctions);
|
39
|
+
console.log('๐ Estimated Readability:', `${qualityMetrics.estimatedReadability}/10`);
|
40
|
+
console.log('โ ๏ธ Potential Issues:', qualityMetrics.potentialIssues.length || 'None');
|
41
|
+
console.log();
|
42
|
+
|
43
|
+
// Test 2: Effectiveness Scoring
|
44
|
+
const userPrompt = "Write a JavaScript function to calculate the fibonacci sequence up to n terms. Include proper error handling and use JSDoc comments.";
|
45
|
+
|
46
|
+
const aiResponse = `Here's a JavaScript function for calculating the Fibonacci sequence:
|
47
|
+
|
48
|
+
\`\`\`javascript
|
49
|
+
${testCode}
|
50
|
+
\`\`\`
|
51
|
+
|
52
|
+
This function includes error handling and JSDoc comments as requested.`;
|
53
|
+
|
54
|
+
console.log('2. Testing Effectiveness Analysis:');
|
55
|
+
console.log('๐ค User Prompt:', userPrompt.substring(0, 80) + '...');
|
56
|
+
console.log('๐ค AI Response preview:', aiResponse.substring(0, 80) + '...\n');
|
57
|
+
|
58
|
+
// Extract code and score effectiveness
|
59
|
+
const extractedCode = extractCodeFromResponse(aiResponse);
|
60
|
+
if (extractedCode) {
|
61
|
+
console.log('๐ Extracted Code Language:', extractedCode.language);
|
62
|
+
|
63
|
+
const effectiveness = scoreEffectiveness(userPrompt, aiResponse, extractedCode.code);
|
64
|
+
|
65
|
+
console.log('๐ Effectiveness Scores:');
|
66
|
+
console.log('๐ฏ Prompt Clarity Match:', `${effectiveness.promptClarityMatch}/100`);
|
67
|
+
console.log('โ
Code Completeness:', `${effectiveness.codeCompleteness}/100`);
|
68
|
+
console.log('๐ง Code Correctness:', `${effectiveness.codeCorrectness}/100`);
|
69
|
+
console.log('โก Code Efficiency:', `${effectiveness.codeEfficiency}/100`);
|
70
|
+
console.log('๐ Overall Effectiveness:', `${effectiveness.overallEffectiveness}/100`);
|
71
|
+
} else {
|
72
|
+
console.log('โ Failed to extract code from response');
|
73
|
+
}
|
74
|
+
|
75
|
+
console.log('\n๐ Code Analysis Feature is WORKING! ๐');
|
76
|
+
console.log('\n๐ก This same analysis runs on every coding request through the TokNxr proxy.');
|
77
|
+
console.log('๐ It provides insights into AI performance beyond just token costs.');
|
package/test-coding.mjs
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
// Test the code analysis feature with Gemini (working API)
|
2
|
+
import axios from 'axios';
|
3
|
+
|
4
|
+
const prompt = "Write a JavaScript function to calculate the fibonacci sequence up to n terms. Include proper error handling and comments.";
|
5
|
+
|
6
|
+
const geminiRequest = {
|
7
|
+
contents: [{
|
8
|
+
parts: [{
|
9
|
+
text: prompt
|
10
|
+
}]
|
11
|
+
}]
|
12
|
+
};
|
13
|
+
|
14
|
+
console.log('๐ฏ Sending coding request to Gemini via proxy...');
|
15
|
+
console.log('๐ Prompt:', prompt.substring(0, 100) + '...');
|
16
|
+
|
17
|
+
try {
|
18
|
+
const response = await axios.post('http://localhost:8787/gemini/v1beta/models/gemini-2.5-flash:generateContent', geminiRequest, {
|
19
|
+
headers: { 'Content-Type': 'application/json' }
|
20
|
+
});
|
21
|
+
|
22
|
+
console.log('โ
Request successful!');
|
23
|
+
console.log('๐ Response preview:', response.data.candidates[0].content.parts[0].text.substring(0, 200) + '...');
|
24
|
+
|
25
|
+
} catch (error) {
|
26
|
+
console.error('โ Request failed:', error.response?.data || error.message);
|
27
|
+
}
|
@@ -0,0 +1,118 @@
|
|
1
|
+
// Quick script to generate sample coding interactions for testing the dashboard
|
2
|
+
import fs from 'fs';
|
3
|
+
import path from 'path';
|
4
|
+
import { fileURLToPath } from 'url';
|
5
|
+
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
7
|
+
const __dirname = path.dirname(__filename);
|
8
|
+
|
9
|
+
const sampleInteractions = [
|
10
|
+
{
|
11
|
+
timestamp: new Date(Date.now() - 1000 * 60 * 30).toISOString(), // 30 min ago
|
12
|
+
provider: 'Gemini-Pro',
|
13
|
+
model: 'gemini-2.5-flash',
|
14
|
+
promptTokens: 150,
|
15
|
+
completionTokens: 200,
|
16
|
+
totalTokens: 350,
|
17
|
+
costUSD: 0.05,
|
18
|
+
taskType: 'coding',
|
19
|
+
userPrompt: 'Create a React component for a todo list',
|
20
|
+
aiResponse: 'Here\'s a React todo component...',
|
21
|
+
extractedCode: `function TodoList() {
|
22
|
+
const [todos, setTodos] = useState([]);
|
23
|
+
return (
|
24
|
+
<div>
|
25
|
+
<h1>My Todos</h1>
|
26
|
+
{/* Todo implementation */}
|
27
|
+
</div>
|
28
|
+
);
|
29
|
+
}`,
|
30
|
+
codeQualityScore: 85,
|
31
|
+
codeQualityMetrics: {
|
32
|
+
syntaxValid: true,
|
33
|
+
estimatedReadability: 0.8,
|
34
|
+
hasFunctions: true,
|
35
|
+
hasClasses: false,
|
36
|
+
linesOfCode: 12,
|
37
|
+
potentialIssues: []
|
38
|
+
},
|
39
|
+
effectivenessScore: 88
|
40
|
+
},
|
41
|
+
{
|
42
|
+
timestamp: new Date(Date.now() - 1000 * 60 * 15).toISOString(), // 15 min ago
|
43
|
+
provider: 'OpenAI-GPT4',
|
44
|
+
model: 'gpt-4',
|
45
|
+
promptTokens: 80,
|
46
|
+
completionTokens: 120,
|
47
|
+
totalTokens: 200,
|
48
|
+
costUSD: 0.02,
|
49
|
+
taskType: 'coding',
|
50
|
+
userPrompt: 'Write a Python function to calculate fibonacci',
|
51
|
+
aiResponse: 'def fibonacci(n):...',
|
52
|
+
extractedCode: `def fibonacci(n):
|
53
|
+
if n <= 1:
|
54
|
+
return n
|
55
|
+
return fibonacci(n-1) + fibonacci(n-2)`,
|
56
|
+
codeQualityScore: 92,
|
57
|
+
codeQualityMetrics: {
|
58
|
+
syntaxValid: true,
|
59
|
+
estimatedReadability: 0.9,
|
60
|
+
hasFunctions: true,
|
61
|
+
hasClasses: false,
|
62
|
+
linesOfCode: 4,
|
63
|
+
potentialIssues: []
|
64
|
+
},
|
65
|
+
effectivenessScore: 95
|
66
|
+
},
|
67
|
+
{
|
68
|
+
timestamp: new Date(Date.now() - 1000 * 60 * 5).toISOString(), // 5 min ago
|
69
|
+
provider: 'Gemini-Pro',
|
70
|
+
model: 'gemini-2.5-flash',
|
71
|
+
promptTokens: 200,
|
72
|
+
completionTokens: 300,
|
73
|
+
totalTokens: 500,
|
74
|
+
costUSD: 0.08,
|
75
|
+
taskType: 'coding',
|
76
|
+
userPrompt: 'Create a TypeScript API endpoint',
|
77
|
+
aiResponse: 'Here\'s a TypeScript API endpoint...',
|
78
|
+
extractedCode: `import express from 'express';
|
79
|
+
|
80
|
+
const app = express();
|
81
|
+
|
82
|
+
app.get('/api/users', async (req, res) => {
|
83
|
+
// Implementation here
|
84
|
+
});
|
85
|
+
|
86
|
+
export default app;`,
|
87
|
+
codeQualityScore: 78,
|
88
|
+
codeQualityMetrics: {
|
89
|
+
syntaxValid: true,
|
90
|
+
estimatedReadability: 0.7,
|
91
|
+
hasFunctions: true,
|
92
|
+
hasClasses: false,
|
93
|
+
linesOfCode: 8,
|
94
|
+
potentialIssues: ['Missing error handling']
|
95
|
+
},
|
96
|
+
effectivenessScore: 82
|
97
|
+
}
|
98
|
+
];
|
99
|
+
|
100
|
+
const logFilePath = path.resolve(process.cwd(), 'interactions.log');
|
101
|
+
|
102
|
+
// Read existing log file
|
103
|
+
let existingContent = '';
|
104
|
+
if (fs.existsSync(logFilePath)) {
|
105
|
+
existingContent = fs.readFileSync(logFilePath, 'utf8');
|
106
|
+
}
|
107
|
+
|
108
|
+
// Combine existing and sample data
|
109
|
+
const allInteractions = existingContent.trim()
|
110
|
+
? existingContent.trim() + '\n' + sampleInteractions.map(i => JSON.stringify(i)).join('\n')
|
111
|
+
: sampleInteractions.map(i => JSON.stringify(i)).join('\n');
|
112
|
+
|
113
|
+
// Write back to file
|
114
|
+
fs.writeFileSync(logFilePath, allInteractions + '\n');
|
115
|
+
|
116
|
+
console.log(`โ
Generated ${sampleInteractions.length} sample coding interactions`);
|
117
|
+
console.log(`๐ Total interactions in log: ${allInteractions.split('\n').filter(Boolean).length}`);
|
118
|
+
console.log(`๐ Dashboard available at: http://localhost:8788/dashboard`);
|
package/test-proxy.mjs
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
import axios from 'axios';
|
2
|
+
|
3
|
+
const PROXY_URL = 'http://localhost:8787/api/chat';
|
4
|
+
|
5
|
+
async function runTest() {
|
6
|
+
console.log('Sending test request to proxy for Ollama...');
|
7
|
+
try {
|
8
|
+
const response = await axios.post(PROXY_URL, {
|
9
|
+
model: 'llama3.2:3b',
|
10
|
+
messages: [{ role: 'user', content: 'Hello, world!' }],
|
11
|
+
stream: false, // For this test, we will not stream the response
|
12
|
+
}, {
|
13
|
+
headers: {
|
14
|
+
'Content-Type': 'application/json',
|
15
|
+
},
|
16
|
+
});
|
17
|
+
|
18
|
+
console.log('Received response from proxy:');
|
19
|
+
console.log(response.data);
|
20
|
+
} catch (error) {
|
21
|
+
console.error('Error during test:', error.message);
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
runTest();
|
@@ -0,0 +1,63 @@
|
|
1
|
+
{
|
2
|
+
"providers": [
|
3
|
+
{
|
4
|
+
"name": "Ollama-Llama3",
|
5
|
+
"routePrefix": "/ollama",
|
6
|
+
"targetUrl": "http://localhost:11434/api/chat",
|
7
|
+
"apiKeyEnvVar": null,
|
8
|
+
"tokenMapping": {
|
9
|
+
"prompt": "prompt_eval_count",
|
10
|
+
"completion": "eval_count"
|
11
|
+
}
|
12
|
+
},
|
13
|
+
{
|
14
|
+
"name": "Gemini-Pro",
|
15
|
+
"routePrefix": "/gemini",
|
16
|
+
"targetUrl": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent",
|
17
|
+
"apiKeyEnvVar": "GEMINI_API_KEY",
|
18
|
+
"authHeader": "x-goog-api-key",
|
19
|
+
"tokenMapping": {
|
20
|
+
"prompt": "usageMetadata.promptTokenCount",
|
21
|
+
"completion": "usageMetadata.candidatesTokenCount",
|
22
|
+
"total": "usageMetadata.totalTokenCount"
|
23
|
+
}
|
24
|
+
},
|
25
|
+
{
|
26
|
+
"name": "Gemini-Free",
|
27
|
+
"routePrefix": "/gemini-free",
|
28
|
+
"targetUrl": "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent",
|
29
|
+
"apiKeyEnvVar": "GEMINI_API_KEY",
|
30
|
+
"authHeader": "x-goog-api-key",
|
31
|
+
"tokenMapping": {
|
32
|
+
"prompt": "usageMetadata.promptTokenCount",
|
33
|
+
"completion": "usageMetadata.candidatesTokenCount",
|
34
|
+
"total": "usageMetadata.totalTokenCount"
|
35
|
+
}
|
36
|
+
},
|
37
|
+
{
|
38
|
+
"name": "OpenAI-GPT4",
|
39
|
+
"routePrefix": "/openai",
|
40
|
+
"targetUrl": "https://api.openai.com/v1/chat/completions",
|
41
|
+
"apiKeyEnvVar": "OPENAI_API_KEY",
|
42
|
+
"authHeader": "Authorization",
|
43
|
+
"authScheme": "Bearer",
|
44
|
+
"tokenMapping": {
|
45
|
+
"prompt": "usage.prompt_tokens",
|
46
|
+
"completion": "usage.completion_tokens",
|
47
|
+
"total": "usage.total_tokens"
|
48
|
+
}
|
49
|
+
},
|
50
|
+
{
|
51
|
+
"name": "Anthropic-Claude",
|
52
|
+
"routePrefix": "/anthropic",
|
53
|
+
"targetUrl": "https://api.anthropic.com/v1/messages",
|
54
|
+
"apiKeyEnvVar": "ANTHROPIC_API_KEY",
|
55
|
+
"authHeader": "x-api-key",
|
56
|
+
"tokenMapping": {
|
57
|
+
"prompt": "usage.input_tokens",
|
58
|
+
"completion": "usage.output_tokens",
|
59
|
+
"total": "usage.total_tokens"
|
60
|
+
}
|
61
|
+
}
|
62
|
+
]
|
63
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
{
|
2
|
+
"version": "1",
|
3
|
+
"monthlyUSD": 50,
|
4
|
+
"perProviderMonthlyUSD": {
|
5
|
+
"Gemini-Pro": 30,
|
6
|
+
"Gemini-Free": 10,
|
7
|
+
"Ollama-Llama3": 0,
|
8
|
+
"OpenAI-GPT4": 20,
|
9
|
+
"Anthropic-Claude": 15
|
10
|
+
},
|
11
|
+
"webhookUrl": "",
|
12
|
+
"_comments": {
|
13
|
+
"monthlyUSD": "Total monthly budget across all providers in USD",
|
14
|
+
"perProviderMonthlyUSD": "Individual provider budget limits",
|
15
|
+
"webhookUrl": "Optional webhook URL for budget alert notifications (leave empty for no alerts)",
|
16
|
+
"supportedProviders": ["Gemini-Pro", "Ollama-Llama3", "OpenAI-GPT4", "Gemini-Free", "Anthropic-Claude"]
|
17
|
+
}
|
18
|
+
}
|
package/tsconfig.json
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
{
|
2
|
+
"compilerOptions": {
|
3
|
+
"target": "ES2020",
|
4
|
+
"module": "NodeNext",
|
5
|
+
"moduleResolution": "NodeNext",
|
6
|
+
"outDir": "./lib",
|
7
|
+
"rootDir": "./src",
|
8
|
+
"strict": true,
|
9
|
+
"esModuleInterop": true,
|
10
|
+
"allowSyntheticDefaultImports": true,
|
11
|
+
"skipLibCheck": true,
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
13
|
+
"downlevelIteration": true,
|
14
|
+
"jsx": "react-jsx",
|
15
|
+
"jsxImportSource": "react"
|
16
|
+
},
|
17
|
+
"include": ["src/**/*"],
|
18
|
+
"exclude": ["node_modules", "lib"]
|
19
|
+
}
|