-
Notifications
You must be signed in to change notification settings - Fork 23
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
feat: add subscribers to kit #61
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,17 @@ | ||||||||||||||
import { openai } from "@ai-sdk/openai"; | ||||||||||||||
import { convertToCoreMessages, streamText } from "ai"; | ||||||||||||||
|
||||||||||||||
export const maxDuration = 30; | ||||||||||||||
|
||||||||||||||
export async function POST(req: Request) { | ||||||||||||||
const { messages } = await req.json(); | ||||||||||||||
|
||||||||||||||
const result = await streamText({ | ||||||||||||||
model: openai("gpt-4o-mini"), | ||||||||||||||
system: | ||||||||||||||
"You generate markdown documents for users. Unless specified, this is a draft. Keep things shortish. Do not add any supplementary text, as everything you say will be placed into a document. If you're confused however, it's okay to ask a user for info. Responses must be either a chat response, or a document. Don't add bold styling to headings.", | ||||||||||||||
messages: convertToCoreMessages(messages), | ||||||||||||||
}); | ||||||||||||||
Comment on lines
+9
to
+14
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainExtract system prompt to a configuration file. The system prompt is hardcoded and quite long. Consider extracting it to a separate configuration file for better maintainability. Additionally, verify if "gpt-4o-mini" is the correct model name as it seems unusual. 🌐 Web query:
💡 Result: OpenAI's GPT-4 family includes multiple model variants with distinct capabilities and naming conventions. Here's a breakdown of available models based on current documentation: Latest GA Models
Older GA Models
Specialized Variants
Key distinctions:
Older versions like Citations:
Action: Extract Hardcoded System Prompt & Confirm "gpt-4o-mini" Is Valid
|
||||||||||||||
|
||||||||||||||
return result.toDataStreamResponse(); | ||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
"use server"; | ||
|
||
import { openai } from "@ai-sdk/openai"; | ||
import { type CoreMessage, streamText } from "ai"; | ||
import { createStreamableValue } from "ai/rsc"; | ||
|
||
// Send messages to AI and stream a result back | ||
export async function continueConversation(messages: CoreMessage[]) { | ||
const result = await streamText({ | ||
model: openai("gpt-4o-mini"), | ||
messages, | ||
}); | ||
|
||
const stream = createStreamableValue(result.textStream); | ||
return stream.value; | ||
} | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,22 @@ | ||||||||||||||||||
"use client"; | ||||||||||||||||||
|
||||||||||||||||||
import { EditorContent, type JSONContent, useEditor } from "@tiptap/react"; | ||||||||||||||||||
import StarterKit from "@tiptap/starter-kit"; | ||||||||||||||||||
import { useEffect, useState } from "react"; | ||||||||||||||||||
|
||||||||||||||||||
const Tiptap = ({ content }: { content: JSONContent }) => { | ||||||||||||||||||
const [editorState, setEditorState] = useState<JSONContent | null>(null); | ||||||||||||||||||
|
||||||||||||||||||
const editor = useEditor({ | ||||||||||||||||||
extensions: [StarterKit], | ||||||||||||||||||
content: content, | ||||||||||||||||||
}); | ||||||||||||||||||
|
||||||||||||||||||
useEffect(() => { | ||||||||||||||||||
setEditorState(JSON.parse(editor?.getText() || "{}")); | ||||||||||||||||||
}, [editor]); | ||||||||||||||||||
Comment on lines
+15
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use the built-in method to obtain the editor’s content as JSON.
useEffect(() => {
- setEditorState(JSON.parse(editor?.getText() || "{}"));
+ if (editor) {
+ setEditorState(editor.getJSON());
+ }
}, [editor]); 📝 Committable suggestion
Suggested change
|
||||||||||||||||||
|
||||||||||||||||||
return <EditorContent editor={editor} />; | ||||||||||||||||||
}; | ||||||||||||||||||
|
||||||||||||||||||
export default Tiptap; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,53 +8,53 @@ import ky from "ky"; | |
import { createSafeActionClient } from "next-safe-action"; | ||
import { waitlistSchema } from "./schema"; | ||
|
||
interface ZapSubscriberPayload { | ||
email_address: string; | ||
referrer: string; | ||
} | ||
|
||
export const joinWaitlist = createSafeActionClient() | ||
.schema(waitlistSchema) | ||
.action(async ({ parsedInput }) => { | ||
const kitPayload: ZapSubscriberPayload = { | ||
email_address: parsedInput.email, | ||
referrer: typeof window !== 'undefined' ? window.location.href : 'https://trycomp.ai', | ||
}; | ||
Comment on lines
+19
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove duplicated code and fix server-side window check. The code has the following issues:
Apply this diff to fix the issues: +const getReferrer = () => {
+ return 'https://trycomp.ai';
+};
- const kitPayload: ZapSubscriberPayload = {
- email_address: parsedInput.email,
- referrer: typeof window !== 'undefined' ? window.location.href : 'https://trycomp.ai',
- };
+ const payload: ZapSubscriberPayload = {
+ email_address: parsedInput.email,
+ referrer: getReferrer(),
+ };
- const zapPayload: ZapSubscriberPayload = {
- email_address: parsedInput.email,
- referrer: typeof window !== 'undefined' ? window.location.href : 'https://trycomp.ai',
- };
-
await ky.post(
process.env.ZAP_WEBHOOK_URL as string,
{
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
- json: kitPayload,
+ json: payload,
}
); Also applies to: 36-39 |
||
|
||
if (!resend) { | ||
throw new Error("Resend not initialized - missing API key"); | ||
} | ||
|
||
const audience = await resend.contacts.list({ | ||
audienceId: process.env.RESEND_AUDIENCE_ID!, | ||
}); | ||
|
||
const isInAudience = audience.data?.data?.some( | ||
(contact: { email: string }) => contact.email === parsedInput.email, | ||
); | ||
|
||
if (!isInAudience) { | ||
await resend.contacts.create({ | ||
email: parsedInput.email, | ||
firstName: | ||
parsedInput.email.charAt(0).toUpperCase() + | ||
parsedInput.email.split("@")[0].slice(1), | ||
audienceId: process.env.RESEND_AUDIENCE_ID as string, | ||
if (process.env.DISCORD_WEBHOOK_URL) { | ||
await ky.post(process.env.DISCORD_WEBHOOK_URL as string, { | ||
json: { | ||
content: `New waitlist signup: ${parsedInput.email}`, | ||
}, | ||
}); | ||
|
||
await tasks.trigger<typeof introductionEmailTask>( | ||
"introduction-email", | ||
} | ||
Comment on lines
+28
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add error handling and consistent headers. The webhook calls lack error handling and have inconsistent header usage. Apply this diff to improve error handling and header consistency: + const headers = {
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ };
if (process.env.DISCORD_WEBHOOK_URL) {
- await ky.post(process.env.DISCORD_WEBHOOK_URL as string, {
- json: {
- content: `New waitlist signup: ${parsedInput.email}`,
- },
- });
+ try {
+ await ky.post(process.env.DISCORD_WEBHOOK_URL as string, {
+ headers,
+ json: {
+ content: `New waitlist signup: ${parsedInput.email}`,
+ },
+ });
+ } catch (error) {
+ console.error('Failed to send Discord notification:', error);
+ // Continue execution as this is non-critical
+ }
}
if (process.env.ZAP_WEBHOOK_URL) {
- const zapPayload: ZapSubscriberPayload = {
- email_address: parsedInput.email,
- referrer: typeof window !== 'undefined' ? window.location.href : 'https://trycomp.ai',
- };
-
- await ky.post(
- process.env.ZAP_WEBHOOK_URL as string,
- {
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json",
- },
- json: kitPayload,
- }
- );
+ try {
+ await ky.post(
+ process.env.ZAP_WEBHOOK_URL as string,
+ {
+ headers,
+ json: payload,
+ }
+ );
+ } catch (error) {
+ console.error('Failed to send Zapier notification:', error);
+ throw new Error('Failed to add subscriber');
+ }
} Also applies to: 35-51 |
||
if (process.env.ZAP_WEBHOOK_URL) { | ||
const zapPayload: ZapSubscriberPayload = { | ||
email_address: parsedInput.email, | ||
referrer: typeof window !== 'undefined' ? window.location.href : 'https://trycomp.ai', | ||
}; | ||
|
||
await ky.post( | ||
process.env.ZAP_WEBHOOK_URL as string, | ||
{ | ||
email: parsedInput.email, | ||
}, | ||
); | ||
|
||
if (process.env.DISCORD_WEBHOOK_URL) { | ||
await ky.post(process.env.DISCORD_WEBHOOK_URL as string, { | ||
json: { | ||
content: `New waitlist signup: ${parsedInput.email}`, | ||
headers: { | ||
"Content-Type": "application/json", | ||
Accept: "application/json", | ||
}, | ||
}); | ||
} | ||
|
||
await ServerAnalytics.track(parsedInput.email, "waitlist_signup", { | ||
channel: "web", | ||
email: parsedInput.email, | ||
}); | ||
} else { | ||
throw new Error("Email already in audience"); | ||
json: kitPayload, | ||
} | ||
); | ||
} | ||
|
||
await ServerAnalytics.track(parsedInput.email, "waitlist_signup", { | ||
channel: "web", | ||
email: parsedInput.email, | ||
}); | ||
|
||
return { | ||
success: true, | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ | |
"name": "comp.ai", | ||
"version": "0.1.0", | ||
"dependencies": { | ||
"@ai-sdk/openai": "^1.1.12", | ||
"@browserbasehq/sdk": "^2.3.0", | ||
"@bubba/notifications": "workspace:*", | ||
"@date-fns/tz": "^1.2.0", | ||
|
@@ -29,10 +30,14 @@ | |
"@prisma/instrumentation": "^6.3.1", | ||
"@tanstack/react-query": "^5.66.0", | ||
"@tanstack/react-table": "^8.21.2", | ||
"@tiptap/pm": "^2.11.5", | ||
"@tiptap/react": "^2.11.5", | ||
"@tiptap/starter-kit": "^2.11.5", | ||
"@trigger.dev/react-hooks": "3.3.16", | ||
"@trigger.dev/sdk": "3.3.16", | ||
"@uploadthing/react": "^7.2.0", | ||
"@upstash/ratelimit": "^2.0.5", | ||
"ai": "^4.1.41", | ||
"argon2": "^0.41.1", | ||
"bun": "^1.2.2", | ||
"crypto": "^1.0.1", | ||
|
@@ -3185,6 +3190,10 @@ | |
|
||
"cmdk/react-dom": ["[email protected]", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], | ||
|
||
"comp.ai/@ai-sdk/openai": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "@ai-sdk/provider-utils": "2.1.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-lh3zN4J/XEqkjpZAOBajSttF1Nl2qV/7WxRbORn+4jZmQkmzWQbsEUpgQ6ME+heyRvnsRCNq3fkMGehWWxWuXg=="], | ||
|
||
"comp.ai/ai": ["[email protected]", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "@ai-sdk/provider-utils": "2.1.8", "@ai-sdk/react": "1.1.16", "@ai-sdk/ui-utils": "1.1.14", "@opentelemetry/api": "1.9.0", "jsondiffpatch": "0.6.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.0.0" }, "optionalPeers": ["react", "zod"] }, "sha512-qQ5eVm5ivTij0/auLaoggfW3Y+IgWL0uNCCH79P8eUODeJTTqCRvB0B3iz9xl9b4uqOPcgZCVELgLfVODnCJ9g=="], | ||
|
||
"condense-newlines/kind-of": ["[email protected]", "", { "dependencies": { "is-buffer": "^1.1.5" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="], | ||
|
||
"data-urls/whatwg-url": ["[email protected]", "", { "dependencies": { "tr46": "^5.0.0", "webidl-conversions": "^7.0.0" } }, "sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ=="], | ||
|
@@ -3453,6 +3462,18 @@ | |
|
||
"cmdk/react-dom/scheduler": ["[email protected]", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], | ||
|
||
"comp.ai/@ai-sdk/openai/@ai-sdk/provider": ["@ai-sdk/[email protected]", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-q1PJEZ0qD9rVR+8JFEd01/QM++csMT5UVwYXSN2u54BrVw/D8TZLTeg2FEfKK00DgAx0UtWd8XOhhwITP9BT5g=="], | ||
|
||
"comp.ai/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "eventsource-parser": "^3.0.0", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.0.0" }, "optionalPeers": ["zod"] }, "sha512-1j9niMUAFlCBdYRYJr1yoB5kwZcRFBVuBiL1hhrf0ONFNrDiJYA6F+gROOuP16NHhezMfTo60+GeeV1xprHFjg=="], | ||
|
||
"comp.ai/ai/@ai-sdk/provider": ["@ai-sdk/[email protected]", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-q1PJEZ0qD9rVR+8JFEd01/QM++csMT5UVwYXSN2u54BrVw/D8TZLTeg2FEfKK00DgAx0UtWd8XOhhwITP9BT5g=="], | ||
|
||
"comp.ai/ai/@ai-sdk/provider-utils": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "eventsource-parser": "^3.0.0", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.0.0" }, "optionalPeers": ["zod"] }, "sha512-1j9niMUAFlCBdYRYJr1yoB5kwZcRFBVuBiL1hhrf0ONFNrDiJYA6F+gROOuP16NHhezMfTo60+GeeV1xprHFjg=="], | ||
|
||
"comp.ai/ai/@ai-sdk/react": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider-utils": "2.1.8", "@ai-sdk/ui-utils": "1.1.14", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.0.0" }, "optionalPeers": ["react", "zod"] }, "sha512-4Jx1piCte2+YoDd6ZdwM0Mw29046edw7MMNICImCPv2s7sfwFwe4c1t8waA4PYRefuETmzheqjh80kafQYJf8g=="], | ||
|
||
"comp.ai/ai/@ai-sdk/ui-utils": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "@ai-sdk/provider-utils": "2.1.8", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.0.0" }, "optionalPeers": ["zod"] }, "sha512-JQXcnPRnDfeH1l503s/8+SxJdmgyUKC3QvKjOpTV6Z/LyRWJZrruBoZnVB1OrL9o/WHEguC+rD+p9udv281KzQ=="], | ||
|
||
"data-urls/whatwg-url/tr46": ["[email protected]", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g=="], | ||
|
||
"express/debug/ms": ["[email protected]", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], | ||
|
@@ -3535,6 +3556,14 @@ | |
|
||
"cmdk/@radix-ui/react-dialog/@radix-ui/react-use-controllable-state/@radix-ui/react-use-callback-ref": ["@radix-ui/[email protected]", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg=="], | ||
|
||
"comp.ai/@ai-sdk/openai/@ai-sdk/provider-utils/eventsource-parser": ["[email protected]", "", {}, "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA=="], | ||
|
||
"comp.ai/@ai-sdk/openai/@ai-sdk/provider-utils/nanoid": ["[email protected]", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], | ||
|
||
"comp.ai/ai/@ai-sdk/provider-utils/eventsource-parser": ["[email protected]", "", {}, "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA=="], | ||
|
||
"comp.ai/ai/@ai-sdk/provider-utils/nanoid": ["[email protected]", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], | ||
|
||
"languine/@trigger.dev/sdk/@trigger.dev/core/@opentelemetry/instrumentation": ["@opentelemetry/[email protected]", "", { "dependencies": { "@opentelemetry/api-logs": "0.52.1", "@types/shimmer": "^1.0.2", "import-in-the-middle": "^1.8.1", "require-in-the-middle": "^7.1.1", "semver": "^7.5.2", "shimmer": "^1.2.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw=="], | ||
|
||
"languine/@trigger.dev/sdk/@trigger.dev/core/eventsource-parser": ["[email protected]", "", {}, "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA=="], | ||
|
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.
🛠️ Refactor suggestion
Add error handling for request body parsing.
The current implementation lacks error handling for JSON parsing and message validation.
📝 Committable suggestion