elasticdash-test 0.1.24 → 0.1.25-alpha-2

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.
@@ -0,0 +1,173 @@
1
+ # Test Matchers
2
+
3
+ ElasticDash Test provides AI-specific matchers for asserting on workflow traces.
4
+
5
+ ## Overview
6
+
7
+ All matchers work with `expect(ctx.trace)` after importing the test setup:
8
+
9
+ ```ts
10
+ import '../node_modules/elasticdash-test/dist/test-setup.js'
11
+ import { expect } from 'expect'
12
+
13
+ aiTest('my test', async (ctx) => {
14
+ // ... run your workflow
15
+ expect(ctx.trace).toHaveLLMStep({ model: 'gpt-4' })
16
+ })
17
+ ```
18
+
19
+ ---
20
+
21
+ ## `toHaveLLMStep(config?)`
22
+
23
+ Assert the trace contains at least one LLM step matching the given config. All fields are optional and combined with AND logic.
24
+
25
+ ```ts
26
+ expect(ctx.trace).toHaveLLMStep({ model: 'gpt-4' })
27
+ expect(ctx.trace).toHaveLLMStep({ contains: 'order confirmed' }) // searches prompt + completion
28
+ expect(ctx.trace).toHaveLLMStep({ promptContains: 'order status' }) // searches prompt only
29
+ expect(ctx.trace).toHaveLLMStep({ outputContains: 'order confirmed' }) // searches completion only
30
+ expect(ctx.trace).toHaveLLMStep({ provider: 'openai' })
31
+ expect(ctx.trace).toHaveLLMStep({ provider: 'openai', promptContains: 'order status' })
32
+ expect(ctx.trace).toHaveLLMStep({ promptContains: 'retry', times: 3 }) // exactly 3 matching steps
33
+ expect(ctx.trace).toHaveLLMStep({ provider: 'openai', minTimes: 2 }) // at least 2 matching steps
34
+ expect(ctx.trace).toHaveLLMStep({ outputContains: 'error', maxTimes: 1 }) // at most 1 matching step
35
+ ```
36
+
37
+ ### Configuration Options
38
+
39
+ | Field | Description |
40
+ |---|---|
41
+ | `model` | Exact model name match (e.g. `'gpt-4o'`) |
42
+ | `contains` | Substring match across prompt + completion (case-insensitive) |
43
+ | `promptContains` | Substring match in prompt only (case-insensitive) |
44
+ | `outputContains` | Substring match in completion only (case-insensitive) |
45
+ | `provider` | Provider name: `'openai'`, `'gemini'`, or `'grok'` |
46
+ | `times` | Exact match count (fails unless exactly this many steps match) |
47
+ | `minTimes` | Minimum match count (steps matching must be ≥ this value) |
48
+ | `maxTimes` | Maximum match count (steps matching must be ≤ this value) |
49
+
50
+ ---
51
+
52
+ ## `toCallTool(toolName)`
53
+
54
+ Assert the trace contains a tool call with the given name.
55
+
56
+ ```ts
57
+ expect(ctx.trace).toCallTool('chargeCard')
58
+ ```
59
+
60
+ ---
61
+
62
+ ## `toMatchSemanticOutput(expected, options?)`
63
+
64
+ LLM-judged semantic match of combined LLM output vs. the expected string. Defaults to OpenAI GPT-4 with `OPENAI_API_KEY`.
65
+
66
+ ```ts
67
+ expect(ctx.trace).toMatchSemanticOutput('attack stat', {
68
+ provider: 'claude', // 'openai' (default) | 'claude' | 'gemini' | 'grok'
69
+ model: 'claude-3-opus-20240229', // overrides default model for the provider
70
+ sdk: myClaudeClient, // optional SDK instance (uses its chat/messages API)
71
+ })
72
+
73
+ // Minimal, using default OpenAI model
74
+ expect(ctx.trace).toMatchSemanticOutput('order confirmed')
75
+
76
+ // OpenAI-compatible endpoint (e.g., Moonshot/Kimi) via baseURL + apiKey
77
+ expect(ctx.trace).toMatchSemanticOutput('order confirmed', {
78
+ provider: 'openai',
79
+ model: 'kimi-k2-turbo-preview',
80
+ apiKey: process.env.KIMI_API_KEY,
81
+ baseURL: 'https://api.moonshot.ai/v1',
82
+ })
83
+ ```
84
+
85
+ Environment keys by provider: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY` (or `GOOGLE_API_KEY`), `GROK_API_KEY`.
86
+
87
+ ---
88
+
89
+ ## `toEvaluateOutputMetric(config)`
90
+
91
+ Evaluate one LLM step's prompt or result using an LLM and assert a numeric metric condition in the range 0.0–1.0.
92
+
93
+ Defaults: `target='result'`, `condition='atLeast 0.7'`, `provider='openai'`, `model='gpt-4'`.
94
+
95
+ ```ts
96
+ // Evaluate the last LLM result with your own prompt; default condition atLeast 0.7
97
+ expect(ctx.trace).toEvaluateOutputMetric({
98
+ evaluationPrompt: 'Rate how well this answers the user question.',
99
+ })
100
+
101
+ // Check a specific step (3rd LLM prompt), target the prompt text, require >= 0.8 via Claude
102
+ expect(ctx.trace).toEvaluateOutputMetric({
103
+ evaluationPrompt: 'Score coherence of this prompt between 0 and 1.',
104
+ target: 'prompt',
105
+ nth: 3,
106
+ condition: { atLeast: 0.8 },
107
+ provider: 'claude',
108
+ model: 'claude-3-opus-20240229',
109
+ })
110
+
111
+ // Custom comparator: score must be < 0.3
112
+ expect(ctx.trace).toEvaluateOutputMetric({
113
+ evaluationPrompt: 'Rate hallucination risk (0=none, 1=high).',
114
+ condition: { lessThan: 0.3 },
115
+ })
116
+ ```
117
+
118
+ ### Configuration Options
119
+
120
+ - `evaluationPrompt` (required): your scoring instructions; model is asked to return only a number between 0 and 1.
121
+ - `target`: `'result'` (default) or `'prompt'`. Evaluates that text only.
122
+ - `index` / `nth`: pick which LLM step to score (0-based or 1-based). Defaults to the last LLM step.
123
+ - `condition`: one of `greaterThan`, `lessThan`, `atLeast`, `atMost`, `equals`; default is `{ atLeast: 0.7 }`.
124
+ - `provider` / `model` / `sdk` / `apiKey` / `baseURL`: same shape as `toMatchSemanticOutput`.
125
+
126
+ ---
127
+
128
+ ## `toHaveCustomStep(config?)`
129
+
130
+ Assert a recorded custom step (RAG/code/fixed/custom) matches filters.
131
+
132
+ ```ts
133
+ expect(ctx.trace).toHaveCustomStep({ kind: 'rag', name: 'pokemon-search' })
134
+ expect(ctx.trace).toHaveCustomStep({ tag: 'sort:asc' })
135
+ expect(ctx.trace).toHaveCustomStep({ contains: 'pikachu' })
136
+ expect(ctx.trace).toHaveCustomStep({ resultContains: '25' })
137
+ expect(ctx.trace).toHaveCustomStep({ kind: 'rag', minTimes: 1, maxTimes: 2 })
138
+ ```
139
+
140
+ ---
141
+
142
+ ## `toHavePromptWhere(config)`
143
+
144
+ Filter prompts, then assert additional constraints. Example: "all prompts containing A must also contain B".
145
+
146
+ ```ts
147
+ // Prompts that contain "order" must also contain "confirmed"
148
+ expect(ctx.trace).toHavePromptWhere({
149
+ filterContains: 'order',
150
+ requireContains: 'confirmed',
151
+ })
152
+
153
+ // Prompts containing "retry" must NOT contain "cancel"
154
+ expect(ctx.trace).toHavePromptWhere({
155
+ filterContains: 'retry',
156
+ requireNotContains: 'cancel',
157
+ })
158
+
159
+ // And control counts on the filtered subset
160
+ expect(ctx.trace).toHavePromptWhere({
161
+ filterContains: 'order',
162
+ requireContains: 'confirmed',
163
+ minTimes: 1,
164
+ maxTimes: 3,
165
+ })
166
+
167
+ // Check a specific prompt position (1-based nth or 0-based index)
168
+ expect(ctx.trace).toHavePromptWhere({
169
+ filterContains: 'order',
170
+ requireContains: 'confirmed',
171
+ nth: 3, // the 3rd prompt among those containing "order"
172
+ })
173
+ ```