@evalview/node 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 EvalView Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # @evalview/node
2
+
3
+ Drop-in Node.js/Next.js middleware for [EvalView](https://github.com/hidai25/eval-view) testing framework.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @evalview/node
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ### Next.js App Router
14
+
15
+ ```typescript
16
+ // app/api/evalview/route.ts
17
+ import { createEvalViewMiddleware } from '@evalview/node';
18
+
19
+ export const POST = createEvalViewMiddleware({
20
+ targetEndpoint: '/api/unifiedchat', // Your agent's endpoint
21
+ });
22
+ ```
23
+
24
+ ### Express.js
25
+
26
+ ```javascript
27
+ const { createEvalViewMiddleware } = require('@evalview/node');
28
+
29
+ app.post('/api/evalview', createEvalViewMiddleware({
30
+ targetEndpoint: '/api/your-agent',
31
+ }));
32
+ ```
33
+
34
+ ## Configuration
35
+
36
+ ```typescript
37
+ createEvalViewMiddleware({
38
+ // Required: Endpoint to forward requests to
39
+ targetEndpoint: '/api/unifiedchat',
40
+
41
+ // Optional: Default user ID for test requests (defaults to 'evalview-test-user')
42
+ // Use an existing user ID from your database
43
+ defaultUserId: 'your-dev-user-id',
44
+
45
+ // Optional: Dynamic user ID resolution
46
+ // Useful for creating users on-the-fly or looking up existing ones
47
+ getUserId: async (req) => {
48
+ // Example: Look up or create user
49
+ const user = await findOrCreateUser('test@example.com');
50
+ return user.id;
51
+ },
52
+
53
+ // Optional: Transform EvalView request to your API format
54
+ transformRequest: (req) => ({
55
+ message: req.query,
56
+ userId: req.context?.userId, // Automatically set by middleware
57
+ // ... your custom mapping
58
+ }),
59
+
60
+ // Optional: Parse your API response to EvalView format
61
+ parseResponse: (responseText, startTime) => ({
62
+ session_id: `session-${startTime}`,
63
+ output: '...',
64
+ steps: [...],
65
+ cost: 0.05,
66
+ latency: Date.now() - startTime,
67
+ }),
68
+
69
+ // Optional: Base URL for requests
70
+ baseUrl: process.env.API_BASE_URL,
71
+ });
72
+ ```
73
+
74
+ ## Default Behavior
75
+
76
+ Works out-of-the-box with Tapescope-style APIs that:
77
+ - Accept: `{ message, userId, conversationId, route, history }`
78
+ - Return: NDJSON stream with `tool_call` and `message_complete` events
79
+
80
+ ## Testing
81
+
82
+ Point EvalView CLI to your endpoint:
83
+
84
+ ```yaml
85
+ # .evalview/config.yaml
86
+ adapter: http
87
+ endpoint: http://localhost:3000/api/evalview
88
+ ```
89
+
90
+ Then run tests:
91
+ ```bash
92
+ evalview run
93
+ ```
94
+
95
+ ## License
96
+
97
+ MIT
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @evalview/node
3
+ *
4
+ * Drop-in Node.js middleware for EvalView testing framework
5
+ */
6
+ export { createEvalViewMiddleware, type EvalViewRequest, type EvalViewResponse, type EvalViewStep, type MiddlewareConfig, } from './middleware';
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /**
3
+ * @evalview/node
4
+ *
5
+ * Drop-in Node.js middleware for EvalView testing framework
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.createEvalViewMiddleware = void 0;
9
+ var middleware_1 = require("./middleware");
10
+ Object.defineProperty(exports, "createEvalViewMiddleware", { enumerable: true, get: function () { return middleware_1.createEvalViewMiddleware; } });
@@ -0,0 +1,65 @@
1
+ /**
2
+ * EvalView Middleware for Node.js/Next.js
3
+ *
4
+ * Drop-in middleware that translates EvalView standard format
5
+ * to your agent's API format and back.
6
+ *
7
+ * Usage:
8
+ * import { createEvalViewMiddleware } from '@evalview/node'
9
+ *
10
+ * app.post('/api/evalview', createEvalViewMiddleware({
11
+ * targetEndpoint: '/api/unifiedchat',
12
+ * parseResponse: (ndjson) => ({ output, steps, cost, latency })
13
+ * }))
14
+ */
15
+ export interface EvalViewRequest {
16
+ query: string;
17
+ context?: {
18
+ route?: string;
19
+ userId?: string;
20
+ conversationId?: string;
21
+ history?: any[];
22
+ [key: string]: any;
23
+ };
24
+ enable_tracing?: boolean;
25
+ }
26
+ export interface EvalViewStep {
27
+ id: string;
28
+ name: string;
29
+ tool: string;
30
+ parameters: any;
31
+ output?: any;
32
+ success: boolean;
33
+ latency?: number;
34
+ cost?: number;
35
+ tokens?: number;
36
+ }
37
+ export interface EvalViewResponse {
38
+ session_id: string;
39
+ output: string;
40
+ steps: EvalViewStep[];
41
+ cost: number;
42
+ latency: number;
43
+ tokens?: number;
44
+ }
45
+ export interface MiddlewareConfig {
46
+ /** Endpoint to forward requests to (e.g., '/api/unifiedchat') */
47
+ targetEndpoint: string;
48
+ /** Optional: Transform EvalView request to your API format */
49
+ transformRequest?: (req: EvalViewRequest) => any;
50
+ /** Optional: Parse your API response to EvalView format */
51
+ parseResponse?: (responseText: string, startTime: number) => EvalViewResponse;
52
+ /** Optional: Base URL for requests (defaults to current host) */
53
+ baseUrl?: string;
54
+ /** Optional: Default user ID for test requests (defaults to 'evalview-test-user') */
55
+ defaultUserId?: string;
56
+ /** Optional: Function to get/create user ID dynamically */
57
+ getUserId?: (req: EvalViewRequest) => string | Promise<string>;
58
+ }
59
+ /**
60
+ * Create EvalView middleware handler
61
+ *
62
+ * @param config - Middleware configuration
63
+ * @returns Request handler function
64
+ */
65
+ export declare function createEvalViewMiddleware(config: MiddlewareConfig): (req: any) => Promise<import("undici-types").Response>;
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ /**
3
+ * EvalView Middleware for Node.js/Next.js
4
+ *
5
+ * Drop-in middleware that translates EvalView standard format
6
+ * to your agent's API format and back.
7
+ *
8
+ * Usage:
9
+ * import { createEvalViewMiddleware } from '@evalview/node'
10
+ *
11
+ * app.post('/api/evalview', createEvalViewMiddleware({
12
+ * targetEndpoint: '/api/unifiedchat',
13
+ * parseResponse: (ndjson) => ({ output, steps, cost, latency })
14
+ * }))
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.createEvalViewMiddleware = createEvalViewMiddleware;
18
+ const DEBUG = process.env.EVALVIEW_DEBUG === 'true';
19
+ /**
20
+ * Default Tapescope request transformer
21
+ */
22
+ function defaultTransformRequest(req) {
23
+ const timestamp = Date.now();
24
+ return {
25
+ message: req.query,
26
+ userId: req.context?.userId, // Set by middleware
27
+ conversationId: req.context?.conversationId || `eval-${timestamp}`,
28
+ route: req.context?.route || 'orchestrator',
29
+ history: req.context?.history || [],
30
+ };
31
+ }
32
+ /**
33
+ * Default Tapescope NDJSON response parser
34
+ * Handles both pure NDJSON and SSE format (with "data: " prefix)
35
+ */
36
+ function defaultParseResponse(ndjsonText, startTime) {
37
+ const lines = ndjsonText.trim().split('\n');
38
+ const events = lines
39
+ .filter(line => line.trim())
40
+ .map(line => {
41
+ try {
42
+ // Strip SSE "data: " prefix if present
43
+ const jsonLine = line.startsWith('data: ') ? line.slice(6) : line;
44
+ return JSON.parse(jsonLine);
45
+ }
46
+ catch (e) {
47
+ return null;
48
+ }
49
+ })
50
+ .filter(Boolean);
51
+ const steps = [];
52
+ let finalOutput = '';
53
+ let totalCost = 0;
54
+ let totalTokens = 0;
55
+ const sessionId = `session-${startTime}`;
56
+ for (const event of events) {
57
+ const eventType = event.type;
58
+ // Capture tool calls as steps
59
+ if (eventType === 'tool_call') {
60
+ steps.push({
61
+ id: `step-${steps.length}`,
62
+ name: event.tool || 'unknown',
63
+ tool: event.tool || 'unknown',
64
+ parameters: event.params || {},
65
+ output: event.result,
66
+ success: true,
67
+ latency: 0,
68
+ cost: 0,
69
+ });
70
+ }
71
+ // Capture final message
72
+ if (eventType === 'message_complete') {
73
+ finalOutput = event.final?.text || '';
74
+ totalCost = event.data?.metadata?.cost || 0;
75
+ // Add synthesis step if present
76
+ if (event.tool) {
77
+ steps.push({
78
+ id: `step-${steps.length}`,
79
+ name: event.tool,
80
+ tool: event.tool,
81
+ parameters: {},
82
+ success: event.ok || false,
83
+ latency: 0,
84
+ cost: totalCost,
85
+ });
86
+ }
87
+ }
88
+ // Accumulate streaming text (fallback if final not set)
89
+ if (eventType === 'text_delta' && !finalOutput) {
90
+ finalOutput += event.delta || '';
91
+ }
92
+ }
93
+ const endTime = Date.now();
94
+ const latency = endTime - startTime;
95
+ return {
96
+ session_id: sessionId,
97
+ output: finalOutput,
98
+ steps,
99
+ cost: totalCost,
100
+ latency,
101
+ tokens: totalTokens,
102
+ };
103
+ }
104
+ /**
105
+ * Create EvalView middleware handler
106
+ *
107
+ * @param config - Middleware configuration
108
+ * @returns Request handler function
109
+ */
110
+ function createEvalViewMiddleware(config) {
111
+ const { targetEndpoint, transformRequest = defaultTransformRequest, parseResponse = defaultParseResponse, baseUrl, defaultUserId = 'evalview-test-user', getUserId, } = config;
112
+ return async function evalViewHandler(req) {
113
+ const startTime = Date.now();
114
+ try {
115
+ // Parse EvalView request (Next.js App Router)
116
+ const evalViewReq = await req.json();
117
+ // Resolve user ID (priority: context.userId > getUserId() > defaultUserId)
118
+ if (!evalViewReq.context) {
119
+ evalViewReq.context = {};
120
+ }
121
+ if (!evalViewReq.context.userId) {
122
+ evalViewReq.context.userId = getUserId
123
+ ? await getUserId(evalViewReq)
124
+ : defaultUserId;
125
+ }
126
+ if (DEBUG)
127
+ console.log('[EvalView] Received request:', {
128
+ query: evalViewReq.query,
129
+ route: evalViewReq.context?.route,
130
+ });
131
+ // Transform to target API format
132
+ const targetReq = transformRequest(evalViewReq);
133
+ if (DEBUG)
134
+ console.log('[EvalView] Calling target endpoint:', targetEndpoint);
135
+ // Determine base URL
136
+ const host = req.headers?.get?.('host') || req.headers?.host || 'localhost:3000';
137
+ const requestBaseUrl = baseUrl || `http://${host}`;
138
+ // Call target endpoint
139
+ const response = await fetch(`${requestBaseUrl}${targetEndpoint}`, {
140
+ method: 'POST',
141
+ headers: {
142
+ 'Content-Type': 'application/json',
143
+ },
144
+ body: JSON.stringify(targetReq),
145
+ });
146
+ if (!response.ok) {
147
+ throw new Error(`Target endpoint failed: ${response.status} ${response.statusText}`);
148
+ }
149
+ // Get response text
150
+ const responseText = await response.text();
151
+ if (DEBUG)
152
+ console.log('[EvalView] Received response, parsing...');
153
+ // Parse and translate response
154
+ const evalViewRes = parseResponse(responseText, startTime);
155
+ if (DEBUG)
156
+ console.log('[EvalView] Translated response:', {
157
+ steps: evalViewRes.steps.length,
158
+ outputLength: evalViewRes.output.length,
159
+ cost: evalViewRes.cost,
160
+ latency: evalViewRes.latency,
161
+ });
162
+ // Return JSON response (Next.js App Router format)
163
+ return new Response(JSON.stringify(evalViewRes), {
164
+ status: 200,
165
+ headers: { 'Content-Type': 'application/json' },
166
+ });
167
+ }
168
+ catch (error) {
169
+ console.error('[EvalView] Error:', error);
170
+ return new Response(JSON.stringify({
171
+ error: error.message || 'EvalView middleware failed',
172
+ session_id: `error-${startTime}`,
173
+ output: '',
174
+ steps: [],
175
+ cost: 0,
176
+ latency: Date.now() - startTime,
177
+ }), {
178
+ status: 500,
179
+ headers: { 'Content-Type': 'application/json' },
180
+ });
181
+ }
182
+ };
183
+ }
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@evalview/node",
3
+ "version": "0.1.0",
4
+ "description": "Drop-in Node.js middleware for EvalView testing framework",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.js"
13
+ },
14
+ "./middleware": {
15
+ "types": "./dist/middleware.d.ts",
16
+ "import": "./dist/middleware.js",
17
+ "require": "./dist/middleware.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsc",
27
+ "dev": "tsc --watch",
28
+ "clean": "rm -rf dist",
29
+ "prepublishOnly": "npm run clean && npm run build"
30
+ },
31
+ "keywords": [
32
+ "evalview",
33
+ "testing",
34
+ "ai-agents",
35
+ "middleware",
36
+ "llm",
37
+ "evaluation"
38
+ ],
39
+ "author": "EvalView",
40
+ "license": "Apache-2.0",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/hidai25/eval-view.git",
44
+ "directory": "sdks/node"
45
+ },
46
+ "bugs": {
47
+ "url": "https://github.com/hidai25/eval-view/issues"
48
+ },
49
+ "homepage": "https://github.com/hidai25/eval-view/tree/main/sdks/node#readme",
50
+ "engines": {
51
+ "node": ">=16.0.0"
52
+ },
53
+ "peerDependencies": {
54
+ "next": ">=13.0.0"
55
+ },
56
+ "peerDependenciesMeta": {
57
+ "next": {
58
+ "optional": true
59
+ }
60
+ },
61
+ "devDependencies": {
62
+ "@types/node": "^20.0.0",
63
+ "typescript": "^5.0.0"
64
+ }
65
+ }