llmProviders.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import { StorageEnum } from '../base/enums';
  2. import { createStorage } from '../base/base';
  3. import type { BaseStorage } from '../base/types';
  4. import { LLMProviderEnum } from './types';
  5. // Interface for OpenRouter model information
  6. export interface OpenRouterModel {
  7. id: string;
  8. name: string;
  9. description: string;
  10. pricing: {
  11. prompt: number;
  12. completion: number;
  13. };
  14. }
  15. // Interface for OpenRouter API response
  16. export interface OpenRouterModelsResponse {
  17. data: OpenRouterModel[];
  18. }
  19. // Interface for a single provider configuration
  20. export interface ProviderConfig {
  21. apiKey: string;
  22. baseUrl?: string;
  23. }
  24. // Interface for storing multiple LLM provider configurations
  25. export interface LLMKeyRecord {
  26. providers: Record<LLMProviderEnum, ProviderConfig>;
  27. }
  28. export type LLMProviderStorage = BaseStorage<LLMKeyRecord> & {
  29. setProvider: (provider: LLMProviderEnum, config: ProviderConfig) => Promise<void>;
  30. getProvider: (provider: LLMProviderEnum) => Promise<ProviderConfig | undefined>;
  31. removeProvider: (provider: LLMProviderEnum) => Promise<void>;
  32. hasProvider: (provider: LLMProviderEnum) => Promise<boolean>;
  33. getConfiguredProviders: () => Promise<LLMProviderEnum[]>;
  34. getAllProviders: () => Promise<Record<LLMProviderEnum, ProviderConfig>>;
  35. fetchOpenRouterModels: () => Promise<OpenRouterModel[]>;
  36. };
  37. const storage = createStorage<LLMKeyRecord>(
  38. 'llm-api-keys',
  39. { providers: {} as Record<LLMProviderEnum, ProviderConfig> },
  40. {
  41. storageEnum: StorageEnum.Local,
  42. liveUpdate: true,
  43. },
  44. );
  45. export const llmProviderStore: LLMProviderStorage = {
  46. ...storage,
  47. async setProvider(provider: LLMProviderEnum, config: ProviderConfig) {
  48. if (!provider) {
  49. throw new Error('Provider name cannot be empty');
  50. }
  51. if (!config.apiKey) {
  52. throw new Error('API key cannot be empty');
  53. }
  54. const current = (await storage.get()) || { providers: {} };
  55. await storage.set({
  56. providers: {
  57. ...current.providers,
  58. [provider]: config,
  59. },
  60. });
  61. },
  62. async getProvider(provider: LLMProviderEnum) {
  63. const data = (await storage.get()) || { providers: {} };
  64. return data.providers[provider];
  65. },
  66. async removeProvider(provider: LLMProviderEnum) {
  67. const current = (await storage.get()) || { providers: {} };
  68. const newProviders = { ...current.providers };
  69. delete newProviders[provider];
  70. await storage.set({ providers: newProviders });
  71. },
  72. async hasProvider(provider: LLMProviderEnum) {
  73. const data = (await storage.get()) || { providers: {} };
  74. return provider in data.providers;
  75. },
  76. async getConfiguredProviders() {
  77. console.log('Getting configured providers');
  78. const data = await storage.get();
  79. console.log('Raw storage data:', data); // Debug the entire data object
  80. if (!data || !data.providers) {
  81. console.log('No data found, returning empty array');
  82. return [];
  83. }
  84. console.log('Configured providers:', data.providers);
  85. return Object.keys(data.providers) as LLMProviderEnum[];
  86. },
  87. async getAllProviders() {
  88. const data = await storage.get();
  89. return data.providers;
  90. },
  91. async fetchOpenRouterModels() {
  92. try {
  93. const openRouterConfig = await this.getProvider(LLMProviderEnum.OpenRouter);
  94. if (!openRouterConfig || !openRouterConfig.apiKey) {
  95. throw new Error('OpenRouter API key not configured');
  96. }
  97. const baseUrl = openRouterConfig.baseUrl || 'https://openrouter.ai/api/v1';
  98. const response = await fetch(`${baseUrl}/models`, {
  99. method: 'GET',
  100. headers: {
  101. Authorization: `Bearer ${openRouterConfig.apiKey}`,
  102. 'HTTP-Referer': 'https://nanobrowser.extension',
  103. 'X-Title': 'Nanobrowser Extension',
  104. },
  105. });
  106. if (!response.ok) {
  107. throw new Error(`Failed to fetch OpenRouter models: ${response.statusText}`);
  108. }
  109. const data: OpenRouterModelsResponse = await response.json();
  110. return data.data;
  111. } catch (error) {
  112. console.error('Error fetching OpenRouter models:', error);
  113. return [];
  114. }
  115. },
  116. };