-
Notifications
You must be signed in to change notification settings - Fork 2.5k
[DON'T MERGE] Replicate Block: Lessons in Over-Engineering #1789
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
base: staging
Are you sure you want to change the base?
[DON'T MERGE] Replicate Block: Lessons in Over-Engineering #1789
Conversation
- 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.
|
@trmitchell7 is attempting to deploy a commit to the Sim Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this 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)
ReplicateBlockconfig with dynamic inputs and model selectioncreate_predictiontool 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 resolutioninput-with-tags.tsxandtextarea-with-tags.tsx- Shared input components with env var supportlib/api/retry.ts- Configurable retry logic
Architectural Over-Engineering (Needs refactoring)
OpenApiDynamicInputscomponent (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
- Tool implementation - Clean separation, excellent logging, proper type handling
- Schema utilities - Truly provider-agnostic, well-tested, genuinely reusable
- API routes - Solid error handling, caching, environment variable resolution
- Type safety - Comprehensive TypeScript types throughout
- Documentation - Honest about limitations (refreshing self-awareness in comments)
Architectural Issues
- Premature abstraction - Generic parameters/interfaces designed for 3 future providers that don't exist
- Component size - 833-line component should be split into smaller, focused pieces
- False genericity - "Generic" components importing provider-specific dependencies
- 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
23 files reviewed, 3 comments
| import { useReplicateCollections } from '@/hooks/use-replicate-collections' | ||
| import { useReplicateCollectionModels } from '@/hooks/use-replicate-collection-models' | ||
| import { useReplicateSchema } from '@/hooks/use-replicate-schema' |
There was a problem hiding this comment.
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.| 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) { |
There was a problem hiding this comment.
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.| 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 | ||
| } |
There was a problem hiding this comment.
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.
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:
What This Ships:
What I Didn't Ship:
Next Time:
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.tsVerdict: ✅ 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)Verdict: Honest naming, confused implementation. Should either be truly generic OR remove unused params.
The Monolith (Needs Refactoring)
OpenApiDynamicInputs (~1,057 lines including tests)
What it claims:
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:
SchemaFormRenderer)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)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, reusableSchemaForm- Provider-agnostic form rendereruseProviderSchema- Configurable hookReplicateInputs- Composition of aboveBenefit: Each piece is testable, reusable, understandable.
2. Three Hooks For One Provider
Problem: Granularity without benefit.
Current:
Alternative:
Benefit: Less import overhead, easier mocking in tests, clear data flow.
3. Unused Generic Parameters
Current:
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:
Trade-offs:
Benefits:
The Proper Generic Path (When You Have 2+ Providers)
After implementing HuggingFace/Bedrock:
PR: "refactor: Extract common OpenAPI utilities"
What you'd discover:
What you'd extract:
SchemaFormRenderer(truly generic - just needs schema)parseOpenApiSchema(already done!)coerceValue(already done!)What you'd keep separate: