tuneprompt 1.0.6 → 1.0.7

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.
@@ -91,9 +91,6 @@ async function initCommand() {
91
91
  const envContent = `OPENAI_API_KEY=your_key_here
92
92
  ANTHROPIC_API_KEY=your_key_here
93
93
  OPENROUTER_API_KEY=your_key_here
94
-
95
- # For self-hosted or local testing:
96
- # TUNEPROMPT_API_URL=http://localhost:3000
97
94
  `;
98
95
  fs.writeFileSync(envPath, envContent);
99
96
  console.log(chalk_1.default.green('✓ Created .env'));
@@ -2,6 +2,7 @@ import { FailedTest, OptimizationResult } from '../types/fix';
2
2
  export declare class PromptOptimizer {
3
3
  private anthropic?;
4
4
  private openai?;
5
+ private openrouter?;
5
6
  constructor();
6
7
  /**
7
8
  * Main optimization method
@@ -44,19 +44,34 @@ const constraintExtractor_1 = require("./constraintExtractor");
44
44
  class PromptOptimizer {
45
45
  anthropic;
46
46
  openai;
47
+ openrouter;
47
48
  constructor() {
48
49
  const anthropicKey = process.env.ANTHROPIC_API_KEY;
49
- if (anthropicKey && !anthropicKey.startsWith('api_key') && anthropicKey !== 'phc_xxxxx') {
50
+ if (anthropicKey &&
51
+ !anthropicKey.includes('your_key') &&
52
+ !anthropicKey.startsWith('api_key') &&
53
+ anthropicKey !== 'phc_xxxxx') {
50
54
  this.anthropic = new sdk_1.default({
51
55
  apiKey: anthropicKey
52
56
  });
53
57
  }
54
58
  const openaiKey = process.env.OPENAI_API_KEY;
55
- if (openaiKey && !openaiKey.startsWith('api_key')) {
59
+ if (openaiKey && !openaiKey.includes('your_key')) {
56
60
  this.openai = new openai_1.default({
57
61
  apiKey: openaiKey
58
62
  });
59
63
  }
64
+ const openrouterKey = process.env.OPENROUTER_API_KEY;
65
+ if (openrouterKey && !openrouterKey.includes('your_key')) {
66
+ this.openrouter = new openai_1.default({
67
+ baseURL: 'https://openrouter.ai/api/v1',
68
+ apiKey: openrouterKey,
69
+ defaultHeaders: {
70
+ 'HTTP-Referer': 'https://tuneprompt.xyz',
71
+ 'X-Title': 'TunePrompt CLI',
72
+ },
73
+ });
74
+ }
60
75
  }
61
76
  /**
62
77
  * Main optimization method
@@ -118,7 +133,7 @@ class PromptOptimizer {
118
133
  if (provider === 'anthropic' && this.anthropic) {
119
134
  console.log(`⚡ Using Anthropic for candidate generation...`);
120
135
  const response = await this.anthropic.messages.create({
121
- model: 'claude-sonnet-4-20250514',
136
+ model: 'claude-3-5-sonnet-20240620',
122
137
  max_tokens: 4000,
123
138
  temperature: 0.7, // Some creativity for prompt rewriting
124
139
  messages: [{
@@ -174,16 +189,34 @@ class PromptOptimizer {
174
189
  }
175
190
  ];
176
191
  }
177
- else if (provider === 'openrouter') {
178
- // For OpenRouter, we'll use the shadowTester to get a response
192
+ else if (provider === 'openrouter' && this.openrouter) {
179
193
  console.log(`⚡ Using OpenRouter for candidate generation...`);
180
- // Since OpenRouter is used in shadow testing, we'll use a different approach
181
- // For now, we'll return a basic fallback since OpenRouter doesn't support structured outputs as well
182
- return [{
183
- prompt: this.createFallbackPrompt(failedTest),
184
- reasoning: 'Generated using fallback method',
194
+ const response = await this.openrouter.chat.completions.create({
195
+ model: 'anthropic/claude-3-sonnet', // Default robust model on OpenRouter
196
+ messages: [{
197
+ role: 'user',
198
+ content: metaPrompt
199
+ }],
200
+ response_format: { type: 'json_object' }
201
+ });
202
+ const content = response.choices[0]?.message?.content;
203
+ if (!content) {
204
+ // Fallback if model doesn't support JSON mode or returns empty
205
+ throw new Error('No content returned from OpenRouter');
206
+ }
207
+ const parsed = JSON.parse(content);
208
+ return [
209
+ {
210
+ prompt: parsed.candidateA.prompt,
211
+ reasoning: parsed.candidateA.reasoning,
185
212
  score: 0
186
- }];
213
+ },
214
+ {
215
+ prompt: parsed.candidateB.prompt,
216
+ reasoning: parsed.candidateB.reasoning,
217
+ score: 0
218
+ }
219
+ ];
187
220
  }
188
221
  }
189
222
  catch (error) {
@@ -195,7 +228,7 @@ class PromptOptimizer {
195
228
  console.error('All providers failed for candidate generation');
196
229
  return [{
197
230
  prompt: this.createFallbackPrompt(failedTest),
198
- reasoning: 'Fallback prompt with basic improvements',
231
+ reasoning: 'Generated using fallback method',
199
232
  score: 0
200
233
  }];
201
234
  }
@@ -10,7 +10,7 @@ class CloudService {
10
10
  backendUrl;
11
11
  subscriptionId;
12
12
  constructor() {
13
- this.backendUrl = process.env.TUNEPROMPT_API_URL || process.env.BACKEND_URL || 'https://api.tuneprompt.com';
13
+ this.backendUrl = process.env.TUNEPROMPT_API_URL || process.env.BACKEND_URL || 'https://i8e3mu8jlk.execute-api.ap-south-1.amazonaws.com/dev';
14
14
  }
15
15
  async init() {
16
16
  // Load subscription ID from local storage (Phase 2 activation)
@@ -21,9 +21,16 @@ class CloudService {
21
21
  return license?.subscriptionId;
22
22
  }
23
23
  async uploadRun(data) {
24
- if (!this.subscriptionId) {
24
+ // Enforce Pro plan check
25
+ const license = (0, license_1.loadLicense)();
26
+ if (!this.subscriptionId || !license) {
25
27
  return { success: false, error: 'Not activated. Run `tuneprompt activate` first.' };
26
28
  }
29
+ // Check for specific pro plans (if we ever add free tiers)
30
+ const proPlans = ['pro-monthly', 'pro-yearly', 'lifetime'];
31
+ if (!proPlans.includes(license.plan)) {
32
+ return { success: false, error: 'Cloud features are restricted to Pro users. Please upgrade your plan.' };
33
+ }
27
34
  try {
28
35
  const response = await axios_1.default.post(`${this.backendUrl}/api/cloud/ingest-run`, data, {
29
36
  headers: {
@@ -93,9 +93,13 @@ class TestDatabase {
93
93
  }
94
94
  catch (e) {
95
95
  // Column might already exist
96
- if (!e.message.includes('duplicate column name')) {
97
- // Ignore safe errors
98
- }
96
+ }
97
+ // Migration for provider column
98
+ try {
99
+ this.db.exec(`ALTER TABLE test_results ADD COLUMN provider TEXT`);
100
+ }
101
+ catch (e) {
102
+ // Column might already exist
99
103
  }
100
104
  // Run external migrations (Phase 2)
101
105
  (0, migrate_1.runMigrations)(this.db).catch((err) => {
@@ -110,11 +114,11 @@ class TestDatabase {
110
114
  insertRun.run(run.id, run.timestamp.getTime(), run.totalTests, run.passed, run.failed, run.duration);
111
115
  const insertResult = this.db.prepare(`
112
116
  INSERT INTO test_results
113
- (id, run_id, description, prompt, variables, expect, config, file_path, status, score, actual_output, expected_output, error, duration, tokens, cost)
114
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
117
+ (id, run_id, description, prompt, variables, expect, config, file_path, status, score, actual_output, expected_output, error, duration, tokens, cost, provider)
118
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
115
119
  `);
116
120
  for (const result of run.results) {
117
- insertResult.run(result.id, run.id, result.testCase.description, typeof result.testCase.prompt === 'string' ? result.testCase.prompt : JSON.stringify(result.testCase.prompt), result.testCase.variables ? JSON.stringify(result.testCase.variables) : null, typeof result.testCase.expect === 'string' ? result.testCase.expect : JSON.stringify(result.testCase.expect), result.testCase.config ? JSON.stringify(result.testCase.config) : null, result.testCase.filePath || null, result.status, result.score, result.actualOutput, result.expectedOutput, result.error || null, result.metadata.duration, result.metadata.tokens || null, result.metadata.cost || null);
121
+ insertResult.run(result.id, run.id, result.testCase.description, typeof result.testCase.prompt === 'string' ? result.testCase.prompt : JSON.stringify(result.testCase.prompt), result.testCase.variables ? JSON.stringify(result.testCase.variables) : null, typeof result.testCase.expect === 'string' ? result.testCase.expect : JSON.stringify(result.testCase.expect), result.testCase.config ? JSON.stringify(result.testCase.config) : null, result.testCase.filePath || null, result.status, result.score, result.actualOutput, result.expectedOutput, result.error || null, result.metadata.duration, result.metadata.tokens || null, result.metadata.cost || null, result.metadata.provider || null);
118
122
  }
119
123
  }
120
124
  getRecentRuns(limit = 10) {
@@ -194,7 +198,8 @@ class TestDatabase {
194
198
  duration: r.duration,
195
199
  timestamp: new Date(),
196
200
  tokens: r.tokens,
197
- cost: r.cost
201
+ cost: r.cost,
202
+ provider: r.provider
198
203
  }
199
204
  };
200
205
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tuneprompt",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Industrial-grade testing framework for LLM prompts",
5
5
  "repository": {
6
6
  "type": "git",