Skip to content

Conversation

@trmitchell7
Copy link

  • Implement ReplicateBlock with model selection and dynamic inputs via OpenAPI schemas.
  • Add tools/replicate for prediction creation, with sync/async modes.
  • Enhance lib/response-format.ts for schema dereferencing and parsing (reusable for future integrations).
  • Add API routes for schema fetching and collections.
  • Include Vitest unit tests for schema utils and param coercion.
  • Manual testing with models like flux-schnell and llama-3-70b.
  • Aspirational abstractions (e.g., hooks, dynamic inputs) for other OpenAPI providers, but not fully validated.

Branch Stats: 23 commits, 294 files, +43,268 lines, -8,957 lines
Actual Replicate Code: 4 commits, 23 files, ~3,500 lines
Everything Else: Upstream features we rebased through
Lessons Learned: Priceless

Executive Summary

This PR is a masterclass in how enthusiasm + best intentions + "let's do it right" can create a beautifully complex, technically functional, architecturally ambitious, completely unmergeable pull request.

What Works: Replicate integration, dynamic inputs, schema parsing, type coercion
What's Problematic: We built for three future providers that don't exist yet
Recommendation: Extract the good parts, delete the aspirations, ship something simple


Lol this PR roasted by Claude when asked to review:

"The best way to learn is to make mistakes. The best way to make epic mistakes is to over-engineer."
"We chose the path of epic mistakes. And now we're wiser for it."

This PR: A monument to enthusiasm, a testament to learning, and a reminder that sometimes the best code is the code you refactor into simplicity.
Don't merge this. Learn from it. Then build it right.

This PR's value isn't in merging it - it's in what I learned building it.

What I Learned:

  • ✅ How OpenAPI schema parsing should work
  • ✅ Where type coercion belongs (UI, not block)
  • ✅ How to build dynamic form components
  • ✅ When to stop abstracting (before we start)
  • ✅ Why YAGNI is a principle, not a suggestion

What This Ships:

  • 🎁 A working reference implementation
  • 🎁 Tested utilities that ARE reusable
  • 🎁 Knowledge about Replicate's API quirks
  • 🎁 An example of what NOT to do (equally valuable)

What I Didn't Ship:

  • ❌ A clean, mergeable PR
  • ❌ Something that follows Sim's patterns
  • ❌ Code that's simpler than the problem it solves

Next Time:

  • Build for one use case
  • Ship it
  • Get feedback
  • Iterate

What Was Built

The Core (Actually Decent)

Replicate Integration (~1,400 lines)

  • blocks/blocks/replicate.ts (274 → 141 after refactoring)
  • tools/replicate/create_prediction.ts (289 → 361 after refactoring)
  • tools/replicate/types.ts (73 lines)
  • app/api/replicate/* (3 routes, 374 lines)

Verdict: ✅ Clean, well-structured, follows Sim patterns (after refactoring)

The "Generic" Layer (Aspirationally Useful)

Schema Utilities (~355 lines)

  • lib/schemas/openapi-to-fields.ts
  • Parsing, type coercion, validation, field inference
  • Actually zero Replicate-specific code
  • Well-tested (22 tests, all passing)

Verdict: ✅ Genuinely reusable. This one gets a pass.

Hooks (~548 lines)

  • use-replicate-schema.ts (222 lines)
  • use-replicate-collections.ts (158 lines)
  • use-replicate-collection-models.ts (168 lines)
  • Named honestly, documented as Replicate-only
  • But have "generic" parameters that are unused

Verdict: Honest naming, confused implementation. Should either be truly generic OR remove unused params.

The Monolith (Needs Refactoring)

OpenApiDynamicInputs (~1,057 lines including tests)

  • Component: 833 lines
  • Tests: 224 lines (22 tests passing)
  • Responsibilities: Model selection + Schema fetching + Form rendering + State management

What it claims:

/**
 * GENERIC COMPONENT: Renders dynamic form inputs based on OpenAPI/JSON Schema
 */

The Disconnect: Generic components don't import provider-specific dependencies.

What MCP does right: mcp-dynamic-args.tsx (550 lines) - Honestly MCP-specific, does ONE thing (render args), no false promises.

Verdict: Needs to either:

  • Be truly generic (accept hooks as props, rename to SchemaFormRenderer)
  • OR be honestly specific (rename to ReplicateInputs, remove generic claims)

The Shared Primitives (Accidental Win!)

Input Components (~316 lines)

  • input-with-tags.tsx (153 lines)
  • textarea-with-tags.tsx (163 lines)
  • Tag support for environment variables
  • Used by: Replicate block (ready for MCP Block)

Verdict: ✅ Actually reusable! Sometimes you trip into good architecture.


Why this diff is a mess:

Replicate-Specific (23 files, ~3,500 lines)
The Rebase Collateral (271 files, ~30,800 lines)

Connection to Replicate: None. We just happened to rebase through major infrastructure work.
Impact: Noise in the diff for no reason...
What we should have done: Keep branch focused, rebase right before merge (not during development).

The Good Parts (Worth Keeping/referencing)

1. Schema Parser (lib/schemas/openapi-to-fields.ts)

Lines: 355
Quality: High
Reusability: Actual
Tests: 22 (comprehensive)
Replicate-specific code: 0

Recommendation: Extract this into its own util PR. It's the MVP.

2. Shared Input Primitives

Lines: 316
Quality: High
Already reused by: MCP block
Side effect: We improved MCP block (intentional refactor)

Recommendation: Keep. These are legitimately shared components.

3. Replicate Tool

Lines: 361
Quality: High
Complexity: Appropriate
Logging: Good
Error handling: Comprehensive

Recommendation: Keep with minor tweaks (console.error → logger in API routes).

4. Test Coverage

Lines: 224
Tests: 22
Pass rate: 100%
Coverage: Schema parsing, type coercion, field grouping, validation

Missing: Integration tests, API route tests, hook tests

Recommendation: Keep tests, add integration coverage for critical paths.


The Problematic Parts (Needs Rethinking)

1. The 833-Line Component

Problem: Does too many things.

Solution: Split into:

  • ModelSelector - Standalone, reusable
  • SchemaForm - Provider-agnostic form renderer
  • useProviderSchema - Configurable hook
  • ReplicateInputs - Composition of above

Benefit: Each piece is testable, reusable, understandable.

2. Three Hooks For One Provider

Problem: Granularity without benefit.

Current:

const { collections } = useReplicateCollections({ ... })
const { models } = useReplicateCollectionModels({ ... })
const { schema } = useReplicateSchema({ ... })

Alternative:

const { collections, models, schema } = useReplicateModelData({
  apiKey, model, collection
})
// One hook, three responsibilities, clear ownership

Benefit: Less import overhead, easier mocking in tests, clear data flow.

3. Unused Generic Parameters

Current:

useReplicateSchema({
  endpoint: '/api/replicate/models',  // Always this value
  apiKeyHeaderName: 'x-replicate-api-key',  // Always this value
  ...
})

Problem: Parameters that are always the same aren't parameters.

Solution: Hardcode them. Delete the params. Simplify the interface.


What Should Have Happened (The Parallel Universe Version)

The Simple Path (Actually Shippable)

PR: "feat: Add Replicate block"

Files Changed: 12
Lines Added: ~1,800
Review Time: 1 hour
Merge Confidence: High

Contents:

  • Block config (150 lines) - Simple, boring, works
  • Tool implementation (350 lines) - Straightforward API wrapper
  • API route for schema (150 lines) - Caching, error handling
  • Replicate-specific form component (400 lines) - Does one thing well
  • Schema parser utility (355 lines) - Genuinely reusable
  • Input primitives (316 lines) - Shared with MCP
  • Tests (200 lines) - Coverage for critical paths

Trade-offs:

  • No model selector dropdown (users paste model names - this is fine!)
  • No collection browsing (check Replicate docs - also fine!)
  • Not generic (wait for second provider - definitely fine!)

Benefits:

  • Focused, reviewable, shippable
  • Follows existing patterns (MCP, Gmail blocks)
  • No "aspirational" abstractions
  • Actually works

The Proper Generic Path (When You Have 2+ Providers)

After implementing HuggingFace/Bedrock:

PR: "refactor: Extract common OpenAPI utilities"

What you'd discover:

  • HuggingFace uses model IDs (not owner/name)
  • Bedrock uses ARNs (not owner/name)
  • Each has different auth headers
  • Collections are Replicate-specific
  • Schema formats vary subtly

What you'd extract:

  • SchemaFormRenderer (truly generic - just needs schema)
  • parseOpenApiSchema (already done!)
  • coerceValue (already done!)

What you'd keep separate:

  • Model selection UI (too provider-specific)
  • Schema fetching (different endpoints/formats)
  • Authentication (different header patterns)

Replicate-block-flow

- Implement ReplicateBlock with model selection and dynamic inputs via OpenAPI schemas.
- Add tools/replicate for prediction creation, with sync/async modes.
- Enhance lib/response-format.ts for schema dereferencing and parsing (reusable for future integrations).
- Add API routes for schema fetching and collections.
- Include Vitest unit tests for schema utils and param coercion.
- Manual testing with models like flux-schnell and llama-3-70b.
- Aspirational abstractions (e.g., hooks, dynamic inputs) for other OpenAPI providers, but not fully validated.
@vercel
Copy link

vercel bot commented Nov 1, 2025

@trmitchell7 is attempting to deploy a commit to the Sim Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

This PR implements a working Replicate integration with ambitious aspirations for future OpenAPI providers. The core implementation is solid, but architectural over-engineering creates complexity that may hinder future maintenance.

Key Changes

Core Replicate Integration (Well-executed)

  • ReplicateBlock config with dynamic inputs and model selection
  • create_prediction tool with sync/async modes, proper endpoint routing, and comprehensive error handling
  • 3 API routes for schema fetching, collections, and collection models with environment variable resolution
  • New retry utility with exponential backoff

Genuinely Reusable Utilities (Excellent additions)

  • lib/schemas/openapi-to-fields.ts - Schema parsing, type coercion, validation (22 passing tests)
  • lib/response-format.ts - dereferenceSchema() for OpenAPI $ref/allOf resolution
  • input-with-tags.tsx and textarea-with-tags.tsx - Shared input components with env var support
  • lib/api/retry.ts - Configurable retry logic

Architectural Over-Engineering (Needs refactoring)

  • OpenApiDynamicInputs component (833 lines) handles too many concerns simultaneously
  • Claims to be "GENERIC COMPONENT" but imports Replicate-specific hooks directly
  • Three separate hooks with unused generic parameters that are always passed the same values
  • Block config couples to specific component type openapi-dynamic-inputs

What Works Well

  1. Tool implementation - Clean separation, excellent logging, proper type handling
  2. Schema utilities - Truly provider-agnostic, well-tested, genuinely reusable
  3. API routes - Solid error handling, caching, environment variable resolution
  4. Type safety - Comprehensive TypeScript types throughout
  5. Documentation - Honest about limitations (refreshing self-awareness in comments)

Architectural Issues

  1. Premature abstraction - Generic parameters/interfaces designed for 3 future providers that don't exist
  2. Component size - 833-line component should be split into smaller, focused pieces
  3. False genericity - "Generic" components importing provider-specific dependencies
  4. YAGNI violations - Building for future use cases without validation

Author's Self-Assessment is Accurate

The PR description demonstrates excellent self-awareness: this IS over-engineered, it DOES have aspirational abstractions not validated by real use, and it WOULD benefit from simplification. The author correctly identifies that value lies in learning and reference implementation, not in merging as-is.

Confidence Score: 3/5

  • Safe to merge functionally but carries technical debt that complicates future maintenance and refactoring.
  • Score reflects tension between solid implementation quality and architectural over-engineering. Core functionality (block, tool, API routes, utilities) is well-built with proper error handling and logging. However, architectural choices add complexity without proven benefit: 833-line "generic" component with hard-coded Replicate dependencies, three hooks with unused parameters, and premature abstractions for providers that don't exist. Code works and is well-tested, but will be harder to maintain and refactor than necessary. Not a 4 because architecture needs rethinking; not a 2 because implementation quality is genuinely good.
  • Primary attention needed: openapi-dynamic-inputs.tsx (833 lines, multiple concerns). Secondary: the three Replicate hooks have unused generic parameters that should either be removed or justified with a second provider implementation.

Important Files Changed

File Analysis

Filename Score Overview
apps/sim/tools/replicate/create_prediction.ts 5/5 Clean tool implementation with excellent error handling, logging, and type safety. Properly handles endpoint routing and input validation.
apps/sim/lib/schemas/openapi-to-fields.ts 5/5 Well-designed, truly reusable schema parsing utilities with comprehensive type coercion and validation. Excellent documentation and test coverage.
apps/sim/blocks/blocks/replicate.ts 4/5 Well-structured block config with clear best practices. Uses openapi-dynamic-inputs type which couples it to a large component.
apps/sim/hooks/use-replicate-schema.ts 4/5 Well-implemented hook with debouncing, abort handling, and error recovery. Honest documentation about being Replicate-specific despite generic parameters.
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/openapi-dynamic-inputs/openapi-dynamic-inputs.tsx 3/5 Large 833-line component handling model selection, schema fetching, and form rendering. Claims to be generic but imports Replicate-specific hooks directly. Needs architectural refactoring to be truly reusable.
apps/sim/app/api/replicate/models/[owner]/[name]/schema/route.ts 5/5 Well-implemented API route with proper auth, environment variable resolution, caching, and error handling. Uses dereferenceSchema utility effectively.
apps/sim/lib/response-format.ts 5/5 Added robust dereferenceSchema function for OpenAPI $ref and allOf handling. Well-documented, handles cycles, genuinely reusable.

Sequence Diagram

sequenceDiagram
    participant User
    participant ReplicateBlock
    participant OpenApiDynamicInputs
    participant Hooks as Replicate Hooks
    participant API as API Routes
    participant Replicate as Replicate API
    participant Tool as create_prediction Tool

    User->>ReplicateBlock: Configure API key & model
    ReplicateBlock->>OpenApiDynamicInputs: Render with modelSelector config
    
    Note over OpenApiDynamicInputs: Model Selection Phase
    OpenApiDynamicInputs->>Hooks: useReplicateCollections(apiKey)
    Hooks->>API: GET /api/replicate/collections
    API->>Replicate: GET /v1/collections
    Replicate-->>API: Collection list
    API-->>Hooks: Transformed collections
    Hooks-->>OpenApiDynamicInputs: Display collections dropdown
    
    User->>OpenApiDynamicInputs: Select collection
    OpenApiDynamicInputs->>Hooks: useReplicateCollectionModels(collection)
    Hooks->>API: GET /api/replicate/collections/{slug}
    API->>Replicate: GET /v1/collections/{slug}
    Replicate-->>API: Models in collection
    API-->>Hooks: Model list
    Hooks-->>OpenApiDynamicInputs: Display models dropdown
    
    User->>OpenApiDynamicInputs: Select model (owner/name)
    
    Note over OpenApiDynamicInputs: Schema Fetching Phase
    OpenApiDynamicInputs->>Hooks: useReplicateSchema(model, version, apiKey)
    Hooks->>API: GET /api/replicate/models/{owner}/{name}/schema
    API->>Replicate: GET /v1/models/{owner}/{name}
    Replicate-->>API: OpenAPI schema with $refs
    API->>API: dereferenceSchema (resolve $refs/allOf)
    API-->>Hooks: Dereferenced input schema
    Hooks-->>OpenApiDynamicInputs: Schema ready
    
    Note over OpenApiDynamicInputs: Dynamic Form Rendering
    OpenApiDynamicInputs->>OpenApiDynamicInputs: parseOpenApiSchema()
    OpenApiDynamicInputs->>OpenApiDynamicInputs: inferInputType() for each field
    OpenApiDynamicInputs->>OpenApiDynamicInputs: Render inputs (text/slider/dropdown/etc)
    OpenApiDynamicInputs-->>User: Show dynamic form based on schema
    
    User->>OpenApiDynamicInputs: Fill in parameters (prompt, etc)
    OpenApiDynamicInputs->>OpenApiDynamicInputs: coerceValue() for type safety
    
    Note over User,Tool: Execution Phase
    User->>ReplicateBlock: Execute workflow
    ReplicateBlock->>Tool: Call replicate_create_prediction
    Tool->>Tool: cleanInputs (remove empty values)
    Tool->>Tool: getReplicateEndpoint (official vs community)
    Tool->>Replicate: POST /v1/predictions or /v1/models/{owner}/{name}/predictions
    
    alt Sync Mode
        Replicate-->>Tool: Wait for completion (with Prefer: wait=N header)
        Tool-->>ReplicateBlock: Return completed prediction with output
    else Async Mode
        Replicate-->>Tool: Return prediction ID and URLs
        Tool-->>ReplicateBlock: Return prediction metadata
        Note over Replicate: Webhook called when complete (if configured)
    end
    
    ReplicateBlock-->>User: Display results
Loading

23 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +28 to +30
import { useReplicateCollections } from '@/hooks/use-replicate-collections'
import { useReplicateCollectionModels } from '@/hooks/use-replicate-collection-models'
import { useReplicateSchema } from '@/hooks/use-replicate-schema'
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Component directly imports Replicate-specific hooks despite generic claims. For true reusability, accept hooks as props or rename to ReplicateInputs.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/openapi-dynamic-inputs/openapi-dynamic-inputs.tsx
Line: 28:30

Comment:
**style:** Component directly imports Replicate-specific hooks despite generic claims. For true reusability, accept hooks as props or rename to `ReplicateInputs`.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +232 to +250
export function OpenApiDynamicInputs({
blockId,
subBlockId,
schema: propSchema,
model: propModel,
apiKey: propApiKey,
loading: propLoading = false,
error: propError = null,
onRetry: propOnRetry,
modelSelector,
schemaFetching,
disabled = false,
isPreview = false,
previewValue,
isConnecting = false,
groupFields = false,
preferLongInput = false,
showDescriptions = true,
}: OpenApiDynamicInputsProps) {
Copy link
Contributor

Choose a reason for hiding this comment

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

style: 833-line component handling multiple concerns. Consider splitting into ModelSelector, SchemaForm, and provider-specific wrappers for better testability and reuse.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/openapi-dynamic-inputs/openapi-dynamic-inputs.tsx
Line: 232:250

Comment:
**style:** 833-line component handling multiple concerns. Consider splitting into `ModelSelector`, `SchemaForm`, and provider-specific wrappers for better testability and reuse.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +16 to +21
endpoint?: string
/** Optional custom schema path (overrides default /{owner}/{name}/schema pattern) */
schemaPath?: string
/** Header name for API key (e.g., 'x-replicate-api-key') */
apiKeyHeaderName?: string
}
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Generic parameters like endpoint and apiKeyHeaderName are always passed same values. Consider hardcoding Replicate-specific values until second provider validates abstraction (YAGNI).

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/hooks/use-replicate-schema.ts
Line: 16:21

Comment:
**style:** Generic parameters like `endpoint` and `apiKeyHeaderName` are always passed same values. Consider hardcoding Replicate-specific values until second provider validates abstraction (YAGNI).

How can I resolve this? If you propose a fix, please make it concise.

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.

1 participant