@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.
Files changed (90) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +8 -0
  3. package/dist/src/config.d.ts.map +1 -1
  4. package/dist/src/config.js +0 -2
  5. package/dist/src/config.js.map +1 -1
  6. package/dist/src/config.specs.js +1 -2
  7. package/dist/src/config.specs.js.map +1 -1
  8. package/dist/src/core/constants.d.ts +7 -0
  9. package/dist/src/core/constants.d.ts.map +1 -0
  10. package/dist/src/core/constants.js +8 -0
  11. package/dist/src/core/constants.js.map +1 -0
  12. package/dist/src/core/context.d.ts +7 -0
  13. package/dist/src/core/context.d.ts.map +1 -0
  14. package/dist/src/core/context.js +106 -0
  15. package/dist/src/core/context.js.map +1 -0
  16. package/dist/src/core/generators.d.ts +12 -0
  17. package/dist/src/core/generators.d.ts.map +1 -0
  18. package/dist/src/core/generators.js +310 -0
  19. package/dist/src/core/generators.js.map +1 -0
  20. package/dist/src/core/index.d.ts +8 -0
  21. package/dist/src/core/index.d.ts.map +1 -0
  22. package/dist/src/core/index.js +7 -0
  23. package/dist/src/core/index.js.map +1 -0
  24. package/dist/src/core/providers/custom.d.ts +3 -0
  25. package/dist/src/core/providers/custom.d.ts.map +1 -0
  26. package/dist/src/core/providers/custom.js +9 -0
  27. package/dist/src/core/providers/custom.js.map +1 -0
  28. package/dist/src/core/types.d.ts +73 -0
  29. package/dist/src/core/types.d.ts.map +1 -0
  30. package/dist/src/core/types.js +9 -0
  31. package/dist/src/core/types.js.map +1 -0
  32. package/dist/src/core/utils/errors.d.ts +3 -0
  33. package/dist/src/core/utils/errors.d.ts.map +1 -0
  34. package/dist/src/core/utils/errors.js +82 -0
  35. package/dist/src/core/utils/errors.js.map +1 -0
  36. package/dist/src/core/utils/log.d.ts +3 -0
  37. package/dist/src/core/utils/log.d.ts.map +1 -0
  38. package/dist/src/core/utils/log.js +52 -0
  39. package/dist/src/core/utils/log.js.map +1 -0
  40. package/dist/src/core/utils/validation.d.ts +9 -0
  41. package/dist/src/core/utils/validation.d.ts.map +1 -0
  42. package/dist/src/core/utils/validation.js +45 -0
  43. package/dist/src/core/utils/validation.js.map +1 -0
  44. package/dist/src/index-custom.specs.js +16 -12
  45. package/dist/src/index-custom.specs.js.map +1 -1
  46. package/dist/src/index.d.ts +1 -39
  47. package/dist/src/index.d.ts.map +1 -1
  48. package/dist/src/index.js +1 -566
  49. package/dist/src/index.js.map +1 -1
  50. package/dist/src/index.specs.js +6 -3
  51. package/dist/src/index.specs.js.map +1 -1
  52. package/dist/src/node/config.d.ts +3 -0
  53. package/dist/src/node/config.d.ts.map +1 -0
  54. package/dist/src/node/config.js +72 -0
  55. package/dist/src/node/config.js.map +1 -0
  56. package/dist/src/node/index.d.ts +11 -0
  57. package/dist/src/node/index.d.ts.map +1 -0
  58. package/dist/src/node/index.js +10 -0
  59. package/dist/src/node/index.js.map +1 -0
  60. package/dist/src/node/mcp-server.d.ts +50 -0
  61. package/dist/src/node/mcp-server.d.ts.map +1 -0
  62. package/dist/src/node/mcp-server.js +176 -0
  63. package/dist/src/node/mcp-server.js.map +1 -0
  64. package/dist/src/node/wrappers.d.ts +16 -0
  65. package/dist/src/node/wrappers.d.ts.map +1 -0
  66. package/dist/src/node/wrappers.js +100 -0
  67. package/dist/src/node/wrappers.js.map +1 -0
  68. package/dist/src/providers/custom.specs.js +1 -1
  69. package/dist/src/providers/custom.specs.js.map +1 -1
  70. package/dist/tsconfig.tsbuildinfo +1 -1
  71. package/package.json +19 -3
  72. package/src/config.specs.ts +1 -2
  73. package/src/config.ts +0 -2
  74. package/src/core/constants.ts +8 -0
  75. package/src/core/context.ts +106 -0
  76. package/src/core/generators.ts +424 -0
  77. package/src/core/index.ts +29 -0
  78. package/src/core/providers/custom.ts +10 -0
  79. package/src/core/types.ts +81 -0
  80. package/src/core/utils/errors.ts +91 -0
  81. package/src/core/utils/log.ts +65 -0
  82. package/src/core/utils/validation.ts +69 -0
  83. package/src/index-custom.specs.ts +16 -12
  84. package/src/index.specs.ts +7 -4
  85. package/src/index.ts +1 -756
  86. package/src/node/config.ts +100 -0
  87. package/src/node/index.ts +60 -0
  88. package/src/node/mcp-server.ts +261 -0
  89. package/src/node/wrappers.ts +136 -0
  90. 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 './constants';
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);
@@ -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, AIProvider } from './constants';
4
- import type { AIConfig } from './config';
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', () => {