@auto-engineer/ai-gateway 0.11.14 → 0.11.16
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +8 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +0 -2
- package/dist/src/config.js.map +1 -1
- package/dist/src/config.specs.js +1 -2
- package/dist/src/config.specs.js.map +1 -1
- package/dist/src/core/constants.d.ts +7 -0
- package/dist/src/core/constants.d.ts.map +1 -0
- package/dist/src/core/constants.js +8 -0
- package/dist/src/core/constants.js.map +1 -0
- package/dist/src/core/context.d.ts +7 -0
- package/dist/src/core/context.d.ts.map +1 -0
- package/dist/src/core/context.js +106 -0
- package/dist/src/core/context.js.map +1 -0
- package/dist/src/core/generators.d.ts +12 -0
- package/dist/src/core/generators.d.ts.map +1 -0
- package/dist/src/core/generators.js +310 -0
- package/dist/src/core/generators.js.map +1 -0
- package/dist/src/core/index.d.ts +8 -0
- package/dist/src/core/index.d.ts.map +1 -0
- package/dist/src/core/index.js +7 -0
- package/dist/src/core/index.js.map +1 -0
- package/dist/src/core/providers/custom.d.ts +3 -0
- package/dist/src/core/providers/custom.d.ts.map +1 -0
- package/dist/src/core/providers/custom.js +9 -0
- package/dist/src/core/providers/custom.js.map +1 -0
- package/dist/src/core/types.d.ts +73 -0
- package/dist/src/core/types.d.ts.map +1 -0
- package/dist/src/core/types.js +9 -0
- package/dist/src/core/types.js.map +1 -0
- package/dist/src/core/utils/errors.d.ts +3 -0
- package/dist/src/core/utils/errors.d.ts.map +1 -0
- package/dist/src/core/utils/errors.js +82 -0
- package/dist/src/core/utils/errors.js.map +1 -0
- package/dist/src/core/utils/log.d.ts +3 -0
- package/dist/src/core/utils/log.d.ts.map +1 -0
- package/dist/src/core/utils/log.js +52 -0
- package/dist/src/core/utils/log.js.map +1 -0
- package/dist/src/core/utils/validation.d.ts +9 -0
- package/dist/src/core/utils/validation.d.ts.map +1 -0
- package/dist/src/core/utils/validation.js +45 -0
- package/dist/src/core/utils/validation.js.map +1 -0
- package/dist/src/index-custom.specs.js +16 -12
- package/dist/src/index-custom.specs.js.map +1 -1
- package/dist/src/index.d.ts +1 -39
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -566
- package/dist/src/index.js.map +1 -1
- package/dist/src/index.specs.js +6 -3
- package/dist/src/index.specs.js.map +1 -1
- package/dist/src/node/config.d.ts +3 -0
- package/dist/src/node/config.d.ts.map +1 -0
- package/dist/src/node/config.js +72 -0
- package/dist/src/node/config.js.map +1 -0
- package/dist/src/node/index.d.ts +11 -0
- package/dist/src/node/index.d.ts.map +1 -0
- package/dist/src/node/index.js +10 -0
- package/dist/src/node/index.js.map +1 -0
- package/dist/src/node/mcp-server.d.ts +50 -0
- package/dist/src/node/mcp-server.d.ts.map +1 -0
- package/dist/src/node/mcp-server.js +176 -0
- package/dist/src/node/mcp-server.js.map +1 -0
- package/dist/src/node/wrappers.d.ts +16 -0
- package/dist/src/node/wrappers.d.ts.map +1 -0
- package/dist/src/node/wrappers.js +100 -0
- package/dist/src/node/wrappers.js.map +1 -0
- package/dist/src/providers/custom.specs.js +1 -1
- package/dist/src/providers/custom.specs.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +19 -3
- package/src/config.specs.ts +1 -2
- package/src/config.ts +0 -2
- package/src/core/constants.ts +8 -0
- package/src/core/context.ts +106 -0
- package/src/core/generators.ts +424 -0
- package/src/core/index.ts +29 -0
- package/src/core/providers/custom.ts +10 -0
- package/src/core/types.ts +81 -0
- package/src/core/utils/errors.ts +91 -0
- package/src/core/utils/log.ts +65 -0
- package/src/core/utils/validation.ts +69 -0
- package/src/index-custom.specs.ts +16 -12
- package/src/index.specs.ts +7 -4
- package/src/index.ts +1 -756
- package/src/node/config.ts +100 -0
- package/src/node/index.ts +60 -0
- package/src/node/mcp-server.ts +261 -0
- package/src/node/wrappers.ts +136 -0
- package/src/providers/custom.specs.ts +2 -2
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export enum AIProvider {
|
|
4
|
+
OpenAI = 'openai',
|
|
5
|
+
Anthropic = 'anthropic',
|
|
6
|
+
Google = 'google',
|
|
7
|
+
XAI = 'xai',
|
|
8
|
+
Custom = 'custom',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface CustomProviderConfig {
|
|
12
|
+
name: string;
|
|
13
|
+
baseUrl: string;
|
|
14
|
+
apiKey: string;
|
|
15
|
+
defaultModel: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AIConfig {
|
|
19
|
+
openai?: {
|
|
20
|
+
apiKey: string;
|
|
21
|
+
};
|
|
22
|
+
anthropic?: {
|
|
23
|
+
apiKey: string;
|
|
24
|
+
};
|
|
25
|
+
google?: {
|
|
26
|
+
apiKey: string;
|
|
27
|
+
};
|
|
28
|
+
xai?: {
|
|
29
|
+
apiKey: string;
|
|
30
|
+
};
|
|
31
|
+
custom?: CustomProviderConfig;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface AIContext {
|
|
35
|
+
config: AIConfig;
|
|
36
|
+
defaultProvider?: AIProvider;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface AIOptions {
|
|
40
|
+
provider?: AIProvider;
|
|
41
|
+
model?: string;
|
|
42
|
+
temperature?: number;
|
|
43
|
+
maxTokens?: number;
|
|
44
|
+
streamCallback?: (token: string) => void;
|
|
45
|
+
includeTools?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface StructuredAIOptions<T> extends Omit<AIOptions, 'streamCallback'> {
|
|
49
|
+
schema: z.ZodSchema<T>;
|
|
50
|
+
schemaName?: string;
|
|
51
|
+
schemaDescription?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface StreamStructuredAIOptions<T> extends StructuredAIOptions<T> {
|
|
55
|
+
onPartialObject?: (partialObject: unknown) => void;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface AIToolValidationError extends Error {
|
|
59
|
+
cause?: {
|
|
60
|
+
issues?: unknown[];
|
|
61
|
+
[key: string]: unknown;
|
|
62
|
+
};
|
|
63
|
+
issues?: unknown[];
|
|
64
|
+
errors?: unknown[];
|
|
65
|
+
zodIssues?: unknown[];
|
|
66
|
+
validationDetails?: {
|
|
67
|
+
cause?: {
|
|
68
|
+
issues?: unknown[];
|
|
69
|
+
};
|
|
70
|
+
issues?: unknown[];
|
|
71
|
+
errors?: unknown[];
|
|
72
|
+
};
|
|
73
|
+
schemaDescription?: string;
|
|
74
|
+
[key: string]: unknown;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export type RegisteredToolForAI = {
|
|
78
|
+
parameters: z.ZodSchema;
|
|
79
|
+
description: string;
|
|
80
|
+
execute?: (args: Record<string, unknown>) => Promise<string>;
|
|
81
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { AIProvider } from '../types';
|
|
2
|
+
import { makeLogger } from './log';
|
|
3
|
+
|
|
4
|
+
const debugError = makeLogger('auto:ai-gateway:error');
|
|
5
|
+
|
|
6
|
+
const ERROR_PATTERNS = [
|
|
7
|
+
{
|
|
8
|
+
patterns: ['rate limit', '429'],
|
|
9
|
+
statusCode: 429,
|
|
10
|
+
icon: '⚠️',
|
|
11
|
+
message: 'RATE LIMIT ERROR detected for %s',
|
|
12
|
+
checkRetryAfter: true,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
patterns: ['401', 'unauthorized'],
|
|
16
|
+
statusCode: 401,
|
|
17
|
+
icon: '🔐',
|
|
18
|
+
message: 'AUTHENTICATION ERROR - Check your %s API key',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
patterns: ['quota', 'credits', 'insufficient'],
|
|
22
|
+
icon: '💳',
|
|
23
|
+
message: 'QUOTA/CREDITS ERROR - Insufficient credits for %s',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
patterns: ['model', 'not found'],
|
|
27
|
+
icon: '🤖',
|
|
28
|
+
message: 'MODEL ERROR - Model might not be available for %s',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
patterns: ['timeout', 'timed out'],
|
|
32
|
+
icon: '⏱️',
|
|
33
|
+
message: 'TIMEOUT ERROR - Request timed out for %s',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
patterns: ['ECONNREFUSED', 'ENOTFOUND', 'network'],
|
|
37
|
+
icon: '🌐',
|
|
38
|
+
message: 'NETWORK ERROR - Connection failed to %s',
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
function checkErrorType(message: string, errorAny: Record<string, unknown>, provider: AIProvider): void {
|
|
43
|
+
for (const errorType of ERROR_PATTERNS) {
|
|
44
|
+
const hasPattern = errorType.patterns.some((pattern) => message.includes(pattern));
|
|
45
|
+
const hasStatusCode = errorType.statusCode !== undefined && errorAny.status === errorType.statusCode;
|
|
46
|
+
|
|
47
|
+
if (hasPattern || hasStatusCode) {
|
|
48
|
+
debugError(`${errorType.icon} ${errorType.message}`, provider);
|
|
49
|
+
|
|
50
|
+
if (errorType.checkRetryAfter === true && errorAny.retryAfter !== undefined) {
|
|
51
|
+
debugError('Retry after: %s seconds', String(errorAny.retryAfter));
|
|
52
|
+
}
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function extractErrorDetails(errorAny: Record<string, unknown>): void {
|
|
59
|
+
if (errorAny.response !== undefined && typeof errorAny.response === 'object' && errorAny.response !== null) {
|
|
60
|
+
const response = errorAny.response as Record<string, unknown>;
|
|
61
|
+
debugError('Response status: %s', String(response.status ?? 'unknown'));
|
|
62
|
+
debugError('Response status text: %s', String(response.statusText ?? 'unknown'));
|
|
63
|
+
if (response.data !== undefined) {
|
|
64
|
+
debugError('Response data: %o', response.data);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (errorAny.code !== undefined) {
|
|
69
|
+
debugError('Error code: %s', String(errorAny.code));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (errorAny.type !== undefined) {
|
|
73
|
+
debugError('Error type: %s', String(errorAny.type));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function extractAndLogError(error: unknown, provider: AIProvider, operation: string): void {
|
|
78
|
+
debugError('%s failed for provider %s', operation, provider);
|
|
79
|
+
|
|
80
|
+
if (error instanceof Error) {
|
|
81
|
+
debugError('Error message: %s', error.message);
|
|
82
|
+
debugError('Error name: %s', error.name);
|
|
83
|
+
debugError('Error stack: %s', error.stack);
|
|
84
|
+
const errorAny = error as unknown as Record<string, unknown>;
|
|
85
|
+
checkErrorType(error.message, errorAny, provider);
|
|
86
|
+
extractErrorDetails(errorAny);
|
|
87
|
+
debugError('Full error object: %O', error);
|
|
88
|
+
} else {
|
|
89
|
+
debugError('Unknown error type: %O', error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export type Logger = (...args: unknown[]) => void;
|
|
2
|
+
|
|
3
|
+
type DebugFactory = (ns: string) => Logger;
|
|
4
|
+
|
|
5
|
+
interface ProcessVersions {
|
|
6
|
+
node?: string;
|
|
7
|
+
}
|
|
8
|
+
interface NodeProcess {
|
|
9
|
+
versions?: ProcessVersions;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let debugFactory: DebugFactory | null | undefined = undefined;
|
|
13
|
+
let debugPromise: Promise<void> | null = null;
|
|
14
|
+
|
|
15
|
+
function isNodeEnvironment(): boolean {
|
|
16
|
+
if (typeof process === 'undefined') return false;
|
|
17
|
+
const proc = process as NodeProcess;
|
|
18
|
+
return Boolean(proc.versions?.node);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function loadDebug(): Promise<void> {
|
|
22
|
+
if (!isNodeEnvironment()) {
|
|
23
|
+
debugFactory = null; // permanent no-op on edge
|
|
24
|
+
return Promise.resolve();
|
|
25
|
+
}
|
|
26
|
+
if (debugPromise) return debugPromise;
|
|
27
|
+
|
|
28
|
+
debugPromise = import('debug')
|
|
29
|
+
.then((mod) => {
|
|
30
|
+
const imported = mod as { default?: DebugFactory } | DebugFactory;
|
|
31
|
+
debugFactory = typeof imported === 'function' ? imported : (imported.default ?? null);
|
|
32
|
+
})
|
|
33
|
+
.catch(() => {
|
|
34
|
+
debugFactory = null;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return debugPromise;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function makeLogger(ns: string): Logger {
|
|
41
|
+
const queue: unknown[][] = [];
|
|
42
|
+
let impl: Logger | null = null;
|
|
43
|
+
const MAX_BUFFERED = 100;
|
|
44
|
+
|
|
45
|
+
const logger: Logger = (...args: unknown[]) => {
|
|
46
|
+
if (impl) {
|
|
47
|
+
impl(...args);
|
|
48
|
+
} else if (queue.length < MAX_BUFFERED) {
|
|
49
|
+
queue.push(args);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
void loadDebug().then(() => {
|
|
54
|
+
try {
|
|
55
|
+
impl = debugFactory ? debugFactory(ns) : () => {};
|
|
56
|
+
} catch {
|
|
57
|
+
impl = () => {};
|
|
58
|
+
}
|
|
59
|
+
// flush any buffered logs
|
|
60
|
+
for (const args of queue) impl(...args);
|
|
61
|
+
queue.length = 0;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return logger;
|
|
65
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { AIToolValidationError } from '../types';
|
|
2
|
+
import { makeLogger } from './log';
|
|
3
|
+
|
|
4
|
+
const debugValidation = makeLogger('auto:ai-gateway:validation');
|
|
5
|
+
|
|
6
|
+
export function getEnhancedPrompt(prompt: string, lastError: AIToolValidationError): string {
|
|
7
|
+
const errorDetails = lastError.zodIssues
|
|
8
|
+
? JSON.stringify(lastError.zodIssues, null, 2)
|
|
9
|
+
: lastError.validationDetails?.cause?.issues
|
|
10
|
+
? JSON.stringify(lastError.validationDetails.cause.issues, null, 2)
|
|
11
|
+
: lastError.message;
|
|
12
|
+
|
|
13
|
+
debugValidation('Enhancing prompt with validation error details: %o', errorDetails);
|
|
14
|
+
return `${prompt}\n\n⚠️ IMPORTANT: Your previous response failed validation with the following errors:\n${errorDetails}\n\nPlease fix these errors and ensure your response EXACTLY matches the required schema structure.`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isSchemaError(error: Error): boolean {
|
|
18
|
+
return (
|
|
19
|
+
error.message.includes('response did not match schema') ||
|
|
20
|
+
error.message.includes('TypeValidationError') ||
|
|
21
|
+
error.name === 'AI_TypeValidationError'
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function enhanceValidationError(error: AIToolValidationError): AIToolValidationError {
|
|
26
|
+
const enhancedError = new Error(error.message) as AIToolValidationError;
|
|
27
|
+
Object.assign(enhancedError, error);
|
|
28
|
+
|
|
29
|
+
if (error.cause !== undefined || error.issues !== undefined || error.errors !== undefined) {
|
|
30
|
+
enhancedError.validationDetails = {
|
|
31
|
+
cause: error.cause,
|
|
32
|
+
issues: error.issues,
|
|
33
|
+
errors: error.errors,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (
|
|
38
|
+
error.message.includes('response did not match schema') &&
|
|
39
|
+
'cause' in error &&
|
|
40
|
+
typeof error.cause === 'object' &&
|
|
41
|
+
error.cause !== null &&
|
|
42
|
+
'issues' in error.cause
|
|
43
|
+
) {
|
|
44
|
+
enhancedError.zodIssues = error.cause.issues as unknown[];
|
|
45
|
+
}
|
|
46
|
+
return enhancedError;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function handleFailedRequest(
|
|
50
|
+
lastError: AIToolValidationError | undefined,
|
|
51
|
+
maxRetries: number,
|
|
52
|
+
attempt: number,
|
|
53
|
+
): { shouldRetry: boolean; enhancedError?: AIToolValidationError } {
|
|
54
|
+
if (!lastError || !isSchemaError(lastError) || attempt >= maxRetries - 1) {
|
|
55
|
+
debugValidation(
|
|
56
|
+
'Not retrying - isLastError: %s, isSchemaError: %s, attempt: %d/%d',
|
|
57
|
+
!!lastError,
|
|
58
|
+
lastError ? isSchemaError(lastError) : false,
|
|
59
|
+
attempt + 1,
|
|
60
|
+
maxRetries,
|
|
61
|
+
);
|
|
62
|
+
return { shouldRetry: false, enhancedError: lastError };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
debugValidation('Schema validation failed on attempt %d/%d, will retry', attempt + 1, maxRetries);
|
|
66
|
+
const enhancedError = enhanceValidationError(lastError);
|
|
67
|
+
|
|
68
|
+
return { shouldRetry: true, enhancedError };
|
|
69
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { AIProvider } from './
|
|
3
|
-
import { getAvailableProviders, getDefaultModel, getDefaultAIProvider } from './index';
|
|
2
|
+
import { AIProvider } from './core/types';
|
|
3
|
+
import { getAvailableProviders, getDefaultModel, getDefaultAIProvider, resetGlobalContext } from './index';
|
|
4
4
|
|
|
5
5
|
// Mock the config module
|
|
6
|
-
vi.mock('./config', () => ({
|
|
6
|
+
vi.mock('./node/config', () => ({
|
|
7
7
|
configureAIProvider: vi.fn(),
|
|
8
8
|
}));
|
|
9
9
|
|
|
10
10
|
// Mock the custom provider
|
|
11
|
-
vi.mock('./providers/custom', () => ({
|
|
11
|
+
vi.mock('./core/providers/custom', () => ({
|
|
12
12
|
createCustomProvider: vi.fn(() => ({
|
|
13
13
|
languageModel: vi.fn(),
|
|
14
14
|
})),
|
|
@@ -21,15 +21,17 @@ describe('Index Integration with Custom Providers', () => {
|
|
|
21
21
|
vi.resetModules();
|
|
22
22
|
process.env = { ...originalEnv };
|
|
23
23
|
vi.clearAllMocks();
|
|
24
|
+
resetGlobalContext();
|
|
24
25
|
});
|
|
25
26
|
|
|
26
27
|
afterEach(() => {
|
|
27
28
|
process.env = originalEnv;
|
|
29
|
+
resetGlobalContext();
|
|
28
30
|
});
|
|
29
31
|
|
|
30
32
|
describe('getAvailableProviders', () => {
|
|
31
33
|
it('should include custom provider when configured', async () => {
|
|
32
|
-
const { configureAIProvider } = await import('./config');
|
|
34
|
+
const { configureAIProvider } = await import('./node/config');
|
|
33
35
|
vi.mocked(configureAIProvider).mockReturnValue({
|
|
34
36
|
custom: {
|
|
35
37
|
name: 'litellm',
|
|
@@ -47,7 +49,7 @@ describe('Index Integration with Custom Providers', () => {
|
|
|
47
49
|
});
|
|
48
50
|
|
|
49
51
|
it('should not include custom provider when not configured', async () => {
|
|
50
|
-
const { configureAIProvider } = await import('./config');
|
|
52
|
+
const { configureAIProvider } = await import('./node/config');
|
|
51
53
|
vi.mocked(configureAIProvider).mockReturnValue({
|
|
52
54
|
anthropic: { apiKey: 'sk-anthropic' },
|
|
53
55
|
});
|
|
@@ -59,7 +61,7 @@ describe('Index Integration with Custom Providers', () => {
|
|
|
59
61
|
});
|
|
60
62
|
|
|
61
63
|
it('should handle multiple providers including custom', async () => {
|
|
62
|
-
const { configureAIProvider } = await import('./config');
|
|
64
|
+
const { configureAIProvider } = await import('./node/config');
|
|
63
65
|
vi.mocked(configureAIProvider).mockReturnValue({
|
|
64
66
|
openai: { apiKey: 'sk-openai' },
|
|
65
67
|
anthropic: { apiKey: 'sk-anthropic' },
|
|
@@ -86,7 +88,7 @@ describe('Index Integration with Custom Providers', () => {
|
|
|
86
88
|
|
|
87
89
|
describe('getDefaultModel', () => {
|
|
88
90
|
it('should return custom provider default model when provider is Custom', async () => {
|
|
89
|
-
const { configureAIProvider } = await import('./config');
|
|
91
|
+
const { configureAIProvider } = await import('./node/config');
|
|
90
92
|
vi.mocked(configureAIProvider).mockReturnValue({
|
|
91
93
|
custom: {
|
|
92
94
|
name: 'litellm',
|
|
@@ -101,7 +103,7 @@ describe('Index Integration with Custom Providers', () => {
|
|
|
101
103
|
});
|
|
102
104
|
|
|
103
105
|
it('should throw error when custom provider not configured but requested', async () => {
|
|
104
|
-
const { configureAIProvider } = await import('./config');
|
|
106
|
+
const { configureAIProvider } = await import('./node/config');
|
|
105
107
|
vi.mocked(configureAIProvider).mockReturnValue({
|
|
106
108
|
anthropic: { apiKey: 'sk-anthropic' },
|
|
107
109
|
});
|
|
@@ -112,7 +114,7 @@ describe('Index Integration with Custom Providers', () => {
|
|
|
112
114
|
it('should use environment variable DEFAULT_AI_MODEL for custom provider', async () => {
|
|
113
115
|
process.env.DEFAULT_AI_MODEL = 'env-override-model';
|
|
114
116
|
|
|
115
|
-
const { configureAIProvider } = await import('./config');
|
|
117
|
+
const { configureAIProvider } = await import('./node/config');
|
|
116
118
|
vi.mocked(configureAIProvider).mockReturnValue({
|
|
117
119
|
custom: {
|
|
118
120
|
name: 'custom',
|
|
@@ -129,9 +131,10 @@ describe('Index Integration with Custom Providers', () => {
|
|
|
129
131
|
it('should handle different custom provider models', async () => {
|
|
130
132
|
const testCases = ['gpt-4o', 'claude-3-opus', 'llama3.1:70b', 'mistral-large', 'gemini-1.5-pro'];
|
|
131
133
|
|
|
132
|
-
const { configureAIProvider } = await import('./config');
|
|
134
|
+
const { configureAIProvider } = await import('./node/config');
|
|
133
135
|
|
|
134
136
|
testCases.forEach((testModel) => {
|
|
137
|
+
resetGlobalContext();
|
|
135
138
|
vi.mocked(configureAIProvider).mockReturnValue({
|
|
136
139
|
custom: {
|
|
137
140
|
name: 'test',
|
|
@@ -151,7 +154,7 @@ describe('Index Integration with Custom Providers', () => {
|
|
|
151
154
|
it('should use custom provider as fallback when available', async () => {
|
|
152
155
|
process.env.DEFAULT_AI_PROVIDER = 'nonexistent';
|
|
153
156
|
|
|
154
|
-
const { configureAIProvider } = await import('./config');
|
|
157
|
+
const { configureAIProvider } = await import('./node/config');
|
|
155
158
|
vi.mocked(configureAIProvider).mockReturnValue({
|
|
156
159
|
custom: {
|
|
157
160
|
name: 'custom',
|
|
@@ -183,6 +186,7 @@ describe('Index Integration with Custom Providers', () => {
|
|
|
183
186
|
];
|
|
184
187
|
|
|
185
188
|
testCases.forEach(({ env, expected }) => {
|
|
189
|
+
resetGlobalContext();
|
|
186
190
|
process.env.DEFAULT_AI_PROVIDER = env;
|
|
187
191
|
const provider = getDefaultAIProvider();
|
|
188
192
|
expect(provider).toBe(expected);
|
package/src/index.specs.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
-
import { getAvailableProviders, getDefaultAIProvider, getDefaultModel } from './index';
|
|
3
|
-
import { DEFAULT_MODELS
|
|
4
|
-
import
|
|
2
|
+
import { getAvailableProviders, getDefaultAIProvider, getDefaultModel, resetGlobalContext } from './index';
|
|
3
|
+
import { DEFAULT_MODELS } from './core/constants';
|
|
4
|
+
import { AIProvider } from './core/types';
|
|
5
|
+
import type { AIConfig } from './core/types';
|
|
5
6
|
|
|
6
7
|
// Mock the config module
|
|
7
|
-
vi.mock('./config', () => ({
|
|
8
|
+
vi.mock('./node/config', () => ({
|
|
8
9
|
configureAIProvider: vi.fn(
|
|
9
10
|
() =>
|
|
10
11
|
({
|
|
@@ -22,6 +23,7 @@ describe('Provider Selection Logic', () => {
|
|
|
22
23
|
|
|
23
24
|
beforeEach(() => {
|
|
24
25
|
originalProvider = process.env.DEFAULT_AI_PROVIDER;
|
|
26
|
+
resetGlobalContext();
|
|
25
27
|
});
|
|
26
28
|
|
|
27
29
|
afterEach(() => {
|
|
@@ -30,6 +32,7 @@ describe('Provider Selection Logic', () => {
|
|
|
30
32
|
} else {
|
|
31
33
|
delete process.env.DEFAULT_AI_PROVIDER;
|
|
32
34
|
}
|
|
35
|
+
resetGlobalContext();
|
|
33
36
|
});
|
|
34
37
|
|
|
35
38
|
it('should respect DEFAULT_AI_PROVIDER environment variable when set to anthropic', () => {
|