Skip to content

fix: make catwalk service dependency optional - #689

Closed
jamestrew wants to merge 1 commit into
charmbracelet:mainfrom
jamestrew:fix-catwalk-dependency
Closed

jamestrew wants to merge 1 commit into
charmbracelet:mainfrom
jamestrew:fix-catwalk-dependency

Conversation

@jamestrew

@jamestrew jamestrew commented Aug 9, 2025 •

Copy link
Copy Markdown
Contributor

Describe your changes

If the catwalk service is unreachable, even if you provide your own provider(s) config, crush will fail with "failed to load providers".

Allow crush to work when catwalk service is unreachable if custom providers are configured. Falls back gracefully to user-configured providers only, with clear error when neither catwalk nor custom providers are available.

Related issue/discussion: #453

Checklist before requesting a review

Allow crush to work when catwalk service is unreachable if custom
providers are configured. Falls back gracefully to user-configured
providers only, with clear error when neither catwalk nor custom
providers are available.
@charmcli

charmcli commented Aug 9, 2025 •

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@jamestrew

Copy link
Copy Markdown
Contributor Author

I have read the Contributor License Agreement (CLA) and hereby sign the CLA.

@jamestrew jamestrew mentioned this pull request Aug 15, 2025
@bbrodriges

Copy link
Copy Markdown
Contributor

@andreynering @caarlos0 @kujtimiihoxha could you accept this PR? IMO it is crucial for offline usage and/or potential corporate restrictions

@billysan

Copy link
Copy Markdown

please approve

@nemo-cpt

nemo-cpt commented Aug 21, 2025 •

Copy link
Copy Markdown

I encountered the same issue and tried to fix it like you do, but I still saw "failed to load providers" and some other issue in TUI because Providers() function is called directly from some places.

So I suggest considering following improvement over your useful solution to be able completely suppress catwalk access via config.

--- internal/config/config.go      (index)
+++ internal/config/config.go      (working tree)
@@ -142,6 +142,7 @@ type Options struct {
        Debug                bool        `json:"debug,omitempty" jsonschema:"description=Enable debug logging,default=false"`
        DebugLSP             bool        `json:"debug_lsp,omitempty" jsonschema:"description=Enable debug logging for LSP servers,default=false"`
        DisableAutoSummarize bool        `json:"disable_auto_summarize,omitempty" jsonschema:"description=Disable automatic conversation summarization,default=false"`
+       DisableCatwalk       bool        `json:"disable_catwalk,omitempty" jsonschema:"description=Disable loading providers from catwalk,default=false"`
        DataDirectory        string      `json:"data_directory,omitempty" jsonschema:"description=Directory for storing application data (relative to working directory),default=.crush,example=.crush"` // Relative to the cwd
 }

--- internal/config/load.go        (index)
+++ internal/config/load.go        (working tree)
@@ -67,11 +67,13 @@ func Load(workingDir string, debug bool) (*Config, error) {
        )

	// Load known providers, this loads the config from catwalk
	// If catwalk is unavailable, continue with empty known providers and rely on user config
-       providers, err := Providers()
+       providers, err := ProvidersWithConfig(cfg)
       if err != nil {
               slog.Warn("Failed to load providers from catwalk service, continuing with user-configured providers only", "error", err)
               providers = []catwalk.Provider{}
        }
       cfg.knownProviders = providers

        env := env.New()
        // Configure providers
--- internal/config/provider.go    (index)
+++ internal/config/provider.go    (working tree)
@@ -75,6 +75,15 @@ func loadProvidersFromCache(path string) ([]catwalk.Provider, error) {
 }

 func Providers() ([]catwalk.Provider, error) {
+       return ProvidersWithConfig(Get())
+}
+
+func ProvidersWithConfig(cfg *Config) ([]catwalk.Provider, error) {
+       // Check if catwalk is disabled in config
+       if cfg != nil && cfg.Options.DisableCatwalk {
+               return []catwalk.Provider{}, nil
+       }
        catwalkURL := cmp.Or(os.Getenv("CATWALK_URL"), defaultCatwalkURL)
        client := catwalk.NewWithURL(catwalkURL)
        path := providerCacheFileData()

@kujtimiihoxha

Copy link
Copy Markdown
Contributor

I think what we need to do here is make catwalk optional by exposing the embedded json config from the catwalk package. The idea of catwalk is that users will get the latest updates without having to update crush, but we could fallback to the embedded json if catwalk is not reachable.

@tazjin

tazjin commented Aug 21, 2025

Copy link
Copy Markdown
Contributor

@kujtimiihoxha It'd be useful (but not critical) to be able to disable it completely. I only use self-hosted models, and all the other stuff in the list is noise.

@jamestrew

Copy link
Copy Markdown
Contributor Author

I encountered the same issue and tried to fix it like you do, but I still saw "failed to load providers" and some other issue in TUI because Providers() function is called directly from some places.

So I suggest considering following improvement over your useful solution to be able completely suppress catwalk access via config.

--- internal/config/config.go      (index)
+++ internal/config/config.go      (working tree)
@@ -142,6 +142,7 @@ type Options struct {
        Debug                bool        `json:"debug,omitempty" jsonschema:"description=Enable debug logging,default=false"`
        DebugLSP             bool        `json:"debug_lsp,omitempty" jsonschema:"description=Enable debug logging for LSP servers,default=false"`
        DisableAutoSummarize bool        `json:"disable_auto_summarize,omitempty" jsonschema:"description=Disable automatic conversation summarization,default=false"`
+       DisableCatwalk       bool        `json:"disable_catwalk,omitempty" jsonschema:"description=Disable loading providers from catwalk,default=false"`
        DataDirectory        string      `json:"data_directory,omitempty" jsonschema:"description=Directory for storing application data (relative to working directory),default=.crush,example=.crush"` // Relative to the cwd
 }

--- internal/config/load.go        (index)
+++ internal/config/load.go        (working tree)
@@ -67,11 +67,13 @@ func Load(workingDir string, debug bool) (*Config, error) {
        )

	// Load known providers, this loads the config from catwalk
	// If catwalk is unavailable, continue with empty known providers and rely on user config
-       providers, err := Providers()
+       providers, err := ProvidersWithConfig(cfg)
       if err != nil {
               slog.Warn("Failed to load providers from catwalk service, continuing with user-configured providers only", "error", err)
               providers = []catwalk.Provider{}
        }
       cfg.knownProviders = providers

        env := env.New()
        // Configure providers
--- internal/config/provider.go    (index)
+++ internal/config/provider.go    (working tree)
@@ -75,6 +75,15 @@ func loadProvidersFromCache(path string) ([]catwalk.Provider, error) {
 }

 func Providers() ([]catwalk.Provider, error) {
+       return ProvidersWithConfig(Get())
+}
+
+func ProvidersWithConfig(cfg *Config) ([]catwalk.Provider, error) {
+       // Check if catwalk is disabled in config
+       if cfg != nil && cfg.Options.DisableCatwalk {
+               return []catwalk.Provider{}, nil
+       }
        catwalkURL := cmp.Or(os.Getenv("CATWALK_URL"), defaultCatwalkURL)
        client := catwalk.NewWithURL(catwalkURL)
        path := providerCacheFileData()

I'll take a closer look at this but in my opinion, users shouldn't have to set some config option to bypass catwalk access.

If a working provider config is given, catwalk should be entirely optional by default without additional configuration.

@jamestrew

Copy link
Copy Markdown
Contributor Author

@kujtimiihoxha It'd be useful (but not critical) to be able to disable it completely. I only use self-hosted models, and all the other stuff in the list is noise.

To add to this, I'm sure many who can't access catwalk (due to corporate firewall restrictions) won't be able to access anything but their self-hosted provider.

@andreynering

Copy link
Copy Markdown
Member

I just opened this PR with a mode advanced implementation of this.

@andreynering

Copy link
Copy Markdown
Member

Closing in favor of #866

@jamestrew
jamestrew deleted the fix-catwalk-dependency branch September 11, 2025 00:02
@meowgorithm

Copy link
Copy Markdown
Member

Alrighty, provider auto-updates can now be disabled in thew new Crush v0.8.0. Details are in the README.

Enjoy, and let us know if you have any questions!

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.

9 participants