Skip to content

fix(restate): use /fetch subpath and fix env handling#1974

Merged
yujonglee merged 4 commits intomainfrom
devin/1764331778-fix-restate-env
Nov 29, 2025
Merged

fix(restate): use /fetch subpath and fix env handling#1974
yujonglee merged 4 commits intomainfrom
devin/1764331778-fix-restate-env

Conversation

@yujonglee
Copy link
Contributor

@yujonglee yujonglee commented Nov 28, 2025

fix(restate): use /fetch subpath and fix env handling

Summary

This PR fixes two issues with the Restate Cloudflare Workers integration:

  1. SDK import path: Updated all Restate SDK imports to use @restatedev/restate-sdk-cloudflare-workers/fetch instead of the root export, matching the official Restate Cloudflare Workers template. This addresses the set_log_level error referenced in sdk-typescript#622.

  2. Environment variable handling: The RESTATE_INGRESS_URL in apps/web/src/utils/restate.ts was reading directly from process.env, which doesn't work reliably in Vite browser builds. Added VITE_RESTATE_INGRESS_URL to the client env schema with a sensible default (http://localhost:8080) and updated the utils file to use the validated env.

Also added build and start scripts to apps/restate/package.json to match the official template.

Updates since last revision

Properly typed the AudioPipeline workflow client (addressing reviewer feedback):

  • Added AudioPipelineDefinition type with ctx: unknown parameter matching the server-side handler signatures (the context parameter is stripped by IngressWorkflowClient)
  • Added AudioPipelineClient type alias using IngressWorkflowClient<AudioPipelineDefinition>
  • Removed the unsafe as any cast - workflowSubmit, workflowAttach, and getStatus are now properly typed
  • Still using z.any() for llmResult to work around TanStack Start's server function type inference

Review & Testing Checklist for Human

  • Verify runtime behavior: The AudioPipelineDefinition type pattern (with ctx: unknown as first parameter) is based on reading the SDK's IngressWorkflowClient conditional types - confirm workflowSubmit and workflowAttach work correctly at runtime
  • Verify the /fetch import path works correctly with wrangler dev --port 9080 (may require Cloudflare login)
  • Test that VITE_RESTATE_INGRESS_URL is correctly available in the browser build
  • Confirm the audio upload workflow still functions end-to-end with these changes

Recommended test plan:

  1. Run pnpm -F @hypr/restate dev and verify the worker starts without the set_log_level error
  2. Run pnpm -F web dev and verify the Restate client can connect using the env variable
  3. Test an audio upload through the web UI to verify the full workflow

Notes

The known issue sdk-typescript#622 describes a WASM-related error that may still occur in certain environments. If the set_log_level error persists after these changes, it's likely an upstream SDK bug rather than a configuration issue.

Link to Devin run: https://app.devin.ai/sessions/3c61dfe0b167467daf2d2c4c02ec4a2d
Requested by: yujonglee (@yujonglee)

- Update Restate SDK imports to use @restatedev/restate-sdk-cloudflare-workers/fetch (matching official template)
- Remove Node SDK dependency and dev:local script (no longer needed)
- Add VITE_RESTATE_INGRESS_URL to client env schema with default localhost:8080
- Update apps/web/src/utils/restate.ts to use validated env instead of process.env
- Add build and start scripts to match official template

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@netlify
Copy link

netlify bot commented Nov 28, 2025

Deploy Preview for hyprnote-storybook ready!

Name Link
🔨 Latest commit 4c9b27b
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/692a46d3cf4624000812b089
😎 Deploy Preview https://deploy-preview-1974--hyprnote-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Nov 28, 2025

Deploy Preview for hyprnote ready!

Name Link
🔨 Latest commit 4c9b27b
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/692a46d3ee88930008434458
😎 Deploy Preview https://deploy-preview-1974--hyprnote.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 28, 2025

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Updated Restate SDK imports to the /fetch submodule in the restate app, added build and start scripts to apps/restate/package.json, introduced client env var VITE_RESTATE_INGRESS_URL and switched web ingress lookup to env, and refactored audio pipeline types/usage to use a workflow client and new input type.

Changes

Cohort / File(s) Change Summary
Restate SDK import path updates
apps/restate/src/audioPipeline.ts, apps/restate/src/index.ts, apps/restate/src/userRateLimiter.ts
Updated imports from @restatedev/restate-sdk-cloudflare-workers to @restatedev/restate-sdk-cloudflare-workers/fetch.
Package.json scripts
apps/restate/package.json
Added npm scripts: build -> tsc --noEmitOnError, start -> wrangler dev --port 9080.
Client env var addition
apps/web/src/env.ts
Added VITE_RESTATE_INGRESS_URL to client env schema with default "http://localhost:8080".
Ingress URL sourcing refactor
apps/web/src/utils/restate.ts
Switched RESTATE_INGRESS_URL to read from imported env.VITE_RESTATE_INGRESS_URL instead of process.env fallback.
Audio pipeline API/type and usage changes
apps/web/src/functions/transcription.ts
Introduced StartAudioPipelineInput and workflow-based AudioPipelineDefinition/AudioPipelineClient; replaced direct AudioPipeline calls with IngressWorkflowClient usage for start/status/result flows; StatusState.llmResult typing loosened.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Focus areas:
    • Confirm /fetch submodule is correct for runtime environment.
    • Verify VITE_RESTATE_INGRESS_URL is injected into client builds and that removing process.env fallback won't break deployments.
    • Review transcription.ts workflow-client usage and new types for runtime/typing safety.
    • Check the loosened StatusState.llmResult type for intentionality.

Possibly related PRs

Suggested reviewers

  • duckduckhero

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: updating Restate SDK imports to use the /fetch subpath and fixing environment variable handling for the ingress URL.
Description check ✅ Passed The pull request description clearly explains the two main issues being fixed: SDK import path updates and environment variable handling, along with added scripts to match the official template.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4268f07 and 4c9b27b.

📒 Files selected for processing (3)
  • apps/restate/src/audioPipeline.ts (1 hunks)
  • apps/web/src/functions/transcription.ts (5 hunks)
  • apps/web/src/utils/restate.ts (1 hunks)

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
apps/restate/package.json (1)

6-8: Clarify build vs typecheck tsc flags

build runs tsc --noEmitOnError while typecheck runs tsc --noEmit. If noEmit isn’t set in tsconfig, build will emit JS into the source tree, which may not be what you want for a Workers-only bundle.

If the goal is “typecheck-only” for both, consider aligning them; otherwise, documenting that build is the emit step would avoid confusion.

apps/web/src/utils/restate.ts (1)

1-4: Web Restate ingress now correctly flows through typed env

Switching to env.VITE_RESTATE_INGRESS_URL is a nice improvement over ad‑hoc process.env access and keeps this aligned with apps/web/src/env.ts.

One thing to watch: because VITE_RESTATE_INGRESS_URL has a default in the env schema, a missing value in staging/prod will silently fall back to that default. Make sure your deploys always set VITE_RESTATE_INGRESS_URL explicitly so the web app hits the right Restate ingress.

apps/web/src/env.ts (1)

33-33: Consider making VITE_RESTATE_INGRESS_URL required outside development

Having default("http://localhost:8080") is great for local dev, but in staging/prod a missing VITE_RESTATE_INGRESS_URL will silently point the web app at localhost instead of failing fast.

You could mirror the VITE_POSTHOG_API_KEY pattern so it’s only optional in dev:

-    VITE_RESTATE_INGRESS_URL: z.string().default("http://localhost:8080"),
+    VITE_RESTATE_INGRESS_URL: isDev
+      ? z.string().default("http://localhost:8080")
+      : z.string().min(1),

That keeps DX in development while enforcing an explicit, non-empty value in other environments.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 604854a and ea89179.

📒 Files selected for processing (6)
  • apps/restate/package.json (1 hunks)
  • apps/restate/src/audioPipeline.ts (1 hunks)
  • apps/restate/src/index.ts (1 hunks)
  • apps/restate/src/userRateLimiter.ts (1 hunks)
  • apps/web/src/env.ts (1 hunks)
  • apps/web/src/utils/restate.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces

Files:

  • apps/restate/src/audioPipeline.ts
  • apps/web/src/env.ts
  • apps/web/src/utils/restate.ts
  • apps/restate/src/userRateLimiter.ts
  • apps/restate/src/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/restate/src/audioPipeline.ts
  • apps/web/src/env.ts
  • apps/web/src/utils/restate.ts
  • apps/restate/src/userRateLimiter.ts
  • apps/restate/src/index.ts
🧬 Code graph analysis (1)
apps/web/src/utils/restate.ts (1)
apps/web/src/env.ts (1)
  • env (6-38)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: fmt
🔇 Additional comments (3)
apps/restate/src/index.ts (1)

1-1: Switch to /fetch Restate SDK entrypoint looks consistent

Importing from @restatedev/restate-sdk-cloudflare-workers/fetch lines up with the other Restate modules in this PR, and the rest of the handler wiring stays the same.

Just make sure the /fetch subpath in the version you’re using re-exports createEndpointHandler with the same types so wrangler dev/deploy builds cleanly.

apps/restate/src/userRateLimiter.ts (1)

1-1: Import update to /fetch matches the rest of the Restate app

The rate limiter still uses the same Restate APIs; only the import source moved to @restatedev/restate-sdk-cloudflare-workers/fetch, which is consistent with audioPipeline and index.ts.

As long as this subpath in your SDK version exports object, Context, TerminalError, etc., this change is good.

apps/restate/src/audioPipeline.ts (1)

2-2: Restate /fetch import aligns with workflow usage

The workflow still calls restate.workflow and restate.handlers.workflow.*; only the import source changed to @restatedev/restate-sdk-cloudflare-workers/fetch, which matches the rest of the Restate app in this PR.

Confirm that this entrypoint in your SDK version exposes the workflow APIs you’re using.

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/functions/transcription.ts (1)

131-136: TypeScript error: workflowAttach not defined on AudioPipeline type.

Line 136 calls workflowAttach() on a client typed as AudioPipeline, but the AudioPipeline type (lines 34-37) doesn't include this method. This should cause a TypeScript compilation error.

This is inconsistent with startAudioPipeline (lines 62-69), which casts to any before calling workflowSubmit. The inconsistency suggests the type definitions are incomplete rather than a deliberate design choice.

This will be resolved by updating the AudioPipeline type definition to include all workflow client methods, as suggested in the earlier comment on lines 29-37.

🧹 Nitpick comments (1)
apps/web/src/functions/transcription.ts (1)

23-23: Type safety reduction: Consider whether z.any() is necessary.

Changing llmResult from z.unknown().optional() to z.any().optional() removes type safety by allowing the any type to bypass TypeScript's type checking. While z.unknown() requires explicit type narrowing, it prevents accidental misuse of the LLM result.

Unless there's a specific reason why type narrowing is problematic in consumer code, consider keeping z.unknown() for better type safety.

Apply this diff to restore type safety:

-  llmResult: z.any().optional(),
+  llmResult: z.unknown().optional(),

If z.any() is required due to downstream code complexity, please document why type narrowing isn't feasible.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea89179 and bf7fd7f.

📒 Files selected for processing (1)
  • apps/web/src/functions/transcription.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces

Files:

  • apps/web/src/functions/transcription.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/functions/transcription.ts
🧬 Code graph analysis (1)
apps/web/src/functions/transcription.ts (2)
apps/web/src/utils/restate.ts (1)
  • StatusState (5-16)
apps/restate/src/audioPipeline.ts (2)
  • StartAudioPipelineInput (13-13)
  • AudioPipeline (260-260)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: fmt

- Add AudioPipelineDefinition type with ctx parameter matching server-side handler signatures
- Add AudioPipelineClient type alias using IngressWorkflowClient<AudioPipelineDefinition>
- Remove unsafe 'as any' cast from workflowSubmit call
- Update all workflowClient calls to use the new types

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
@argos-ci
Copy link

argos-ci bot commented Nov 29, 2025

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
web (Inspect) ⚠️ Changes detected (Review) 3 changed Nov 29, 2025, 1:08 AM

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
apps/web/src/functions/transcription.ts (2)

24-24: llmResult: z.any() is pragmatic but loosens validation.

Switching from z.unknown() to z.any() removes both runtime and compile‑time constraints on llmResult. That’s fine as a quick way to unblock typing, but if you know even a partial shape (e.g. top‑level fields), consider tightening this later with a structured schema to catch regressions.


30-47: Audio pipeline definition/client typing is solid and fixes earlier gaps.

Defining StartAudioPipelineInput, AudioPipelineDefinition, and AudioPipelineClient = IngressWorkflowClient<AudioPipelineDefinition> cleanly models the workflow contract and removes the need for unsafe casts called out in earlier reviews. The comment about ctx being stripped on the ingress client is accurate and helpful.

If the server‑side workflow definition lives in another package, you might eventually want to centralize these types in a shared module to avoid drift between client and worker, but that’s optional and can be deferred.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf7fd7f and 4268f07.

📒 Files selected for processing (1)
  • apps/web/src/functions/transcription.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces

Files:

  • apps/web/src/functions/transcription.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/functions/transcription.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: fmt
  • GitHub Check: ci (macos, macos-14)
🔇 Additional comments (4)
apps/web/src/functions/transcription.ts (4)

2-2: Typed Restate client import looks appropriate.

Using a type-only import for IngressWorkflowClient matches the new AudioPipelineClient alias and keeps runtime bundle clean. Just ensure your installed @restatedev/restate-sdk-clients version exposes this type and that connect().workflowClient is generic over the same definition.


71-79: startAudioPipeline: typed workflowSubmit usage looks correct.

Constructing a typed AudioPipelineClient and calling workflowSubmit({ userId, audioUrl }) with the pipelineId as the workflow identity matches the new definition and resolves the previous as any issue. Returning handle.invocationId alongside pipelineId gives callers everything they need to attach or inspect status later.


108-113: getAudioPipelineStatus: consistent, strongly typed status fetch.

Reusing AudioPipelineClient here and calling workflowClient.getStatus() bound to the provided pipelineId aligns with the AudioPipelineDefinition and keeps status shape (StatusStateType) consistent with the rest of the flow.


141-145: getAudioPipelineResult: workflowAttach usage matches the workflow client pattern.

Creating an AudioPipelineClient for the given pipelineId and calling workflowAttach() is the expected Restate pattern for retrieving the workflow result; wiring is consistent with startAudioPipeline/getAudioPipelineStatus and should behave well end‑to‑end once the worker side is aligned.

Resolved merge conflict in apps/web/src/functions/transcription.ts:
- Kept AudioPipelineDefinition and AudioPipelineClient types with IngressWorkflowClient
- Updated StartAudioPipelineInput to use fileId instead of audioUrl (from main)
- Updated llmResult to z.string() (from main)
- Kept properly typed workflowClient calls without 'as any' cast

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
@yujonglee yujonglee merged commit 06e80fb into main Nov 29, 2025
9 of 11 checks passed
@yujonglee yujonglee deleted the devin/1764331778-fix-restate-env branch November 29, 2025 01:07
@devin-ai-integration
Copy link
Contributor

Devin is archived and cannot be woken up. Please unarchive Devin if you want to continue using it.

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

Comments