impact-testing-sdk 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.
@@ -0,0 +1,20 @@
1
+ import type { ImpactConfig } from './types.js';
2
+ type OpenAILike = {
3
+ chat: {
4
+ completions: {
5
+ create(...args: unknown[]): Promise<unknown>;
6
+ };
7
+ };
8
+ };
9
+ type AnthropicLike = {
10
+ messages: {
11
+ create(...args: unknown[]): Promise<unknown>;
12
+ };
13
+ };
14
+ export declare class ImpactClient {
15
+ private readonly config;
16
+ constructor(config: ImpactConfig);
17
+ wrapOpenAI<T extends OpenAILike>(client: T): T;
18
+ wrapAnthropic<T extends AnthropicLike>(client: T): T;
19
+ }
20
+ export {};
@@ -0,0 +1,19 @@
1
+ import { wrapOpenAI } from './interceptors/openai.js';
2
+ import { wrapAnthropic } from './interceptors/anthropic.js';
3
+ export class ImpactClient {
4
+ config;
5
+ constructor(config) {
6
+ if (!config.apiKey)
7
+ throw new Error('[ImpactClient] apiKey is required');
8
+ if (!config.shadowModel)
9
+ throw new Error('[ImpactClient] shadowModel is required');
10
+ this.config = config;
11
+ }
12
+ wrapOpenAI(client) {
13
+ return wrapOpenAI(client, this.config);
14
+ }
15
+ wrapAnthropic(client) {
16
+ return wrapAnthropic(client, this.config);
17
+ }
18
+ }
19
+ //# sourceMappingURL=ImpactClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImpactClient.js","sourceRoot":"","sources":["../src/ImpactClient.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAU3D,MAAM,OAAO,YAAY;IACN,MAAM,CAAc;IAErC,YAAY,MAAoB;QAC9B,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;QACxE,IAAI,CAAC,MAAM,CAAC,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAA;QAClF,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;IAED,UAAU,CAAuB,MAAS;QACxC,OAAO,UAAU,CAAC,MAAe,EAAE,IAAI,CAAC,MAAM,CAAM,CAAA;IACtD,CAAC;IAED,aAAa,CAA0B,MAAS;QAC9C,OAAO,aAAa,CAAC,MAAe,EAAE,IAAI,CAAC,MAAM,CAAM,CAAA;IACzD,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ import type { ImpactConfig, RunPayload } from './types.js';
2
+ export declare function estimateCost(model: string, totalTokens: number): number;
3
+ export declare function sendRun(payload: RunPayload, config: ImpactConfig): void;
package/dist/client.js ADDED
@@ -0,0 +1,31 @@
1
+ // Cost per 1K tokens (input + output combined) — approximate, update as needed
2
+ const COST_PER_1K = {
3
+ 'gpt-4o': 0.0075,
4
+ 'gpt-4o-mini': 0.000225,
5
+ 'gpt-4-turbo': 0.015,
6
+ 'gpt-3.5-turbo': 0.001,
7
+ 'claude-3-5-sonnet': 0.009,
8
+ 'claude-3-haiku': 0.000375,
9
+ 'claude-3-opus': 0.045,
10
+ };
11
+ export function estimateCost(model, totalTokens) {
12
+ const base = Object.entries(COST_PER_1K).find(([key]) => model.toLowerCase().includes(key));
13
+ if (!base)
14
+ return 0;
15
+ return (totalTokens / 1000) * base[1];
16
+ }
17
+ // Fire-and-forget — never throws, never blocks the caller
18
+ export function sendRun(payload, config) {
19
+ const url = `${config.serverUrl ?? 'http://localhost:4000'}/runs`;
20
+ fetch(url, {
21
+ method: 'POST',
22
+ headers: {
23
+ 'Content-Type': 'application/json',
24
+ 'x-api-key': config.apiKey,
25
+ },
26
+ body: JSON.stringify(payload),
27
+ }).catch((err) => {
28
+ console.warn('[impact-sdk] Failed to send run (non-blocking):', err?.message);
29
+ });
30
+ }
31
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,+EAA+E;AAC/E,MAAM,WAAW,GAA2B;IAC1C,QAAQ,EAAgB,MAAM;IAC9B,aAAa,EAAW,QAAQ;IAChC,aAAa,EAAW,KAAK;IAC7B,eAAe,EAAS,KAAK;IAC7B,mBAAmB,EAAK,KAAK;IAC7B,gBAAgB,EAAQ,QAAQ;IAChC,eAAe,EAAS,KAAK;CAC9B,CAAA;AAED,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,WAAmB;IAC7D,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CACtD,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAClC,CAAA;IACD,IAAI,CAAC,IAAI;QAAE,OAAO,CAAC,CAAA;IACnB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;AACvC,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,OAAO,CAAC,OAAmB,EAAE,MAAoB;IAC/D,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,SAAS,IAAI,uBAAuB,OAAO,CAAA;IAEjE,KAAK,CAAC,GAAG,EAAE;QACT,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,WAAW,EAAE,MAAM,CAAC,MAAM;SAC3B;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KAC9B,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACf,OAAO,CAAC,IAAI,CAAC,iDAAiD,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;IAC/E,CAAC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { ImpactClient } from './ImpactClient.js';
2
+ export { wrapOpenAI } from './interceptors/openai.js';
3
+ export { wrapAnthropic } from './interceptors/anthropic.js';
4
+ export type { ImpactConfig, RunPayload } from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { ImpactClient } from './ImpactClient.js';
2
+ export { wrapOpenAI } from './interceptors/openai.js';
3
+ export { wrapAnthropic } from './interceptors/anthropic.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAmB,mBAAmB,CAAA;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAoB,0BAA0B,CAAA;AACnE,OAAO,EAAE,aAAa,EAAE,MAAiB,6BAA6B,CAAA"}
@@ -0,0 +1,8 @@
1
+ import type { ImpactConfig, AnthropicParams, AnthropicResponse } from '../types.js';
2
+ type AnthropicLike = {
3
+ messages: {
4
+ create(params: AnthropicParams, options?: unknown): Promise<AnthropicResponse>;
5
+ };
6
+ };
7
+ export declare function wrapAnthropic<T extends AnthropicLike>(client: T, config: ImpactConfig): T;
8
+ export {};
@@ -0,0 +1,29 @@
1
+ import { sendRun, estimateCost } from '../client.js';
2
+ export function wrapAnthropic(client, config) {
3
+ const originalCreate = client.messages.create.bind(client.messages);
4
+ client.messages.create = async (params, options) => {
5
+ const start = Date.now();
6
+ const response = await originalCreate(params, options);
7
+ const latencyMs = Date.now() - start;
8
+ const output = response.content
9
+ .filter((b) => b.type === 'text')
10
+ .map((b) => b.text ?? '')
11
+ .join('');
12
+ const totalTokens = (response.usage?.input_tokens ?? 0) + (response.usage?.output_tokens ?? 0);
13
+ const prompt = params.messages.map((m) => m.content).join('\n');
14
+ sendRun({
15
+ prompt,
16
+ model: params.model,
17
+ shadowModel: config.shadowModel,
18
+ primaryResponse: {
19
+ output,
20
+ latencyMs,
21
+ tokens: totalTokens,
22
+ cost: estimateCost(params.model, totalTokens),
23
+ },
24
+ }, config);
25
+ return response;
26
+ };
27
+ return client;
28
+ }
29
+ //# sourceMappingURL=anthropic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/interceptors/anthropic.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAQpD,MAAM,UAAU,aAAa,CAA0B,MAAS,EAAE,MAAoB;IACpF,MAAM,cAAc,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAEnE,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,KAAK,EAC5B,MAAuB,EACvB,OAAiB,EACW,EAAE;QAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACxB,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACtD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAA;QAEpC,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO;aAC5B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;aAChC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;aACxB,IAAI,CAAC,EAAE,CAAC,CAAA;QAEX,MAAM,WAAW,GACf,CAAC,QAAQ,CAAC,KAAK,EAAE,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,aAAa,IAAI,CAAC,CAAC,CAAA;QAE5E,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAE/D,OAAO,CACL;YACE,MAAM;YACN,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,eAAe,EAAE;gBACf,MAAM;gBACN,SAAS;gBACT,MAAM,EAAE,WAAW;gBACnB,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC;aAC9C;SACF,EACD,MAAM,CACP,CAAA;QAED,OAAO,QAAQ,CAAA;IACjB,CAAC,CAAA;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ImpactConfig, ChatParams, ChatResponse } from '../types.js';
2
+ type OpenAILike = {
3
+ chat: {
4
+ completions: {
5
+ create(params: ChatParams, options?: unknown): Promise<ChatResponse>;
6
+ };
7
+ };
8
+ };
9
+ export declare function wrapOpenAI<T extends OpenAILike>(client: T, config: ImpactConfig): T;
10
+ export {};
@@ -0,0 +1,26 @@
1
+ import { sendRun, estimateCost } from '../client.js';
2
+ export function wrapOpenAI(client, config) {
3
+ const originalCreate = client.chat.completions.create.bind(client.chat.completions);
4
+ client.chat.completions.create = async (params, options) => {
5
+ const start = Date.now();
6
+ const response = await originalCreate(params, options);
7
+ const latencyMs = Date.now() - start;
8
+ const output = response.choices[0]?.message?.content ?? '';
9
+ const totalTokens = response.usage?.total_tokens ?? 0;
10
+ const prompt = params.messages.map((m) => m.content).join('\n');
11
+ sendRun({
12
+ prompt,
13
+ model: params.model,
14
+ shadowModel: config.shadowModel,
15
+ primaryResponse: {
16
+ output,
17
+ latencyMs,
18
+ tokens: totalTokens,
19
+ cost: estimateCost(params.model, totalTokens),
20
+ },
21
+ }, config);
22
+ return response;
23
+ };
24
+ return client;
25
+ }
26
+ //# sourceMappingURL=openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/interceptors/openai.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAUpD,MAAM,UAAU,UAAU,CAAuB,MAAS,EAAE,MAAoB;IAC9E,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAEnF,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,KAAK,EACpC,MAAkB,EAClB,OAAiB,EACM,EAAE;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACxB,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACtD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAA;QAEpC,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,CAAA;QAC1D,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,EAAE,YAAY,IAAI,CAAC,CAAA;QACrD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAE/D,OAAO,CACL;YACE,MAAM;YACN,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,eAAe,EAAE;gBACf,MAAM;gBACN,SAAS;gBACT,MAAM,EAAE,WAAW;gBACnB,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC;aAC9C;SACF,EACD,MAAM,CACP,CAAA;QAED,OAAO,QAAQ,CAAA;IACjB,CAAC,CAAA;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,57 @@
1
+ export type ImpactConfig = {
2
+ apiKey: string;
3
+ shadowModel: string;
4
+ serverUrl?: string;
5
+ };
6
+ export type RunPayload = {
7
+ prompt: string;
8
+ model: string;
9
+ shadowModel: string;
10
+ primaryResponse: {
11
+ output: string;
12
+ latencyMs: number;
13
+ tokens: number;
14
+ cost: number;
15
+ };
16
+ };
17
+ export type ChatResponse = {
18
+ choices: Array<{
19
+ message?: {
20
+ content?: string | null;
21
+ };
22
+ }>;
23
+ usage?: {
24
+ total_tokens?: number;
25
+ prompt_tokens?: number;
26
+ completion_tokens?: number;
27
+ };
28
+ model?: string;
29
+ };
30
+ export type ChatParams = {
31
+ model: string;
32
+ messages: Array<{
33
+ role: string;
34
+ content: string;
35
+ }>;
36
+ [key: string]: unknown;
37
+ };
38
+ export type AnthropicResponse = {
39
+ content: Array<{
40
+ type: string;
41
+ text?: string;
42
+ }>;
43
+ usage?: {
44
+ input_tokens?: number;
45
+ output_tokens?: number;
46
+ };
47
+ model?: string;
48
+ };
49
+ export type AnthropicParams = {
50
+ model: string;
51
+ messages: Array<{
52
+ role: string;
53
+ content: string;
54
+ }>;
55
+ max_tokens: number;
56
+ [key: string]: unknown;
57
+ };
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "impact-testing-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Shadow LLM testing SDK — intercept OpenAI and Anthropic calls and compare responses against a shadow model.",
5
+ "keywords": ["llm", "openai", "anthropic", "testing", "shadow", "ai"],
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/index.js",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "engines": {
21
+ "node": ">=18.0.0"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc",
25
+ "dev": "tsc --watch",
26
+ "prepublishOnly": "npm run build"
27
+ },
28
+ "peerDependencies": {
29
+ "@anthropic-ai/sdk": ">=0.20.0",
30
+ "openai": ">=4.0.0"
31
+ },
32
+ "peerDependenciesMeta": {
33
+ "openai": { "optional": true },
34
+ "@anthropic-ai/sdk": { "optional": true }
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^20.14.0",
38
+ "typescript": "^5.4.5"
39
+ }
40
+ }