llmProviders.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import { StorageEnum } from '../base/enums';
  2. import { createStorage } from '../base/base';
  3. import type { BaseStorage } from '../base/types';
  4. import { llmProviderModelNames, ProviderTypeEnum, OPENAI_PROVIDER, ANTHROPIC_PROVIDER, GEMINI_PROVIDER } 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. }
  13. // Interface for storing multiple LLM provider configurations
  14. export interface LLMKeyRecord {
  15. providers: Record<string, ProviderConfig>;
  16. }
  17. export type LLMProviderStorage = BaseStorage<LLMKeyRecord> & {
  18. setProvider: (provider: string, config: ProviderConfig) => Promise<void>;
  19. getProvider: (provider: string) => Promise<ProviderConfig | undefined>;
  20. removeProvider: (provider: string) => Promise<void>;
  21. hasProvider: (provider: string) => Promise<boolean>;
  22. getConfiguredProviders: () => Promise<string[]>;
  23. getAllProviders: () => Promise<Record<string, ProviderConfig>>;
  24. };
  25. const storage = createStorage<LLMKeyRecord>(
  26. 'llm-api-keys',
  27. { providers: {} },
  28. {
  29. storageEnum: StorageEnum.Local,
  30. liveUpdate: true,
  31. },
  32. );
  33. // Helper function to determine provider type from provider name
  34. function getProviderTypeFromName(provider: string): ProviderTypeEnum {
  35. switch (provider) {
  36. case OPENAI_PROVIDER:
  37. return ProviderTypeEnum.OpenAI;
  38. case ANTHROPIC_PROVIDER:
  39. return ProviderTypeEnum.Anthropic;
  40. case GEMINI_PROVIDER:
  41. return ProviderTypeEnum.Gemini;
  42. default:
  43. return ProviderTypeEnum.CustomOpenAI;
  44. }
  45. }
  46. // Helper function to get display name from provider name
  47. function getDisplayNameFromProvider(provider: string): string {
  48. switch (provider) {
  49. case OPENAI_PROVIDER:
  50. return 'OpenAI';
  51. case ANTHROPIC_PROVIDER:
  52. return 'Anthropic';
  53. case GEMINI_PROVIDER:
  54. return 'Gemini';
  55. default:
  56. return provider; // Use the provider string as display name for custom providers
  57. }
  58. }
  59. export const llmProviderStore: LLMProviderStorage = {
  60. ...storage,
  61. async setProvider(provider: string, config: ProviderConfig) {
  62. if (!provider) {
  63. throw new Error('Provider name cannot be empty');
  64. }
  65. if (config.apiKey === undefined) {
  66. throw new Error('API key must be provided (can be empty for local models)');
  67. }
  68. if (!config.modelNames) {
  69. throw new Error('Model names must be provided');
  70. }
  71. // Ensure backward compatibility by filling in missing fields
  72. const completeConfig: ProviderConfig = {
  73. ...config,
  74. name: config.name || getDisplayNameFromProvider(provider),
  75. type: config.type || getProviderTypeFromName(provider),
  76. modelNames: config.modelNames,
  77. };
  78. const current = (await storage.get()) || { providers: {} };
  79. await storage.set({
  80. providers: {
  81. ...current.providers,
  82. [provider]: completeConfig,
  83. },
  84. });
  85. },
  86. async getProvider(provider: string) {
  87. const data = (await storage.get()) || { providers: {} };
  88. const config = data.providers[provider];
  89. // If we have a config but it's missing some fields, fill them in
  90. if (config) {
  91. if (!config.name) {
  92. config.name = getDisplayNameFromProvider(provider);
  93. }
  94. if (!config.type) {
  95. config.type = getProviderTypeFromName(provider);
  96. }
  97. if (!config.modelNames) {
  98. config.modelNames = llmProviderModelNames[provider as keyof typeof llmProviderModelNames] || [];
  99. }
  100. }
  101. return config;
  102. },
  103. async removeProvider(provider: string) {
  104. const current = (await storage.get()) || { providers: {} };
  105. const newProviders = { ...current.providers };
  106. delete newProviders[provider];
  107. await storage.set({ providers: newProviders });
  108. },
  109. async hasProvider(provider: string) {
  110. const data = (await storage.get()) || { providers: {} };
  111. return provider in data.providers;
  112. },
  113. async getConfiguredProviders() {
  114. console.log('Getting configured providers');
  115. const data = await storage.get();
  116. console.log('Raw storage data:', data); // Debug the entire data object
  117. if (!data || !data.providers) {
  118. console.log('No data found, returning empty array');
  119. return [];
  120. }
  121. console.log('Configured providers:', data.providers);
  122. return Object.keys(data.providers);
  123. },
  124. async getAllProviders() {
  125. const data = await storage.get();
  126. return data.providers;
  127. },
  128. };