llmProviders.ts 5.0 KB

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