@memberjunction/unit-testing 6.1.0-edge.1 → 6.1.0-edge.3

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,115 @@
1
+ /**
2
+ * Factories producing REAL-typed `@memberjunction/ai` chat values for tests.
3
+ *
4
+ * Every value built here uses the real classes and interfaces exported by
5
+ * `@memberjunction/ai` (`ChatResult`, `ChatParams`, `ModelUsage`, `AIErrorInfo`) —
6
+ * never a structural look-alike. Hand-faked shapes silently drift as the real
7
+ * contracts evolve (the exact bug class behind the historical failover bug);
8
+ * building from the real types makes the compiler enforce the contract in test
9
+ * code too.
10
+ */
11
+ import { ChatParams, ChatResult, ErrorAnalyzer, ModelUsage, } from '@memberjunction/ai';
12
+ /**
13
+ * Builds a real {@link ModelUsage} instance. Defaults: 10 prompt tokens,
14
+ * 5 completion tokens, cost 0.001 — so `totalTokens` is 15 out of the box
15
+ * (the shape the AI-stack tests have standardized on).
16
+ */
17
+ export function makeModelUsage(overrides = {}) {
18
+ const cost = 'cost' in overrides ? overrides.cost : 0.001;
19
+ const usage = new ModelUsage(overrides.promptTokens ?? 10, overrides.completionTokens ?? 5, cost, overrides.costCurrency);
20
+ if (overrides.cacheReadTokens !== undefined)
21
+ usage.cacheReadTokens = overrides.cacheReadTokens;
22
+ if (overrides.cacheWriteTokens !== undefined)
23
+ usage.cacheWriteTokens = overrides.cacheWriteTokens;
24
+ if (overrides.queueTime !== undefined)
25
+ usage.queueTime = overrides.queueTime;
26
+ if (overrides.promptTime !== undefined)
27
+ usage.promptTime = overrides.promptTime;
28
+ if (overrides.completionTime !== undefined)
29
+ usage.completionTime = overrides.completionTime;
30
+ return usage;
31
+ }
32
+ /**
33
+ * Builds a successful {@link ChatResult} the way real drivers do: one assistant
34
+ * choice carrying `content`, a real {@link ModelUsage}, `statusText: 'success'`.
35
+ */
36
+ export function makeSuccessChatResult(content = 'test response', options = {}) {
37
+ const startTime = options.startTime ?? new Date();
38
+ const endTime = options.endTime ?? startTime;
39
+ const result = new ChatResult(true, startTime, endTime);
40
+ result.statusText = 'success';
41
+ const choice = {
42
+ message: { role: 'assistant', content, thinking: options.thinking ?? null },
43
+ finish_reason: options.finishReason ?? 'stop',
44
+ index: 0,
45
+ };
46
+ result.data = {
47
+ choices: [choice],
48
+ model: options.model,
49
+ usage: options.usage ?? makeModelUsage(),
50
+ };
51
+ return result;
52
+ }
53
+ /**
54
+ * Builds a failed {@link ChatResult} (`success: false`). By default the shape
55
+ * mirrors BaseLLM's own error construction: empty choices, zero usage,
56
+ * `statusText: 'error'`. `errorInfo` is only attached when supplied, so tests
57
+ * can model both diagnosed and undiagnosed failures.
58
+ */
59
+ export function makeFailedChatResult(options = {}) {
60
+ const startTime = options.startTime ?? new Date();
61
+ const endTime = options.endTime ?? startTime;
62
+ const result = new ChatResult(false, startTime, endTime);
63
+ result.statusText = options.statusText ?? 'error';
64
+ result.errorMessage = options.errorMessage ?? 'Test failure';
65
+ if (!options.omitData) {
66
+ result.data = { choices: [], usage: new ModelUsage(0, 0) };
67
+ }
68
+ if (options.exception !== undefined) {
69
+ result.exception = options.exception;
70
+ }
71
+ if (options.errorInfo !== undefined) {
72
+ result.errorInfo = options.errorInfo;
73
+ }
74
+ return result;
75
+ }
76
+ /**
77
+ * Builds a failed {@link ChatResult} exactly the way real provider drivers do:
78
+ * the `errorInfo` comes from the REAL `ErrorAnalyzer`, so `errorType`,
79
+ * `severity`, and `canFailover` match what AnthropicLLM / OpenAILLM / GeminiLLM
80
+ * would report for this error. This is the value to use when testing failover
81
+ * logic — drivers catch provider errors internally and RETURN this shape rather
82
+ * than throwing (the historical failover bug class).
83
+ */
84
+ export function makeDriverFailureChatResult(error, providerName = 'TestLLM') {
85
+ return makeFailedChatResult({
86
+ errorMessage: error.message,
87
+ exception: error,
88
+ errorInfo: ErrorAnalyzer.analyzeError(error, providerName),
89
+ });
90
+ }
91
+ /**
92
+ * Builds a real {@link AIErrorInfo} with sensible defaults
93
+ * (`Unknown` / `Retriable` / failover-eligible), overridable per test.
94
+ */
95
+ export function makeErrorInfo(overrides = {}) {
96
+ return {
97
+ errorType: 'Unknown',
98
+ severity: 'Retriable',
99
+ canFailover: true,
100
+ ...overrides,
101
+ };
102
+ }
103
+ /**
104
+ * Builds a real {@link ChatParams} instance (so class-level defaults like
105
+ * `enableCaching` behave exactly as in production) with a default model and a
106
+ * single user message, overridable per test.
107
+ */
108
+ export function makeChatParams(overrides = {}) {
109
+ const params = new ChatParams();
110
+ params.model = 'test-model';
111
+ params.messages = [{ role: 'user', content: 'Hello from makeChatParams' }];
112
+ Object.assign(params, overrides);
113
+ return params;
114
+ }
115
+ //# sourceMappingURL=chat-result-factories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-result-factories.js","sourceRoot":"","sources":["../../src/ai/chat-result-factories.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ import { BaseLLM } from '@memberjunction/ai';
2
+ /**
3
+ * Token counts a scripted vendor response reports, expressed provider-agnostically. Adapters map
4
+ * these into the vendor's native usage shape (e.g. `prompt_tokens` / `promptTokens` /
5
+ * `prompt_eval_count`); the suite asserts they round-trip into `ModelUsage` unchanged.
6
+ */
7
+ export interface ExpectedUsageCounts {
8
+ PromptTokens: number;
9
+ CompletionTokens: number;
10
+ }
11
+ /** Where a scripted vendor failure strikes. */
12
+ export type FailureSite =
13
+ /** The non-streaming completion call fails. */
14
+ 'nonStreaming'
15
+ /** Creating the stream fails (connection refused, auth error, ...). */
16
+ | 'streamStart'
17
+ /** The stream fails part-way through, after emitting `ChunkBeforeError`. */
18
+ | 'midStream';
19
+ /**
20
+ * The catalog of contract deviations this suite knows how to pin. Each kind flips a specific
21
+ * assertion from the ideal contract to the driver's actual behavior.
22
+ */
23
+ export type LLMConformanceDeviationKind =
24
+ /**
25
+ * The driver's streaming path reports ModelUsage(0, 0) even though the vendor stream carries
26
+ * token counts (e.g. the driver's processStreamingChunk never reads chunk usage).
27
+ */
28
+ 'StreamingDropsUsage'
29
+ /**
30
+ * The driver's failed non-streaming ChatResult ('failedResult' mode) does not populate
31
+ * `errorInfo`, so retry/failover layers get no structured error classification.
32
+ */
33
+ | 'FailedResultLacksErrorInfo';
34
+ /** A documented, pinned deviation from the shared contract. */
35
+ export interface LLMConformanceDeviation {
36
+ Kind: LLMConformanceDeviationKind;
37
+ /** WHY the driver deviates — required so the ledger is self-explanatory. */
38
+ Reason: string;
39
+ }
40
+ /**
41
+ * Configuration for {@link RunLLMConformanceSuite}. The `Script*` functions program the provider
42
+ * test file's vendor-SDK mock; the suite tells the adapter WHAT the vendor should do, the adapter
43
+ * translates that into the vendor's native wire shapes.
44
+ */
45
+ export interface LLMConformanceSuiteConfig {
46
+ /** Human-readable provider name, used in describe() labels. */
47
+ ProviderName: string;
48
+ /**
49
+ * Construct a fresh driver instance wired to the scriptable vendor mock. Called in
50
+ * beforeEach, so every test gets a clean instance (and clean streaming state).
51
+ */
52
+ CreateLLM: () => BaseLLM;
53
+ /** Whether the driver declares streaming support (asserted against `SupportsStreaming`). */
54
+ SupportsStreaming: boolean;
55
+ /**
56
+ * How the driver surfaces NON-cancellation vendor failures on the NON-streaming path.
57
+ * BaseLLM imposes no rule here (ChatCompletions, plural, is the normalizing layer):
58
+ * - 'throws': the vendor error propagates out of ChatCompletion (OpenAI family, Groq,
59
+ * Cerebras, Mistral).
60
+ * - 'failedResult': the driver catches and resolves ChatResult{success:false} (Ollama, Azure).
61
+ */
62
+ NonStreamingFailureMode: 'throws' | 'failedResult';
63
+ /**
64
+ * What happens when a STREAMING request is made with an already-aborted token. Required when
65
+ * `SupportsStreaming` is true.
66
+ * - 'resolvesCancelled': the driver detects the pre-abort itself (empty stream + cancelled
67
+ * finalize) and the promise resolves with the documented cancelled shape (Groq, Cerebras).
68
+ * - 'rejectsErrorResult': createStreamingRequest throws (driver pre-check or SDK abort
69
+ * error), and BaseLLM's outer catch — which does NOT special-case cancellation — rejects
70
+ * with a generic statusText:'error' ChatResult instead of the cancelled shape (OpenAI
71
+ * family, Mistral, Ollama, Azure). Pinned under a `[KNOWN DEVIATION]` test name.
72
+ */
73
+ PreAbortedStreamingBehavior?: 'resolvesCancelled' | 'rejectsErrorResult';
74
+ /** Program the vendor mock: the next non-streaming call succeeds with this content + usage. */
75
+ ScriptNonStreamingSuccess: (content: string, usage: ExpectedUsageCounts) => void;
76
+ /**
77
+ * Program the vendor mock: the next streaming call emits these text chunks (one vendor chunk
78
+ * per entry, in order) and then completes, reporting `usage` in the vendor's native place for
79
+ * stream usage (final chunk / usage event) even if the driver is known to drop it.
80
+ */
81
+ ScriptStreamingSuccess: (chunks: string[], usage: ExpectedUsageCounts) => void;
82
+ /**
83
+ * Program the vendor mock: the next call fails with `error` at the given site. For
84
+ * 'midStream', the stream must first emit `options.ChunkBeforeError` (as a normal text chunk)
85
+ * and then throw, so the suite can prove partial content is discarded.
86
+ */
87
+ ScriptFailure: (error: Error, at: FailureSite, options?: {
88
+ ChunkBeforeError?: string;
89
+ }) => void;
90
+ /**
91
+ * Program the vendor mock for a mid-stream cancellation: emit `chunksBeforeAbort` as normal
92
+ * text chunks, then abort `controller`, then end the stream the way the real SDK ends an
93
+ * aborted stream (throw its abort error, or end iteration silently — whichever is faithful).
94
+ */
95
+ ScriptStreamingCancellation: (chunksBeforeAbort: string[], controller: AbortController) => void;
96
+ /** Documented deviations from the ideal contract — see {@link LLMConformanceDeviation}. */
97
+ KnownDeviations?: LLMConformanceDeviation[];
98
+ }
99
+ /**
100
+ * Run the shared BaseLLM streaming/ChatResult conformance suite against one provider driver.
101
+ * Registers a full `describe` block — call it at the top level of a provider test file.
102
+ */
103
+ export declare function RunLLMConformanceSuite(config: LLMConformanceSuiteConfig): void;
104
+ //# sourceMappingURL=llm-conformance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-conformance.d.ts","sourceRoot":"","sources":["../../src/ai/llm-conformance.ts"],"names":[],"mappings":"AA+DA,OAAO,EAAE,OAAO,EAA+E,MAAM,oBAAoB,CAAC;AAE1H;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;CAC5B;AAED,+CAA+C;AAC/C,MAAM,MAAM,WAAW;AACnB,+CAA+C;AAC7C,cAAc;AAChB,uEAAuE;GACrE,aAAa;AACf,4EAA4E;GAC1E,WAAW,CAAC;AAElB;;;GAGG;AACH,MAAM,MAAM,2BAA2B;AACnC;;;GAGG;AACD,qBAAqB;AACvB;;;GAGG;GACD,4BAA4B,CAAC;AAEnC,+DAA+D;AAC/D,MAAM,WAAW,uBAAuB;IACpC,IAAI,EAAE,2BAA2B,CAAC;IAClC,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACtC,+DAA+D;IAC/D,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,SAAS,EAAE,MAAM,OAAO,CAAC;IAEzB,4FAA4F;IAC5F,iBAAiB,EAAE,OAAO,CAAC;IAE3B;;;;;;OAMG;IACH,uBAAuB,EAAE,QAAQ,GAAG,cAAc,CAAC;IAEnD;;;;;;;;;OASG;IACH,2BAA2B,CAAC,EAAE,mBAAmB,GAAG,oBAAoB,CAAC;IAEzE,+FAA+F;IAC/F,yBAAyB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAEjF;;;;OAIG;IACH,sBAAsB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAE/E;;;;OAIG;IACH,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAEhG;;;;OAIG;IACH,2BAA2B,EAAE,CAAC,iBAAiB,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,eAAe,KAAK,IAAI,CAAC;IAEhG,2FAA2F;IAC3F,eAAe,CAAC,EAAE,uBAAuB,EAAE,CAAC;CAC/C;AA8ED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,yBAAyB,GAAG,IAAI,CAsX9E"}