vibe-match 0.1.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/README.md +296 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17782 -0
- package/dist/matchers/toBeSimilarTo.d.ts +25 -0
- package/dist/matchers/toBeSimilarTo.d.ts.map +1 -0
- package/dist/matchers/toBeVectorSimilarTo.d.ts +21 -0
- package/dist/matchers/toBeVectorSimilarTo.d.ts.map +1 -0
- package/dist/matchers/toMention.d.ts +24 -0
- package/dist/matchers/toMention.d.ts.map +1 -0
- package/dist/matchers/toSatisfyCriteria.d.ts +40 -0
- package/dist/matchers/toSatisfyCriteria.d.ts.map +1 -0
- package/dist/matchers.d.ts +20 -0
- package/dist/matchers.d.ts.map +1 -0
- package/dist/providers.d.ts +38 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/types.d.ts +210 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# vibe-match
|
|
2
|
+
|
|
3
|
+
Semantic test matchers powered by LLMs. Test your AI outputs with fuzzy matching that understands meaning, not just exact text.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -D vibe-match
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun add -D vibe-match
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
yarn add -D vibe-match
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { vibeMatchers, type VibeMatchConfig } from "vibe-match";
|
|
23
|
+
|
|
24
|
+
const config: VibeMatchConfig = {
|
|
25
|
+
apiKeys: {
|
|
26
|
+
openai: process.env.OPENAI_API_KEY,
|
|
27
|
+
},
|
|
28
|
+
languageModel: "openai:gpt-4o-mini",
|
|
29
|
+
embeddingModel: "openai:text-embedding-3-small",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
expect.extend(vibeMatchers(config));
|
|
33
|
+
|
|
34
|
+
// Now you can use semantic matchers!
|
|
35
|
+
expect("Hello there!").toBeSimilarTo("Hi!");
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Provider Examples
|
|
39
|
+
|
|
40
|
+
**OpenAI:**
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const config: VibeMatchConfig = {
|
|
44
|
+
apiKeys: {
|
|
45
|
+
openai: process.env.OPENAI_API_KEY,
|
|
46
|
+
},
|
|
47
|
+
languageModel: "openai:gpt-4o-mini",
|
|
48
|
+
embeddingModel: "openai:text-embedding-3-small",
|
|
49
|
+
};
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Anthropic** (use OpenAI for embeddings since Anthropic doesn't provide them):
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
const config: VibeMatchConfig = {
|
|
56
|
+
apiKeys: {
|
|
57
|
+
anthropic: process.env.ANTHROPIC_API_KEY,
|
|
58
|
+
openai: process.env.OPENAI_API_KEY, // Required for embeddings
|
|
59
|
+
},
|
|
60
|
+
languageModel: "anthropic:claude-sonnet-4-20250514",
|
|
61
|
+
embeddingModel: "openai:text-embedding-3-small",
|
|
62
|
+
};
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Google (Gemini):**
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
const config: VibeMatchConfig = {
|
|
69
|
+
apiKeys: {
|
|
70
|
+
google: process.env.GOOGLE_GENERATIVE_AI_API_KEY,
|
|
71
|
+
},
|
|
72
|
+
languageModel: "google:gemini-2.0-flash",
|
|
73
|
+
embeddingModel: "google:text-embedding-004",
|
|
74
|
+
};
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Mistral:**
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
const config: VibeMatchConfig = {
|
|
81
|
+
apiKeys: {
|
|
82
|
+
mistral: process.env.MISTRAL_API_KEY,
|
|
83
|
+
},
|
|
84
|
+
languageModel: "mistral:mistral-small-latest",
|
|
85
|
+
embeddingModel: "mistral:mistral-embed",
|
|
86
|
+
};
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Grok (xAI)** (use OpenAI for embeddings since xAI doesn't provide them):
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
const config: VibeMatchConfig = {
|
|
93
|
+
apiKeys: {
|
|
94
|
+
xai: process.env.XAI_API_KEY,
|
|
95
|
+
openai: process.env.OPENAI_API_KEY, // Required for embeddings
|
|
96
|
+
},
|
|
97
|
+
languageModel: "xai:grok-2-1212",
|
|
98
|
+
embeddingModel: "openai:text-embedding-3-small",
|
|
99
|
+
};
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Custom Models
|
|
103
|
+
|
|
104
|
+
You can also pass custom AI SDK model instances directly. This is useful for:
|
|
105
|
+
|
|
106
|
+
- Using OpenAI-compatible APIs (OpenRouter, Together AI, Fireworks, etc.)
|
|
107
|
+
- Using providers not built into vibe-match
|
|
108
|
+
- Configuring models with custom options
|
|
109
|
+
|
|
110
|
+
**OpenRouter:**
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
114
|
+
|
|
115
|
+
const openrouter = createOpenAI({
|
|
116
|
+
baseURL: "https://openrouter.ai/api/v1",
|
|
117
|
+
apiKey: process.env.OPENROUTER_API_KEY,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const config: VibeMatchConfig = {
|
|
121
|
+
languageModel: openrouter("anthropic/claude-sonnet-4"),
|
|
122
|
+
embeddingModel: openrouter.embedding("openai/text-embedding-3-small"),
|
|
123
|
+
};
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Together AI:**
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
130
|
+
|
|
131
|
+
const together = createOpenAI({
|
|
132
|
+
baseURL: "https://api.together.xyz/v1",
|
|
133
|
+
apiKey: process.env.TOGETHER_API_KEY,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const config: VibeMatchConfig = {
|
|
137
|
+
languageModel: together("meta-llama/Llama-3.3-70B-Instruct-Turbo"),
|
|
138
|
+
embeddingModel: together.embedding(
|
|
139
|
+
"togethercomputer/m2-bert-80M-8k-retrieval",
|
|
140
|
+
),
|
|
141
|
+
};
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Any AI SDK Provider:**
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { createAzure } from "@ai-sdk/azure";
|
|
148
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
149
|
+
|
|
150
|
+
const azure = createAzure({
|
|
151
|
+
resourceName: "my-resource",
|
|
152
|
+
apiKey: process.env.AZURE_API_KEY,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
156
|
+
|
|
157
|
+
const config: VibeMatchConfig = {
|
|
158
|
+
languageModel: azure("my-gpt-4-deployment"),
|
|
159
|
+
embeddingModel: openai.embedding("text-embedding-3-small"),
|
|
160
|
+
};
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Matchers
|
|
164
|
+
|
|
165
|
+
### `toBeSimilarTo(expected, options?)`
|
|
166
|
+
|
|
167
|
+
Tests if two strings are semantically similar.
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
expect("I love cheese pizza").toBeSimilarTo("Cheese pizza is my favorite", {
|
|
171
|
+
level: "loose", // "loose" | "normal" | "strict"
|
|
172
|
+
threshold: 0.75, // Percentage of samples that must pass
|
|
173
|
+
samples: 5, // Number of LLM calls to make
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### `toMention(concept, options?)`
|
|
178
|
+
|
|
179
|
+
Tests if a string mentions a concept (directly, by description, or by implication).
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
expect("The red and yellow clown mascot").toMention("McDonald's", {
|
|
183
|
+
threshold: 0.75,
|
|
184
|
+
samples: 5,
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### `toSatisfyCriteria(criteria, options?)`
|
|
189
|
+
|
|
190
|
+
Tests if text satisfies one or more criteria. Accepts either a single criterion string or an array of criteria.
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
// Single criterion
|
|
194
|
+
expect("Hi John, here's how to reset your password...").toSatisfyCriteria(
|
|
195
|
+
"Addresses the user by name",
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
// Multiple criteria
|
|
199
|
+
expect("Hi John, here's how to reset your password...").toSatisfyCriteria(
|
|
200
|
+
["Addresses the user by name", "Provides a specific solution"],
|
|
201
|
+
{
|
|
202
|
+
mode: "all", // "all" | "any" | "threshold"
|
|
203
|
+
threshold: 0.75, // For "threshold" mode: percentage that must pass
|
|
204
|
+
samples: 3, // Number of LLM calls per criterion
|
|
205
|
+
},
|
|
206
|
+
);
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### `toBeVectorSimilarTo(expected, options?)`
|
|
210
|
+
|
|
211
|
+
Tests semantic similarity using embedding vectors and cosine similarity.
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
expect("A guide to baking bread").toBeVectorSimilarTo("Bread baking tutorial", {
|
|
215
|
+
threshold: 0.85, // Minimum cosine similarity (0-1)
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Inline Model Overrides
|
|
220
|
+
|
|
221
|
+
You can override the language model or embedding model for individual matcher calls. This is useful when you want to use a different model for specific tests without changing your global configuration.
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
import {
|
|
225
|
+
vibeMatchers,
|
|
226
|
+
type LanguageModelV1,
|
|
227
|
+
type EmbeddingModel,
|
|
228
|
+
} from "vibe-match";
|
|
229
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
230
|
+
|
|
231
|
+
// Configure with default models
|
|
232
|
+
const config: VibeMatchConfig = {
|
|
233
|
+
apiKeys: { openai: process.env.OPENAI_API_KEY },
|
|
234
|
+
languageModel: "openai:gpt-4o-mini",
|
|
235
|
+
embeddingModel: "openai:text-embedding-3-small",
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
expect.extend(vibeMatchers(config));
|
|
239
|
+
|
|
240
|
+
// Create a more powerful model for specific tests
|
|
241
|
+
const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
242
|
+
const gpt4o = openai("gpt-4o");
|
|
243
|
+
|
|
244
|
+
// Use inline override for this specific assertion
|
|
245
|
+
expect(response).toBeSimilarTo(expected, {
|
|
246
|
+
languageModel: gpt4o, // Use GPT-4o for this test only
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Language Model Overrides
|
|
251
|
+
|
|
252
|
+
The following matchers support the `languageModel` option:
|
|
253
|
+
|
|
254
|
+
- `toBeSimilarTo` — for semantic similarity checks
|
|
255
|
+
- `toMention` — for concept mention detection
|
|
256
|
+
- `toSatisfyCriteria` — for criteria evaluation
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
// Use a stronger model for critical tests
|
|
260
|
+
expect(aiResponse).toSatisfyCriteria(
|
|
261
|
+
["Is factually accurate", "Cites sources"],
|
|
262
|
+
{
|
|
263
|
+
languageModel: gpt4o,
|
|
264
|
+
mode: "all",
|
|
265
|
+
},
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
expect(summary).toMention("quarterly revenue", {
|
|
269
|
+
languageModel: gpt4o,
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Embedding Model Overrides
|
|
274
|
+
|
|
275
|
+
The `toBeVectorSimilarTo` matcher supports the `embeddingModel` option:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
const largeEmbedding = openai.embedding("text-embedding-3-large");
|
|
279
|
+
|
|
280
|
+
expect(document).toBeVectorSimilarTo(reference, {
|
|
281
|
+
embeddingModel: largeEmbedding, // Use larger embedding model
|
|
282
|
+
threshold: 0.9,
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Supported Providers
|
|
287
|
+
|
|
288
|
+
| Provider | Language Models | Embedding Models |
|
|
289
|
+
| ---------- | --------------- | ---------------- |
|
|
290
|
+
| OpenAI | ✅ | ✅ |
|
|
291
|
+
| Anthropic | ✅ | ❌ |
|
|
292
|
+
| Google | ✅ | ✅ |
|
|
293
|
+
| Mistral | ✅ | ✅ |
|
|
294
|
+
| xAI (Grok) | ✅ | ❌ |
|
|
295
|
+
|
|
296
|
+
You can mix and match providers—use Anthropic for language models and OpenAI for embeddings, for example.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { vibeMatchers } from "./matchers";
|
|
2
|
+
export type { VibeMatchConfig, VibeMatchStringConfig, VibeMatchCustomModelConfig, VibeMatchPrompts, VibeMatchApiKeys, } from "./types";
|
|
3
|
+
export type { LanguageModelString, EmbeddingModelString } from "./providers";
|
|
4
|
+
export type { LanguageModelV1, EmbeddingModel } from "ai";
|
|
5
|
+
export type { ToBeSimilarToOptions } from "./matchers/toBeSimilarTo";
|
|
6
|
+
export type { ToMentionOptions } from "./matchers/toMention";
|
|
7
|
+
export type { ToSatisfyCriteriaOptions } from "./matchers/toSatisfyCriteria";
|
|
8
|
+
export type { VectorSimilarityOptions } from "./matchers/toBeVectorSimilarTo";
|
|
9
|
+
export { DEFAULT_SIMILARITY_PROMPT } from "./matchers/toBeSimilarTo";
|
|
10
|
+
export { DEFAULT_MENTION_PROMPT } from "./matchers/toMention";
|
|
11
|
+
export { DEFAULT_SATISFY_CRITERIA_PROMPT } from "./matchers/toSatisfyCriteria";
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EACV,eAAe,EACf,qBAAqB,EACrB,0BAA0B,EAC1B,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAG7E,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,IAAI,CAAC;AAG1D,YAAY,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACrE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,YAAY,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAC7E,YAAY,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAG9E,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,+BAA+B,EAAE,MAAM,8BAA8B,CAAC"}
|