Skip to content

Conversation

josefarias
Copy link

@josefarias josefarias commented Sep 28, 2025

What this does

This PR adds Replicate as a provider.

Replicate may stand out from other providers in several areas:

  • They don’t expose pricing via API (that I can tell). I’ve opted for not including pricing for now.
  • Different users/companies will name their Replicate models using different schemes. So it’s hard to come up with a general pattern to identify models of the same family. I’ve opted not to include model families for now.
  • Replicate mostly wants to you send them an HTTP request and either wait for them to ping you back via webhook, or poll until the result is ready.
    • This led me to propose a new class: RubyLLM::DeferredImage, whose method #to_blob will return nil if the image isn’t ready, or binary data if it is.
    • I’m also proposing we add RubyLLM.image_from, which knows how to fetch an image when it’s ready. This is useful because some providers, like Replicate, require authentication to fetch the finished result.
  • Replicate models have drastically different input signatures. I’ve opted to allow arbitrary kwargs in calls to RubyLLM.paint—we’ll forward any unrecognized params to {ProviderInstance}#paint.
    • I folded the existing size kwarg into these arbitrary kwargs to keep uniformity across providers.
  • Replicate’s API is quite flexible, offering a sync mode (vs the default async mode with webhooks/polling), a streaming option, and a number of models with drastically different capabilities. In order to keep things revieweable and completable with reasonable effort, I’ve opted to limit this PR to the default async mode, and to text-to-image models.
    • Including the rest of the functionality should be fairly manageable as follow-up PRs.

Please do let me know if any of the assumptions above don’t hold up or you’d like to do things differently.

Thanks for reading!

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Performance improvement

Scope check

  • I read the Contributing Guide
  • This aligns with RubyLLM's focus on LLM communication
  • This isn't application-specific logic that belongs in user code
  • This benefits most users, not just my specific use case

Quality check

  • I ran overcommit --install and all hooks pass
  • I tested my changes thoroughly
    • For provider changes: Re-recorded VCR cassettes with bundle exec rake vcr:record[provider_name]
    • All tests pass: bundle exec rspec
  • I updated documentation if needed
  • I didn't modify auto-generated files manually (models.json, aliases.json)

API changes

  • Breaking change
  • New public methods/classes
  • Changed method signatures
  • No API changes

Related issues

Closes #410

config.around do |example|
cassette_name = example.full_description.parameterize(separator: '_').delete_prefix('rubyllm_')
VCR.use_cassette(cassette_name) do
VCR.use_cassette(cassette_name, record: :new_episodes) do
Copy link
Author

@josefarias josefarias Oct 4, 2025

Choose a reason for hiding this comment

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

This is so existing stubs are kept and only new HTTP interactions are recorded onto the existing cassette. Makes it easier to contribute if you don’t have API keys for other providers. Specially for cassettes related to model lists like spec/fixtures/vcr_cassettes/models_refresh_updates_models_and_returns_a_chainable_models_instance.yml

Copy link
Author

Choose a reason for hiding this comment

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

I had to patch the models.rake update logic to append the replicate models to existing ones (patch not committed). Without this patch, the whole file would get overwritten when regenerating and models would be missing, seeing as I don’t have credentials for all supported providers.

I believe this worked for regenerating the models.json and aliases.json files, but I don’t think it worked well for this file. I’m a bit lost as to how to solve this, any guidance is appreciated. 🙏

@josefarias josefarias force-pushed the add-replicate-provider branch from ac7a529 to 5dc8df3 Compare October 4, 2025 22:51
Current replicate models output images

Drop 'replicate' prefix from model names

Force array on replicate webhook filters

Run models rake tasks

I hacked the models refresh logic to append the replicate models to existing ones.
Without this patch, the whole file would get overwritten and models would be missing,
seeing as I don’t have credentials for all supported providers.

Fold image size into model params

Record model list cassettes

Add tests

Return DeferredImage

Poll in deferred image tests

Imageable -> Blobbable

Remove unnecessary test

No need to set `@model_id`

Update docs

Typo
@josefarias josefarias force-pushed the add-replicate-provider branch from 5dc8df3 to f46795d Compare October 4, 2025 23:01
prediction = @connection.get(url).body
return unless prediction['status'] == 'succeeded'

image_url = Array(prediction['output']).first
Copy link
Author

@josefarias josefarias Oct 4, 2025

Choose a reason for hiding this comment

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

Some Replicate models return multiple images as an array, others just return a single URI.

Saving the RubyLLM::DeferredImage only supports a single file. The multiple files use case deviates too much from the RubyLLM::Image functionality. If that behavior is needed users should go the webhook route instead, as suggested in the docs.

@josefarias josefarias marked this pull request as ready for review October 4, 2025 23:06
@josefarias josefarias mentioned this pull request Oct 4, 2025
6 tasks
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.

[FEATURE] Support for Replicate
1 participant