@indemn/cli 1.0.0 → 1.1.1
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/.claude-plugin/marketplace.json +28 -0
- package/README.md +29 -23
- package/dist/cli/agents.d.ts.map +1 -1
- package/dist/cli/agents.js +20 -0
- package/dist/cli/agents.js.map +1 -1
- package/dist/cli/eval.d.ts.map +1 -1
- package/dist/cli/eval.js +38 -0
- package/dist/cli/eval.js.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +40 -0
- package/dist/mcp/tools.js.map +1 -1
- package/dist/report/agent-card/cover.d.ts +20 -0
- package/dist/report/agent-card/cover.d.ts.map +1 -0
- package/dist/report/agent-card/cover.js +58 -0
- package/dist/report/agent-card/cover.js.map +1 -0
- package/dist/report/agent-card/document.d.ts +7 -0
- package/dist/report/agent-card/document.d.ts.map +1 -0
- package/dist/report/agent-card/document.js +144 -0
- package/dist/report/agent-card/document.js.map +1 -0
- package/dist/report/agent-card/types.d.ts +77 -0
- package/dist/report/agent-card/types.d.ts.map +1 -0
- package/dist/report/agent-card/types.js +3 -0
- package/dist/report/agent-card/types.js.map +1 -0
- package/dist/report/assets/Indemn_PrimaryLogo_Iris.png +0 -0
- package/dist/report/assets/assets/Indemn_PrimaryLogo_Iris.png +0 -0
- package/dist/report/assets/assets/fonts/Barlow-Bold.ttf +0 -0
- package/dist/report/assets/assets/fonts/Barlow-Medium.ttf +0 -0
- package/dist/report/assets/assets/fonts/Barlow-Regular.ttf +0 -0
- package/dist/report/assets/assets/fonts/Barlow-SemiBold.ttf +0 -0
- package/dist/report/assets/fonts/Barlow-Bold.ttf +0 -0
- package/dist/report/assets/fonts/Barlow-Medium.ttf +0 -0
- package/dist/report/assets/fonts/Barlow-Regular.ttf +0 -0
- package/dist/report/assets/fonts/Barlow-SemiBold.ttf +0 -0
- package/dist/report/eval-report/cover.d.ts +16 -0
- package/dist/report/eval-report/cover.d.ts.map +1 -0
- package/dist/report/eval-report/cover.js +8 -0
- package/dist/report/eval-report/cover.js.map +1 -0
- package/dist/report/eval-report/document.d.ts +19 -0
- package/dist/report/eval-report/document.d.ts.map +1 -0
- package/dist/report/eval-report/document.js +32 -0
- package/dist/report/eval-report/document.js.map +1 -0
- package/dist/report/eval-report/example-page.d.ts +11 -0
- package/dist/report/eval-report/example-page.d.ts.map +1 -0
- package/dist/report/eval-report/example-page.js +18 -0
- package/dist/report/eval-report/example-page.js.map +1 -0
- package/dist/report/eval-report/item-page.d.ts +9 -0
- package/dist/report/eval-report/item-page.d.ts.map +1 -0
- package/dist/report/eval-report/item-page.js +72 -0
- package/dist/report/eval-report/item-page.js.map +1 -0
- package/dist/report/eval-report/matrix-page.d.ts +8 -0
- package/dist/report/eval-report/matrix-page.d.ts.map +1 -0
- package/dist/report/eval-report/matrix-page.js +31 -0
- package/dist/report/eval-report/matrix-page.js.map +1 -0
- package/dist/report/eval-report/types.d.ts +69 -0
- package/dist/report/eval-report/types.d.ts.map +1 -0
- package/dist/report/eval-report/types.js +7 -0
- package/dist/report/eval-report/types.js.map +1 -0
- package/dist/report/eval-report/v2-cover.d.ts +9 -0
- package/dist/report/eval-report/v2-cover.d.ts.map +1 -0
- package/dist/report/eval-report/v2-cover.js +32 -0
- package/dist/report/eval-report/v2-cover.js.map +1 -0
- package/dist/report/export.d.ts +17 -0
- package/dist/report/export.d.ts.map +1 -0
- package/dist/report/export.js +327 -0
- package/dist/report/export.js.map +1 -0
- package/dist/report/generate.d.ts +7 -0
- package/dist/report/generate.d.ts.map +1 -0
- package/dist/report/generate.js +30 -0
- package/dist/report/generate.js.map +1 -0
- package/dist/report/scoring.d.ts +90 -0
- package/dist/report/scoring.d.ts.map +1 -0
- package/dist/report/scoring.js +174 -0
- package/dist/report/scoring.js.map +1 -0
- package/dist/report/styles.d.ts +111 -0
- package/dist/report/styles.d.ts.map +1 -0
- package/dist/report/styles.js +134 -0
- package/dist/report/styles.js.map +1 -0
- package/docs/plans/2026-03-13-cli-exports.md +819 -0
- package/package.json +14 -4
- package/skills/eval-analysis/SKILL.md +178 -0
- package/skills/eval-analysis/references/data-shapes.md +279 -0
- package/src/cli/agents.ts +22 -0
- package/src/cli/eval.ts +42 -0
- package/src/cli/index.ts +1 -1
- package/src/mcp/tools.ts +50 -0
- package/src/report/agent-card/cover.tsx +232 -0
- package/src/report/agent-card/document.tsx +582 -0
- package/src/report/agent-card/types.ts +86 -0
- package/src/report/assets/Indemn_PrimaryLogo_Iris.png +0 -0
- package/src/report/assets/fonts/Barlow-Bold.ttf +0 -0
- package/src/report/assets/fonts/Barlow-Medium.ttf +0 -0
- package/src/report/assets/fonts/Barlow-Regular.ttf +0 -0
- package/src/report/assets/fonts/Barlow-SemiBold.ttf +0 -0
- package/src/report/eval-report/cover.tsx +69 -0
- package/src/report/eval-report/document.tsx +105 -0
- package/src/report/eval-report/example-page.tsx +121 -0
- package/src/report/eval-report/item-page.tsx +211 -0
- package/src/report/eval-report/matrix-page.tsx +124 -0
- package/src/report/eval-report/types.ts +77 -0
- package/src/report/eval-report/v2-cover.tsx +102 -0
- package/src/report/export.ts +394 -0
- package/src/report/generate.tsx +37 -0
- package/src/report/scoring.ts +281 -0
- package/src/report/styles.ts +138 -0
- package/tsconfig.json +2 -1
package/package.json
CHANGED
|
@@ -1,38 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@indemn/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "CLI, MCP server, and plugin for managing Indemn AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/sdk/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"indemn": "
|
|
8
|
+
"indemn": "dist/cli/index.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc",
|
|
12
|
+
"postbuild": "cp -r src/report/assets dist/report/assets",
|
|
12
13
|
"dev": "tsx --watch src/cli/index.ts",
|
|
13
14
|
"test": "vitest run",
|
|
14
15
|
"test:watch": "vitest"
|
|
15
16
|
},
|
|
16
|
-
"keywords": [
|
|
17
|
+
"keywords": [
|
|
18
|
+
"ai-agents",
|
|
19
|
+
"insurance",
|
|
20
|
+
"evaluations",
|
|
21
|
+
"indemn",
|
|
22
|
+
"mcp"
|
|
23
|
+
],
|
|
17
24
|
"author": "Indemn <support@indemn.com>",
|
|
18
25
|
"license": "MIT",
|
|
19
26
|
"repository": {
|
|
20
27
|
"type": "git",
|
|
21
|
-
"url": "https://github.com/indemn-ai/indemn-cli"
|
|
28
|
+
"url": "git+https://github.com/indemn-ai/indemn-cli.git"
|
|
22
29
|
},
|
|
23
30
|
"dependencies": {
|
|
24
31
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
32
|
+
"@react-pdf/renderer": "^4.3.2",
|
|
25
33
|
"chalk": "^5.4.1",
|
|
26
34
|
"cli-table3": "^0.6.5",
|
|
27
35
|
"commander": "^13.1.0",
|
|
28
36
|
"conf": "^13.1.0",
|
|
29
37
|
"firebase": "^11.6.0",
|
|
30
38
|
"ora": "^8.2.0",
|
|
39
|
+
"react": "^19.2.4",
|
|
31
40
|
"socket.io-client": "^4.8.1",
|
|
32
41
|
"zod": "^3.24.0"
|
|
33
42
|
},
|
|
34
43
|
"devDependencies": {
|
|
35
44
|
"@types/node": "^22.15.0",
|
|
45
|
+
"@types/react": "^19.2.14",
|
|
36
46
|
"tsx": "^4.19.0",
|
|
37
47
|
"typescript": "^5.9.3",
|
|
38
48
|
"vitest": "^3.1.0"
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: eval-analysis
|
|
3
|
+
description: Analyze evaluation results to classify failures as agent issues vs evaluation issues. Also export conversation traces for sharing. Use when the user asks to analyze eval results, understand why tests failed, triage failures, improve evaluation scores, pull conversation traces, or share eval results.
|
|
4
|
+
argument-hint: [run-id or agent-id]
|
|
5
|
+
allowed-tools: Bash(indemn *)
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Eval Analysis
|
|
9
|
+
|
|
10
|
+
Analyze evaluation run results, classify each failure, and recommend fixes. Also supports exporting conversation traces and generating branded PDF reports.
|
|
11
|
+
|
|
12
|
+
**Input:** `$ARGUMENTS` — a run_id (UUID) or agent_id (MongoDB ObjectId)
|
|
13
|
+
|
|
14
|
+
## Step 1: Resolve the Run
|
|
15
|
+
|
|
16
|
+
Determine what `$ARGUMENTS` is:
|
|
17
|
+
- **UUID format** (contains hyphens, 36 chars) → it's a `run_id`. Use directly.
|
|
18
|
+
- **ObjectId format** (24 hex chars) → it's an `agent_id`. Fetch the latest completed run:
|
|
19
|
+
```bash
|
|
20
|
+
indemn eval list --agent-id $ARGUMENTS --json --limit 5
|
|
21
|
+
```
|
|
22
|
+
Extract the latest `run_id` with `status: completed`.
|
|
23
|
+
|
|
24
|
+
If no completed runs exist, tell the user and stop.
|
|
25
|
+
|
|
26
|
+
## Step 2: Pull All Data
|
|
27
|
+
|
|
28
|
+
Fetch all data using the CLI (parallelize where possible):
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Run summary
|
|
32
|
+
indemn eval status <run-id> --json
|
|
33
|
+
|
|
34
|
+
# Per-item results
|
|
35
|
+
indemn eval results <run-id> --json
|
|
36
|
+
|
|
37
|
+
# Rubric (get rubric_id from run summary)
|
|
38
|
+
indemn rubric get <rubric-id> --json
|
|
39
|
+
|
|
40
|
+
# Test set (get test_set_id from run summary)
|
|
41
|
+
indemn testset get <test-set-id> --json
|
|
42
|
+
|
|
43
|
+
# Bot context (get agent_id from run summary)
|
|
44
|
+
indemn eval bot-context <agent-id> --json
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For exact response shapes, see `references/data-shapes.md`.
|
|
48
|
+
|
|
49
|
+
## Step 3: Present Run Overview
|
|
50
|
+
|
|
51
|
+
Show the user a summary table:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
Run: {run_id}
|
|
55
|
+
Agent: {bot_name} ({agent_id})
|
|
56
|
+
LLM: {bot_llm_provider}/{bot_llm_model}
|
|
57
|
+
Status: {passed}/{total} items passed ({pass_rate}%)
|
|
58
|
+
|
|
59
|
+
Criteria: {criteria_passed}/{criteria_total} checks passed
|
|
60
|
+
Rubric: {rubric_rules_passed}/{rubric_rules_total} rules passed
|
|
61
|
+
|
|
62
|
+
Component Scores:
|
|
63
|
+
prompt: {score}% ({passed}/{total})
|
|
64
|
+
knowledge_base: {score}% ({passed}/{total})
|
|
65
|
+
function: {score}% ({passed}/{total})
|
|
66
|
+
general: {score}% ({passed}/{total})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Step 4: Analyze Each Failed Item
|
|
70
|
+
|
|
71
|
+
For every result where `passed == false`:
|
|
72
|
+
|
|
73
|
+
1. **Get the test item** — match `test_case_id` to the test set's `items[].item_id` to get the item name, type, inputs, and success_criteria
|
|
74
|
+
2. **Check criteria scores** — from `criteria_scores[]`, list which criteria passed and which failed with their reasoning
|
|
75
|
+
3. **Check rubric scores** — from `rubric_scores[]`, list which rules passed and which failed with their reasoning
|
|
76
|
+
4. **Check for divergence** — flag when criteria say FAIL but rubric says PASS (or vice versa). Divergence is a strong signal of an evaluation problem.
|
|
77
|
+
5. **Read the conversation** — look at `output` field for the full agent response or conversation transcript
|
|
78
|
+
6. **Cross-reference system prompt** — check if the agent's behavior aligns with what the system prompt instructs
|
|
79
|
+
7. **Classify** — assign one of four categories (see below)
|
|
80
|
+
|
|
81
|
+
### Failure Categories
|
|
82
|
+
|
|
83
|
+
| Category | Signal | Action |
|
|
84
|
+
|----------|--------|--------|
|
|
85
|
+
| **Real Agent Problem** | Both criteria and rubric agree agent misbehaved, or output contradicts system prompt | Investigate agent code/prompt |
|
|
86
|
+
| **Evaluation Problem** | Criteria ambiguous/too strict, evaluator reasoning shows misunderstanding, agent response was reasonable | Update test set criteria |
|
|
87
|
+
| **Test Design Problem** | Test assumptions wrong (e.g., assumed KB lacked info it has), criteria contradict agent's actual capabilities | Redesign test item |
|
|
88
|
+
| **KB/Retrieval Problem** | Agent behavior correct but retrieval returned wrong content, or KB is missing needed info | Fix KB content or retrieval config |
|
|
89
|
+
|
|
90
|
+
### Divergence Analysis
|
|
91
|
+
|
|
92
|
+
When `criteria_passed != rubric_passed` for an item, this is a first-class finding:
|
|
93
|
+
- **Criteria FAIL + Rubric PASS** → criteria are likely too strict or testing the wrong thing
|
|
94
|
+
- **Criteria PASS + Rubric FAIL** → rubric may have rules that don't apply to this scenario, or agent violated a behavioral rule despite answering correctly
|
|
95
|
+
|
|
96
|
+
Always highlight divergences prominently in the report.
|
|
97
|
+
|
|
98
|
+
## Step 5: Aggregate Findings
|
|
99
|
+
|
|
100
|
+
Group all failed items by category:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
## Findings
|
|
104
|
+
|
|
105
|
+
### Real Agent Problems (N items)
|
|
106
|
+
- Item X: [description of what went wrong]
|
|
107
|
+
|
|
108
|
+
### Evaluation Problems (N items)
|
|
109
|
+
- Item Y: [why the criteria/rubric is wrong]
|
|
110
|
+
|
|
111
|
+
### Test Design Problems (N items)
|
|
112
|
+
- Item Z: [what assumption was incorrect]
|
|
113
|
+
|
|
114
|
+
### KB/Retrieval Problems (N items)
|
|
115
|
+
- Item W: [what retrieval issue occurred]
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Step 6: Recommendations
|
|
119
|
+
|
|
120
|
+
Prioritize fixes:
|
|
121
|
+
|
|
122
|
+
- **P1 — Evaluation fixes** (false failures inflate failure count, masking real issues): specific criteria rewrites or rubric rule adjustments
|
|
123
|
+
- **P2 — Agent investigation** (real bugs): which code paths or prompt sections to examine
|
|
124
|
+
- **P3 — KB/Retrieval fixes**: which knowledge bases need content updates or which retrieval configs need tuning
|
|
125
|
+
|
|
126
|
+
For each recommendation, be specific: quote the current criterion/rule, explain what's wrong, and propose the exact replacement text.
|
|
127
|
+
|
|
128
|
+
## Step 7: Offer to Execute Fixes
|
|
129
|
+
|
|
130
|
+
Ask the user if they want to apply the evaluation fixes. If yes:
|
|
131
|
+
|
|
132
|
+
**Update test set criteria** (creates a new version, non-destructive):
|
|
133
|
+
```bash
|
|
134
|
+
# Save the updated test set to a JSON file, then:
|
|
135
|
+
indemn testset update <test-set-id> --file updated-testset.json
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Update rubric rules** (creates a new version, non-destructive):
|
|
139
|
+
```bash
|
|
140
|
+
# Save the updated rubric to a JSON file, then:
|
|
141
|
+
indemn rubric update <rubric-id> --file updated-rubric.json
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Both update commands create new versions — the previous version is preserved and accessible via `indemn testset versions <id>` / `indemn rubric versions <id>`.
|
|
145
|
+
|
|
146
|
+
After applying fixes, suggest re-running the evaluation:
|
|
147
|
+
```bash
|
|
148
|
+
indemn eval run <agent-id> --test-set <test-set-id> --rubric <rubric-id> --wait
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Export: Conversation Traces
|
|
152
|
+
|
|
153
|
+
When the user wants to **share results** or **export conversation traces** (rather than triage failures):
|
|
154
|
+
|
|
155
|
+
### Markdown Export
|
|
156
|
+
```bash
|
|
157
|
+
indemn eval export <run-id>
|
|
158
|
+
# Optional: specify output path
|
|
159
|
+
indemn eval export <run-id> --output results.md
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Produces a structured markdown file with all conversations, criteria scores, and rubric scores.
|
|
163
|
+
|
|
164
|
+
### PDF Report
|
|
165
|
+
```bash
|
|
166
|
+
indemn eval report <run-id>
|
|
167
|
+
# Optional: specify output path
|
|
168
|
+
indemn eval report <run-id> --output report.pdf
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Produces a branded Indemn PDF with cover page, dual scores (criteria + rubric), and per-item detail pages.
|
|
172
|
+
|
|
173
|
+
### Agent Card
|
|
174
|
+
```bash
|
|
175
|
+
indemn agents card <agent-id>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Produces a multi-page branded PDF with system prompt, tools, KBs, LLM config, evaluation history, and metadata.
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# Evaluation API Data Shapes
|
|
2
|
+
|
|
3
|
+
Response shapes from the evaluations API at `localhost:8002`. All endpoints prefixed with `/api/v1`.
|
|
4
|
+
|
|
5
|
+
## EvaluationRun
|
|
6
|
+
|
|
7
|
+
**Endpoint:** `GET /runs/{run_id}`
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"run_id": "uuid",
|
|
12
|
+
"dataset_id": "uuid",
|
|
13
|
+
"agent_id": "mongodb-objectid",
|
|
14
|
+
"status": "pending | running | completed | failed",
|
|
15
|
+
"total": 13,
|
|
16
|
+
"completed": 13,
|
|
17
|
+
"passed": 8,
|
|
18
|
+
"failed": 5,
|
|
19
|
+
"concurrency": 3,
|
|
20
|
+
"started_at": "datetime",
|
|
21
|
+
"completed_at": "datetime",
|
|
22
|
+
"error": "string | null",
|
|
23
|
+
"rubric_id": "uuid | null",
|
|
24
|
+
"rubric_version": 1,
|
|
25
|
+
"test_set_id": "uuid | null",
|
|
26
|
+
"test_set_version": 1,
|
|
27
|
+
"component_scope_filter": "string | null",
|
|
28
|
+
"component_ids_filter": ["string"] ,
|
|
29
|
+
"component_scores": {
|
|
30
|
+
"prompt": { "score": 0.85, "total": 20, "passed": 17 },
|
|
31
|
+
"knowledge_base": { "score": 0.75, "total": 8, "passed": 6 },
|
|
32
|
+
"function": { "score": 1.0, "total": 4, "passed": 4 },
|
|
33
|
+
"general": { "score": 0.9, "total": 10, "passed": 9 }
|
|
34
|
+
},
|
|
35
|
+
"criteria_passed": 35,
|
|
36
|
+
"criteria_total": 40,
|
|
37
|
+
"rubric_rules_passed": 90,
|
|
38
|
+
"rubric_rules_total": 100,
|
|
39
|
+
"bot_llm_provider": "openai",
|
|
40
|
+
"bot_llm_model": "gpt-4.1",
|
|
41
|
+
"created_at": "datetime"
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Key fields:**
|
|
46
|
+
- `component_scores` — rubric rule pass rates broken down by component scope (prompt, knowledge_base, function, general). Null if no rubric used.
|
|
47
|
+
- `criteria_passed/total` — aggregate criterion-level pass counts across ALL results
|
|
48
|
+
- `rubric_rules_passed/total` — aggregate rubric rule pass counts across ALL results
|
|
49
|
+
- `test_set_id` — present for V2 runs. `dataset_id` is for V1 question-set-based runs.
|
|
50
|
+
|
|
51
|
+
## EvaluationResult
|
|
52
|
+
|
|
53
|
+
**Endpoint:** `GET /runs/{run_id}/results`
|
|
54
|
+
|
|
55
|
+
Returns an array of results, one per test item.
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"result_id": "uuid",
|
|
60
|
+
"run_id": "uuid",
|
|
61
|
+
"test_case_id": "uuid (matches TestItem.item_id)",
|
|
62
|
+
"langsmith_run_id": "uuid | null",
|
|
63
|
+
"input": {
|
|
64
|
+
"persona": "...",
|
|
65
|
+
"initial_message": "...",
|
|
66
|
+
"max_turns": 8
|
|
67
|
+
},
|
|
68
|
+
"output": {
|
|
69
|
+
"messages": [
|
|
70
|
+
{ "role": "user", "content": "..." },
|
|
71
|
+
{ "role": "assistant", "content": "..." }
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
"scores": {
|
|
75
|
+
"evaluator_name": { "score": 0.8, "comment": "JSON string with details" }
|
|
76
|
+
},
|
|
77
|
+
"item_name": "PTO Policy Inquiry",
|
|
78
|
+
"item_type": "scenario",
|
|
79
|
+
"criteria_scores": [
|
|
80
|
+
{
|
|
81
|
+
"criterion": "Agent retrieves PTO policy from handbook KB",
|
|
82
|
+
"passed": false,
|
|
83
|
+
"score": 0.0,
|
|
84
|
+
"reasoning": "Agent did not find PTO information..."
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
"criteria_passed": false,
|
|
88
|
+
"rubric_scores": [
|
|
89
|
+
{
|
|
90
|
+
"rule_id": "BRIEF_RESPONSES",
|
|
91
|
+
"rule_name": "Brief, Focused Responses",
|
|
92
|
+
"severity": "medium",
|
|
93
|
+
"passed": true,
|
|
94
|
+
"score": 1.0,
|
|
95
|
+
"reasoning": "Agent kept responses concise..."
|
|
96
|
+
}
|
|
97
|
+
],
|
|
98
|
+
"rubric_passed": true,
|
|
99
|
+
"passed": false,
|
|
100
|
+
"duration_ms": 45000,
|
|
101
|
+
"created_at": "datetime"
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**V1 vs V2 scoring:**
|
|
106
|
+
- **V1** (question-set runs): `scores` dict populated, `criteria_scores`/`rubric_scores` are null. The `comment` field in scores is a JSON string — parse it for per-criterion details.
|
|
107
|
+
- **V2** (test-set runs): `criteria_scores[]` and `rubric_scores[]` populated with structured data. `scores` may still be present.
|
|
108
|
+
|
|
109
|
+
**Passed logic:** `passed = criteria_passed AND rubric_passed`. An item fails if ANY criterion fails OR any high-severity rubric rule fails.
|
|
110
|
+
|
|
111
|
+
## Rubric
|
|
112
|
+
|
|
113
|
+
**Endpoint:** `GET /rubrics/{rubric_id}`
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"rubric_id": "uuid",
|
|
118
|
+
"agent_id": "mongodb-objectid",
|
|
119
|
+
"version": 1,
|
|
120
|
+
"name": "O'Connor Internal Ops Associate Rubric",
|
|
121
|
+
"description": "...",
|
|
122
|
+
"severity_definitions": {
|
|
123
|
+
"high": "Critical violation...",
|
|
124
|
+
"medium": "Moderate violation...",
|
|
125
|
+
"low": "Minor violation..."
|
|
126
|
+
},
|
|
127
|
+
"rules": [
|
|
128
|
+
{
|
|
129
|
+
"id": "BRIEF_RESPONSES",
|
|
130
|
+
"name": "Brief, Focused Responses",
|
|
131
|
+
"severity": "medium",
|
|
132
|
+
"category": "response_quality",
|
|
133
|
+
"description": "Agent should provide concise answers...",
|
|
134
|
+
"prompt_reference": "Section: Communication Style",
|
|
135
|
+
"evaluation_criteria": {
|
|
136
|
+
"pass_conditions": ["Response is under 3 sentences for simple questions"],
|
|
137
|
+
"fail_conditions": ["Response includes unnecessary preamble or filler"]
|
|
138
|
+
},
|
|
139
|
+
"examples": {
|
|
140
|
+
"violation": "...",
|
|
141
|
+
"correct": "..."
|
|
142
|
+
},
|
|
143
|
+
"component_scope": "prompt | knowledge_base | function | general | null",
|
|
144
|
+
"component_ids": ["objectid", "..."],
|
|
145
|
+
"component_names": ["HR Handbook KB"]
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
"created_at": "datetime",
|
|
149
|
+
"updated_at": "datetime"
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Key fields:**
|
|
154
|
+
- `rules[].evaluation_criteria` — pass/fail conditions the evaluator LLM uses to judge
|
|
155
|
+
- `rules[].severity` — high severity failures cause the entire item to fail
|
|
156
|
+
- `rules[].component_scope` — links rule to a specific component type for component-level scoring
|
|
157
|
+
- `rules[].component_ids` / `component_names` — links rule to specific bot components (KBs, tools, etc.)
|
|
158
|
+
|
|
159
|
+
## TestSet
|
|
160
|
+
|
|
161
|
+
**Endpoint:** `GET /test-sets/{test_set_id}`
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"test_set_id": "uuid",
|
|
166
|
+
"agent_id": "mongodb-objectid",
|
|
167
|
+
"version": 2,
|
|
168
|
+
"name": "O'Connor Internal Ops Associate Test Set",
|
|
169
|
+
"description": "...",
|
|
170
|
+
"items": [
|
|
171
|
+
{
|
|
172
|
+
"item_id": "uuid (matches EvaluationResult.test_case_id)",
|
|
173
|
+
"type": "single_turn | scenario",
|
|
174
|
+
"name": "PTO Policy Inquiry",
|
|
175
|
+
"inputs": {
|
|
176
|
+
"message": "What is our PTO policy?",
|
|
177
|
+
"persona": "An employee asking about...",
|
|
178
|
+
"initial_message": "Hi, I need to know...",
|
|
179
|
+
"max_turns": 8
|
|
180
|
+
},
|
|
181
|
+
"expected": {
|
|
182
|
+
"success_criteria": [
|
|
183
|
+
"Agent retrieves PTO policy from knowledge base",
|
|
184
|
+
"Response includes accrual rates or refers to handbook"
|
|
185
|
+
],
|
|
186
|
+
"should_use_tools": ["search_kb"],
|
|
187
|
+
"should_not_use_tools": ["live_handoff"],
|
|
188
|
+
"expected_outcome": "Employee gets PTO info"
|
|
189
|
+
},
|
|
190
|
+
"tags": ["hr", "policy"],
|
|
191
|
+
"priority": "high"
|
|
192
|
+
}
|
|
193
|
+
],
|
|
194
|
+
"created_at": "datetime",
|
|
195
|
+
"updated_at": "datetime"
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Key fields:**
|
|
200
|
+
- `items[].item_id` — join key to `EvaluationResult.test_case_id`
|
|
201
|
+
- `items[].type` — `single_turn` sends one message; `scenario` runs multi-turn simulation with persona
|
|
202
|
+
- `items[].inputs` — for single_turn: `message`; for scenario: `persona`, `initial_message`, `max_turns`
|
|
203
|
+
- `items[].expected.success_criteria` — the criteria that become `criteria_scores[]` in results
|
|
204
|
+
|
|
205
|
+
## BotContext
|
|
206
|
+
|
|
207
|
+
**Endpoint:** `GET /bots/{agent_id}/context`
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
{
|
|
211
|
+
"bot_id": "mongodb-objectid",
|
|
212
|
+
"bot_name": "O'Connor Internal Operations Associate",
|
|
213
|
+
"organization_id": "mongodb-objectid",
|
|
214
|
+
"organization_name": "O'Connor Insurance",
|
|
215
|
+
"system_prompt": "You are an AI assistant for O'Connor Insurance...",
|
|
216
|
+
"llm_config": {
|
|
217
|
+
"provider": "openai",
|
|
218
|
+
"model": "gpt-4.1",
|
|
219
|
+
"parameters": { "temperature": 0.3 }
|
|
220
|
+
},
|
|
221
|
+
"tools": [
|
|
222
|
+
{
|
|
223
|
+
"id": "objectid",
|
|
224
|
+
"name": "Live Chat Handoff",
|
|
225
|
+
"tool_name": "live_handoff",
|
|
226
|
+
"type": "human_handoff",
|
|
227
|
+
"description": "Transfer to a live agent when...",
|
|
228
|
+
"output_instructions": "..."
|
|
229
|
+
}
|
|
230
|
+
],
|
|
231
|
+
"knowledge_bases": [
|
|
232
|
+
{
|
|
233
|
+
"id": "objectid",
|
|
234
|
+
"name": "O'Connor Employee Handbook",
|
|
235
|
+
"type": "file",
|
|
236
|
+
"document_count": 3
|
|
237
|
+
}
|
|
238
|
+
],
|
|
239
|
+
"harness_context": {
|
|
240
|
+
"version": "1.0",
|
|
241
|
+
"description": "LangGraph-based agent...",
|
|
242
|
+
"execution_flow": [
|
|
243
|
+
{ "step": 1, "node": "invoke_model", "description": "..." }
|
|
244
|
+
],
|
|
245
|
+
"key_behaviors": ["..."],
|
|
246
|
+
"evaluation_notes": ["..."]
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Key fields:**
|
|
252
|
+
- `system_prompt` — the full agent prompt; cross-reference against agent behavior to distinguish real bugs from expected behavior
|
|
253
|
+
- `tools[]` — available tools with their descriptions; critical for understanding handoff, search, and function-calling behavior
|
|
254
|
+
- `knowledge_bases[]` — connected KBs; check if test items assume KB content that may not exist
|
|
255
|
+
- `harness_context` — how bot-service executes the agent; useful for understanding tool call flow
|
|
256
|
+
|
|
257
|
+
## Versioning Endpoints
|
|
258
|
+
|
|
259
|
+
Both test sets and rubrics support version history:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
# List all versions
|
|
263
|
+
GET /test-sets/{test_set_id}/versions
|
|
264
|
+
GET /rubrics/{rubric_id}/versions
|
|
265
|
+
|
|
266
|
+
# Get a specific version
|
|
267
|
+
GET /test-sets/{test_set_id}/versions/{version}
|
|
268
|
+
GET /rubrics/{rubric_id}/versions/{version}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
PUT to update creates a new version automatically — previous versions are preserved.
|
|
272
|
+
|
|
273
|
+
## Listing Runs for an Agent
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
GET /runs?agent_id={agent_id}&status=completed&limit=5
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Returns runs sorted by `created_at` descending. Use this when given an agent_id instead of a run_id.
|
package/src/cli/agents.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Command } from 'commander';
|
|
|
2
2
|
import ora from 'ora';
|
|
3
3
|
import { IndemnClient } from '../sdk/client.js';
|
|
4
4
|
import { AgentsSDK } from '../sdk/agents.js';
|
|
5
|
+
import { exportAgentCard } from '../report/export.js';
|
|
5
6
|
import { formatTable, printJson, printError, printSuccess, confirm } from './utils/output.js';
|
|
6
7
|
|
|
7
8
|
function formatChannels(channels: unknown[]): string {
|
|
@@ -178,6 +179,27 @@ export function registerAgentCommands(program: Command): void {
|
|
|
178
179
|
}
|
|
179
180
|
});
|
|
180
181
|
|
|
182
|
+
agents
|
|
183
|
+
.command('card')
|
|
184
|
+
.argument('<id>', 'Agent ID')
|
|
185
|
+
.description('Generate branded agent card PDF')
|
|
186
|
+
.option('--output <path>', 'Output file path')
|
|
187
|
+
.action(async (id: string, options) => {
|
|
188
|
+
try {
|
|
189
|
+
const client = new IndemnClient();
|
|
190
|
+
const spinner = ora('Fetching agent data...').start();
|
|
191
|
+
|
|
192
|
+
spinner.text = 'Generating PDF...';
|
|
193
|
+
const result = await exportAgentCard(id, options.output, client);
|
|
194
|
+
|
|
195
|
+
spinner.stop();
|
|
196
|
+
printSuccess(`Agent card saved to ${result.file_path}`);
|
|
197
|
+
} catch (err) {
|
|
198
|
+
printError(err);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
181
203
|
agents
|
|
182
204
|
.command('clone')
|
|
183
205
|
.argument('<id>', 'Agent ID to clone')
|
package/src/cli/eval.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { IndemnClient } from '../sdk/client.js';
|
|
|
6
6
|
import { EvalSDK } from '../sdk/eval.js';
|
|
7
7
|
import { RubricsSDK } from '../sdk/rubrics.js';
|
|
8
8
|
import { TestSetsSDK } from '../sdk/test-sets.js';
|
|
9
|
+
import { exportEvalMarkdown, exportEvalReport } from '../report/export.js';
|
|
9
10
|
import { formatTable, printJson, printError, printSuccess, confirm } from './utils/output.js';
|
|
10
11
|
|
|
11
12
|
export function registerEvalCommands(program: Command): void {
|
|
@@ -242,6 +243,47 @@ export function registerEvalCommands(program: Command): void {
|
|
|
242
243
|
}
|
|
243
244
|
});
|
|
244
245
|
|
|
246
|
+
eval_
|
|
247
|
+
.command('export')
|
|
248
|
+
.argument('<run-id>', 'Evaluation run ID')
|
|
249
|
+
.description('Export evaluation results as markdown')
|
|
250
|
+
.option('--output <path>', 'Output file path')
|
|
251
|
+
.action(async (runId: string, options) => {
|
|
252
|
+
try {
|
|
253
|
+
const client = new IndemnClient();
|
|
254
|
+
const spinner = ora('Fetching evaluation data...').start();
|
|
255
|
+
|
|
256
|
+
const result = await exportEvalMarkdown(runId, options.output, client);
|
|
257
|
+
|
|
258
|
+
spinner.stop();
|
|
259
|
+
printSuccess(`Exported to ${result.file_path}`);
|
|
260
|
+
} catch (err) {
|
|
261
|
+
printError(err);
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
eval_
|
|
267
|
+
.command('report')
|
|
268
|
+
.argument('<run-id>', 'Evaluation run ID')
|
|
269
|
+
.description('Generate branded evaluation report PDF')
|
|
270
|
+
.option('--output <path>', 'Output file path')
|
|
271
|
+
.action(async (runId: string, options) => {
|
|
272
|
+
try {
|
|
273
|
+
const client = new IndemnClient();
|
|
274
|
+
const spinner = ora('Fetching evaluation data...').start();
|
|
275
|
+
|
|
276
|
+
spinner.text = 'Generating PDF...';
|
|
277
|
+
const result = await exportEvalReport(runId, options.output, client);
|
|
278
|
+
|
|
279
|
+
spinner.stop();
|
|
280
|
+
printSuccess(`Report saved to ${result.file_path}`);
|
|
281
|
+
} catch (err) {
|
|
282
|
+
printError(err);
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
245
287
|
// Rubric commands — registered on program, not nested under eval
|
|
246
288
|
const rubric = program.command('rubric').description('Manage evaluation rubrics');
|
|
247
289
|
|
package/src/cli/index.ts
CHANGED
package/src/mcp/tools.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { RubricsSDK } from '../sdk/rubrics.js';
|
|
|
9
9
|
import { TestSetsSDK } from '../sdk/test-sets.js';
|
|
10
10
|
import { EvalSDK } from '../sdk/eval.js';
|
|
11
11
|
import { ChatSession } from '../sdk/chat.js';
|
|
12
|
+
import { exportEvalMarkdown, exportEvalReport, exportAgentCard } from '../report/export.js';
|
|
12
13
|
|
|
13
14
|
// Helper: format a successful MCP tool result
|
|
14
15
|
function success(data: unknown) {
|
|
@@ -968,4 +969,53 @@ export function registerTools(server: McpServer, client: IndemnClient): void {
|
|
|
968
969
|
} catch (err) { return error(err); }
|
|
969
970
|
},
|
|
970
971
|
);
|
|
972
|
+
|
|
973
|
+
// ---------------------------------------------------------------------------
|
|
974
|
+
// Export tools
|
|
975
|
+
// ---------------------------------------------------------------------------
|
|
976
|
+
|
|
977
|
+
server.tool(
|
|
978
|
+
'export_eval_markdown',
|
|
979
|
+
'Export evaluation results as a structured markdown file with conversations, scores, and criteria',
|
|
980
|
+
{
|
|
981
|
+
run_id: z.string().describe('The evaluation run ID'),
|
|
982
|
+
output_path: z.string().optional().describe('Output file path (defaults to current directory)'),
|
|
983
|
+
},
|
|
984
|
+
async (args) => {
|
|
985
|
+
try {
|
|
986
|
+
const result = await exportEvalMarkdown(args.run_id, args.output_path, client);
|
|
987
|
+
return success(result);
|
|
988
|
+
} catch (err) { return error(err); }
|
|
989
|
+
},
|
|
990
|
+
);
|
|
991
|
+
|
|
992
|
+
server.tool(
|
|
993
|
+
'export_eval_report',
|
|
994
|
+
'Generate a branded PDF evaluation report with cover page, scores, and per-item details',
|
|
995
|
+
{
|
|
996
|
+
run_id: z.string().describe('The evaluation run ID'),
|
|
997
|
+
output_path: z.string().optional().describe('Output file path (defaults to current directory)'),
|
|
998
|
+
},
|
|
999
|
+
async (args) => {
|
|
1000
|
+
try {
|
|
1001
|
+
const result = await exportEvalReport(args.run_id, args.output_path, client);
|
|
1002
|
+
return success(result);
|
|
1003
|
+
} catch (err) { return error(err); }
|
|
1004
|
+
},
|
|
1005
|
+
);
|
|
1006
|
+
|
|
1007
|
+
server.tool(
|
|
1008
|
+
'export_agent_card',
|
|
1009
|
+
'Generate a branded PDF agent card with system prompt, tools, KBs, LLM config, and evaluation history',
|
|
1010
|
+
{
|
|
1011
|
+
agent_id: z.string().describe('The agent ID'),
|
|
1012
|
+
output_path: z.string().optional().describe('Output file path (defaults to current directory)'),
|
|
1013
|
+
},
|
|
1014
|
+
async (args) => {
|
|
1015
|
+
try {
|
|
1016
|
+
const result = await exportAgentCard(args.agent_id, args.output_path, client);
|
|
1017
|
+
return success(result);
|
|
1018
|
+
} catch (err) { return error(err); }
|
|
1019
|
+
},
|
|
1020
|
+
);
|
|
971
1021
|
}
|