Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ apps/web/scripts-dist

# File uploads in development
apps/web/uploads
apps/web/storage
apps/web/public/uploads
apps/web/public/storage
apps/web/public/files
uploads
storage

Expand Down
30 changes: 24 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,41 @@

### Action Layer (`apps/web/src/actions/`)

- Server actions use `authProcedure.createServerAction()` pattern
- Server actions use `authProcedure` pattern
- Input validation with Zod schemas
- Actions fetch model instances using repositories before calling services
- **Admin-only actions**: Place under `actions/admin/` directory for backoffice functionality
- Example pattern:

```typescript
export const updateApiKeyAction = authProcedure
.createServerAction()
.input(z.object({ id: z.number(), name: z.string() }))
.handler(async ({ input, ctx }) => {
.inputSchema(z.object({ id: z.number(), name: z.string() }))
.action(async ({ parsedInput, ctx }) => {
const repo = new Repository(ctx.workspace.id)
const model = await repo.find(input.id).then((r) => r.unwrap())
return updateService(model, { name: input.name }).then((r) => r.unwrap())
const model = await repo.find(parsedInput.id).then((r) => r.unwrap())
return updateService(model, { name: parsedInput.name }).then((r) =>
r.unwrap(),
)
})
```

- For writing an action with a different scope. Let's say withing projects:

```typescript
import { withProject, withProjectSchema } from '../../procedures'
export const updateProjectAction = withProject
.inputSchema(withProjectSchema.extend({ id: z.number(), name: z.string() }))
.action(async ({ parsedInput, ctx }) => {
const repo = new ProjectRepository(ctx.workspace.id)
const model = await repo.find(parsedInput.id).then((r) => r.unwrap())
return updateProjectService(model, { name: parsedInput.name }).then((r) =>
r.unwrap(),
)
})
```

`withProject` procedure inherits from `authProcedure` and adds project validation.

### Store Layer (`apps/web/src/stores/`)

- Use SWR for data fetching with custom hooks
Expand Down
2 changes: 1 addition & 1 deletion apps/gateway/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"dependencies": {
"@hono/node-server": "1.13.2",
"@hono/swagger-ui": "0.4.1",
"@hono/zod-openapi": "0.16.4",
"@hono/zod-openapi": "1.1.1",
"@latitude-data/constants": "workspace:^",
"@latitude-data/core": "workspace:^",
"@latitude-data/env": "workspace:^",
Expand Down
18 changes: 9 additions & 9 deletions apps/gateway/src/openApi/schemas/ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ export const languageModelUsageSchema = z.object({
export const toolCallSchema = z.object({
id: z.string(),
name: z.string(),
arguments: z.record(z.any()),
arguments: z.record(z.string(), z.any()),
})

export const configSchema = z.object({}).passthrough()
export const providerLogSchema = z.object({}).passthrough()
export const configSchema = z.record(z.string(), z.any())
export const providerLogSchema = z.record(z.string(), z.any())
export const chainStepResponseSchema = z.discriminatedUnion('streamType', [
z.object({
streamType: z.literal('text'),
Expand Down Expand Up @@ -58,7 +58,7 @@ export const chainEventDtoResponseSchema = z.discriminatedUnion('streamType', [
export const legacyChainEventDtoSchema = z.discriminatedUnion('event', [
z.object({
event: z.literal(StreamEventTypes.Provider),
data: z.object({}).passthrough(),
data: z.record(z.string(), z.any()),
}),
z.object({
event: z.literal(StreamEventTypes.Latitude),
Expand All @@ -79,7 +79,7 @@ export const legacyChainEventDtoSchema = z.discriminatedUnion('event', [
type: z.literal(LegacyChainEventTypes.Complete),
config: configSchema,
messages: z.array(messageSchema).optional(),
object: z.object({}).passthrough().optional(),
object: z.record(z.string(), z.any()).optional(),
response: chainEventDtoResponseSchema,
uuid: z.string().optional(),
}),
Expand All @@ -106,8 +106,8 @@ export const ProjectSchema = z.object({
id: z.number(),
name: z.string(),
workspaceId: z.number(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
lastEditedAt: z.string().datetime().optional(),
deletedAt: z.string().datetime().nullable().optional(),
createdAt: z.iso.datetime(),
updatedAt: z.iso.datetime(),
lastEditedAt: z.iso.datetime().optional(),
deletedAt: z.iso.datetime().nullable().optional(),
})
2 changes: 1 addition & 1 deletion apps/gateway/src/openApi/schemas/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { LogSources } from '@latitude-data/core/browser'
export const internalInfoSchema = z.object({
__internal: z
.object({
source: z.nativeEnum(LogSources).optional(),
source: z.enum(LogSources).optional(),
})
.optional(),
})
6 changes: 3 additions & 3 deletions apps/gateway/src/presenters/documentPresenter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ export const documentPresenterSchema = z.object({
path: z.string(),
content: z.string(),
contentHash: z.string().optional(),
config: z.object({}).passthrough(),
parameters: z.record(z.object({ type: z.nativeEnum(ParameterType) })),
provider: z.nativeEnum(Providers).optional(),
config: z.record(z.string(), z.any()),
parameters: z.record(z.string(), z.object({ type: z.enum(ParameterType) })),
provider: z.enum(Providers).optional(),
})
type Parameters = z.infer<typeof documentPresenterSchema>['parameters']

Expand Down
2 changes: 1 addition & 1 deletion apps/gateway/src/routes/api/v1/run/run.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const runRoute = createRoute({
schema: internalInfoSchema.extend({
path: z.string(),
customIdentifier: z.string().optional(),
parameters: z.record(z.any()).optional().default({}),
parameters: z.record(z.string(), z.any()).optional().default({}),
}),
},
},
Expand Down
2 changes: 1 addition & 1 deletion apps/gateway/src/routes/api/v2/documents/run/run.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const runRoute = createRoute({
path: z.string(),
stream: z.boolean().default(false),
customIdentifier: z.string().optional(),
parameters: z.record(z.any()).optional().default({}),
parameters: z.record(z.string(), z.any()).optional().default({}),
}),
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,15 @@ const step: ChainStepResponse<'text'> = {
streamType: 'text',
text: 'fake-response-text',
reasoning: undefined,
usage: { promptTokens: 4, completionTokens: 6, totalTokens: 10 },
usage: {
inputTokens: 4,
outputTokens: 6,
promptTokens: 4,
completionTokens: 6,
totalTokens: 10,
reasoningTokens: 0,
cachedInputTokens: 0,
},
toolCalls: [],
documentLogUuid: 'fake-document-log-uuid',
providerLog: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export const CommitSchema = z.object({
authorName: z.string().nullable(),
authorEmail: z.string().nullable(),
authorId: z.number().nullable(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
createdAt: z.iso.datetime(),
updatedAt: z.iso.datetime(),
status: z.string(),
parentCommitUuid: z.string().nullable(),
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ const documentLogSchema = z.object({
commitId: z.number(),
resolvedContent: z.string(),
contentHash: z.string(),
parameters: z.record(z.any()),
parameters: z.record(z.string(), z.any()),
customIdentifier: z.string().optional(),
duration: z.number().optional(),
source: z.nativeEnum(LogSources),
source: z.enum(LogSources),
createdAt: z.date(),
updatedAt: z.date(),
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const runRoute = createRoute({
path: z.string(),
stream: z.boolean().default(false),
customIdentifier: z.string().optional(),
parameters: z.record(z.any()).optional().default({}),
parameters: z.record(z.string(), z.any()).optional().default({}),
tools: z.array(z.string()).optional().default([]),
userMessage: z.string().optional(),
background: z.boolean().default(false),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export const VersionSchema = z.object({
authorName: z.string().nullable(),
aucommitUuidthorEmail: z.string().nullable(),
authorId: z.number().nullable(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
createdAt: z.iso.datetime(),
updatedAt: z.iso.datetime(),
status: z.string(),
parentCommitUuid: z.string().nullable(),
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export const VersionSchema = z.object({
version: z.number().nullable(),
userId: z.string(),
mergedAt: z.string().nullable(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
createdAt: z.iso.datetime(),
updatedAt: z.iso.datetime(),
deletedAt: z.string().nullable(),
})

Expand Down
10 changes: 5 additions & 5 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"test:watch": "TZ=UTC vitest"
},
"dependencies": {
"@ai-sdk/rsc": "^1.0.44",
"@aws-sdk/client-s3": "3.850.0",
"@datadog/browser-rum": "^6.21.1",
"@intercom/messenger-js-sdk": "0.0.14",
Expand All @@ -38,10 +39,10 @@
"@pilcrowjs/object-parser": "0.0.4",
"@pipedream/sdk": "^2.0.0",
"@sindresorhus/slugify": "2.2.1",
"@t3-oss/env-nextjs": "0.10.1",
"@t3-oss/env-nextjs": "0.13.8",
"@types/diff-match-patch": "1.0.36",
"@types/ip": "1.1.3",
"ai": "4.2.1",
"ai": "catalog:",
"arctic": "3.6.0",
"argon2": "0.44.0",
"bullmq": "5.44.4",
Expand All @@ -59,6 +60,7 @@
"monaco-editor": "0.50.0",
"nanoid": "5.0.9",
"next": "15.5.4",
"next-safe-action": "^8.0.11",
"next-themes": "0.3.0",
"nextjs-toploader": "1.6.12",
"nprogress": "0.2.0",
Expand All @@ -78,9 +80,6 @@
"use-debounce": "10.0.1",
"yaml": "2.4.5",
"zod": "catalog:",
"zod-to-json-schema": "3.24.5",
"zsa": "0.5.1",
"zsa-react": "0.2.3",
"zustand": "4.5.6"
},
"devDependencies": {
Expand All @@ -93,6 +92,7 @@
"@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^11.1.6",
"@standard-schema/spec": "^1.0.0",
"@testing-library/dom": "^10.3.2",
"@testing-library/react": "^16.0.0",
"@testing-library/react-hooks": "^8.0.1",
Expand Down
11 changes: 11 additions & 0 deletions apps/web/rollup.config.workers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ import terser from '@rollup/plugin-terser'
const isProduction = process.env.NODE_ENV === 'production'

export default defineConfig({
onwarn(warning, warn) {
if (
// Suppress circular dependency warnings from Zod v4.
// issue: https://github.com/colinhacks/zod/issues/5275
warning.code === 'CIRCULAR_DEPENDENCY' &&
/zod\/v4/.test(warning.message)
) {
return
}
warn(warning)
},
input: 'src/workers/readMetadata.ts',
output: {
entryFileNames: 'readMetadata.[hash].js',
Expand Down
19 changes: 7 additions & 12 deletions apps/web/src/actions/actions/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,18 @@ import { executeAction } from '@latitude-data/core/services/actions/execute'
import { z } from 'zod'
import { authProcedure, withRateLimit } from '../procedures'

export const executeBackendAction = (
await withRateLimit(authProcedure, {
limit: 10,
period: 60,
})
)
.createServerAction()
.input(
export const executeBackendAction = authProcedure
.use(withRateLimit({ limit: 10, period: 60 }))
.inputSchema(
z.object({
type: z.nativeEnum(ActionType),
type: z.enum(ActionType),
parameters: z.custom<ActionBackendParameters>(),
}),
)
.handler(async ({ input, ctx }) => {
.action(async ({ parsedInput, ctx }) => {
const result = await executeAction({
type: input.type,
parameters: input.parameters,
type: parsedInput.type,
parameters: parsedInput.parameters,
user: ctx.user,
workspace: ctx.workspace,
}).then((r) => r.unwrap())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ import { Result, TypedResult } from '@latitude-data/core/lib/Result'
import { Commit } from '@latitude-data/core/browser'

export const manualEmailTriggerAction = withAdmin
.createServerAction()
.input(
.inputSchema(
z.object({
recipient: z.string().email(),
senderEmail: z.string().email(),
recipient: z.string().pipe(z.email()),
senderEmail: z.string().pipe(z.email()),
senderName: z.string(),
subject: z.string(),
body: z.string(),
Expand All @@ -25,7 +24,7 @@ export const manualEmailTriggerAction = withAdmin
commitUuid: z.string().optional(),
}),
)
.handler(async ({ ctx, input }) => {
.action(async ({ ctx, parsedInput: input }) => {
let commitResult: TypedResult<Commit | undefined> = Result.ok(undefined)

if (input.commitUuid && input.projectId) {
Expand Down
11 changes: 5 additions & 6 deletions apps/web/src/actions/admin/features/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@ import { withAdmin } from '../../procedures'
import { createFeature } from '@latitude-data/core/services/features/create'

export const createFeatureAction = withAdmin
.createServerAction()
.input(
.inputSchema(
z.object({
name: z.string().min(1, { message: 'Name is required' }),
name: z.string().min(1, { error: 'Name is required' }),
description: z.string().optional(),
}),
)
.handler(async ({ input }) => {
.action(async ({ parsedInput }) => {
const result = await createFeature({
name: input.name,
description: input.description,
name: parsedInput.name,
description: parsedInput.description,
})

return result.unwrap()
Expand Down
13 changes: 5 additions & 8 deletions apps/web/src/actions/admin/features/destroy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,12 @@ import { destroyFeature } from '@latitude-data/core/services/features/destroy'
import { FeaturesRepository } from '@latitude-data/core/repositories/featuresRepository'

export const destroyFeatureAction = withAdmin
.createServerAction()
.input(
z.object({
id: z.number(),
}),
)
.handler(async ({ input }) => {
.inputSchema(z.object({ id: z.number() }))
.action(async ({ parsedInput }) => {
const featuresRepo = new FeaturesRepository()
const feature = await featuresRepo.find(input.id).then((r) => r.unwrap())
const feature = await featuresRepo
.find(parsedInput.id)
.then((r) => r.unwrap())

const result = await destroyFeature(feature)
return result.unwrap()
Expand Down
Loading
Loading