llmProviders.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import { StorageEnum } from '../base/enums';
  2. import { createStorage } from '../base/base';
  3. import type { BaseStorage } from '../base/types';
  4. import { type AgentNameEnum, llmProviderModelNames, llmProviderParameters, ProviderTypeEnum } from './types';
  5. // Interface for a single provider configuration
  6. export interface ProviderConfig {
  7. name?: string; // Display name in the options
  8. type?: ProviderTypeEnum; // Help to decide which LangChain ChatModel package to use
  9. apiKey: string; // Must be provided, but may be empty for local models
  10. baseUrl?: string; // Optional base URL if provided
  11. modelNames?: string[]; // Chosen model names, if not provided use hardcoded names from llmProviderModelNames
  12. createdAt?: number; // Timestamp in milliseconds when the provider was created
  13. }
  14. // Interface for storing multiple LLM provider configurations
  15. // The key is the provider id, which is the same as the provider type for built-in providers, but is custom for custom providers
  16. export interface LLMKeyRecord {
  17. providers: Record<string, ProviderConfig>;
  18. }
  19. export type LLMProviderStorage = BaseStorage<LLMKeyRecord> & {
  20. setProvider: (providerId: string, config: ProviderConfig) => Promise<void>;
  21. getProvider: (providerId: string) => Promise<ProviderConfig | undefined>;
  22. removeProvider: (providerId: string) => Promise<void>;
  23. hasProvider: (providerId: string) => Promise<boolean>;
  24. getAllProviders: () => Promise<Record<string, ProviderConfig>>;
  25. };
  26. // Storage for LLM provider configurations
  27. // use "llm-api-keys" as the key for the storage, for backward compatibility
  28. const storage = createStorage<LLMKeyRecord>(
  29. 'llm-api-keys',
  30. { providers: {} },
  31. {
  32. storageEnum: StorageEnum.Local,
  33. liveUpdate: true,
  34. },
  35. );
  36. // Helper function to determine provider type from provider name
  37. // Make sure to update this function if you add a new provider type
  38. export function getProviderTypeByProviderId(providerId: string): ProviderTypeEnum {
  39. switch (providerId) {
  40. case ProviderTypeEnum.OpenAI:
  41. case ProviderTypeEnum.Anthropic:
  42. case ProviderTypeEnum.Gemini:
  43. case ProviderTypeEnum.Grok:
  44. case ProviderTypeEnum.Ollama:
  45. return providerId;
  46. default:
  47. return ProviderTypeEnum.CustomOpenAI;
  48. }
  49. }
  50. // Helper function to get display name from provider id
  51. // Make sure to update this function if you add a new provider type
  52. export function getDefaultDisplayNameFromProviderId(providerId: string): string {
  53. switch (providerId) {
  54. case ProviderTypeEnum.OpenAI:
  55. return 'OpenAI';
  56. case ProviderTypeEnum.Anthropic:
  57. return 'Anthropic';
  58. case ProviderTypeEnum.Gemini:
  59. return 'Gemini';
  60. case ProviderTypeEnum.Grok:
  61. return 'Grok';
  62. case ProviderTypeEnum.Ollama:
  63. return 'Ollama';
  64. default:
  65. return providerId; // Use the provider id as display name for custom providers by default
  66. }
  67. }
  68. // Get default configuration for built-in providers
  69. // Make sure to update this function if you add a new provider type
  70. export function getDefaultProviderConfig(providerId: string): ProviderConfig {
  71. switch (providerId) {
  72. case ProviderTypeEnum.OpenAI:
  73. case ProviderTypeEnum.Anthropic:
  74. case ProviderTypeEnum.Gemini:
  75. case ProviderTypeEnum.Grok:
  76. return {
  77. apiKey: '',
  78. name: getDefaultDisplayNameFromProviderId(providerId),
  79. type: providerId,
  80. modelNames: [...(llmProviderModelNames[providerId] || [])],
  81. createdAt: Date.now(),
  82. };
  83. case ProviderTypeEnum.Ollama:
  84. return {
  85. apiKey: 'ollama', // Set default API key for Ollama
  86. name: getDefaultDisplayNameFromProviderId(ProviderTypeEnum.Ollama),
  87. type: ProviderTypeEnum.Ollama,
  88. modelNames: [],
  89. baseUrl: 'http://localhost:11434',
  90. createdAt: Date.now(),
  91. };
  92. default:
  93. return {
  94. apiKey: '',
  95. name: getDefaultDisplayNameFromProviderId(providerId),
  96. type: ProviderTypeEnum.CustomOpenAI,
  97. baseUrl: '',
  98. modelNames: [],
  99. createdAt: Date.now(),
  100. };
  101. }
  102. }
  103. export function getDefaultAgentModelParams(providerId: string, agentName: AgentNameEnum): Record<string, number> {
  104. const newParameters = llmProviderParameters[providerId as keyof typeof llmProviderParameters]?.[agentName] || {
  105. temperature: 0.1,
  106. topP: 0.1,
  107. };
  108. return newParameters;
  109. }
  110. // Helper function to ensure backward compatibility for provider configs
  111. function ensureBackwardCompatibility(providerId: string, config: ProviderConfig): ProviderConfig {
  112. const updatedConfig = { ...config };
  113. if (!updatedConfig.name) {
  114. updatedConfig.name = getDefaultDisplayNameFromProviderId(providerId);
  115. }
  116. if (!updatedConfig.type) {
  117. updatedConfig.type = getProviderTypeByProviderId(providerId);
  118. }
  119. if (!updatedConfig.modelNames) {
  120. updatedConfig.modelNames = llmProviderModelNames[providerId as keyof typeof llmProviderModelNames] || [];
  121. }
  122. if (!updatedConfig.createdAt) {
  123. // if createdAt is not set, set it to "03/04/2025" for backward compatibility
  124. updatedConfig.createdAt = new Date('03/04/2025').getTime();
  125. }
  126. return updatedConfig;
  127. }
  128. export const llmProviderStore: LLMProviderStorage = {
  129. ...storage,
  130. async setProvider(providerId: string, config: ProviderConfig) {
  131. if (!providerId) {
  132. throw new Error('Provider id cannot be empty');
  133. }
  134. if (config.apiKey === undefined) {
  135. throw new Error('API key must be provided (can be empty for local models)');
  136. }
  137. if (!config.modelNames) {
  138. throw new Error('Model names must be provided');
  139. }
  140. // Ensure backward compatibility by filling in missing fields
  141. const completeConfig: ProviderConfig = {
  142. ...config,
  143. name: config.name || getDefaultDisplayNameFromProviderId(providerId),
  144. type: config.type || getProviderTypeByProviderId(providerId),
  145. modelNames: config.modelNames,
  146. createdAt: config.createdAt || Date.now(),
  147. };
  148. const current = (await storage.get()) || { providers: {} };
  149. await storage.set({
  150. providers: {
  151. ...current.providers,
  152. [providerId]: completeConfig,
  153. },
  154. });
  155. },
  156. async getProvider(providerId: string) {
  157. const data = (await storage.get()) || { providers: {} };
  158. const config = data.providers[providerId];
  159. return config ? ensureBackwardCompatibility(providerId, config) : undefined;
  160. },
  161. async removeProvider(providerId: string) {
  162. const current = (await storage.get()) || { providers: {} };
  163. const newProviders = { ...current.providers };
  164. delete newProviders[providerId];
  165. await storage.set({ providers: newProviders });
  166. },
  167. async hasProvider(providerId: string) {
  168. const data = (await storage.get()) || { providers: {} };
  169. return providerId in data.providers;
  170. },
  171. async getAllProviders() {
  172. const data = await storage.get();
  173. const providers = { ...data.providers };
  174. // Add backward compatibility for all providers
  175. for (const [providerId, config] of Object.entries(providers)) {
  176. providers[providerId] = ensureBackwardCompatibility(providerId, config);
  177. }
  178. return providers;
  179. },
  180. };