Skip to content

Commit 8553f24

Browse files
authored
feat: Add support for Vercel AIProvider to the AI SDK (#946)
1 parent ed8c332 commit 8553f24

File tree

3 files changed

+114
-35
lines changed

3 files changed

+114
-35
lines changed

packages/sdk/server-ai/src/LDAIClientImpl.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as Mustache from 'mustache';
33
import { LDContext, LDLogger } from '@launchdarkly/js-server-sdk-common';
44

55
import { LDAIAgent, LDAIAgentConfig, LDAIAgentDefaults } from './api/agents';
6-
import { TrackedChat, TrackedChatFactory } from './api/chat';
6+
import { SupportedAIProvider, TrackedChat, TrackedChatFactory } from './api/chat';
77
import {
88
LDAIConfig,
99
LDAIConfigTracker,
@@ -233,6 +233,7 @@ export class LDAIClientImpl implements LDAIClient {
233233
context: LDContext,
234234
defaultValue: LDAIDefaults,
235235
variables?: Record<string, unknown>,
236+
defaultAiProvider?: SupportedAIProvider,
236237
): Promise<TrackedChat | undefined> {
237238
// Track chat initialization
238239
this._ldClient.track('$ld:ai:config:function:initChat', context, key, 1);
@@ -246,6 +247,6 @@ export class LDAIClientImpl implements LDAIClient {
246247
}
247248

248249
// Create the TrackedChat instance based on the provider
249-
return TrackedChatFactory.create(aiConfig, aiConfig.tracker, this._logger);
250+
return TrackedChatFactory.create(aiConfig, aiConfig.tracker, this._logger, defaultAiProvider);
250251
}
251252
}

packages/sdk/server-ai/src/api/LDAIClient.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { LDContext } from '@launchdarkly/js-server-sdk-common';
22

33
import { LDAIAgent, LDAIAgentConfig, LDAIAgentDefaults } from './agents';
4-
import { TrackedChat } from './chat';
4+
import { SupportedAIProvider, TrackedChat } from './chat';
55
import { LDAIConfig, LDAIDefaults } from './config/LDAIConfig';
66

77
/**
@@ -186,5 +186,6 @@ export interface LDAIClient {
186186
context: LDContext,
187187
defaultValue: LDAIDefaults,
188188
variables?: Record<string, unknown>,
189+
defaultAiProvider?: SupportedAIProvider,
189190
): Promise<TrackedChat | undefined>;
190191
}

packages/sdk/server-ai/src/api/chat/TrackedChatFactory.ts

Lines changed: 109 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@ import { LDAIConfigTracker } from '../config/LDAIConfigTracker';
55
import { AIProvider } from '../providers/AIProvider';
66
import { TrackedChat } from './TrackedChat';
77

8+
/**
9+
* List of supported AI providers.
10+
*/
11+
export const SUPPORTED_AI_PROVIDERS = [
12+
'openai',
13+
// Multi-provider packages should be last in the list
14+
'langchain',
15+
'vercel',
16+
] as const;
17+
18+
/**
19+
* Type representing the supported AI providers.
20+
*/
21+
export type SupportedAIProvider = (typeof SUPPORTED_AI_PROVIDERS)[number];
22+
823
/**
924
* Factory for creating TrackedChat instances based on the provider configuration.
1025
*/
@@ -17,21 +32,22 @@ export class TrackedChatFactory {
1732
* @param aiConfig The AI configuration
1833
* @param tracker The tracker for AI operations
1934
* @param logger Optional logger for logging provider initialization
35+
* @param defaultAiProvider Optional default AI provider to use
2036
*/
2137
static async create(
2238
aiConfig: LDAIConfig,
2339
tracker: LDAIConfigTracker,
2440
logger?: LDLogger,
41+
defaultAiProvider?: SupportedAIProvider,
2542
): Promise<TrackedChat | undefined> {
26-
const provider = await this._createAIProvider(aiConfig, logger);
43+
const provider = await this._createAIProvider(aiConfig, logger, defaultAiProvider);
2744
if (!provider) {
2845
logger?.warn(
2946
`Provider is not supported or failed to initialize: ${aiConfig.provider?.name ?? 'unknown'}`,
3047
);
3148
return undefined;
3249
}
3350

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

@@ -42,53 +58,114 @@ export class TrackedChatFactory {
4258
private static async _createAIProvider(
4359
aiConfig: LDAIConfig,
4460
logger?: LDLogger,
61+
defaultAiProvider?: SupportedAIProvider,
4562
): Promise<AIProvider | undefined> {
4663
const providerName = aiConfig.provider?.name?.toLowerCase();
47-
logger?.debug(`Attempting to create AI provider: ${providerName ?? 'unknown'}`);
48-
let provider: AIProvider | undefined;
64+
// Determine which providers to try based on defaultAiProvider
65+
const providersToTry = this._getProvidersToTry(defaultAiProvider, providerName);
4966

50-
// Try specific implementations for the provider
51-
switch (providerName) {
52-
case 'openai':
53-
// TODO: Return OpenAI AIProvider implementation when available
54-
provider = undefined;
55-
break;
56-
case 'bedrock':
57-
// TODO: Return Bedrock AIProvider implementation when available
58-
provider = undefined;
59-
break;
60-
default:
61-
provider = undefined;
67+
// Try each provider in order
68+
// eslint-disable-next-line no-restricted-syntax
69+
for (const providerType of providersToTry) {
70+
// eslint-disable-next-line no-await-in-loop
71+
const provider = await this._tryCreateProvider(providerType, aiConfig, logger);
72+
if (provider) {
73+
return provider;
74+
}
6275
}
6376

64-
// If no specific implementation worked, try the multi-provider packages
65-
if (!provider) {
66-
provider = await this._createLangChainProvider(aiConfig, logger);
77+
return undefined;
78+
}
79+
80+
/**
81+
* Determine which providers to try based on defaultAiProvider and providerName.
82+
*/
83+
private static _getProvidersToTry(
84+
defaultAiProvider?: SupportedAIProvider,
85+
providerName?: string,
86+
): SupportedAIProvider[] {
87+
// If defaultAiProvider is set, only try that specific provider
88+
if (defaultAiProvider) {
89+
return [defaultAiProvider];
90+
}
91+
92+
// If no defaultAiProvider is set, try all providers in order
93+
const providerSet = new Set<SupportedAIProvider>();
94+
95+
// First try the specific provider if it's supported
96+
if (providerName && SUPPORTED_AI_PROVIDERS.includes(providerName as SupportedAIProvider)) {
97+
providerSet.add(providerName as SupportedAIProvider);
6798
}
6899

69-
return provider;
100+
// Then try multi-provider packages, but avoid duplicates
101+
const multiProviderPackages: SupportedAIProvider[] = ['langchain', 'vercel'];
102+
multiProviderPackages.forEach((provider) => {
103+
providerSet.add(provider);
104+
});
105+
106+
return Array.from(providerSet);
70107
}
71108

72109
/**
73-
* Create a LangChain AIProvider instance if the LangChain provider is available.
110+
* Try to create a provider of the specified type.
74111
*/
75-
private static async _createLangChainProvider(
112+
private static async _tryCreateProvider(
113+
providerType: SupportedAIProvider,
114+
aiConfig: LDAIConfig,
115+
logger?: LDLogger,
116+
): Promise<AIProvider | undefined> {
117+
switch (providerType) {
118+
case 'openai':
119+
return this._createProvider(
120+
'@launchdarkly/server-sdk-ai-openai',
121+
'OpenAIProvider',
122+
aiConfig,
123+
logger,
124+
);
125+
case 'langchain':
126+
return this._createProvider(
127+
'@launchdarkly/server-sdk-ai-langchain',
128+
'LangChainProvider',
129+
aiConfig,
130+
logger,
131+
);
132+
case 'vercel':
133+
return this._createProvider(
134+
'@launchdarkly/server-sdk-ai-vercel',
135+
'VercelProvider',
136+
aiConfig,
137+
logger,
138+
);
139+
default:
140+
return undefined;
141+
}
142+
}
143+
144+
/**
145+
* Create a provider instance dynamically.
146+
*/
147+
private static async _createProvider(
148+
packageName: string,
149+
providerClassName: string,
76150
aiConfig: LDAIConfig,
77151
logger?: LDLogger,
78152
): Promise<AIProvider | undefined> {
79153
try {
80-
logger?.debug('Attempting to load LangChain provider');
81-
// Try to dynamically import the LangChain provider
82-
// This will work if @launchdarkly/server-sdk-ai-langchain is installed
83-
// eslint-disable-next-line import/no-extraneous-dependencies, global-require
84-
const { LangChainProvider } = require('@launchdarkly/server-sdk-ai-langchain');
85-
86-
const provider = await LangChainProvider.create(aiConfig, logger);
87-
logger?.debug('Successfully created LangChain provider');
154+
// Try to dynamically import the provider
155+
// This will work if the package is installed
156+
// eslint-disable-next-line import/no-extraneous-dependencies, global-require, import/no-dynamic-require
157+
const { [providerClassName]: ProviderClass } = require(packageName);
158+
159+
const provider = await ProviderClass.create(aiConfig, logger);
160+
logger?.debug(
161+
`Successfully created AIProvider for: ${aiConfig.provider?.name} with package ${packageName}`,
162+
);
88163
return provider;
89164
} catch (error) {
90-
// If the LangChain provider is not available or creation fails, return undefined
91-
logger?.error(`Error creating LangChain provider: ${error}`);
165+
// If the provider is not available or creation fails, return undefined
166+
logger?.warn(
167+
`Error creating AIProvider for: ${aiConfig.provider?.name} with package ${packageName}: ${error}`,
168+
);
92169
return undefined;
93170
}
94171
}

0 commit comments

Comments
 (0)