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
170 changes: 160 additions & 10 deletions packages/ai-providers/server-ai-vercel/src/VercelProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,50 @@ import type {
LDTokenUsage,
} from '@launchdarkly/server-sdk-ai';

import type {
VercelAIModelParameters,
VercelAISDKConfig,
VercelAISDKMapOptions,
VercelAISDKProvider,
} from './types';

/**
* Vercel AI implementation of AIProvider.
* This provider integrates Vercel AI SDK with LaunchDarkly's tracking capabilities.
*/
export class VercelProvider extends AIProvider {
private _model: LanguageModel;
private _parameters: Record<string, unknown>;
private _parameters: VercelAIModelParameters;

constructor(model: LanguageModel, parameters: Record<string, unknown>, logger?: LDLogger) {
/**
* Constructor for the VercelProvider.
* @param model - The Vercel AI model to use.
* @param parameters - The Vercel AI model parameters.
* @param logger - The logger to use for the Vercel AI provider.
*/
constructor(model: LanguageModel, parameters: VercelAIModelParameters, logger?: LDLogger) {
super(logger);
this._model = model;
this._parameters = parameters;
}

// =============================================================================
// MAIN FACTORY METHOD
// MAIN FACTORY METHODS
// =============================================================================

/**
* Static factory method to create a Vercel AIProvider from an AI configuration.
* This method auto-detects the provider and creates the model.
* Note: Messages from the AI config are not included in the provider - messages
* should be passed at invocation time via invokeModel().
*
* @param aiConfig The LaunchDarkly AI configuration
* @param logger Optional logger
* @returns A Promise that resolves to a configured VercelProvider
*/
static async create(aiConfig: LDAIConfig, logger?: LDLogger): Promise<VercelProvider> {
const model = await VercelProvider.createVercelModel(aiConfig);
const parameters = aiConfig.model?.parameters || {};
const parameters = VercelProvider.mapParameters(aiConfig.model?.parameters);
return new VercelProvider(model, parameters, logger);
}

Expand All @@ -46,8 +66,6 @@ export class VercelProvider extends AIProvider {
*/
async invokeModel(messages: LDMessage[]): Promise<ChatResponse> {
// Call Vercel AI generateText
// Type assertion: our MinLanguageModel is compatible with the expected LanguageModel interface
// The generateText function will work with any object that has the required properties
const result = await generateText({
model: this._model,
messages,
Expand Down Expand Up @@ -121,19 +139,151 @@ export class VercelProvider extends AIProvider {
};
}

/**
* Create a metrics extractor for Vercel AI SDK streaming results.
* Use this with tracker.trackStreamMetricsOf() for streaming operations like streamText.
*
* The extractor waits for the stream's response promise to resolve, then extracts
* metrics from the completed response.
*
* @returns A metrics extractor function for streaming results
*
* @example
* const stream = aiConfig.tracker.trackStreamMetricsOf(
* () => streamText(vercelConfig),
* VercelProvider.createStreamMetricsExtractor()
* );
*
* for await (const chunk of stream.textStream) {
* process.stdout.write(chunk);
* }
*/
static createStreamMetricsExtractor() {
return async (stream: any): Promise<LDAIMetrics> => {
// Wait for stream to complete
const result = await stream.response;
// Extract metrics from completed response
return VercelProvider.createAIMetrics(result);
};
}

/**
* Map LaunchDarkly model parameters to Vercel AI SDK parameters.
*
* Parameter mappings:
* - max_tokens → maxTokens
* - max_completion_tokens → maxOutputTokens
* - temperature → temperature
* - top_p → topP
* - top_k → topK
* - presence_penalty → presencePenalty
* - frequency_penalty → frequencyPenalty
* - stop → stopSequences
* - seed → seed
*
* @param parameters The LaunchDarkly model parameters to map
* @returns An object containing mapped Vercel AI SDK parameters
*/
static mapParameters(parameters?: { [index: string]: unknown }): VercelAIModelParameters {
if (!parameters) {
return {};
}

const params: VercelAIModelParameters = {};

// Map token limits
if (parameters.max_tokens !== undefined) {
params.maxTokens = parameters.max_tokens as number;
}
if (parameters.max_completion_tokens !== undefined) {
params.maxOutputTokens = parameters.max_completion_tokens as number;
}

// Map remaining parameters
if (parameters.temperature !== undefined) {
params.temperature = parameters.temperature as number;
}
if (parameters.top_p !== undefined) {
params.topP = parameters.top_p as number;
}
if (parameters.top_k !== undefined) {
params.topK = parameters.top_k as number;
}
if (parameters.presence_penalty !== undefined) {
params.presencePenalty = parameters.presence_penalty as number;
}
if (parameters.frequency_penalty !== undefined) {
params.frequencyPenalty = parameters.frequency_penalty as number;
}
if (parameters.stop !== undefined) {
params.stopSequences = parameters.stop as string[];
}
if (parameters.seed !== undefined) {
params.seed = parameters.seed as number;
}

return params;
}

/**
* Convert an AI configuration to Vercel AI SDK parameters.
* This static method allows converting an LDAIConfig to VercelAISDKConfig without
* requiring an instance of VercelProvider.
*
* @param aiConfig The LaunchDarkly AI configuration
* @param provider A Vercel AI SDK Provider or a map of provider names to Vercel AI SDK Providers
* @param options Optional mapping options
* @returns A configuration directly usable in Vercel AI SDK generateText() and streamText()
* @throws {Error} if a Vercel AI SDK model cannot be determined from the given provider parameter
*/
static toVercelAISDK<TMod>(
aiConfig: LDAIConfig,
provider: VercelAISDKProvider<TMod> | Record<string, VercelAISDKProvider<TMod>>,
options?: VercelAISDKMapOptions | undefined,
): VercelAISDKConfig<TMod> {
// Determine the model from the provider
let model: TMod | undefined;
if (typeof provider === 'function') {
model = provider(aiConfig.model?.name ?? '');
} else {
model = provider[aiConfig.provider?.name ?? '']?.(aiConfig.model?.name ?? '');
}
if (!model) {
throw new Error(
'Vercel AI SDK model cannot be determined from the supplied provider parameter.',
);
}

// Merge messages from config and options
let messages: LDMessage[] | undefined;
const configMessages = ('messages' in aiConfig ? aiConfig.messages : undefined) as
| LDMessage[]
| undefined;
if (configMessages || options?.nonInterpolatedMessages) {
messages = [...(configMessages ?? []), ...(options?.nonInterpolatedMessages ?? [])];
}

// Map parameters using the shared mapping method
const params = VercelProvider.mapParameters(aiConfig.model?.parameters);

// Build and return the Vercel AI SDK configuration
return {
model,
messages,
...params,
};
}

/**
* Create a Vercel AI model from an AI configuration.
* This method creates a Vercel AI model based on the provider configuration.
* This method auto-detects the provider and creates the model instance.
*
* @param aiConfig The LaunchDarkly AI configuration
* @returns A Promise that resolves to a configured Vercel AI model
*/
static async createVercelModel(aiConfig: LDAIConfig): Promise<LanguageModel> {
const providerName = VercelProvider.mapProvider(aiConfig.provider?.name || '');
const modelName = aiConfig.model?.name || '';
// Parameters are not used in model creation but kept for future use
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const parameters = aiConfig.model?.parameters || {};

// Map provider names to their corresponding Vercel AI SDK imports
switch (providerName) {
Expand Down
6 changes: 6 additions & 0 deletions packages/ai-providers/server-ai-vercel/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
export { VercelProvider } from './VercelProvider';
export type {
VercelAIModelParameters,
VercelAISDKConfig,
VercelAISDKMapOptions,
VercelAISDKProvider,
} from './types';
41 changes: 41 additions & 0 deletions packages/ai-providers/server-ai-vercel/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { LDMessage } from '@launchdarkly/server-sdk-ai';

/**
* Vercel AI SDK Provider type - a function that takes a model name and returns a model instance.
*/
export type VercelAISDKProvider<TMod> = (modelName: string) => TMod;

/**
* Options for mapping to Vercel AI SDK configuration.
*/
export interface VercelAISDKMapOptions {
/**
* Additional messages that should not be interpolated.
*/
nonInterpolatedMessages?: LDMessage[] | undefined;
}

/**
* Vercel AI SDK model parameters.
* These are the parameters that can be passed to Vercel AI SDK methods like generateText() and streamText().
*/
export interface VercelAIModelParameters {
maxTokens?: number | undefined;
maxOutputTokens?: number | undefined;
temperature?: number | undefined;
topP?: number | undefined;
topK?: number | undefined;
presencePenalty?: number | undefined;
frequencyPenalty?: number | undefined;
stopSequences?: string[] | undefined;
seed?: number | undefined;
}

/**
* Configuration format compatible with Vercel AI SDK's generateText() and streamText() methods.
*/
export interface VercelAISDKConfig<TMod> extends VercelAIModelParameters {
model: TMod;
messages?: LDMessage[] | undefined;
}