-
Notifications
You must be signed in to change notification settings - Fork 189
feat: Add Cloudflare Workers AI integration #5
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
Conversation
…ry/api@1.9.0 as a dependency
…ude VECTORIZE and AI bindings
- Introduced a new API endpoint for text summarization using Cloudflare Workers AI. - Added error handling for API requests and validation for input data. - Updated README.md to include AI features, usage instructions, and configuration details.
WalkthroughAdds an AI summarization feature backed by Cloudflare Workers AI. Introduces a POST /api/summarize route with auth, validation, and centralized error handling. Implements SummarizerService using the AI binding. Updates environment typings and Wrangler config for AI and Vectorize. Removes several npm scripts. Expands README with AI sections. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User/Client
participant API as POST /api/summarize
participant Auth as Auth Service
participant CF as Cloudflare Env (AI, VECTORIZE)
participant S as SummarizerService
participant AI as Workers AI Model
U->>API: Request { text, config } + headers
API->>Auth: getAuthInstance() / session from headers
Auth-->>API: session or null
alt Unauthenticated
API-->>U: 401 { success:false, error:"Unauthorized" }
else Authenticated
API->>CF: Access AI binding
alt AI unavailable
API-->>U: 500 { success:false, error:"AI unavailable" }
else AI available
API->>API: Validate body (Zod)
alt Validation error
API-->>U: 400 { success:false, error, field }
else Valid
API->>S: summarize(text, config)
S->>AI: ai.run(model, messages)
AI-->>S: summary content
S-->>API: { summary, lengths, tokens }
API-->>U: 200 { success:true, data }
end
end
end
note over API: Fallback errors handled by handleApiError
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
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.
Actionable comments posted: 3
🧹 Nitpick comments (7)
wrangler.jsonc (2)
29-31: Verify preview R2 bucket is provisioned
preview_bucket_namemust exist in the target account, or preview/dev runs will fail. Ensure lifecycle/ACLs match the prod bucket.
33-38: Provision the Vectorize index and confirm binding/type alignment
- Ensure an index named
next-cf-app-indexexists (or create it before deploy), otherwise publish will fail.- Cross‑check the Env typing (
VECTORIZE) uses the correct class (VectorizevsVectorizeIndex) for your account’s Vectorize tier; see comment inworker-configuration.d.ts.src/app/api/summarize/route.ts (1)
1-1: Use standard import syntax for Zod v4.The import statement is using the v4 subpath, but Zod 4 is now the default export from "zod". Consider updating to the standard import for cleaner code.
Apply this diff to use the standard import:
-import { z } from "zod/v4"; +import { z } from "zod";Note: Your existing "zod/v4" imports will continue to work forever, but the standard import is now preferred.
src/services/summarizer.service.ts (1)
1-1: Use standard import syntax for Zod v4.Zod 4 is now the default export from "zod", so the v4 subpath import is no longer necessary.
Apply this diff to use the standard import:
-import { z } from "zod/v4"; +import { z } from "zod";Note: Your existing "zod/v4" imports will continue to work forever, but the standard import is now preferred.
cloudflare-env.d.ts (1)
2-2: Generated file: avoid manual editsThis file is generated by Wrangler; avoid hand edits. If types drift (e.g., switching to
Vectorize), regenerate viawrangler typesrather than modifying this file.src/lib/api-error.ts (2)
3-16: Add explicit return type and favor 422 for validation errors
- Add
: Responseto the function signature for clarity.- 422 Unprocessable Entity is more idiomatic for validation failures.
- Guard the field path in case it’s empty.
Apply this diff:
-export default function handleApiError(error: unknown) { +export default function handleApiError(error: unknown): Response { if (error instanceof z.ZodError) { - const firstError = error.issues[0]; + const firstError = error.issues[0]; + const field = + firstError?.path && firstError.path.length + ? firstError.path.join(".") + : undefined; return new Response( JSON.stringify({ success: false, error: firstError.message, - field: firstError.path.join("."), + field, }), { - status: 400, + status: 422, headers: { "Content-Type": "application/json" }, }, ); }
1-1: Unify zod imports — package.json uses zod ^4.1.8
- package.json: zod = ^4.1.8
- Repo mixes imports. "zod/v4": src/lib/api-error.ts, src/services/summarizer.service.ts. "zod": src/modules/auth/models/auth.model.ts, src/modules/todos/components/add-category.tsx, src/modules/todos/components/todo-form.tsx, src/modules/todos/schemas/category.schema.ts, src/modules/todos/schemas/todo.schema.ts.
- Action: pick one import style and make all imports consistent (e.g., import { z } from "zod").
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
README.md(8 hunks)cloudflare-env.d.ts(2 hunks)package.json(0 hunks)src/app/api/summarize/route.ts(1 hunks)src/lib/api-error.ts(1 hunks)src/services/summarizer.service.ts(1 hunks)worker-configuration.d.ts(2 hunks)wrangler.jsonc(1 hunks)
💤 Files with no reviewable changes (1)
- package.json
🧰 Additional context used
🧬 Code graph analysis (1)
src/app/api/summarize/route.ts (3)
src/modules/auth/utils/auth-utils.ts (1)
getAuthInstance(97-99)src/services/summarizer.service.ts (2)
summarizeRequestSchema(12-19)SummarizerService(34-110)src/lib/api-error.ts (1)
handleApiError(3-46)
🔇 Additional comments (19)
wrangler.jsonc (1)
39-41: AI binding looks good—confirm account feature is enabledThe
AIbinding is correct for Workers AI. Make sure Workers AI is enabled on the account/zone used by this Worker.worker-configuration.d.ts (2)
2-2: No action neededGenerated header; safe to keep as-is.
17-18: Use Vectorize (RC) instead of VectorizeIndex (beta)Change VECTORIZE's env type to Vectorize to match the modern binding (exposes methods like queryById); AI: Ai is correct.
Apply this diff:
- VECTORIZE: VectorizeIndex; + VECTORIZE: Vectorize;Run local checks:
rg -nP -C2 'env.(AI|VECTORIZE)\b' && rg -nP -C2 '\b(VectorizeIndex|Vectorize)\b'src/app/api/summarize/route.ts (4)
10-32: LGTM! Robust authentication implementation.The authentication flow correctly validates user sessions and returns appropriate 401 responses with consistent JSON structure. The use of Better Auth session validation is properly implemented.
34-50: LGTM! Proper AI service availability check.The implementation correctly validates the AI service availability and returns structured error responses when the service is unavailable. This prevents runtime errors and provides clear feedback to clients.
52-60: LGTM! Proper request validation and service usage.The implementation correctly parses and validates the request body using the schema, then properly instantiates and uses the SummarizerService. The error handling is delegated appropriately to the centralized handler.
62-78: LGTM! Consistent response structure and error handling.The success response maintains a consistent JSON structure matching the error responses, and the centralized error handling via
handleApiErrorensures proper error responses across all failure scenarios.README.md (7)
35-35: LGTM! Clear AI integration addition.The addition of Cloudflare Workers AI to the tech stack is well-documented and clearly describes the edge AI inference capability.
49-49: LGTM! Data flow documentation updated appropriately.The AI Processing step is properly integrated into the data flow architecture, providing clear understanding of where AI fits in the overall system.
69-73: LGTM! Clear AI permissions documentation.The documentation properly explains the additional AI permissions required for the Cloudflare API token, making setup clear for developers.
Also applies to: 78-78
189-193: LGTM! Wrangler configuration example updated.The AI binding configuration is properly documented in the wrangler.jsonc example, providing clear guidance for setup.
475-475: LGTM! Project structure documentation updated.The project structure section is properly updated to include the new AI summarization endpoint and service, maintaining consistency with the actual code organization.
Also applies to: 498-500
510-576: LGTM! Comprehensive AI development documentation.The AI development section provides excellent documentation covering:
- Authentication requirements for testing
- Multiple testing approaches (browser console, cURL, Postman)
- Service architecture explanation
- Available AI model options
This will help developers understand and test the AI functionality effectively.
652-659: LGTM! Well-organized roadmap additions.The TODO sections are well-organized with clear AI feature roadmap and analytics/performance items that align with the introduced AI capabilities.
Also applies to: 665-670
src/services/summarizer.service.ts (4)
3-10: LGTM! Well-designed configuration schema.The configuration schema provides sensible defaults and proper validation constraints. The enum for style options and reasonable limits for maxLength are well thought out.
12-19: LGTM! Robust input validation schema.The text validation with proper length constraints (50-50000 characters) and trimming is appropriate for summarization use cases. The minimum ensures meaningful content while the maximum prevents excessive token usage.
21-32: LGTM! Well-defined TypeScript interfaces.The type definitions are clean and provide good structure for the service. The token usage tracking in
SummaryResultis particularly valuable for monitoring and cost management.
34-74: LGTM! Solid AI service implementation.The summarization logic is well-implemented with:
- Proper parameter destructuring with defaults
- Token estimation for cost awareness
- Appropriate AI model usage (@cf/meta/llama-3.2-1b-instruct)
- Good error handling with fallback for empty responses
- Comprehensive result metadata
cloudflare-env.d.ts (1)
17-18: AI and VECTORIZE bindings verifiedwrangler.jsonc binds "ai" → "AI" and "vectorize" → "VECTORIZE", so cloudflare-env.d.ts's
AI: AiandVECTORIZE: VectorizeIndexmatch the config. If your account is on Vectorize RC, consider switching the type fromVectorizeIndextoVectorizeto avoid future deprecations.
| // Handle AI/service errors | ||
| const message = | ||
| error instanceof Error ? error.message : "Internal server error"; | ||
| return new Response( | ||
| JSON.stringify({ | ||
| success: false, | ||
| error: message, | ||
| }), | ||
| { | ||
| status: 500, | ||
| headers: { "Content-Type": "application/json" }, | ||
| }, | ||
| ); | ||
| } |
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.
Don’t leak internal error messages; map status when present and log server-side
Returning raw error.message in 500s can disclose internals. Log the error and return a generic message; also respect status/statusCode when present (e.g., auth libs) to preserve 401/403/4xx semantics.
Apply this diff:
- // Handle AI/service errors
- const message =
- error instanceof Error ? error.message : "Internal server error";
- return new Response(
- JSON.stringify({
- success: false,
- error: message,
- }),
- {
- status: 500,
- headers: { "Content-Type": "application/json" },
- },
- );
+ // Handle domain/HTTP-like errors and default to 500 without leaking internals
+ const statusFromError =
+ typeof (error as any)?.status === "number"
+ ? (error as any).status
+ : typeof (error as any)?.statusCode === "number"
+ ? (error as any).statusCode
+ : undefined;
+ const status =
+ statusFromError && statusFromError >= 400 && statusFromError <= 599
+ ? statusFromError
+ : 500;
+ const safeMessage =
+ status >= 500
+ ? "Internal server error"
+ : error instanceof Error
+ ? error.message
+ : "Request failed";
+ console.error("[API_ERROR]", error);
+ return new Response(
+ JSON.stringify({
+ success: false,
+ error: safeMessage,
+ }),
+ {
+ status,
+ headers: { "Content-Type": "application/json" },
+ },
+ );📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Handle AI/service errors | |
| const message = | |
| error instanceof Error ? error.message : "Internal server error"; | |
| return new Response( | |
| JSON.stringify({ | |
| success: false, | |
| error: message, | |
| }), | |
| { | |
| status: 500, | |
| headers: { "Content-Type": "application/json" }, | |
| }, | |
| ); | |
| } | |
| // Handle domain/HTTP-like errors and default to 500 without leaking internals | |
| const statusFromError = | |
| typeof (error as any)?.status === "number" | |
| ? (error as any).status | |
| : typeof (error as any)?.statusCode === "number" | |
| ? (error as any).statusCode | |
| : undefined; | |
| const status = | |
| statusFromError && statusFromError >= 400 && statusFromError <= 599 | |
| ? statusFromError | |
| : 500; | |
| const safeMessage = | |
| status >= 500 | |
| ? "Internal server error" | |
| : error instanceof Error | |
| ? error.message | |
| : "Request failed"; | |
| console.error("[API_ERROR]", error); | |
| return new Response( | |
| JSON.stringify({ | |
| success: false, | |
| error: safeMessage, | |
| }), | |
| { | |
| status, | |
| headers: { "Content-Type": "application/json" }, | |
| }, | |
| ); | |
| } |
| language = "English", | ||
| } = config || {}; | ||
|
|
||
| const systemPrompt = this.buildSystenPrompt(maxLength, style, language); |
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.
Fix typo in method name.
There's a typo in the method name buildSystenPrompt - it should be buildSystemPrompt.
Apply this diff to fix the typo:
- const systemPrompt = this.buildSystenPrompt(maxLength, style, language);
+ const systemPrompt = this.buildSystemPrompt(maxLength, style, language);And update the method signature:
- private buildSystenPrompt(
+ private buildSystemPrompt(📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const systemPrompt = this.buildSystenPrompt(maxLength, style, language); | |
| const systemPrompt = this.buildSystemPrompt(maxLength, style, language); |
🤖 Prompt for AI Agents
In src/services/summarizer.service.ts around line 47, there's a typo: the call
uses buildSystenPrompt but the correct method name is buildSystemPrompt; rename
the call to buildSystemPrompt and also update the actual method
declaration/signature to match that name (rename the method from
buildSystenPrompt to buildSystemPrompt) so callers and definition use the same
correct identifier.
| private buildSystenPrompt( | ||
| maxLength: number, | ||
| style: string, | ||
| language: string, | ||
| ): string { | ||
| const styleInstructructions: Record<SummaryStyles, string> = { | ||
| concise: | ||
| "Create a brief, concise summary focusing on the main points.", | ||
| detailed: | ||
| "Create a comprehensive summary that covers key details and context.", | ||
| "bullet-points": | ||
| "Create a summary using bullet points to highlight key information.", | ||
| }; | ||
|
|
||
| return `You are a professional text summarizer. ${styleInstructructions[style as keyof typeof styleInstructructions]} | ||
|
|
||
| Instructions: | ||
| - Summarize in ${language} | ||
| - Keep the summary under ${maxLength} words | ||
| - Focus on the most important information | ||
| - Maintain the original meaning and context | ||
| - Use clear, readable language | ||
| - Do not add your own opinions or interpretations | ||
| - DO NOT START THE SUMMARY WITH the following phrases: | ||
| - "Here is a summary of the text" | ||
| - "Here is a summary of the text:" | ||
| - "Here is a summary of the text in bullet points" | ||
| - "Here is the summary:" | ||
| - "Here is the summary in bullet points:" | ||
| - "Here is the summary in ${language}:" | ||
| - "Here is the summary in ${language} in bullet points:" | ||
|
|
||
| Output only the summary, nothing else.`; | ||
| } | ||
| } |
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.
Fix typo and improve prompt generation.
The method has a typo in its name and there's also a typo in "styleInstructructions". The prompt engineering is well-designed with specific instructions to avoid common AI response patterns.
Apply this diff to fix the issues:
- private buildSystenPrompt(
+ private buildSystemPrompt(
maxLength: number,
style: string,
language: string,
): string {
- const styleInstructructions: Record<SummaryStyles, string> = {
+ const styleInstructions: Record<SummaryStyles, string> = {
concise:
"Create a brief, concise summary focusing on the main points.",
detailed:
"Create a comprehensive summary that covers key details and context.",
"bullet-points":
"Create a summary using bullet points to highlight key information.",
};
- return `You are a professional text summarizer. ${styleInstructructions[style as keyof typeof styleInstructructions]}
+ return `You are a professional text summarizer. ${styleInstructions[style as keyof typeof styleInstructions]}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private buildSystenPrompt( | |
| maxLength: number, | |
| style: string, | |
| language: string, | |
| ): string { | |
| const styleInstructructions: Record<SummaryStyles, string> = { | |
| concise: | |
| "Create a brief, concise summary focusing on the main points.", | |
| detailed: | |
| "Create a comprehensive summary that covers key details and context.", | |
| "bullet-points": | |
| "Create a summary using bullet points to highlight key information.", | |
| }; | |
| return `You are a professional text summarizer. ${styleInstructructions[style as keyof typeof styleInstructructions]} | |
| Instructions: | |
| - Summarize in ${language} | |
| - Keep the summary under ${maxLength} words | |
| - Focus on the most important information | |
| - Maintain the original meaning and context | |
| - Use clear, readable language | |
| - Do not add your own opinions or interpretations | |
| - DO NOT START THE SUMMARY WITH the following phrases: | |
| - "Here is a summary of the text" | |
| - "Here is a summary of the text:" | |
| - "Here is a summary of the text in bullet points" | |
| - "Here is the summary:" | |
| - "Here is the summary in bullet points:" | |
| - "Here is the summary in ${language}:" | |
| - "Here is the summary in ${language} in bullet points:" | |
| Output only the summary, nothing else.`; | |
| } | |
| } | |
| private buildSystemPrompt( | |
| maxLength: number, | |
| style: string, | |
| language: string, | |
| ): string { | |
| const styleInstructions: Record<SummaryStyles, string> = { | |
| concise: | |
| "Create a brief, concise summary focusing on the main points.", | |
| detailed: | |
| "Create a comprehensive summary that covers key details and context.", | |
| "bullet-points": | |
| "Create a summary using bullet points to highlight key information.", | |
| }; | |
| return `You are a professional text summarizer. ${styleInstructions[style as keyof typeof styleInstructions]} | |
| Instructions: | |
| - Summarize in ${language} | |
| - Keep the summary under ${maxLength} words | |
| - Focus on the most important information | |
| - Maintain the original meaning and context | |
| - Use clear, readable language | |
| - Do not add your own opinions or interpretations | |
| - DO NOT START THE SUMMARY WITH the following phrases: | |
| - "Here is a summary of the text" | |
| - "Here is a summary of the text:" | |
| - "Here is a summary of the text in bullet points" | |
| - "Here is the summary:" | |
| - "Here is the summary in bullet points:" | |
| - "Here is the summary in ${language}:" | |
| - "Here is the summary in ${language} in bullet points:" | |
| Output only the summary, nothing else.`; | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/services/summarizer.service.ts around lines 76 to 110, the method name
buildSystenPrompt and the variable styleInstructructions contain typos; rename
the method to buildSystemPrompt and rename styleInstructructions to
styleInstructions, update the type/usage (style: string and Record key typing)
to match the corrected identifier, and update any internal references or
external callers to the new method name so signatures and imports remain
consistent; keep the prompt string content unchanged except for the identifier
fixes.
🤖 AI Integration with Authentication
Adds Cloudflare Workers AI integration featuring text summarization powered by Llama 3.2 1B Instruct model with user authentication.
Features Added
/api/summarize) with multiple styles (concise, detailed, bullet-points)Technical Changes
SummarizerServiceclass for AI business logichandleApiErrorutilityDocumentation
Testing
Authentication required - login first, then test via browser console or tools with session cookies.
Example Request:
{ "text": "Your text to summarize...", "config": { "maxLength": 200, "style": "concise", "language": "English" } }Summary by CodeRabbit
New Features
Documentation
Chores