Skip to content

Commit

Permalink
Add Loops client to capture contact when a signup is created
Browse files Browse the repository at this point in the history
  • Loading branch information
andresgutgon committed Oct 28, 2024
1 parent 3bf4389 commit 86f01cb
Show file tree
Hide file tree
Showing 18 changed files with 3,056 additions and 2,002 deletions.
2 changes: 2 additions & 0 deletions apps/infra/Pulumi.core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ config:
secure: v1:4SUt0/rEtwZMk0ts:BarCGxQjAzapKjZlsyNw9Vb6lyMGiMAdPlYCkrpfd4SU+fEb
infra:LATITUDE_LLM_AWS_ACCESS_SECRET:
secure: v1:/+DMPM/zWsNsNHDg:JIlxC1v0ocfdfX1TsDXXTZClL8Uza74CMq5YSst7Ymu+CPulRNgUejrSQEFVW3F6BkXMA1QU3s4=
infra:LOOPS_API_KEY:
secure: v1:CfDlDBMg8qJ2lK4/:dB4mdVHD8neW+FjaUfQnscPyroBokUCFOX8Z2Yx4BoIhGJY25pmbiow8mDb4wLiK
infra:MAILER_API_KEY:
secure: v1:Br02wGwX0UOAZiNp:wIzQFfWCzWygBRCVJNDs/ZP65EXEAIFhduPOOniN6EOeXPyJ4UHjOefPyO2bhOBkR5E3CWLmQBsVjIGrGwAHZYZCQg==
infra:MAILGUN_MAILER_API_KEY:
Expand Down
4 changes: 4 additions & 0 deletions apps/infra/src/app/production/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const datasetGeneratorWorkspaceApiKeyArn = coreStack.requireOutput(
)
const supportAppIdArn = coreStack.requireOutput('supportAppIdArn')
const supportAppSecretKeyArn = coreStack.requireOutput('supportAppSecretKeyArn')
const loopsSecretApiKeyArn = coreStack.requireOutput('loopsSecretApiKeyArn')

const getSecretString = (arn: pulumi.Output<any>) => {
return arn.apply((secretId) =>
Expand Down Expand Up @@ -68,6 +69,7 @@ export const defaultProviderApiKey = getSecretString(defaultProviderApiKeyArn)
export const postHogApiKey = getSecretString(postHogApiKeyArn)
export const supportAppId = getSecretString(supportAppIdArn)
export const supportAppSecretKey = getSecretString(supportAppSecretKeyArn)
export const loopsSecretApiKey = getSecretString(loopsSecretApiKeyArn)
export const datasetGeneratorWorkspaceApiKey = getSecretString(
datasetGeneratorWorkspaceApiKeyArn,
)
Expand Down Expand Up @@ -109,6 +111,7 @@ export const environment = pulumi
copilotEvaluationSuggestionPromptPath,
supportAppId,
supportAppSecretKey,
loopsSecretApiKey,
])
.apply(() => {
return [
Expand Down Expand Up @@ -175,5 +178,6 @@ export const environment = pulumi
},
{ name: 'SUPPORT_APP_ID', value: supportAppId },
{ name: 'SUPPORT_APP_SECRET_KEY', value: supportAppSecretKey },
{ name: 'LOOPS_API_KEY', value: loopsSecretApiKey },
]
})
6 changes: 6 additions & 0 deletions apps/infra/src/core/secrets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ const supportAppSecretKey = createSecretWithVersion(
'SUPPORT_APP_SECRET_KEY',
'Support app secret key for the support chat',
)

const loopsSecreApiKey = createSecretWithVersion(
'LOOPS_API_KEY',
'Marketing tool loops app secret key',
)
export const mailerApiKeyArn = mailerApiKey.arn
export const mailgunMailerApiKeyArn = mailgunMailerApiKey.arn
export const sentryDsnArn = sentryDsn.arn
Expand All @@ -116,5 +121,6 @@ export const defaultProviderApiKeyArn = defaultProviderApiKey.arn
export const postHogApiKeyArn = postHogApiKey.arn
export const supportAppIdArn = supportAppId.arn
export const supportAppSecretKeyArn = supportAppSecretKey.arn
export const loopsSecretApiKeyArn = loopsSecreApiKey.arn
export const datasetGeneratorWorkspaceApiKeyArn =
datasetGeneratorWorkspaceApiKey.arn
20 changes: 20 additions & 0 deletions apps/web/src/services/user/setupService.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Providers, RewardType } from '@latitude-data/core/browser'
import { database } from '@latitude-data/core/client'
import { publisher } from '@latitude-data/core/events/publisher'
import { createProject, helpers } from '@latitude-data/core/factories'
import { Result } from '@latitude-data/core/lib/Result'
import {
Expand All @@ -21,6 +22,7 @@ import setupService from './setupService'
const mocks = vi.hoisted(() => ({
claimReward: vi.fn(),
}))
const publisherSpy = vi.spyOn(publisher, 'publishLater')

vi.mock('@latitude-data/core/services/claimedRewards/claim', () => ({
claimReward: mocks.claimReward,
Expand Down Expand Up @@ -83,6 +85,24 @@ describe('setupService', () => {
expect(createdProviderApiKey?.authorId).toBe(user.id)
})

it('publishes userCreated event', async () => {
const result = await setupService({
email: '[email protected]',
name: 'Test User',
companyName: 'Test Company',
})

const user = result.value?.user!
expect(publisherSpy).toHaveBeenCalledWith({
type: 'userCreated',
data: {
...user,
userEmail: user.email,
workspaceId: result.value?.workspace.id,
},
})
})

it('should import the default project when calling setup service', async () => {
const prompt = helpers.createPrompt({
provider: 'Latitude',
Expand Down
10 changes: 10 additions & 0 deletions apps/web/src/services/user/setupService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Providers, RewardType } from '@latitude-data/core/browser'
import { SessionData } from '@latitude-data/core/data-access'
import { publisher } from '@latitude-data/core/events/publisher'
import { Result } from '@latitude-data/core/lib/Result'
import Transaction, {
PromisedResult,
Expand Down Expand Up @@ -89,6 +90,15 @@ export default function setupService({
const result = Result.findError(results)
if (result) return result

publisher.publishLater({
type: 'userCreated',
data: {
...user,
workspaceId: workspace.id,
userEmail: user.email,
},
})

return Result.ok({
user,
workspace,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"nodemailer-html-to-text": "^3.2.0",
"nodemailer-mailgun-transport": "^2.1.5",
"pg": "^8.12.0",
"loops": "^3.3.0",
"posthog-node": "^4.2.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"@types/pg": "^8.11.6",
"@types/uuid": "^10.0.0",
"ai": "^3.4.7",
"loops": "^3.3.0",
"argon2": "^0.41.0",
"csv-parse": "^5.5.6",
"drizzle-kit": "^0.22.8",
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/events/events.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { LanguageModelUsage } from 'ai'

import {
import type {
ChainStepResponse,
Commit,
Dataset,
Expand All @@ -19,7 +19,7 @@ import {
StreamType,
User,
Workspace,
} from '../../browser'
} from '../browser'
import { PartialConfig } from '../services/ai'

export type Events =
Expand Down
43 changes: 43 additions & 0 deletions packages/core/src/events/handlers/createLoopsContact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Result } from '@latitude-data/core/lib/Result'
import { env } from '@latitude-data/env'
import { LoopsClient } from 'loops'

import { type UserCreatedEvent } from '../events'

function getApiKey() {
const apiKey = env.LOOPS_API_KEY

if (apiKey === undefined) {
throw new Error('LOOPS_API_KEY is not set')
}

return apiKey
}

export async function createLoopsContact({
data: event,
}: {
data: UserCreatedEvent
}) {
// In dev is an empty string
const apiKey = getApiKey()
if (apiKey === '') return Result.nil()

const client = new LoopsClient(apiKey)
const data = event.data
const response = await client.createContact(data.userEmail, {
userId: data.id,
firstName: data.name,
workspaceId: data.workspaceId,
source: 'latitudeLlmAppSignup',
userGroup: 'LLMs',
subscribed: true,
})

if (!response.success) {
// This will be capture by Workers Sentry
throw new Error(response.message)
}

return Result.ok(response.id)
}
3 changes: 2 additions & 1 deletion packages/core/src/events/handlers/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IEventsHandlers } from '../events'
import { createClaimInvitationReferralJob } from './createClaimInvitationReferralJob'
import { createLoopsContact } from './createLoopsContact'
import { notifyToClientDocumentLogCreatedJob } from './notifyToClientDocumentLogCreatedJob'
import { notifyToClientEvaluationResultCreatedJob } from './notifyToClientEvaluationResultCreatedJob'
import { runLiveEvaluationsJob } from './runLiveEvaluationsJob'
Expand Down Expand Up @@ -30,7 +31,7 @@ export const EventHandlers: IEventsHandlers = {
providerApiKeyCreated: [],
providerLogCreated: [],
sendReferralInvitation: [sendReferralInvitationJob],
userCreated: [],
userCreated: [createLoopsContact],
userInvited: [],
workspaceCreated: [],
documentRunRequested: [],
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/jobs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const QUEUES = {
'sendInvitationToUserJob',
'sendMagicLinkJob',
'sendReferralInvitationJob',
'createLoopsContact',
],
},
[Queues.liveEvaluationsQueue]: {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/jobs/job-definitions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from '../../events/handlers/notifyToClientEvaluationResultCreatedJob'
export * from '../../events/handlers/sendMagicLinkHandler'
export * from '../../events/handlers/sendInvitationToUser'
export * from '../../events/handlers/sendReferralInvitation'
export * from '../../events/handlers/createLoopsContact'
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MessageRole } from '@latitude-data/compiler'
import { ContentType, MessageRole } from '@latitude-data/compiler'
import * as factories from '@latitude-data/core/factories'
import { LanguageModelUsage, TextStreamPart } from 'ai'
import { beforeEach, describe, expect, it } from 'vitest'
Expand Down Expand Up @@ -57,7 +57,7 @@ describe('ProviderProcessor', () => {
messages: [
{
role: MessageRole.user,
content: 'MY TEXT',
content: [{ text: 'Hello', type: ContentType.text }],
},
],
toolCalls: [],
Expand Down
7 changes: 4 additions & 3 deletions packages/core/src/services/commits/runDocumentAtCommit.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ChainStepResponse,
Commit,
ErrorableEntity,
LogSources,
Expand All @@ -8,7 +9,7 @@ import {
} from '../../browser'
import { publisher } from '../../events/publisher'
import { generateUUIDIdentifier, Result } from '../../lib'
import { ChainResponse, runChain } from '../chains/run'
import { runChain } from '../chains/run'
import { createDocumentLog } from '../documentLogs'
import { getResolvedContent } from '../documents'
import { buildProvidersMap } from '../providerApiKeys/buildMap'
Expand Down Expand Up @@ -37,7 +38,7 @@ export async function createDocumentRunResult({
publishEvent: boolean
customIdentifier?: string
duration?: number
response?: ChainResponse<StreamType>
response?: ChainStepResponse<StreamType>
}) {
const durantionInMs = duration ?? 0
if (publishEvent) {
Expand Down Expand Up @@ -148,7 +149,7 @@ export async function runDocumentAtCommit({
resolvedContent: result.value,
customIdentifier,
source,
response,
response: response.value,
duration: await run.duration,
publishEvent: !response.error,
})
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/services/events/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { events } from '../../schema'

export async function createEvent(event: LatitudeEvent, db = database) {
return Transaction.call(async (tx) => {
let workspaceId: number | undefined
let workspaceId: number | undefined | null
if ('workspaceId' in event.data) {
workspaceId = event.data.workspaceId
}
Expand Down
2 changes: 2 additions & 0 deletions packages/env/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export const env = createEnv({
NEXT_PUBLIC_POSTHOG_HOST: z.string(),
NEXT_PUBLIC_POSTHOG_KEY: z.string(),
SUPPORT_APP_SECRET_KEY: z.string().optional(),
LOOPS_API_KEY: z.string().optional(),
NEXT_PUBLIC_SUPPORT_APP_ID: z.string().optional(),
NODE_ENV: z.string(),
QUEUE_HOST: z.string(),
Expand Down Expand Up @@ -124,5 +125,6 @@ export const env = createEnv({
QUEUE_PORT: process.env.QUEUE_PORT ?? '6379',
SUPPORT_APP_ID: process.env.SUPPORT_APP_ID ?? '',
SUPPORT_APP_SECRET_KEY: process.env.SUPPORT_APP_SECRET_KEY ?? '',
LOOPS_API_KEY: process.env.LOOPS_API_KEY ?? '',
},
})
Loading

0 comments on commit 86f01cb

Please sign in to comment.