Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: refactored LLM Providers: Adapting Modular Approach #832

Merged
merged 7 commits into from
Dec 21, 2024

Conversation

thecodacus
Copy link
Collaborator

LLM Providers Refactor: Modular Approach

Overview

This PR refactors the LLM providers section, which was previously dispersed across multiple files, into a single modular structure. Each provider is now encapsulated within its own dedicated file, containing all the logic required to support both client-side and server-side functionality. This reorganization aims to enhance maintainability, scalability, and clarity in how providers are managed.

Key Changes

1. Introduction of a Modular Provider Structure

  • Change: Each LLM provider now exists as a standalone file under app/lib/modules/llm/providers/.
  • Impact: New providers can be added simply by creating a new file, following the established structure.
  • Benefits: Enhanced code organization, easier provider additions, and reduced risk of breaking changes.

2. Centralized Provider Management via LLMManager

  • Change: Added LLMManager, a singleton responsible for loading, managing, and retrieving LLM providers.
  • Impact: All provider-related logic is now managed in one place.
  • Benefits: Simplified provider registration and lookup, making it easier to maintain and extend.

3. Base Provider Class

  • Change: A BaseProvider class was introduced to standardize provider implementation.
  • Impact: All providers extend from BaseProvider, enforcing a consistent contract for API interactions.
  • Benefits: Enforces consistent methods, such as getModelInstance and getDynamicModels, which promotes uniformity across providers.

4. New getDynamicModels Support

  • Change: Providers can now define the getDynamicModels method to retrieve model information dynamically.
  • Impact: Providers can now fetch available models dynamically at runtime.
  • Benefits: Supports real-time updates to available models, enhancing flexibility and responsiveness.

5. Removal of Legacy Code

  • Change: Deprecated provider logic was removed from scattered files.
  • Impact: Reduces technical debt and eliminates duplicate logic.
  • Benefits: Cleaner, more maintainable codebase.

Technical Details

File Structure and Organization

app/lib/modules/llm/
  |- base-provider.ts        // Abstract base class for providers
  |- manager.ts              // LLMManager for centralized provider control
  |- providers/              // Directory containing all provider implementations
      |- dummy-provider.ts    // Example provider file
      |- ... (other providers) 

LLMManager Singleton

  • Responsibilities:
    • Dynamically loads all providers from the providers/ directory.
    • Registers each provider instance and maintains a lookup map for quick access.
    • Exposes methods to retrieve models, default providers, and registered providers.

Simplified Code Example

// manager.ts
class LLMManager {
  private static _instance: LLMManager;
  private _providers: Map<string, BaseProvider> = new Map();

  static getInstance(): LLMManager {
    if (!LLMManager._instance) {
      LLMManager._instance = new LLMManager();
    }
    return LLMManager._instance;
  }

  registerProvider(provider: BaseProvider) {
    this._providers.set(provider.name, provider);
  }

  getProvider(name: string): BaseProvider | undefined {
    return this._providers.get(name);
  }
}

BaseProvider Class

  • Responsibilities:
    • Enforces consistent methods like getModelInstance and getDynamicModels for all providers.
    • Simplifies shared logic for API interactions and model retrieval.

Simplified Code Example

// base-provider.ts
export abstract class BaseProvider {
  abstract name: string;
  abstract getModelInstance(options: { model: string }): any;
  
  // New optional method for dynamic model retrieval
  getDynamicModels?(options: { apiKeys?: Record<string, string>; providerSettings?: any; serverEnv?: Record<string, string> }): Promise<ModelInfo[]> {
    return Promise.resolve([]);
  }

  getProviderBaseUrlAndKey() {
    // Shared logic to retrieve API keys and base URLs
  }
}

Example Provider Implementation: DummyProvider

  • File Location: app/lib/modules/llm/providers/dummy-provider.ts
  • Responsibilities: Implements a Dummy provider, extending BaseProvider, and includes support for dynamic model fetching.

Simplified Code Example

// dummy-provider.ts
import { BaseProvider } from '~/lib/modules/llm/base-provider';
import type { ModelInfo } from '~/lib/modules/llm/types';
import { createOpenAI } from '@ai-sdk/openai';

export class DummyProvider extends BaseProvider {
  name = 'DummyProvider';
  
  async getDynamicModels(options: { apiKeys?: Record<string, string>; providerSettings?: any; serverEnv?: Record<string, string> }): Promise<ModelInfo[]> {
    const apiKey = options.apiKeys?.['DummyProvider'];
    if (!apiKey) throw new Error('API key is missing');
    
    const response = await fetch('https://dummy.api.com/v1/models', {
      headers: { 'Authorization': `Bearer ${apiKey}` }
    });
    const data = await response.json();
    return data.data.map((model: any) => ({
      name: model.id,
      label: model.id,
      provider: this.name,
      maxTokenAllowed: 8000,
    }));
  }
  
  // To be used by the server
  getModelInstance(options: { 
        model: string, serverEnv: Env;
        apiKeys?: Record<string, string>;
        providerSettings?: Record<string, IProviderSetting>; 
    }): LanguageModelV1 {
    
    const { apiKeys, providerSettings, serverEnv, model } = options;
    
    const { baseUrl, apiKey } = this.getProviderBaseUrlAndKey({
      apiKeys,
      providerSettings,
      serverEnv: serverEnv as any,
      defaultBaseUrlKey: 'DUMMY_API_BASE_URL',
      defaultApiTokenKey: '',
    });
    
    // create a model instance
    return createOpenAI({ baseURL, apiKey })(options.model);
  }
}

System Changes

  • Infrastructure: No changes required.
  • Integration Points: No changes required.
  • Performance: Improved performance due to a more modular and efficient lookup system.

Testing

  • Unit Tests: New unit tests have been added for LLMManager, BaseProvider, and individual providers.
  • Integration Tests: End-to-end tests validate provider functionality.

Migration Impact

  • Breaking Changes: None.
  • Required Updates: None for consumers of the system, but provider logic was relocated.
  • Migration Steps: None required.
  • Backward Compatibility: Fully backward compatible.

Future Improvements

  • Additional Provider Support: New providers can now be added easily without major refactoring.
  • Dynamic Provider Loading: Consider auto-loading providers from an external configuration file.
  • Improved Error Handling: Enhance error reporting for failed provider registration.

@thecodacus thecodacus changed the title refactor: LLM Providers Refactor: Adapting Modular Approach refactor: refactored LLM Providers Refactor: Adapting Modular Approach Dec 19, 2024
@thecodacus thecodacus marked this pull request as ready for review December 19, 2024 08:51
Copy link
Collaborator

@mrsimpson mrsimpson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great PR! Thanks!

I was just implementing a test-provider and got really annoyed by the current architecture.

I have not tested it yet, but a registry is definitely the way to go!

The only thing I'm a bit nervous about is that we still have a mix of frontend related information (like the model names) and configuration which should be available to the server only (for hiding credentials).
Currently, it's not possible to define both with env variables.
But this is actually not the core of this PR (and this PR will make it easier to add such mechanisms), so I don't see this as a blocker.

@thecodacus thecodacus changed the title refactor: refactored LLM Providers Refactor: Adapting Modular Approach refactor: refactored LLM Providers: Adapting Modular Approach Dec 19, 2024
@aliasfoxkde
Copy link
Collaborator

Just tested it and I like the code and the direction..

Some thoughts though:

  • Should we include a template.ts file for adding providers easier (or something) (or will this be taken care of somewhere else)?
  • Like it would be nice to keep the average user from needing to modify code and whatnot.
  • Why so many levels deep? Consider moving to ./app/modules/providers (normal users will be possibly modifying this)?

It doesn't appear that any of the providers are "dynamic" getting models:
image
Note: It appears that only the static models are loading.

Though they do seem to be getting loaded in (unless I'm not understanding when this happens)?
image

And this is kind of weird (blanks at the end of the list):
image

@thecodacus
Copy link
Collaborator Author

thecodacus commented Dec 19, 2024

  • Should we include a template.ts file for adding providers easier (or something) (or will this be taken care of somewhere else)?

better we add the steps to the docs

Why so many levels deep? Consider moving to ./app/modules/providers (normal users will be possibly modifying this)?

the idea is to modularize everything.
so llm folder is one module, which holds the loading provider code and base providers
and providers folder is where only the dynamic codes will reside. so we have llm->providers

and the modules folder holds parent for all future modules not just llm modules,

Note: It appears that only the static models are loading.

Though they do seem to be getting loaded in (unless I'm not understanding when this happens)?

its only loading the code file not the model dynamic models at the moment. so the provider code files are dynamically loaded at runtime.. the manager looks for files in that folder and looks into the code and checks for a Provider in that file.

so you just add the file and the manager will automatically detects that and loads it.

but the model loading happens after you opens the webpage

@thecodacus
Copy link
Collaborator Author

It doesn't appear that any of the providers are "dynamic" getting models:

I haven't added any dynamic model loading for any provider except ollama,lmstudio, openrouter, and togatherai
these already had dynamic model loading, and I am not trying to add/update model providers right now but only the refactoring

@thecodacus thecodacus merged commit 7295352 into stackblitz-labs:main Dec 21, 2024
3 checks passed
@thecodacus thecodacus added this to the v0.0.4 milestone Dec 25, 2024
przbadu added a commit to przbadu/bolt.diy that referenced this pull request Jan 6, 2025
* Fixed console error for SettingsWIndow & Removed ts-nocheck where not needed

* fix: added wait till terminal prompt for bolt shell execution

* removed logs

* add/toc-for-readme

added a TOC for the README file, renamed some headings to better suite the TOC

* Update README.md

* feat: added terminal error capturing and automated fix prompt

* add: xAI grok-2-1212 model

* feat: Data Tab

Removed Chat History Tab
Added Data Tab
Data tab can export and delete chat history, import API keys, import and export settings

* Update DataTab.tsx

* feat: improved providers list style

made the list 2 columns wide and separate out the experimental providers

* fixed API Key import

* updated styling wordings and animations icons

* updated title header

* docs: updated setup guide to have more detailed instructions

* updated some text

* chore: update commit hash to 95dbcf1

* chore: update commit hash to de64007

* chore: update commit hash to 381d490

* chore: update commit hash to a53b10f

* Update ProvidersTab.tsx

* Update ProvidersTab.tsx

* chore: update commit hash to 75ec49b

* docs: updated style in faq

updated style in FAQ docs to be an accordion like style
added a TOC to the index page in the docs

* chore: update commit hash to 636f87f

* docs: updated Contributing

updated Contributing in the docs
updated Contributing and FAQ in the GitHub part as well

* docs: added info on updating using docker

Added docker-compose --profile development up --build  to the update section

* docs: added info on the Releases Page

Added the option to download from the Releases Page instead of git clone in the README

* docs: added info on both ways to set api keys

* chore: update commit hash to ab5cde3

* refactor: updated vite config to inject add version metadata into the app on build (stackblitz-labs#841)

* refactor: removes commit.json and used vite.config to load these variables

* updated precommit hook

* updated the pre start script

* updated the workflows

* ci: updated the docs ci to only trigger if any files changed in the docs folder (stackblitz-labs#849)

* docs: updated download link (stackblitz-labs#850)

* fix: add Message Processing Throttling to Prevent Browser Crashes (stackblitz-labs#848)

* fix hotfix for version metadata issue (stackblitz-labs#853)

* refactor:  refactored LLM Providers: Adapting Modular Approach (stackblitz-labs#832)

* refactor: Refactoring Providers to have providers as modules

* updated package and lock file

* added grok model back

* updated registry system

* ignored alert on project reload

* updated read me

* fix: provider menu dropdown fix (ghost providers) (stackblitz-labs#862)

* better osc code cleanup

* fix: ollama provider module base url hotfix for docker (stackblitz-labs#863)

* fix: ollama base url hotfix

* cleanup logic

* docs: updated env.example of OLLAMA & LMSTUDIO base url (stackblitz-labs#877)

* correct OLLAMA_API_BASE_URL

* correct OLLAMA_API_BASE_URL

* correct OLLAMA_API_BASE_URL

* fix: check for updates does not look for commit.json now (stackblitz-labs#861)

* feat: add Starter template menu in homepage (stackblitz-labs#884)

* added icons and component

* updated unocss to add dynamic icons

* removed temp logs

* updated readme

* feat: catch errors from web container preview and show in actionable alert so user can send them to AI for fixing (stackblitz-labs#856)

* Catch errors from web container

* Show fix error popup on errors in preview

* Remove unneeded action type

* PR comments

* Cleanup urls in stacktrace

---------

Co-authored-by: Anirban Kar <thecodacus@gmail.com>

* ci: improved change-log generation script and cleaner release ci action (stackblitz-labs#896)

* build: improved-changelog

* added a better change log script

* improved changelog script

* improved change log script

* fix: detect and remove markdown block syntax that llms sometimes hallucinate for file actions (stackblitz-labs#886)

* Clean out markdown syntax

* Remove identation removal

* Improve for streaming

* feat: redact file contents from chat and put latest files into system prompt  (stackblitz-labs#904)

* feat: added Automatic Code Template Detection And Import (stackblitz-labs#867)

* initial setup

* updated template list

* added optional switch to control this feature

* removed some logs

* fix: import folder filtering

* fix: add defaults for LMStudio to work out of the box (stackblitz-labs#928)

* feat: added hyperbolic llm models (stackblitz-labs#943)

* Added Hyperbolic Models

* Fix: Fixed problem in connecting with hyperbolic models

* added dynamic models for hyperbolic

* removed logs

* fix: refresh model list after api key changes (stackblitz-labs#944)

* fix: better model loading ui feedback and model list update (stackblitz-labs#954)

* fix: better model loading feedback and model list update

* added load on providersettings  update

* fix: updated logger and model caching minor bugfix #release (stackblitz-labs#895)

* fix: updated logger and model caching

* usage token stream issue fix

* minor changes

* updated starter template change to fix the app title

* starter template bigfix

* fixed hydretion errors and raw logs

* removed raw log

* made auto select template false by default

* more cleaner logs and updated logic to call dynamicModels only if not found in static models

* updated starter template instructions

* browser console log improved for firefox

* provider icons fix icons

* chore: release version 0.0.4

* fix: hotfix auto select starter template works without github token #release (stackblitz-labs#959)

* fix: hotfix starter template fix, updated header link to use navigate

* template auth fix

* updated changelog script

* chore: release version 0.0.5

* fix: show warning on starter template failure and continue (stackblitz-labs#960)

* Update hyperbolic.ts

Changed updated Hyperbolic Settings link

* fix: introduce our own cors proxy for git import to fix 403 errors on isometric git cors proxy (stackblitz-labs#924)

* Exploration of improving git import

* Fix our own git proxy

* Clean out file counting for progress, does not seem to work well anyways

* fix: git private clone with custom proxy (stackblitz-labs#1010)

* cookie fix

* fix: git private clone with custom proxy

* list -fix

* docs: updating copyright in LICENSE (stackblitz-labs#796)

* fix: added XAI to docker config (stackblitz-labs#274)

* commit

* Create .env.example

* Update docker-compose.yaml

---------

Co-authored-by: Anirban Kar <thecodacus@gmail.com>

* ci: docker Image creation pipeline (stackblitz-labs#1011)

* Create docker.yaml

* Add build target

* Use build target var

* Use github token instead

---------

Co-authored-by: kris1803 <kristiansstraume17@gmail.com>
Co-authored-by: Anirban Kar <thecodacus@gmail.com>
Co-authored-by: Dustin Loring <dustinwloring1988@gmail.com>
Co-authored-by: GK <gokul@aospa.co>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Cole Medin <cole@dynamous.ai>
Co-authored-by: Eduard Ruzga <wonderwhy.er@gmail.com>
Co-authored-by: Alex Parker <128879861+Soumyaranjan-17@users.noreply.github.com>
Co-authored-by: Juan Manuel Campos Olvera <juan4106@hotmail.com>
Co-authored-by: Arsalaan Ahmed <147995884+Ahmed-Rahil@users.noreply.github.com>
Co-authored-by: Gaurav-Wankhede <73575353+Gaurav-Wankhede@users.noreply.github.com>
Co-authored-by: Siddarth <pullakhandam.siddartha@gmail.com>
Co-authored-by: twsl <45483159+twsl@users.noreply.github.com>
JJ-Dynamite pushed a commit to val-x/valenClient that referenced this pull request Jan 29, 2025
…litz-labs#832)

* refactor: Refactoring Providers to have providers as modules

* updated package and lock file

* added grok model back

* updated registry system
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants