Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/sdk/server-ai/src/LDAIClientImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as Mustache from 'mustache';
import { LDContext, LDLogger } from '@launchdarkly/js-server-sdk-common';

import { LDAIAgent, LDAIAgentConfig, LDAIAgentDefaults } from './api/agents';
import { TrackedChat, TrackedChatFactory } from './api/chat';
import { SupportedAIProvider, TrackedChat, TrackedChatFactory } from './api/chat';
import {
LDAIConfig,
LDAIConfigTracker,
Expand Down Expand Up @@ -233,6 +233,7 @@ export class LDAIClientImpl implements LDAIClient {
context: LDContext,
defaultValue: LDAIDefaults,
variables?: Record<string, unknown>,
defaultAiProvider?: SupportedAIProvider,
): Promise<TrackedChat | undefined> {
// Track chat initialization
this._ldClient.track('$ld:ai:config:function:initChat', context, key, 1);
Expand All @@ -246,6 +247,6 @@ export class LDAIClientImpl implements LDAIClient {
}

// Create the TrackedChat instance based on the provider
return TrackedChatFactory.create(aiConfig, aiConfig.tracker, this._logger);
return TrackedChatFactory.create(aiConfig, aiConfig.tracker, this._logger, defaultAiProvider);
}
}
3 changes: 2 additions & 1 deletion packages/sdk/server-ai/src/api/LDAIClient.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { LDContext } from '@launchdarkly/js-server-sdk-common';

import { LDAIAgent, LDAIAgentConfig, LDAIAgentDefaults } from './agents';
import { TrackedChat } from './chat';
import { SupportedAIProvider, TrackedChat } from './chat';
import { LDAIConfig, LDAIDefaults } from './config/LDAIConfig';

/**
Expand Down Expand Up @@ -186,5 +186,6 @@ export interface LDAIClient {
context: LDContext,
defaultValue: LDAIDefaults,
variables?: Record<string, unknown>,
defaultAiProvider?: SupportedAIProvider,
): Promise<TrackedChat | undefined>;
}
124 changes: 92 additions & 32 deletions packages/sdk/server-ai/src/api/chat/TrackedChatFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@
import { AIProvider } from '../providers/AIProvider';
import { TrackedChat } from './TrackedChat';

/**
* List of supported AI providers.
*/
export const SUPPORTED_AI_PROVIDERS = [
'openai',
// Multi-provider packages should be last in the list
'langchain',
'vercel',
] as const;

/**
* Type representing the supported AI providers.
*/
export type SupportedAIProvider = (typeof SUPPORTED_AI_PROVIDERS)[number];

/**
* Factory for creating TrackedChat instances based on the provider configuration.
*/
Expand All @@ -17,21 +32,22 @@
* @param aiConfig The AI configuration
* @param tracker The tracker for AI operations
* @param logger Optional logger for logging provider initialization
* @param defaultAiProvider Optional default AI provider to use
*/
static async create(
aiConfig: LDAIConfig,
tracker: LDAIConfigTracker,
logger?: LDLogger,
defaultAiProvider?: SupportedAIProvider,
): Promise<TrackedChat | undefined> {
const provider = await this._createAIProvider(aiConfig, logger);
const provider = await this._createAIProvider(aiConfig, logger, defaultAiProvider);
if (!provider) {
logger?.warn(
`Provider is not supported or failed to initialize: ${aiConfig.provider?.name ?? 'unknown'}`,
);
return undefined;
}

logger?.debug(`Successfully created TrackedChat for provider: ${aiConfig.provider?.name}`);
return new TrackedChat(aiConfig, tracker, provider);
}

Expand All @@ -42,54 +58,98 @@
private static async _createAIProvider(
aiConfig: LDAIConfig,
logger?: LDLogger,
defaultAiProvider?: SupportedAIProvider,
): Promise<AIProvider | undefined> {
const providerName = aiConfig.provider?.name?.toLowerCase();
logger?.debug(`Attempting to create AI provider: ${providerName ?? 'unknown'}`);
let provider: AIProvider | undefined;
// Determine which providers to try based on defaultAiProvider
const providersToTry = this._getProvidersToTry(defaultAiProvider, providerName);

// Try specific implementations for the provider
switch (providerName) {
case 'openai':
// TODO: Return OpenAI AIProvider implementation when available
provider = undefined;
break;
case 'bedrock':
// TODO: Return Bedrock AIProvider implementation when available
provider = undefined;
break;
default:
provider = undefined;
// Try each provider in order
// eslint-disable-next-line no-restricted-syntax
for (const providerType of providersToTry) {
// eslint-disable-next-line no-await-in-loop
const provider = await this._tryCreateProvider(providerType, aiConfig, logger);
if (provider) {
return provider;
}
}

// If no specific implementation worked, try the multi-provider packages
if (!provider) {
provider = await this._createLangChainProvider(aiConfig, logger);
return undefined;
}

/**
* Determine which providers to try based on defaultAiProvider and providerName.
*/
private static _getProvidersToTry(
defaultAiProvider?: SupportedAIProvider,
providerName?: string,
): SupportedAIProvider[] {
// If defaultAiProvider is set, only try that specific provider
if (defaultAiProvider) {
return [defaultAiProvider];
}

// If no defaultAiProvider is set, try all providers in order
const providers: SupportedAIProvider[] = [];

// First try the specific provider if it's supported
if (providerName && SUPPORTED_AI_PROVIDERS.includes(providerName as SupportedAIProvider)) {
providers.push(providerName as SupportedAIProvider);
}

return provider;
// Then try multi-provider packages
providers.push('langchain', 'vercel');

return providers;
}

/**
* Try to create a provider of the specified type.
*/
private static async _tryCreateProvider(
providerType: SupportedAIProvider,
aiConfig: LDAIConfig,
logger?: LDLogger,
): Promise<AIProvider | undefined> {
switch (providerType) {
case 'openai':
return this._createProvider('@launchdarkly/server-sdk-ai-openai', 'OpenAIProvider', aiConfig, logger);

Check failure on line 116 in packages/sdk/server-ai/src/api/chat/TrackedChatFactory.ts

View workflow job for this annotation

GitHub Actions / build-test-server-sdk-ai

Replace `'@launchdarkly/server-sdk-ai-openai',·'OpenAIProvider',·aiConfig,·logger` with `⏎··········'@launchdarkly/server-sdk-ai-openai',⏎··········'OpenAIProvider',⏎··········aiConfig,⏎··········logger,⏎········`
case 'langchain':
return this._createProvider('@launchdarkly/server-sdk-ai-langchain', 'LangChainProvider', aiConfig, logger);

Check failure on line 118 in packages/sdk/server-ai/src/api/chat/TrackedChatFactory.ts

View workflow job for this annotation

GitHub Actions / build-test-server-sdk-ai

Replace `'@launchdarkly/server-sdk-ai-langchain',·'LangChainProvider',·aiConfig,·logger` with `⏎··········'@launchdarkly/server-sdk-ai-langchain',⏎··········'LangChainProvider',⏎··········aiConfig,⏎··········logger,⏎········`
case 'vercel':
return this._createProvider('@launchdarkly/server-sdk-ai-vercel', 'VercelProvider', aiConfig, logger);

Check failure on line 120 in packages/sdk/server-ai/src/api/chat/TrackedChatFactory.ts

View workflow job for this annotation

GitHub Actions / build-test-server-sdk-ai

Replace `'@launchdarkly/server-sdk-ai-vercel',·'VercelProvider',·aiConfig,·logger` with `⏎··········'@launchdarkly/server-sdk-ai-vercel',⏎··········'VercelProvider',⏎··········aiConfig,⏎··········logger,⏎········`
default:
return undefined;
}
}

/**
* Create a LangChain AIProvider instance if the LangChain provider is available.
* Create a provider instance dynamically.
*/
private static async _createLangChainProvider(
private static async _createProvider(
packageName: string,
providerClassName: string,
aiConfig: LDAIConfig,
logger?: LDLogger,
): Promise<AIProvider | undefined> {
try {
logger?.debug('Attempting to load LangChain provider');
// Try to dynamically import the LangChain provider
// This will work if @launchdarkly/server-sdk-ai-langchain is installed
// eslint-disable-next-line import/no-extraneous-dependencies, global-require
const { LangChainProvider } = require('@launchdarkly/server-sdk-ai-langchain');

const provider = await LangChainProvider.create(aiConfig, logger);
logger?.debug('Successfully created LangChain provider');
// 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);

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 LangChain provider is not available or creation fails, return undefined
logger?.error(`Error creating LangChain provider: ${error}`);
// If the provider is not available or creation fails, return undefined
logger?.warn(
`Error creating AIProvider for: ${aiConfig.provider?.name} with package ${packageName}: ${error}`,
);
return undefined;
}
}

Check failure on line 153 in packages/sdk/server-ai/src/api/chat/TrackedChatFactory.ts

View workflow job for this annotation

GitHub Actions / build-test-server-sdk-ai

Delete `⏎`

}
Loading