diff --git a/packages/ai-providers/server-ai-langchain/jest.config.js b/packages/ai-providers/server-ai-langchain/jest.config.cjs similarity index 100% rename from packages/ai-providers/server-ai-langchain/jest.config.js rename to packages/ai-providers/server-ai-langchain/jest.config.cjs diff --git a/packages/ai-providers/server-ai-langchain/package.json b/packages/ai-providers/server-ai-langchain/package.json index 4a5aebccb0..5c085b8a57 100644 --- a/packages/ai-providers/server-ai-langchain/package.json +++ b/packages/ai-providers/server-ai-langchain/package.json @@ -7,17 +7,33 @@ "type": "git", "url": "https://github.com/launchdarkly/js-core.git" }, - "main": "dist/index.js", - "types": "dist/index.d.ts", - "type": "commonjs", + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "type": "module", + "exports": { + ".": { + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + }, + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } + }, "scripts": { - "build": "npx tsc", + "build": "tsup-node", "lint": "npx eslint . --ext .ts", "prettier": "prettier --write '**/*.@(js|ts|tsx|json|css)' --ignore-path ../../../.prettierignore", "lint:fix": "yarn run lint --fix", "check": "yarn prettier && yarn lint && yarn build && yarn test", "test": "jest" }, + "files": [ + "dist" + ], "keywords": [ "launchdarkly", "ai", @@ -27,6 +43,7 @@ "author": "LaunchDarkly", "license": "Apache-2.0", "devDependencies": { + "@langchain/community": "^0.3.0", "@langchain/core": "^0.3.0", "@launchdarkly/server-sdk-ai": "^0.14.1", "@trivago/prettier-plugin-sort-imports": "^4.1.1", @@ -44,9 +61,11 @@ "langchain": "^0.3.0", "prettier": "^3.0.0", "ts-jest": "^29.1.1", + "tsup": "^8.5.1", "typescript": "5.1.6" }, "peerDependencies": { + "@langchain/community": "^0.2.0 || ^0.3.0", "@langchain/core": "^0.2.0 || ^0.3.0", "@launchdarkly/server-sdk-ai": "^0.14.0", "langchain": "^0.2.0 || ^0.3.0" diff --git a/packages/ai-providers/server-ai-langchain/tsconfig.eslint.json b/packages/ai-providers/server-ai-langchain/tsconfig.eslint.json index 56c9b38305..39dd46ee4c 100644 --- a/packages/ai-providers/server-ai-langchain/tsconfig.eslint.json +++ b/packages/ai-providers/server-ai-langchain/tsconfig.eslint.json @@ -1,5 +1,5 @@ { "extends": "./tsconfig.json", - "include": ["/**/*.ts"], + "include": ["**/*.ts", "**/*.js"], "exclude": ["node_modules"] } diff --git a/packages/ai-providers/server-ai-langchain/tsconfig.json b/packages/ai-providers/server-ai-langchain/tsconfig.json index 6238d6a0f5..9e6ed5ffb2 100644 --- a/packages/ai-providers/server-ai-langchain/tsconfig.json +++ b/packages/ai-providers/server-ai-langchain/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "ES2020", - "module": "CommonJS", + "module": "ES2020", "lib": ["ES2020"], "moduleResolution": "node", "esModuleInterop": true, diff --git a/packages/ai-providers/server-ai-langchain/tsup.config.ts b/packages/ai-providers/server-ai-langchain/tsup.config.ts new file mode 100644 index 0000000000..c9bcc09917 --- /dev/null +++ b/packages/ai-providers/server-ai-langchain/tsup.config.ts @@ -0,0 +1,14 @@ +// It is a dev dependency and the linter doesn't understand. +// eslint-disable-next-line import/no-extraneous-dependencies +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: { + index: 'src/index.ts', + }, + format: ['esm', 'cjs'], + splitting: false, + sourcemap: true, + clean: true, + dts: true, +}); diff --git a/packages/ai-providers/server-ai-openai/jest.config.js b/packages/ai-providers/server-ai-openai/jest.config.cjs similarity index 100% rename from packages/ai-providers/server-ai-openai/jest.config.js rename to packages/ai-providers/server-ai-openai/jest.config.cjs diff --git a/packages/ai-providers/server-ai-openai/package.json b/packages/ai-providers/server-ai-openai/package.json index 90fd6832b9..d92d8c5f78 100644 --- a/packages/ai-providers/server-ai-openai/package.json +++ b/packages/ai-providers/server-ai-openai/package.json @@ -7,17 +7,33 @@ "type": "git", "url": "https://github.com/launchdarkly/js-core.git" }, - "main": "dist/index.js", - "types": "dist/index.d.ts", - "type": "commonjs", + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "type": "module", + "exports": { + ".": { + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + }, + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } + }, "scripts": { - "build": "npx tsc", + "build": "tsup-node", "lint": "npx eslint . --ext .ts", "prettier": "prettier --write '**/*.@(js|ts|tsx|json|css)' --ignore-path ../../../.prettierignore", "lint:fix": "yarn run lint --fix", "check": "yarn prettier && yarn lint && yarn build && yarn test", "test": "jest" }, + "files": [ + "dist" + ], "keywords": [ "launchdarkly", "ai", @@ -44,6 +60,7 @@ "openai": "^6.0.0", "prettier": "^3.0.0", "ts-jest": "^29.1.1", + "tsup": "^8.5.1", "typescript": "5.1.6" }, "peerDependencies": { diff --git a/packages/ai-providers/server-ai-openai/tsconfig.eslint.json b/packages/ai-providers/server-ai-openai/tsconfig.eslint.json index 56c9b38305..39dd46ee4c 100644 --- a/packages/ai-providers/server-ai-openai/tsconfig.eslint.json +++ b/packages/ai-providers/server-ai-openai/tsconfig.eslint.json @@ -1,5 +1,5 @@ { "extends": "./tsconfig.json", - "include": ["/**/*.ts"], + "include": ["**/*.ts", "**/*.js"], "exclude": ["node_modules"] } diff --git a/packages/ai-providers/server-ai-openai/tsconfig.json b/packages/ai-providers/server-ai-openai/tsconfig.json index 6238d6a0f5..9e6ed5ffb2 100644 --- a/packages/ai-providers/server-ai-openai/tsconfig.json +++ b/packages/ai-providers/server-ai-openai/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "ES2020", - "module": "CommonJS", + "module": "ES2020", "lib": ["ES2020"], "moduleResolution": "node", "esModuleInterop": true, diff --git a/packages/ai-providers/server-ai-openai/tsup.config.ts b/packages/ai-providers/server-ai-openai/tsup.config.ts new file mode 100644 index 0000000000..c9bcc09917 --- /dev/null +++ b/packages/ai-providers/server-ai-openai/tsup.config.ts @@ -0,0 +1,14 @@ +// It is a dev dependency and the linter doesn't understand. +// eslint-disable-next-line import/no-extraneous-dependencies +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: { + index: 'src/index.ts', + }, + format: ['esm', 'cjs'], + splitting: false, + sourcemap: true, + clean: true, + dts: true, +}); diff --git a/packages/ai-providers/server-ai-vercel/jest.config.js b/packages/ai-providers/server-ai-vercel/jest.config.cjs similarity index 100% rename from packages/ai-providers/server-ai-vercel/jest.config.js rename to packages/ai-providers/server-ai-vercel/jest.config.cjs diff --git a/packages/ai-providers/server-ai-vercel/package.json b/packages/ai-providers/server-ai-vercel/package.json index 8fc2c196ba..2c2c29ed5e 100644 --- a/packages/ai-providers/server-ai-vercel/package.json +++ b/packages/ai-providers/server-ai-vercel/package.json @@ -7,17 +7,33 @@ "type": "git", "url": "https://github.com/launchdarkly/js-core.git" }, - "main": "dist/index.js", - "types": "dist/index.d.ts", - "type": "commonjs", + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "type": "module", + "exports": { + ".": { + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + }, + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } + }, "scripts": { - "build": "npx tsc", + "build": "tsup-node", "lint": "npx eslint . --ext .ts", "prettier": "prettier --write '**/*.@(js|ts|tsx|json|css)' --ignore-path ../../../.prettierignore", "lint:fix": "yarn run lint --fix", "check": "yarn prettier && yarn lint && yarn build && yarn test", "test": "jest" }, + "files": [ + "dist" + ], "keywords": [ "launchdarkly", "ai", @@ -48,6 +64,7 @@ "jest": "^29.6.1", "prettier": "^3.0.0", "ts-jest": "^29.1.1", + "tsup": "^8.5.1", "typescript": "5.1.6" }, "peerDependencies": { diff --git a/packages/ai-providers/server-ai-vercel/tsconfig.eslint.json b/packages/ai-providers/server-ai-vercel/tsconfig.eslint.json index 56c9b38305..39dd46ee4c 100644 --- a/packages/ai-providers/server-ai-vercel/tsconfig.eslint.json +++ b/packages/ai-providers/server-ai-vercel/tsconfig.eslint.json @@ -1,5 +1,5 @@ { "extends": "./tsconfig.json", - "include": ["/**/*.ts"], + "include": ["**/*.ts", "**/*.js"], "exclude": ["node_modules"] } diff --git a/packages/ai-providers/server-ai-vercel/tsconfig.json b/packages/ai-providers/server-ai-vercel/tsconfig.json index 6238d6a0f5..9e6ed5ffb2 100644 --- a/packages/ai-providers/server-ai-vercel/tsconfig.json +++ b/packages/ai-providers/server-ai-vercel/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "ES2020", - "module": "CommonJS", + "module": "ES2020", "lib": ["ES2020"], "moduleResolution": "node", "esModuleInterop": true, diff --git a/packages/ai-providers/server-ai-vercel/tsup.config.ts b/packages/ai-providers/server-ai-vercel/tsup.config.ts new file mode 100644 index 0000000000..c9bcc09917 --- /dev/null +++ b/packages/ai-providers/server-ai-vercel/tsup.config.ts @@ -0,0 +1,14 @@ +// It is a dev dependency and the linter doesn't understand. +// eslint-disable-next-line import/no-extraneous-dependencies +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: { + index: 'src/index.ts', + }, + format: ['esm', 'cjs'], + splitting: false, + sourcemap: true, + clean: true, + dts: true, +}); diff --git a/packages/sdk/server-ai/__tests__/LDAIConfigTrackerImpl.test.ts b/packages/sdk/server-ai/__tests__/LDAIConfigTrackerImpl.test.ts index 33ec018cac..02965a5e5f 100644 --- a/packages/sdk/server-ai/__tests__/LDAIConfigTrackerImpl.test.ts +++ b/packages/sdk/server-ai/__tests__/LDAIConfigTrackerImpl.test.ts @@ -1,9 +1,9 @@ import { LDContext } from '@launchdarkly/js-server-sdk-common'; -import { name as aiSdkName, version as aiSdkVersion } from '../package.json'; import { LDFeedbackKind } from '../src/api/metrics'; import { LDAIConfigTrackerImpl } from '../src/LDAIConfigTrackerImpl'; import { LDClientMin } from '../src/LDClientMin'; +import { aiSdkName, aiSdkVersion } from '../src/sdkInfo'; const mockTrack = jest.fn(); const mockVariation = jest.fn(); diff --git a/packages/sdk/server-ai/jest.config.js b/packages/sdk/server-ai/jest.config.cjs similarity index 100% rename from packages/sdk/server-ai/jest.config.js rename to packages/sdk/server-ai/jest.config.cjs diff --git a/packages/sdk/server-ai/package.json b/packages/sdk/server-ai/package.json index 43393662e6..5c9609267f 100644 --- a/packages/sdk/server-ai/package.json +++ b/packages/sdk/server-ai/package.json @@ -7,17 +7,33 @@ "type": "git", "url": "https://github.com/launchdarkly/js-core.git" }, - "main": "dist/src/index.js", - "types": "dist/src/index.d.ts", - "type": "commonjs", + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "type": "module", + "exports": { + ".": { + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + }, + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } + }, "scripts": { - "build": "npx tsc", + "build": "tsup-node", "lint": "npx eslint . --ext .ts", "prettier": "prettier --write '**/*.@(js|ts|tsx|json|css)' --ignore-path ../../../.prettierignore", "lint:fix": "yarn run lint --fix", "check": "yarn prettier && yarn lint && yarn build && yarn test", "test": "jest" }, + "files": [ + "dist" + ], "keywords": [ "launchdarkly", "ai", @@ -45,6 +61,7 @@ "jest": "^29.6.1", "prettier": "^3.0.0", "ts-jest": "^29.1.1", + "tsup": "^8.5.1", "typescript": "5.1.6" }, "peerDependencies": { diff --git a/packages/sdk/server-ai/src/LDAIClientImpl.ts b/packages/sdk/server-ai/src/LDAIClientImpl.ts index 6955e1a05f..318ddaed71 100644 --- a/packages/sdk/server-ai/src/LDAIClientImpl.ts +++ b/packages/sdk/server-ai/src/LDAIClientImpl.ts @@ -1,4 +1,4 @@ -import * as Mustache from 'mustache'; +import Mustache from 'mustache'; import { LDContext, LDLogger } from '@launchdarkly/js-server-sdk-common'; diff --git a/packages/sdk/server-ai/src/LDAIConfigTrackerImpl.ts b/packages/sdk/server-ai/src/LDAIConfigTrackerImpl.ts index 582b03224e..3615febcea 100644 --- a/packages/sdk/server-ai/src/LDAIConfigTrackerImpl.ts +++ b/packages/sdk/server-ai/src/LDAIConfigTrackerImpl.ts @@ -1,6 +1,5 @@ import { LDContext } from '@launchdarkly/js-server-sdk-common'; -import { name as aiSdkName, version as aiSdkVersion } from '../package.json'; import { LDAIConfigTracker } from './api/config'; import { LDAIMetricSummary } from './api/config/LDAIConfigTracker'; import { EvalScore, JudgeResponse } from './api/judge/types'; @@ -13,6 +12,7 @@ import { LDTokenUsage, } from './api/metrics'; import { LDClientMin } from './LDClientMin'; +import { aiSdkName, aiSdkVersion } from './sdkInfo'; export class LDAIConfigTrackerImpl implements LDAIConfigTracker { private _trackedMetrics: LDAIMetricSummary = {}; diff --git a/packages/sdk/server-ai/src/api/judge/Judge.ts b/packages/sdk/server-ai/src/api/judge/Judge.ts index e608743acd..e71a43bdd3 100644 --- a/packages/sdk/server-ai/src/api/judge/Judge.ts +++ b/packages/sdk/server-ai/src/api/judge/Judge.ts @@ -1,4 +1,4 @@ -import * as Mustache from 'mustache'; +import Mustache from 'mustache'; import { LDLogger } from '@launchdarkly/js-server-sdk-common'; diff --git a/packages/sdk/server-ai/src/api/providers/AIProviderFactory.ts b/packages/sdk/server-ai/src/api/providers/AIProviderFactory.ts index 0d33eb69a4..f5b15c69c7 100644 --- a/packages/sdk/server-ai/src/api/providers/AIProviderFactory.ts +++ b/packages/sdk/server-ai/src/api/providers/AIProviderFactory.ts @@ -43,9 +43,13 @@ export class AIProviderFactory { // Try each provider in order // eslint-disable-next-line no-restricted-syntax for (const providerType of providersToTry) { + logger?.debug( + `Attempting to create AIProvider for: ${aiConfig.provider?.name} with provider type: ${providerType}`, + ); // eslint-disable-next-line no-await-in-loop const provider = await this._tryCreateProvider(providerType, aiConfig, logger); if (provider) { + logger?.debug(`Successfully created AIProvider for: ${aiConfig.provider?.name}`); return provider; } } @@ -93,58 +97,35 @@ export class AIProviderFactory { providerType: SupportedAIProvider, aiConfig: LDAIConfigKind, logger?: LDLogger, - ): Promise { - switch (providerType) { - case 'openai': - return this._createProvider( - '@launchdarkly/server-sdk-ai-openai', - 'OpenAIProvider', - aiConfig, - logger, - ); - case 'langchain': - return this._createProvider( - '@launchdarkly/server-sdk-ai-langchain', - 'LangChainProvider', - aiConfig, - logger, - ); - case 'vercel': - return this._createProvider( - '@launchdarkly/server-sdk-ai-vercel', - 'VercelProvider', - aiConfig, - logger, - ); - default: - return undefined; - } - } - - /** - * Create a provider instance dynamically. - */ - private static async _createProvider( - packageName: string, - providerClassName: string, - aiConfig: LDAIConfigKind, - logger?: LDLogger, ): Promise { try { - // Try to dynamically import the provider - // This will work if the package is installed - // eslint-disable-next-line import/no-extraneous-dependencies, global-require, import/no-dynamic-require - const { [providerClassName]: ProviderClass } = require(packageName); + let module; - const provider = await ProviderClass.create(aiConfig, logger); - logger?.debug( - `Successfully created AIProvider for: ${aiConfig.provider?.name} with package ${packageName}`, - ); - return provider; - } catch (error) { - // If the provider is not available or creation fails, return undefined + switch (providerType) { + case 'openai': { + // eslint-disable-next-line import/no-extraneous-dependencies + module = await import('@launchdarkly/server-sdk-ai-openai' as any); + const provider = (await module.OpenAIProvider.create(aiConfig, logger)) as AIProvider; + return provider; + } + case 'langchain': { + // eslint-disable-next-line import/no-extraneous-dependencies + module = await import('@launchdarkly/server-sdk-ai-langchain' as any); + const provider = (await module.LangChainProvider.create(aiConfig, logger)) as AIProvider; + return provider; + } + case 'vercel': { + // eslint-disable-next-line import/no-extraneous-dependencies + module = await import('@launchdarkly/server-sdk-ai-vercel' as any); + const provider = (await module.VercelProvider.create(aiConfig, logger)) as AIProvider; + return provider; + } + default: + return undefined; + } + } catch (error: any) { logger?.warn( - `Error creating AIProvider for: ${aiConfig.provider?.name} with package ${packageName}: ${error}`, + `Unable to create AIProvider. Check that you have installed the correct package. ${error.message}`, ); return undefined; } diff --git a/packages/sdk/server-ai/src/index.ts b/packages/sdk/server-ai/src/index.ts index 3dfc66c0dd..7c1bb54b3d 100644 --- a/packages/sdk/server-ai/src/index.ts +++ b/packages/sdk/server-ai/src/index.ts @@ -6,6 +6,10 @@ * * @packageDocumentation */ +// IMPORTANT: Namespace import required for CJS compatibility. js-server-sdk-common is CommonJS-only; +// Node.js ESM can't reliably import named exports from CJS. DO NOT change to named imports. +import * as common from '@launchdarkly/js-server-sdk-common'; + import { LDAIClient } from './api/LDAIClient'; import { LDAIClientImpl } from './LDAIClientImpl'; import { LDClientMin } from './LDClientMin'; @@ -19,6 +23,6 @@ export function initAi(ldClient: LDClientMin): LDAIClient { return new LDAIClientImpl(ldClient); } -export { LDLogger } from '@launchdarkly/js-server-sdk-common'; +export type LDLogger = common.LDLogger; export * from './api'; diff --git a/packages/sdk/server-ai/src/sdkInfo.ts b/packages/sdk/server-ai/src/sdkInfo.ts new file mode 100644 index 0000000000..253d0011aa --- /dev/null +++ b/packages/sdk/server-ai/src/sdkInfo.ts @@ -0,0 +1,2 @@ +export const aiSdkName = '@launchdarkly/server-sdk-ai'; +export const aiSdkVersion = '0.14.1'; // x-release-please-version diff --git a/packages/sdk/server-ai/tsconfig.eslint.json b/packages/sdk/server-ai/tsconfig.eslint.json index 156dde8255..7cdd6638e1 100644 --- a/packages/sdk/server-ai/tsconfig.eslint.json +++ b/packages/sdk/server-ai/tsconfig.eslint.json @@ -1,5 +1,5 @@ { "extends": "./tsconfig.json", - "include": ["**/*.ts"], + "include": ["**/*.ts", "**/*.js"], "exclude": ["node_modules", "dist"] } diff --git a/packages/sdk/server-ai/tsconfig.json b/packages/sdk/server-ai/tsconfig.json index 19ba2bdc20..8d7285cfe5 100644 --- a/packages/sdk/server-ai/tsconfig.json +++ b/packages/sdk/server-ai/tsconfig.json @@ -1,21 +1,21 @@ { "compilerOptions": { - // Uses "." so it can load package.json. "rootDir": ".", "outDir": "dist", - "target": "es2017", - "lib": ["es6"], - "module": "commonjs", + "target": "es2020", + "lib": ["es2020"], + "module": "ESNext", "strict": true, "noImplicitOverride": true, - // Needed for CommonJS modules. "allowSyntheticDefaultImports": true, + "esModuleInterop": true, "sourceMap": true, "declaration": true, - "declarationMap": true, // enables importers to jump to source + "declarationMap": true, "resolveJsonModule": true, "stripInternal": true, - "moduleResolution": "node" + "moduleResolution": "bundler" }, + "include": ["src"], "exclude": ["**/*.test.ts", "dist", "node_modules", "__tests__", "examples"] } diff --git a/packages/sdk/server-ai/tsup.config.ts b/packages/sdk/server-ai/tsup.config.ts new file mode 100644 index 0000000000..c9bcc09917 --- /dev/null +++ b/packages/sdk/server-ai/tsup.config.ts @@ -0,0 +1,14 @@ +// It is a dev dependency and the linter doesn't understand. +// eslint-disable-next-line import/no-extraneous-dependencies +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: { + index: 'src/index.ts', + }, + format: ['esm', 'cjs'], + splitting: false, + sourcemap: true, + clean: true, + dts: true, +}); diff --git a/release-please-config.json b/release-please-config.json index c748ecd195..171bb4445d 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -85,6 +85,7 @@ "packages/sdk/server-ai": { "bump-minor-pre-major": true, "extra-files": [ + "src/sdkInfo.ts", { "type": "json", "path": "examples/bedrock/package.json",