promptfoo 0.18.2 → 0.18.3
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/dist/package.json +1 -1
- package/dist/src/assertions.d.ts.map +1 -1
- package/dist/src/assertions.js +4 -4
- package/dist/src/assertions.js.map +1 -1
- package/dist/src/cache.d.ts +1 -1
- package/dist/src/cache.d.ts.map +1 -1
- package/dist/src/cache.js +4 -4
- package/dist/src/cache.js.map +1 -1
- package/dist/src/evaluator.d.ts.map +1 -1
- package/dist/src/evaluator.js +3 -2
- package/dist/src/evaluator.js.map +1 -1
- package/dist/src/providers/azureopenai.d.ts.map +1 -1
- package/dist/src/providers/azureopenai.js +3 -3
- package/dist/src/providers/azureopenai.js.map +1 -1
- package/dist/src/providers/llama.js +1 -1
- package/dist/src/providers/llama.js.map +1 -1
- package/dist/src/providers/localai.js +2 -2
- package/dist/src/providers/localai.js.map +1 -1
- package/dist/src/providers/ollama.d.ts +9 -0
- package/dist/src/providers/ollama.d.ts.map +1 -0
- package/dist/src/providers/ollama.js +66 -0
- package/dist/src/providers/ollama.js.map +1 -0
- package/dist/src/providers/openai.js +3 -3
- package/dist/src/providers/openai.js.map +1 -1
- package/dist/src/providers.d.ts.map +1 -1
- package/dist/src/providers.js +5 -0
- package/dist/src/providers.js.map +1 -1
- package/dist/src/util.d.ts +2 -0
- package/dist/src/util.d.ts.map +1 -1
- package/dist/src/util.js +13 -5
- package/dist/src/util.js.map +1 -1
- package/dist/src/web/client/assets/{index-af22e73c.js → index-6d2a3573.js} +1 -1
- package/dist/src/web/client/index.html +1 -1
- package/package.json +1 -1
- package/src/assertions.ts +3 -2
- package/src/cache.ts +3 -2
- package/src/evaluator.ts +3 -1
- package/src/providers/azureopenai.ts +16 -6
- package/src/providers/llama.ts +2 -2
- package/src/providers/localai.ts +3 -3
- package/src/providers/ollama.ts +88 -0
- package/src/providers/openai.ts +4 -4
- package/src/providers.ts +16 -2
- package/src/util.ts +13 -4
- package/src/web/client/src/ResultsView.tsx +1 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import logger from '../logger';
|
|
2
|
+
import { fetchWithCache } from '../cache';
|
|
3
|
+
|
|
4
|
+
import type { ApiProvider, ProviderResponse } from '../types.js';
|
|
5
|
+
import { REQUEST_TIMEOUT_MS } from './shared';
|
|
6
|
+
|
|
7
|
+
interface OllamaJsonL {
|
|
8
|
+
model: string;
|
|
9
|
+
created_at: string;
|
|
10
|
+
response?: string;
|
|
11
|
+
done: boolean;
|
|
12
|
+
context?: number[];
|
|
13
|
+
total_duration?: number;
|
|
14
|
+
load_duration?: number;
|
|
15
|
+
sample_count?: number;
|
|
16
|
+
sample_duration?: number;
|
|
17
|
+
prompt_eval_count?: number;
|
|
18
|
+
prompt_eval_duration?: number;
|
|
19
|
+
eval_count?: number;
|
|
20
|
+
eval_duration?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class OllamaProvider implements ApiProvider {
|
|
24
|
+
modelName: string;
|
|
25
|
+
|
|
26
|
+
constructor(modelName: string) {
|
|
27
|
+
this.modelName = modelName;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
id(): string {
|
|
31
|
+
return `ollama:${this.modelName}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
toString(): string {
|
|
35
|
+
return `[Ollama Provider ${this.modelName}]`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async callApi(prompt: string): Promise<ProviderResponse> {
|
|
39
|
+
const params = {
|
|
40
|
+
model: this.modelName,
|
|
41
|
+
prompt,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
logger.debug(`Calling Ollama API: ${JSON.stringify(params)}`);
|
|
45
|
+
let response;
|
|
46
|
+
try {
|
|
47
|
+
response = await fetchWithCache(
|
|
48
|
+
`${process.env.OLLAMA_BASE_URL || 'http://localhost:11434'}/api/generate`,
|
|
49
|
+
{
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers: {
|
|
52
|
+
'Content-Type': 'application/json',
|
|
53
|
+
},
|
|
54
|
+
body: JSON.stringify(params),
|
|
55
|
+
},
|
|
56
|
+
REQUEST_TIMEOUT_MS,
|
|
57
|
+
'text',
|
|
58
|
+
);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
return {
|
|
61
|
+
error: `API call error: ${String(err)}`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
logger.debug(`\tOllama API response: ${response.data}`);
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const output = response.data
|
|
68
|
+
.split('\n')
|
|
69
|
+
.map((line: string) => {
|
|
70
|
+
const parsed = JSON.parse(line) as OllamaJsonL;
|
|
71
|
+
if (parsed.response) {
|
|
72
|
+
return parsed.response;
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
})
|
|
76
|
+
.filter((s: string | null) => s !== null)
|
|
77
|
+
.join('');
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
output,
|
|
81
|
+
};
|
|
82
|
+
} catch (err) {
|
|
83
|
+
return {
|
|
84
|
+
error: `API response error: ${String(err)}: ${JSON.stringify(response.data)}`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
package/src/providers/openai.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import logger from '../logger';
|
|
2
|
-
import {
|
|
2
|
+
import { fetchWithCache } from '../cache';
|
|
3
3
|
import { REQUEST_TIMEOUT_MS, parseChatPrompt } from './shared';
|
|
4
4
|
|
|
5
5
|
import type { ApiProvider, ProviderEmbeddingResponse, ProviderResponse } from '../types.js';
|
|
@@ -61,7 +61,7 @@ export class OpenAiEmbeddingProvider extends OpenAiGenericProvider {
|
|
|
61
61
|
let data,
|
|
62
62
|
cached = false;
|
|
63
63
|
try {
|
|
64
|
-
({ data, cached } = (await
|
|
64
|
+
({ data, cached } = (await fetchWithCache(
|
|
65
65
|
`https://${this.apiHost}/v1/embeddings`,
|
|
66
66
|
{
|
|
67
67
|
method: 'POST',
|
|
@@ -177,7 +177,7 @@ export class OpenAiCompletionProvider extends OpenAiGenericProvider {
|
|
|
177
177
|
let data,
|
|
178
178
|
cached = false;
|
|
179
179
|
try {
|
|
180
|
-
({ data, cached } = (await
|
|
180
|
+
({ data, cached } = (await fetchWithCache(
|
|
181
181
|
`https://${this.apiHost}/v1/completions`,
|
|
182
182
|
{
|
|
183
183
|
method: 'POST',
|
|
@@ -275,7 +275,7 @@ export class OpenAiChatCompletionProvider extends OpenAiGenericProvider {
|
|
|
275
275
|
let data,
|
|
276
276
|
cached = false;
|
|
277
277
|
try {
|
|
278
|
-
({ data, cached } = (await
|
|
278
|
+
({ data, cached } = (await fetchWithCache(
|
|
279
279
|
`https://${this.apiHost}/v1/chat/completions`,
|
|
280
280
|
{
|
|
281
281
|
method: 'POST',
|
package/src/providers.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { AnthropicCompletionProvider } from './providers/anthropic';
|
|
|
5
5
|
import { ReplicateProvider } from './providers/replicate';
|
|
6
6
|
import { LocalAiCompletionProvider, LocalAiChatProvider } from './providers/localai';
|
|
7
7
|
import { LlamaProvider } from './providers/llama';
|
|
8
|
+
import { OllamaProvider } from './providers/ollama';
|
|
8
9
|
import { ScriptCompletionProvider } from './providers/scriptCompletion';
|
|
9
10
|
import {
|
|
10
11
|
AzureOpenAiChatCompletionProvider,
|
|
@@ -100,9 +101,19 @@ export async function loadApiProvider(
|
|
|
100
101
|
const deploymentName = options[2];
|
|
101
102
|
|
|
102
103
|
if (modelType === 'chat') {
|
|
103
|
-
return new AzureOpenAiChatCompletionProvider(
|
|
104
|
+
return new AzureOpenAiChatCompletionProvider(
|
|
105
|
+
deploymentName,
|
|
106
|
+
undefined,
|
|
107
|
+
context?.config,
|
|
108
|
+
context?.id,
|
|
109
|
+
);
|
|
104
110
|
} else if (modelType === 'completion') {
|
|
105
|
-
return new AzureOpenAiCompletionProvider(
|
|
111
|
+
return new AzureOpenAiCompletionProvider(
|
|
112
|
+
deploymentName,
|
|
113
|
+
undefined,
|
|
114
|
+
context?.config,
|
|
115
|
+
context?.id,
|
|
116
|
+
);
|
|
106
117
|
} else {
|
|
107
118
|
throw new Error(
|
|
108
119
|
`Unknown Azure OpenAI model type: ${modelType}. Use one of the following providers: openai:chat:<model name>, openai:completion:<model name>`,
|
|
@@ -138,6 +149,9 @@ export async function loadApiProvider(
|
|
|
138
149
|
if (providerPath === 'llama' || providerPath.startsWith('llama:')) {
|
|
139
150
|
const modelName = providerPath.split(':')[1];
|
|
140
151
|
return new LlamaProvider(modelName, context?.config);
|
|
152
|
+
} else if (providerPath.startsWith('ollama:')) {
|
|
153
|
+
const modelName = providerPath.split(':')[1];
|
|
154
|
+
return new OllamaProvider(modelName);
|
|
141
155
|
} else if (providerPath?.startsWith('localai:')) {
|
|
142
156
|
const options = providerPath.split(':');
|
|
143
157
|
const modelType = options[1];
|
package/src/util.ts
CHANGED
|
@@ -12,7 +12,6 @@ import { parse as parseCsv } from 'csv-parse/sync';
|
|
|
12
12
|
import { stringify } from 'csv-stringify/sync';
|
|
13
13
|
|
|
14
14
|
import logger from './logger';
|
|
15
|
-
import { assertionFromString } from './assertions';
|
|
16
15
|
import { getDirectory } from './esm';
|
|
17
16
|
|
|
18
17
|
import type { RequestInfo, RequestInit, Response } from 'node-fetch';
|
|
@@ -368,7 +367,7 @@ export function writeOutput(
|
|
|
368
367
|
[...results.table.head.prompts, ...results.table.head.vars],
|
|
369
368
|
...results.table.body.map((row) => [...row.outputs.map(outputToSimpleString), ...row.vars]),
|
|
370
369
|
];
|
|
371
|
-
const htmlOutput =
|
|
370
|
+
const htmlOutput = getNunjucksEngine().renderString(template, {
|
|
372
371
|
table,
|
|
373
372
|
results: results.results,
|
|
374
373
|
});
|
|
@@ -456,10 +455,12 @@ export function writeLatestResults(results: EvaluateSummary, config: Partial<Uni
|
|
|
456
455
|
2,
|
|
457
456
|
),
|
|
458
457
|
);
|
|
459
|
-
|
|
458
|
+
|
|
459
|
+
try {
|
|
460
460
|
fs.unlinkSync(latestResultsPath);
|
|
461
|
-
}
|
|
461
|
+
} catch {}
|
|
462
462
|
fs.symlinkSync(newResultsPath, latestResultsPath);
|
|
463
|
+
|
|
463
464
|
cleanupOldResults();
|
|
464
465
|
} catch (err) {
|
|
465
466
|
logger.error(`Failed to write latest results to ${newResultsPath}:\n${err}`);
|
|
@@ -520,6 +521,7 @@ export function testCaseFromCsvRow(row: CsvRow): TestCase {
|
|
|
520
521
|
for (const [key, value] of Object.entries(row)) {
|
|
521
522
|
if (key === '__expected') {
|
|
522
523
|
if (value.trim() !== '') {
|
|
524
|
+
const { assertionFromString } = require('./assertions');
|
|
523
525
|
asserts.push(assertionFromString(value));
|
|
524
526
|
}
|
|
525
527
|
} else {
|
|
@@ -532,3 +534,10 @@ export function testCaseFromCsvRow(row: CsvRow): TestCase {
|
|
|
532
534
|
assert: asserts,
|
|
533
535
|
};
|
|
534
536
|
}
|
|
537
|
+
|
|
538
|
+
export function getNunjucksEngine() {
|
|
539
|
+
nunjucks.configure({
|
|
540
|
+
autoescape: false,
|
|
541
|
+
});
|
|
542
|
+
return nunjucks;
|
|
543
|
+
}
|
|
@@ -86,7 +86,7 @@ export default function ResultsView({ recentFiles, onRecentFileSelected }: Resul
|
|
|
86
86
|
setFailureFilter(newFailureFilter);
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
-
const [wordBreak, setWordBreak] = React.useState<'break-word' | 'break-all'>('break-
|
|
89
|
+
const [wordBreak, setWordBreak] = React.useState<'break-word' | 'break-all'>('break-word');
|
|
90
90
|
const handleWordBreakChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
91
91
|
setWordBreak(event.target.checked ? 'break-all' : 'break-word');
|
|
92
92
|
};
|