tuneprompt 1.0.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/LICENSE +21 -0
- package/README.md +151 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +146 -0
- package/dist/commands/activate.d.ts +1 -0
- package/dist/commands/activate.js +91 -0
- package/dist/commands/fix.d.ts +1 -0
- package/dist/commands/fix.js +187 -0
- package/dist/commands/history.d.ts +5 -0
- package/dist/commands/history.js +63 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +96 -0
- package/dist/commands/run.d.ts +9 -0
- package/dist/commands/run.js +216 -0
- package/dist/db/migrate.d.ts +2 -0
- package/dist/db/migrate.js +8 -0
- package/dist/engine/constraintExtractor.d.ts +8 -0
- package/dist/engine/constraintExtractor.js +54 -0
- package/dist/engine/loader.d.ts +5 -0
- package/dist/engine/loader.js +74 -0
- package/dist/engine/metaPrompt.d.ts +11 -0
- package/dist/engine/metaPrompt.js +129 -0
- package/dist/engine/optimizer.d.ts +26 -0
- package/dist/engine/optimizer.js +246 -0
- package/dist/engine/reporter.d.ts +7 -0
- package/dist/engine/reporter.js +58 -0
- package/dist/engine/runner.d.ts +9 -0
- package/dist/engine/runner.js +169 -0
- package/dist/engine/shadowTester.d.ts +11 -0
- package/dist/engine/shadowTester.js +156 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +26 -0
- package/dist/providers/anthropic.d.ts +12 -0
- package/dist/providers/anthropic.js +51 -0
- package/dist/providers/base.d.ts +15 -0
- package/dist/providers/base.js +10 -0
- package/dist/providers/openai.d.ts +12 -0
- package/dist/providers/openai.js +58 -0
- package/dist/providers/openrouter.d.ts +11 -0
- package/dist/providers/openrouter.js +83 -0
- package/dist/scoring/exact-match.d.ts +1 -0
- package/dist/scoring/exact-match.js +8 -0
- package/dist/scoring/json-validator.d.ts +4 -0
- package/dist/scoring/json-validator.js +29 -0
- package/dist/scoring/semantic.d.ts +8 -0
- package/dist/scoring/semantic.js +107 -0
- package/dist/services/cloud.service.d.ts +49 -0
- package/dist/services/cloud.service.js +82 -0
- package/dist/storage/database.d.ts +10 -0
- package/dist/storage/database.js +179 -0
- package/dist/types/fix.d.ts +28 -0
- package/dist/types/fix.js +2 -0
- package/dist/types/index.d.ts +58 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/analytics.d.ts +2 -0
- package/dist/utils/analytics.js +22 -0
- package/dist/utils/config.d.ts +3 -0
- package/dist/utils/config.js +70 -0
- package/dist/utils/errorHandler.d.ts +14 -0
- package/dist/utils/errorHandler.js +40 -0
- package/dist/utils/license.d.ts +40 -0
- package/dist/utils/license.js +207 -0
- package/dist/utils/storage.d.ts +2 -0
- package/dist/utils/storage.js +25 -0
- package/package.json +76 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.loadConfig = exports.runTests = void 0;
|
|
18
|
+
__exportStar(require("./engine/runner"), exports);
|
|
19
|
+
__exportStar(require("./engine/loader"), exports);
|
|
20
|
+
__exportStar(require("./engine/optimizer"), exports);
|
|
21
|
+
__exportStar(require("./services/cloud.service"), exports);
|
|
22
|
+
__exportStar(require("./types"), exports);
|
|
23
|
+
var run_1 = require("./commands/run");
|
|
24
|
+
Object.defineProperty(exports, "runTests", { enumerable: true, get: function () { return run_1.runTests; } });
|
|
25
|
+
var config_1 = require("./utils/config");
|
|
26
|
+
Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return config_1.loadConfig; } });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseProvider, ProviderResponse } from './base';
|
|
2
|
+
import { ProviderConfig } from '../types';
|
|
3
|
+
export declare class AnthropicProvider extends BaseProvider {
|
|
4
|
+
private client;
|
|
5
|
+
constructor(config: ProviderConfig);
|
|
6
|
+
complete(prompt: string | {
|
|
7
|
+
system?: string;
|
|
8
|
+
user: string;
|
|
9
|
+
}): Promise<ProviderResponse>;
|
|
10
|
+
getEmbedding(text: string): Promise<number[]>;
|
|
11
|
+
private calculateCost;
|
|
12
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AnthropicProvider = void 0;
|
|
7
|
+
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
8
|
+
const base_1 = require("./base");
|
|
9
|
+
class AnthropicProvider extends base_1.BaseProvider {
|
|
10
|
+
client;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
super(config);
|
|
13
|
+
this.client = new sdk_1.default({
|
|
14
|
+
apiKey: config.apiKey
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async complete(prompt) {
|
|
18
|
+
const systemPrompt = typeof prompt === 'string' ? undefined : prompt.system;
|
|
19
|
+
const userPrompt = typeof prompt === 'string' ? prompt : prompt.user;
|
|
20
|
+
const response = await this.client.messages.create({
|
|
21
|
+
model: this.config.model,
|
|
22
|
+
max_tokens: this.config.maxTokens || 1000,
|
|
23
|
+
temperature: this.config.temperature,
|
|
24
|
+
system: systemPrompt,
|
|
25
|
+
messages: [
|
|
26
|
+
{ role: 'user', content: userPrompt }
|
|
27
|
+
]
|
|
28
|
+
});
|
|
29
|
+
const content = response.content[0].type === 'text'
|
|
30
|
+
? response.content[0].text
|
|
31
|
+
: '';
|
|
32
|
+
return {
|
|
33
|
+
content,
|
|
34
|
+
tokens: response.usage.input_tokens + response.usage.output_tokens,
|
|
35
|
+
cost: this.calculateCost(response.usage.input_tokens, response.usage.output_tokens)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async getEmbedding(text) {
|
|
39
|
+
// Anthropic doesn't provide embeddings, use OpenAI as fallback
|
|
40
|
+
// or implement a separate embedding service
|
|
41
|
+
throw new Error('Anthropic does not support embeddings. Use OpenAI provider.');
|
|
42
|
+
}
|
|
43
|
+
calculateCost(inputTokens, outputTokens) {
|
|
44
|
+
// Claude Sonnet 4 pricing (example)
|
|
45
|
+
const inputCostPer1M = 3.00;
|
|
46
|
+
const outputCostPer1M = 15.00;
|
|
47
|
+
return ((inputTokens / 1_000_000) * inputCostPer1M +
|
|
48
|
+
(outputTokens / 1_000_000) * outputCostPer1M);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.AnthropicProvider = AnthropicProvider;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ProviderConfig } from '../types';
|
|
2
|
+
export interface ProviderResponse {
|
|
3
|
+
content: string;
|
|
4
|
+
tokens?: number;
|
|
5
|
+
cost?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare abstract class BaseProvider {
|
|
8
|
+
protected config: ProviderConfig;
|
|
9
|
+
constructor(config: ProviderConfig);
|
|
10
|
+
abstract complete(prompt: string | {
|
|
11
|
+
system?: string;
|
|
12
|
+
user: string;
|
|
13
|
+
}): Promise<ProviderResponse>;
|
|
14
|
+
abstract getEmbedding(text: string): Promise<number[]>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseProvider, ProviderResponse } from './base';
|
|
2
|
+
import { ProviderConfig } from '../types';
|
|
3
|
+
export declare class OpenAIProvider extends BaseProvider {
|
|
4
|
+
private client;
|
|
5
|
+
constructor(config: ProviderConfig);
|
|
6
|
+
complete(prompt: string | {
|
|
7
|
+
system?: string;
|
|
8
|
+
user: string;
|
|
9
|
+
}): Promise<ProviderResponse>;
|
|
10
|
+
getEmbedding(text: string): Promise<number[]>;
|
|
11
|
+
private calculateCost;
|
|
12
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.OpenAIProvider = void 0;
|
|
7
|
+
const openai_1 = __importDefault(require("openai"));
|
|
8
|
+
const base_1 = require("./base");
|
|
9
|
+
class OpenAIProvider extends base_1.BaseProvider {
|
|
10
|
+
client;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
super(config);
|
|
13
|
+
this.client = new openai_1.default({
|
|
14
|
+
apiKey: config.apiKey,
|
|
15
|
+
baseURL: config.baseURL
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
async complete(prompt) {
|
|
19
|
+
const messages = [];
|
|
20
|
+
if (typeof prompt === 'string') {
|
|
21
|
+
messages.push({ role: 'user', content: prompt });
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
if (prompt.system) {
|
|
25
|
+
messages.push({ role: 'system', content: prompt.system });
|
|
26
|
+
}
|
|
27
|
+
messages.push({ role: 'user', content: prompt.user });
|
|
28
|
+
}
|
|
29
|
+
const response = await this.client.chat.completions.create({
|
|
30
|
+
model: this.config.model,
|
|
31
|
+
messages,
|
|
32
|
+
max_tokens: this.config.maxTokens,
|
|
33
|
+
temperature: this.config.temperature
|
|
34
|
+
});
|
|
35
|
+
const content = response.choices[0]?.message?.content || '';
|
|
36
|
+
const tokens = response.usage?.total_tokens;
|
|
37
|
+
return {
|
|
38
|
+
content,
|
|
39
|
+
tokens,
|
|
40
|
+
cost: this.calculateCost(tokens || 0)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async getEmbedding(text) {
|
|
44
|
+
const response = await this.client.embeddings.create({
|
|
45
|
+
model: 'text-embedding-3-small',
|
|
46
|
+
input: text
|
|
47
|
+
});
|
|
48
|
+
return response.data[0].embedding;
|
|
49
|
+
}
|
|
50
|
+
calculateCost(tokens) {
|
|
51
|
+
// Pricing for GPT-4o (example rates)
|
|
52
|
+
const inputCostPer1k = 0.005;
|
|
53
|
+
const outputCostPer1k = 0.015;
|
|
54
|
+
// Simplified: assume 50/50 input/output split
|
|
55
|
+
return ((tokens / 1000) * (inputCostPer1k + outputCostPer1k)) / 2;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.OpenAIProvider = OpenAIProvider;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseProvider, ProviderResponse } from './base';
|
|
2
|
+
import { ProviderConfig } from '../types';
|
|
3
|
+
export declare class OpenRouterProvider extends BaseProvider {
|
|
4
|
+
private baseURL;
|
|
5
|
+
constructor(config: ProviderConfig);
|
|
6
|
+
complete(prompt: string | {
|
|
7
|
+
system?: string;
|
|
8
|
+
user: string;
|
|
9
|
+
}): Promise<ProviderResponse>;
|
|
10
|
+
getEmbedding(text: string): Promise<number[]>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.OpenRouterProvider = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const base_1 = require("./base");
|
|
9
|
+
class OpenRouterProvider extends base_1.BaseProvider {
|
|
10
|
+
baseURL;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
super(config);
|
|
13
|
+
this.baseURL = config.baseURL || 'https://openrouter.ai/api/v1';
|
|
14
|
+
}
|
|
15
|
+
async complete(prompt) {
|
|
16
|
+
const messages = [];
|
|
17
|
+
if (typeof prompt === 'string') {
|
|
18
|
+
messages.push({ role: 'user', content: prompt });
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
if (prompt.system) {
|
|
22
|
+
messages.push({ role: 'system', content: prompt.system });
|
|
23
|
+
}
|
|
24
|
+
messages.push({ role: 'user', content: prompt.user });
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const response = await axios_1.default.post(`${this.baseURL}/chat/completions`, {
|
|
28
|
+
model: this.config.model,
|
|
29
|
+
messages,
|
|
30
|
+
max_tokens: this.config.maxTokens,
|
|
31
|
+
temperature: this.config.temperature
|
|
32
|
+
}, {
|
|
33
|
+
headers: {
|
|
34
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
35
|
+
'HTTP-Referer': 'https://github.com/tuneprompt/tuneprompt',
|
|
36
|
+
'X-Title': 'TunePrompt',
|
|
37
|
+
'Content-Type': 'application/json'
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
const data = response.data;
|
|
41
|
+
const content = data.choices?.[0]?.message?.content || '';
|
|
42
|
+
const tokens = data.usage?.total_tokens || 0;
|
|
43
|
+
return {
|
|
44
|
+
content,
|
|
45
|
+
tokens,
|
|
46
|
+
cost: 0
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (error.response) {
|
|
51
|
+
const errorMsg = error.response.data?.error?.message || JSON.stringify(error.response.data);
|
|
52
|
+
throw new Error(`OpenRouter API Error (${error.response.status}): ${errorMsg}`);
|
|
53
|
+
}
|
|
54
|
+
throw new Error(`OpenRouter network error: ${error.message}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async getEmbedding(text) {
|
|
58
|
+
try {
|
|
59
|
+
// Attempt to use OpenRouter's embedding endpoint (which proxies to OpenAI or others)
|
|
60
|
+
// Note: User must be entitled to use the requested embedding model
|
|
61
|
+
const response = await axios_1.default.post(`${this.baseURL}/embeddings`, {
|
|
62
|
+
model: 'text-embedding-3-small', // Default fallback, customizable in future
|
|
63
|
+
input: text
|
|
64
|
+
}, {
|
|
65
|
+
headers: {
|
|
66
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
67
|
+
'HTTP-Referer': 'https://github.com/tuneprompt/tuneprompt',
|
|
68
|
+
'X-Title': 'TunePrompt',
|
|
69
|
+
'Content-Type': 'application/json'
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return response.data.data[0].embedding;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (error.response) {
|
|
76
|
+
const errorMsg = error.response.data?.error?.message || JSON.stringify(error.response.data);
|
|
77
|
+
throw new Error(`OpenRouter Embedding Error (${error.response.status}): ${errorMsg}`);
|
|
78
|
+
}
|
|
79
|
+
throw new Error(`OpenRouter embedding network error: ${error.message}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.OpenRouterProvider = OpenRouterProvider;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function exactMatch(expected: string, actual: string): number;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.exactMatch = exactMatch;
|
|
4
|
+
function exactMatch(expected, actual) {
|
|
5
|
+
const normalizedExpected = expected.trim().toLowerCase();
|
|
6
|
+
const normalizedActual = actual.trim().toLowerCase();
|
|
7
|
+
return normalizedExpected === normalizedActual ? 1.0 : 0.0;
|
|
8
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateJSON = validateJSON;
|
|
4
|
+
function validateJSON(expected, actual) {
|
|
5
|
+
try {
|
|
6
|
+
const parsed = JSON.parse(actual);
|
|
7
|
+
// Check if structure matches
|
|
8
|
+
const matches = deepCompare(expected, parsed);
|
|
9
|
+
return { score: matches ? 1.0 : 0.0 };
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
return {
|
|
13
|
+
score: 0.0,
|
|
14
|
+
error: `Invalid JSON: ${error.message}`
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function deepCompare(expected, actual) {
|
|
19
|
+
if (typeof expected !== typeof actual)
|
|
20
|
+
return false;
|
|
21
|
+
if (typeof expected === 'object' && expected !== null) {
|
|
22
|
+
const expectedKeys = Object.keys(expected);
|
|
23
|
+
const actualKeys = Object.keys(actual);
|
|
24
|
+
if (expectedKeys.length !== actualKeys.length)
|
|
25
|
+
return false;
|
|
26
|
+
return expectedKeys.every(key => actualKeys.includes(key) && deepCompare(expected[key], actual[key]));
|
|
27
|
+
}
|
|
28
|
+
return expected === actual;
|
|
29
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BaseProvider } from '../providers/base';
|
|
2
|
+
export declare class SemanticScorer {
|
|
3
|
+
private embeddingProvider;
|
|
4
|
+
constructor(provider: BaseProvider);
|
|
5
|
+
score(expected: string, actual: string): Promise<number>;
|
|
6
|
+
private cosineSimilarity;
|
|
7
|
+
}
|
|
8
|
+
export declare function calculateSemanticSimilarity(actual: string, expected: string): Promise<number>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.SemanticScorer = void 0;
|
|
37
|
+
exports.calculateSemanticSimilarity = calculateSemanticSimilarity;
|
|
38
|
+
class SemanticScorer {
|
|
39
|
+
embeddingProvider;
|
|
40
|
+
constructor(provider) {
|
|
41
|
+
this.embeddingProvider = provider;
|
|
42
|
+
}
|
|
43
|
+
async score(expected, actual) {
|
|
44
|
+
const [expectedEmb, actualEmb] = await Promise.all([
|
|
45
|
+
this.embeddingProvider.getEmbedding(expected),
|
|
46
|
+
this.embeddingProvider.getEmbedding(actual)
|
|
47
|
+
]);
|
|
48
|
+
return this.cosineSimilarity(expectedEmb, actualEmb);
|
|
49
|
+
}
|
|
50
|
+
cosineSimilarity(a, b) {
|
|
51
|
+
if (a.length !== b.length) {
|
|
52
|
+
throw new Error('Vectors must have same length');
|
|
53
|
+
}
|
|
54
|
+
let dotProduct = 0;
|
|
55
|
+
let normA = 0;
|
|
56
|
+
let normB = 0;
|
|
57
|
+
for (let i = 0; i < a.length; i++) {
|
|
58
|
+
dotProduct += a[i] * b[i];
|
|
59
|
+
normA += a[i] * a[i];
|
|
60
|
+
normB += b[i] * b[i];
|
|
61
|
+
}
|
|
62
|
+
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.SemanticScorer = SemanticScorer;
|
|
66
|
+
async function calculateSemanticSimilarity(actual, expected) {
|
|
67
|
+
const providersToTry = [];
|
|
68
|
+
// Prioritize OpenAI if key looks potentially valid
|
|
69
|
+
if (process.env.OPENAI_API_KEY && !process.env.OPENAI_API_KEY.startsWith('api_key')) {
|
|
70
|
+
providersToTry.push('openai');
|
|
71
|
+
}
|
|
72
|
+
// Always try OpenRouter if key is present
|
|
73
|
+
if (process.env.OPENROUTER_API_KEY) {
|
|
74
|
+
providersToTry.push('openrouter');
|
|
75
|
+
}
|
|
76
|
+
// Fallback to OpenAI if nothing else was added
|
|
77
|
+
if (providersToTry.length === 0) {
|
|
78
|
+
providersToTry.push('openai');
|
|
79
|
+
}
|
|
80
|
+
let lastError;
|
|
81
|
+
for (const providerType of providersToTry) {
|
|
82
|
+
try {
|
|
83
|
+
let provider;
|
|
84
|
+
if (providerType === 'openai') {
|
|
85
|
+
const { OpenAIProvider } = await Promise.resolve().then(() => __importStar(require('../providers/openai')));
|
|
86
|
+
provider = new OpenAIProvider({
|
|
87
|
+
apiKey: process.env.OPENAI_API_KEY || '',
|
|
88
|
+
model: 'text-embedding-3-small'
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
const { OpenRouterProvider } = await Promise.resolve().then(() => __importStar(require('../providers/openrouter')));
|
|
93
|
+
provider = new OpenRouterProvider({
|
|
94
|
+
apiKey: process.env.OPENROUTER_API_KEY || '',
|
|
95
|
+
model: 'text-embedding-3-small'
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const scorer = new SemanticScorer(provider);
|
|
99
|
+
return await scorer.score(expected, actual);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
lastError = error;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
throw lastError || new Error('No embedding provider available');
|
|
107
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface RunData {
|
|
2
|
+
project_id: string;
|
|
3
|
+
commit_hash?: string;
|
|
4
|
+
branch_name?: string;
|
|
5
|
+
commit_message?: string;
|
|
6
|
+
environment: 'local' | 'ci' | 'production';
|
|
7
|
+
ci_provider?: string;
|
|
8
|
+
total_tests: number;
|
|
9
|
+
passed_tests: number;
|
|
10
|
+
failed_tests: number;
|
|
11
|
+
duration_ms: number;
|
|
12
|
+
cost_usd: number;
|
|
13
|
+
started_at: string;
|
|
14
|
+
completed_at: string;
|
|
15
|
+
test_results: CloudTestResult[];
|
|
16
|
+
}
|
|
17
|
+
export interface CloudTestResult {
|
|
18
|
+
test_name: string;
|
|
19
|
+
test_description?: string;
|
|
20
|
+
prompt: string;
|
|
21
|
+
input_data?: any;
|
|
22
|
+
expected_output: string;
|
|
23
|
+
actual_output: string;
|
|
24
|
+
score?: number;
|
|
25
|
+
method: string;
|
|
26
|
+
status: 'pass' | 'fail' | 'error';
|
|
27
|
+
model: string;
|
|
28
|
+
tokens_used?: number;
|
|
29
|
+
latency_ms?: number;
|
|
30
|
+
cost_usd?: number;
|
|
31
|
+
error_message?: string;
|
|
32
|
+
error_type?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare class CloudService {
|
|
35
|
+
private backendUrl;
|
|
36
|
+
private subscriptionId?;
|
|
37
|
+
constructor();
|
|
38
|
+
init(): Promise<void>;
|
|
39
|
+
private getSubscriptionId;
|
|
40
|
+
uploadRun(data: RunData): Promise<{
|
|
41
|
+
success: boolean;
|
|
42
|
+
run_id?: string;
|
|
43
|
+
url?: string;
|
|
44
|
+
error?: string;
|
|
45
|
+
}>;
|
|
46
|
+
createProject(name: string, description?: string): Promise<any>;
|
|
47
|
+
getProjects(): Promise<any[]>;
|
|
48
|
+
isAuthenticated(): Promise<boolean>;
|
|
49
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CloudService = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const license_1 = require("../utils/license");
|
|
9
|
+
class CloudService {
|
|
10
|
+
backendUrl;
|
|
11
|
+
subscriptionId;
|
|
12
|
+
constructor() {
|
|
13
|
+
this.backendUrl = process.env.TUNEPROMPT_API_URL || process.env.BACKEND_URL || 'https://api.tuneprompt.com';
|
|
14
|
+
}
|
|
15
|
+
async init() {
|
|
16
|
+
// Load subscription ID from local storage (Phase 2 activation)
|
|
17
|
+
this.subscriptionId = await this.getSubscriptionId();
|
|
18
|
+
}
|
|
19
|
+
async getSubscriptionId() {
|
|
20
|
+
const license = (0, license_1.loadLicense)();
|
|
21
|
+
return license?.subscriptionId;
|
|
22
|
+
}
|
|
23
|
+
async uploadRun(data) {
|
|
24
|
+
if (!this.subscriptionId) {
|
|
25
|
+
return { success: false, error: 'Not activated. Run `tuneprompt activate` first.' };
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const response = await axios_1.default.post(`${this.backendUrl}/api/cloud/ingest-run`, data, {
|
|
29
|
+
headers: {
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
'x-subscription-id': this.subscriptionId,
|
|
32
|
+
},
|
|
33
|
+
timeout: 30000,
|
|
34
|
+
});
|
|
35
|
+
return response.data;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error('Failed to upload run:', error.message);
|
|
39
|
+
return {
|
|
40
|
+
success: false,
|
|
41
|
+
error: error.response?.data?.error || error.message
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async createProject(name, description) {
|
|
46
|
+
if (!this.subscriptionId) {
|
|
47
|
+
throw new Error('Not activated');
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const response = await axios_1.default.post(`${this.backendUrl}/api/cloud/projects`, { name, description }, {
|
|
51
|
+
headers: {
|
|
52
|
+
'Content-Type': 'application/json',
|
|
53
|
+
'x-subscription-id': this.subscriptionId,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
return response.data.project;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
throw new Error(error.response?.data?.error || error.message);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async getProjects() {
|
|
63
|
+
if (!this.subscriptionId) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const response = await axios_1.default.get(`${this.backendUrl}/api/cloud/projects`, {
|
|
68
|
+
headers: {
|
|
69
|
+
'x-subscription-id': this.subscriptionId,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
return response.data.projects || [];
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async isAuthenticated() {
|
|
79
|
+
return !!this.subscriptionId;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.CloudService = CloudService;
|