@launchdarkly/server-sdk-ai 0.11.4 → 0.12.0

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 (97) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/__tests__/LDAIConfigTrackerImpl.test.ts +155 -0
  3. package/__tests__/TrackedChat.test.ts +231 -0
  4. package/dist/LDAIClientImpl.d.ts +4 -0
  5. package/dist/LDAIClientImpl.d.ts.map +1 -1
  6. package/dist/LDAIClientImpl.js +21 -0
  7. package/dist/LDAIClientImpl.js.map +1 -1
  8. package/dist/LDAIConfigTrackerImpl.d.ts +2 -1
  9. package/dist/LDAIConfigTrackerImpl.d.ts.map +1 -1
  10. package/dist/LDAIConfigTrackerImpl.js +24 -0
  11. package/dist/LDAIConfigTrackerImpl.js.map +1 -1
  12. package/dist/LDClientMin.d.ts +2 -1
  13. package/dist/LDClientMin.d.ts.map +1 -1
  14. package/dist/api/LDAIClient.d.ts +39 -0
  15. package/dist/api/LDAIClient.d.ts.map +1 -1
  16. package/dist/api/chat/TrackedChat.d.ts +54 -0
  17. package/dist/api/chat/TrackedChat.d.ts.map +1 -0
  18. package/dist/api/chat/TrackedChat.js +84 -0
  19. package/dist/api/chat/TrackedChat.js.map +1 -0
  20. package/dist/api/chat/index.d.ts +3 -0
  21. package/dist/api/chat/index.d.ts.map +1 -0
  22. package/dist/api/chat/index.js +19 -0
  23. package/dist/api/chat/index.js.map +1 -0
  24. package/dist/api/chat/types.d.ts +16 -0
  25. package/dist/api/chat/types.d.ts.map +1 -0
  26. package/dist/api/chat/types.js +3 -0
  27. package/dist/api/chat/types.js.map +1 -0
  28. package/dist/api/config/LDAIConfigTracker.d.ts +16 -1
  29. package/dist/api/config/LDAIConfigTracker.d.ts.map +1 -1
  30. package/dist/api/index.d.ts +2 -0
  31. package/dist/api/index.d.ts.map +1 -1
  32. package/dist/api/index.js +2 -0
  33. package/dist/api/index.js.map +1 -1
  34. package/dist/api/metrics/LDAIMetrics.d.ts +17 -0
  35. package/dist/api/metrics/LDAIMetrics.d.ts.map +1 -0
  36. package/dist/api/metrics/LDAIMetrics.js +3 -0
  37. package/dist/api/metrics/LDAIMetrics.js.map +1 -0
  38. package/dist/api/metrics/index.d.ts +1 -0
  39. package/dist/api/metrics/index.d.ts.map +1 -1
  40. package/dist/api/metrics/index.js +1 -0
  41. package/dist/api/metrics/index.js.map +1 -1
  42. package/dist/api/providers/AIProvider.d.ts +35 -0
  43. package/dist/api/providers/AIProvider.d.ts.map +1 -0
  44. package/dist/api/providers/AIProvider.js +31 -0
  45. package/dist/api/providers/AIProvider.js.map +1 -0
  46. package/dist/api/providers/AIProviderFactory.d.ts +39 -0
  47. package/dist/api/providers/AIProviderFactory.d.ts.map +1 -0
  48. package/dist/api/providers/AIProviderFactory.js +102 -0
  49. package/dist/api/providers/AIProviderFactory.js.map +1 -0
  50. package/dist/api/providers/index.d.ts +3 -0
  51. package/dist/api/providers/index.d.ts.map +1 -0
  52. package/dist/api/providers/index.js +19 -0
  53. package/dist/api/providers/index.js.map +1 -0
  54. package/docs/assets/search.js +1 -1
  55. package/docs/classes/AIProvider.html +174 -0
  56. package/docs/classes/AIProviderFactory.html +197 -0
  57. package/docs/classes/TrackedChat.html +253 -0
  58. package/docs/enums/LDFeedbackKind.html +14 -7
  59. package/docs/functions/createBedrockTokenUsage.html +12 -5
  60. package/docs/functions/createOpenAiUsage.html +12 -5
  61. package/docs/functions/createVercelAISDKTokenUsage.html +12 -5
  62. package/docs/functions/initAi.html +12 -5
  63. package/docs/index.html +25 -5
  64. package/docs/interfaces/ChatResponse.html +108 -0
  65. package/docs/interfaces/LDAIAgent.html +17 -10
  66. package/docs/interfaces/LDAIAgentConfig.html +15 -8
  67. package/docs/interfaces/LDAIClient.html +55 -9
  68. package/docs/interfaces/LDAIConfig.html +18 -11
  69. package/docs/interfaces/LDAIConfigTracker.html +76 -17
  70. package/docs/interfaces/LDAIMetrics.html +110 -0
  71. package/docs/interfaces/LDMessage.html +14 -7
  72. package/docs/interfaces/LDModelConfig.html +15 -8
  73. package/docs/interfaces/LDProviderConfig.html +13 -6
  74. package/docs/interfaces/LDTokenUsage.html +15 -8
  75. package/docs/interfaces/VercelAISDKConfig.html +23 -16
  76. package/docs/interfaces/VercelAISDKMapOptions.html +13 -6
  77. package/docs/types/LDAIAgentDefaults.html +12 -5
  78. package/docs/types/LDAIDefaults.html +12 -5
  79. package/docs/types/SupportedAIProvider.html +70 -0
  80. package/docs/types/VercelAISDKProvider.html +12 -5
  81. package/docs/variables/SUPPORTED_AI_PROVIDERS.html +70 -0
  82. package/package.json +1 -1
  83. package/src/LDAIClientImpl.ts +36 -2
  84. package/src/LDAIConfigTrackerImpl.ts +32 -0
  85. package/src/LDClientMin.ts +3 -1
  86. package/src/api/LDAIClient.ts +46 -0
  87. package/src/api/chat/TrackedChat.ts +100 -0
  88. package/src/api/chat/index.ts +2 -0
  89. package/src/api/chat/types.ts +17 -0
  90. package/src/api/config/LDAIConfigTracker.ts +20 -1
  91. package/src/api/index.ts +2 -0
  92. package/src/api/metrics/LDAIMetrics.ts +18 -0
  93. package/src/api/metrics/index.ts +1 -0
  94. package/src/api/providers/AIProvider.ts +43 -0
  95. package/src/api/providers/AIProviderFactory.ts +152 -0
  96. package/src/api/providers/index.ts +2 -0
  97. package/tsconfig.eslint.json +2 -2
@@ -1,4 +1,4 @@
1
- import { LDFeedbackKind, LDTokenUsage } from '../metrics';
1
+ import { LDAIMetrics, LDFeedbackKind, LDTokenUsage } from '../metrics';
2
2
 
3
3
  /**
4
4
  * Metrics which have been tracked.
@@ -87,6 +87,25 @@ export interface LDAIConfigTracker {
87
87
  */
88
88
  trackDurationOf(func: () => Promise<any>): Promise<any>;
89
89
 
90
+ /**
91
+ * Track metrics for a generic AI operation.
92
+ *
93
+ * This function will track the duration of the operation, extract metrics using the provided
94
+ * metrics extractor function, and track success or error status accordingly.
95
+ *
96
+ * If the provided function throws, then this method will also throw.
97
+ * In the case the provided function throws, this function will record the duration and an error.
98
+ * A failed operation will not have any token usage data.
99
+ *
100
+ * @param metricsExtractor Function that extracts LDAIMetrics from the operation result
101
+ * @param func Function which executes the operation
102
+ * @returns The result of the operation
103
+ */
104
+ trackMetricsOf<TRes>(
105
+ metricsExtractor: (result: TRes) => LDAIMetrics,
106
+ func: () => Promise<TRes>,
107
+ ): Promise<TRes>;
108
+
90
109
  /**
91
110
  * Track an OpenAI operation.
92
111
  *
package/src/api/index.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from './config';
2
2
  export * from './agents';
3
+ export * from './chat';
3
4
  export * from './metrics';
4
5
  export * from './LDAIClient';
6
+ export * from './providers';
@@ -0,0 +1,18 @@
1
+ import { LDTokenUsage } from './LDTokenUsage';
2
+
3
+ /**
4
+ * Metrics information for AI operations that includes success status and token usage.
5
+ * This class combines success/failure tracking with token usage metrics.
6
+ */
7
+ export interface LDAIMetrics {
8
+ /**
9
+ * Whether the AI operation was successful.
10
+ */
11
+ success: boolean;
12
+
13
+ /**
14
+ * Token usage information for the operation.
15
+ * This will be undefined if no token usage data is available.
16
+ */
17
+ usage?: LDTokenUsage;
18
+ }
@@ -1,5 +1,6 @@
1
1
  export * from './BedrockTokenUsage';
2
2
  export * from './OpenAiUsage';
3
3
  export * from './LDFeedbackKind';
4
+ export * from './LDAIMetrics';
4
5
  export * from './LDTokenUsage';
5
6
  export * from './VercelAISDKTokenUsage';
@@ -0,0 +1,43 @@
1
+ import { LDLogger } from '@launchdarkly/js-server-sdk-common';
2
+
3
+ import { ChatResponse } from '../chat/types';
4
+ import { LDAIConfig, LDMessage } from '../config/LDAIConfig';
5
+
6
+ /**
7
+ * Abstract base class for AI providers that implement chat model functionality.
8
+ * This class provides the contract that all provider implementations must follow
9
+ * to integrate with LaunchDarkly's tracking and configuration capabilities.
10
+ *
11
+ * Following the AICHAT spec recommendation to use base classes with non-abstract methods
12
+ * for better extensibility and backwards compatibility.
13
+ */
14
+ export abstract class AIProvider {
15
+ protected readonly logger?: LDLogger;
16
+
17
+ constructor(logger?: LDLogger) {
18
+ this.logger = logger;
19
+ }
20
+ /**
21
+ * Invoke the chat model with an array of messages.
22
+ * This method should convert messages to provider format, invoke the model,
23
+ * and return a ChatResponse with the result and metrics.
24
+ *
25
+ * @param messages Array of LDMessage objects representing the conversation
26
+ * @returns Promise that resolves to a ChatResponse containing the model's response
27
+ */
28
+ abstract invokeModel(messages: LDMessage[]): Promise<ChatResponse>;
29
+
30
+ /**
31
+ * Static method that constructs an instance of the provider.
32
+ * Each provider implementation must provide their own static create method
33
+ * that accepts an AIConfig and returns a configured instance.
34
+ *
35
+ * @param aiConfig The LaunchDarkly AI configuration
36
+ * @param logger Optional logger for the provider
37
+ * @returns Promise that resolves to a configured provider instance
38
+ */
39
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
40
+ static async create(aiConfig: LDAIConfig, logger?: LDLogger): Promise<AIProvider> {
41
+ throw new Error('Provider implementations must override the static create method');
42
+ }
43
+ }
@@ -0,0 +1,152 @@
1
+ import { LDLogger } from '@launchdarkly/js-server-sdk-common';
2
+
3
+ import { LDAIConfig } from '../config/LDAIConfig';
4
+ import { AIProvider } from './AIProvider';
5
+
6
+ /**
7
+ * List of supported AI providers.
8
+ */
9
+ export const SUPPORTED_AI_PROVIDERS = [
10
+ 'openai',
11
+ // Multi-provider packages should be last in the list
12
+ 'langchain',
13
+ 'vercel',
14
+ ] as const;
15
+
16
+ /**
17
+ * Type representing the supported AI providers.
18
+ */
19
+ export type SupportedAIProvider = (typeof SUPPORTED_AI_PROVIDERS)[number];
20
+
21
+ /**
22
+ * Factory for creating AIProvider instances based on the provider configuration.
23
+ */
24
+ export class AIProviderFactory {
25
+ /**
26
+ * Create an AIProvider instance based on the AI configuration.
27
+ * This method attempts to load provider-specific implementations dynamically.
28
+ * Returns undefined if the provider is not supported.
29
+ *
30
+ * @param aiConfig The AI configuration
31
+ * @param logger Optional logger for logging provider initialization
32
+ * @param defaultAiProvider Optional default AI provider to use
33
+ */
34
+ static async create(
35
+ aiConfig: LDAIConfig,
36
+ logger?: LDLogger,
37
+ defaultAiProvider?: SupportedAIProvider,
38
+ ): Promise<AIProvider | undefined> {
39
+ const providerName = aiConfig.provider?.name?.toLowerCase();
40
+ // Determine which providers to try based on defaultAiProvider
41
+ const providersToTry = this._getProvidersToTry(defaultAiProvider, providerName);
42
+
43
+ // Try each provider in order
44
+ // eslint-disable-next-line no-restricted-syntax
45
+ for (const providerType of providersToTry) {
46
+ // eslint-disable-next-line no-await-in-loop
47
+ const provider = await this._tryCreateProvider(providerType, aiConfig, logger);
48
+ if (provider) {
49
+ return provider;
50
+ }
51
+ }
52
+
53
+ // If no provider was successfully created, log a warning
54
+ logger?.warn(
55
+ `Provider is not supported or failed to initialize: ${aiConfig.provider?.name ?? 'unknown'}`,
56
+ );
57
+ return undefined;
58
+ }
59
+
60
+ /**
61
+ * Determine which providers to try based on defaultAiProvider and providerName.
62
+ */
63
+ private static _getProvidersToTry(
64
+ defaultAiProvider?: SupportedAIProvider,
65
+ providerName?: string,
66
+ ): SupportedAIProvider[] {
67
+ // If defaultAiProvider is set, only try that specific provider
68
+ if (defaultAiProvider) {
69
+ return [defaultAiProvider];
70
+ }
71
+
72
+ // If no defaultAiProvider is set, try all providers in order
73
+ const providerSet = new Set<SupportedAIProvider>();
74
+
75
+ // First try the specific provider if it's supported
76
+ if (providerName && SUPPORTED_AI_PROVIDERS.includes(providerName as SupportedAIProvider)) {
77
+ providerSet.add(providerName as SupportedAIProvider);
78
+ }
79
+
80
+ // Then try multi-provider packages, but avoid duplicates
81
+ const multiProviderPackages: SupportedAIProvider[] = ['langchain', 'vercel'];
82
+ multiProviderPackages.forEach((provider) => {
83
+ providerSet.add(provider);
84
+ });
85
+
86
+ return Array.from(providerSet);
87
+ }
88
+
89
+ /**
90
+ * Try to create a provider of the specified type.
91
+ */
92
+ private static async _tryCreateProvider(
93
+ providerType: SupportedAIProvider,
94
+ aiConfig: LDAIConfig,
95
+ logger?: LDLogger,
96
+ ): Promise<AIProvider | undefined> {
97
+ switch (providerType) {
98
+ case 'openai':
99
+ return this._createProvider(
100
+ '@launchdarkly/server-sdk-ai-openai',
101
+ 'OpenAIProvider',
102
+ aiConfig,
103
+ logger,
104
+ );
105
+ case 'langchain':
106
+ return this._createProvider(
107
+ '@launchdarkly/server-sdk-ai-langchain',
108
+ 'LangChainProvider',
109
+ aiConfig,
110
+ logger,
111
+ );
112
+ case 'vercel':
113
+ return this._createProvider(
114
+ '@launchdarkly/server-sdk-ai-vercel',
115
+ 'VercelProvider',
116
+ aiConfig,
117
+ logger,
118
+ );
119
+ default:
120
+ return undefined;
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Create a provider instance dynamically.
126
+ */
127
+ private static async _createProvider(
128
+ packageName: string,
129
+ providerClassName: string,
130
+ aiConfig: LDAIConfig,
131
+ logger?: LDLogger,
132
+ ): Promise<AIProvider | undefined> {
133
+ try {
134
+ // Try to dynamically import the provider
135
+ // This will work if the package is installed
136
+ // eslint-disable-next-line import/no-extraneous-dependencies, global-require, import/no-dynamic-require
137
+ const { [providerClassName]: ProviderClass } = require(packageName);
138
+
139
+ const provider = await ProviderClass.create(aiConfig, logger);
140
+ logger?.debug(
141
+ `Successfully created AIProvider for: ${aiConfig.provider?.name} with package ${packageName}`,
142
+ );
143
+ return provider;
144
+ } catch (error) {
145
+ // If the provider is not available or creation fails, return undefined
146
+ logger?.warn(
147
+ `Error creating AIProvider for: ${aiConfig.provider?.name} with package ${packageName}: ${error}`,
148
+ );
149
+ return undefined;
150
+ }
151
+ }
152
+ }
@@ -0,0 +1,2 @@
1
+ export * from './AIProvider';
2
+ export * from './AIProviderFactory';
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "extends": "./tsconfig.json",
3
- "include": ["/**/*.ts"],
4
- "exclude": ["node_modules"]
3
+ "include": ["**/*.ts"],
4
+ "exclude": ["node_modules", "dist"]
5
5
  }