From d42df58c820324bcee251cca5029ec30d3ded0d2 Mon Sep 17 00:00:00 2001 From: Gerard Clos Date: Mon, 28 Oct 2024 12:05:27 +0100 Subject: [PATCH] feat(core): user logs - Introduced a new job `uploadDocumentLogsJob` to handle bulk uploads of document logs from CSV files. - Added `createDocumentLogJob` to create individual document logs from parsed CSV data. - Updated `messageSchema` and `messagesSchema` to validate message structures. - Implemented `bulkUploadDocumentLogs` service to parse CSV files and enqueue jobs for processing. - Modified `ProviderLogsRepository` to include additional joins for better data retrieval. - Enhanced `createProviderLog` to handle optional fields and avoid errors when certain data is missing. - Updated various components to handle nullable fields and provide default values where necessary. - Added new constants for delimiter values and maximum upload size. - Updated SDK to include a new `log` method for logging messages and responses. These changes enable users to upload large sets of document logs in bulk, improving efficiency and user experience. The new job definitions and services ensure that the logs are processed asynchronously, reducing the load on the main application. --- apps/gateway/package.json | 1 - apps/gateway/src/common/messageSchema.ts | 73 - apps/gateway/src/common/routes.ts | 32 - .../[conversationUuid]/handlers/chat.ts | 5 +- .../conversations/[conversationUuid]/index.ts | 3 +- .../versions/[versionUuid]/documents/index.ts | 7 +- .../[versionUuid]/documents/logs/index.ts | 8 + .../conversations/[conversationUuid]/index.ts | 3 +- .../versions/[versionUuid]/documents/index.ts | 7 +- .../documents/logs/handlers/post.ts | 50 + .../[versionUuid]/documents/logs/index.ts | 9 + apps/gateway/src/routes/app.ts | 19 +- apps/gateway/test/support/paths.ts | 6 - apps/web/src/actions/datasets/create.ts | 22 +- apps/web/src/actions/documentLogs/upload.ts | 50 + .../_components/DelimiterSelector/index.tsx | 26 +- .../src/app/(private)/datasets/new/page.tsx | 1 - .../EvaluationResultInfo/Metadata.tsx | 5 +- .../evaluations/[evaluationId]/layout.tsx | 11 +- .../DocumentLogs/DocumentLogInfo/Messages.tsx | 1 + .../DocumentLogs/DocumentLogInfo/Metadata.tsx | 145 +- .../documents/[documentUuid]/logs/page.tsx | 18 +- .../logs/upload/UploadLogModal.tsx | 125 + .../[documentUuid]/logs/upload/page.tsx | 19 + apps/web/src/app/_lib/formatUtils.ts | 3 +- apps/web/src/services/routes.ts | 1 + docs/assets/upload_logs_1.png | Bin 0 -> 52748 bytes .../evaluations/running-evaluations.mdx | 2 +- docs/guides/logs/upload-logs.mdx | 44 + docs/guides/sdk/javascript-typescript-sdk.mdx | 48 +- docs/mint.json | 5 +- examples/sdks/typescript/package.json | 17 + examples/sdks/typescript/push_logs.ts | 30 + examples/sdks/typescript/tsconfig.json | 6 + .../core/drizzle/0082_sparkling_redwing.sql | 6 + .../core/drizzle/0083_neat_johnny_storm.sql | 1 + .../core/drizzle/0084_special_whistler.sql | 1 + packages/core/drizzle/0085_silky_warstar.sql | 2 + .../drizzle/0086_polite_forgotten_one.sql | 8 + packages/core/drizzle/meta/0086_snapshot.json | 2989 +++++++++++++++++ packages/core/drizzle/meta/_journal.json | 7 + packages/core/src/constants.ts | 103 +- packages/core/src/jobs/constants.ts | 3 + .../documentLogs/createDocumentLogJob.ts | 46 + .../documentLogs/uploadDocumentLogsJob.ts | 42 + .../core/src/jobs/job-definitions/index.ts | 2 + .../core/src/jobs/job-definitions/types.d.ts | 2 + packages/core/src/lib/readCsv.ts | 8 +- .../getConnectedDocumentsWithMetadata.test.ts | 3 +- .../repositories/providerLogsRepository.ts | 17 +- packages/core/src/schema/messages.ts | 0 .../core/src/schema/models/documentLogs.ts | 2 +- .../core/src/schema/models/providerLogs.ts | 20 +- .../src/services/documentLogs/bulkUpload.ts | 32 + .../computeDocumentLogWithMetadata.test.ts | 10 +- .../core/src/services/documentLogs/create.ts | 35 +- .../core/src/services/documentLogs/index.ts | 1 + .../run/handleEvaluationResponse.ts | 2 +- .../src/services/providerLogs/addMessages.ts | 17 +- .../core/src/services/providerLogs/create.ts | 34 +- .../core/src/tests/factories/documentLogs.ts | 2 +- packages/sdks/typescript/package.json | 20 +- packages/sdks/typescript/src/index.ts | 46 +- packages/sdks/typescript/src/utils/index.ts | 4 + packages/sdks/typescript/src/utils/types.ts | 15 +- .../src/ds/atoms/DropzoneInput/index.tsx | 2 +- .../web-ui/src/ds/atoms/FormField/index.tsx | 16 +- .../src/ds/atoms/FormFieldGroup/index.tsx | 6 +- packages/web-ui/src/ds/atoms/Switch/index.tsx | 4 +- pnpm-lock.yaml | 106 +- 70 files changed, 4078 insertions(+), 338 deletions(-) delete mode 100644 apps/gateway/src/common/messageSchema.ts delete mode 100644 apps/gateway/src/common/routes.ts create mode 100644 apps/gateway/src/routes/api/v1/projects/[projectId]/versions/[versionUuid]/documents/logs/index.ts create mode 100644 apps/gateway/src/routes/api/v2/projects/[projectId]/versions/[versionUuid]/documents/logs/handlers/post.ts create mode 100644 apps/gateway/src/routes/api/v2/projects/[projectId]/versions/[versionUuid]/documents/logs/index.ts delete mode 100644 apps/gateway/test/support/paths.ts create mode 100644 apps/web/src/actions/documentLogs/upload.ts create mode 100644 apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/upload/UploadLogModal.tsx create mode 100644 apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/upload/page.tsx create mode 100644 docs/assets/upload_logs_1.png create mode 100644 docs/guides/logs/upload-logs.mdx create mode 100644 examples/sdks/typescript/package.json create mode 100644 examples/sdks/typescript/push_logs.ts create mode 100644 examples/sdks/typescript/tsconfig.json create mode 100644 packages/core/drizzle/0082_sparkling_redwing.sql create mode 100644 packages/core/drizzle/0083_neat_johnny_storm.sql create mode 100644 packages/core/drizzle/0084_special_whistler.sql create mode 100644 packages/core/drizzle/0085_silky_warstar.sql create mode 100644 packages/core/drizzle/0086_polite_forgotten_one.sql create mode 100644 packages/core/drizzle/meta/0086_snapshot.json create mode 100644 packages/core/src/jobs/job-definitions/documentLogs/createDocumentLogJob.ts create mode 100644 packages/core/src/jobs/job-definitions/documentLogs/uploadDocumentLogsJob.ts create mode 100644 packages/core/src/schema/messages.ts create mode 100644 packages/core/src/services/documentLogs/bulkUpload.ts diff --git a/apps/gateway/package.json b/apps/gateway/package.json index 251b65c57..a3abffe2b 100644 --- a/apps/gateway/package.json +++ b/apps/gateway/package.json @@ -26,7 +26,6 @@ "@t3-oss/env-core": "^0.10.1", "drizzle-orm": "^0.33.0", "hono": "^4.5.3", - "jet-paths": "^1.0.6", "lodash-es": "^4.17.21", "zod": "^3.23.8", "rate-limiter-flexible": "^5.0.3" diff --git a/apps/gateway/src/common/messageSchema.ts b/apps/gateway/src/common/messageSchema.ts deleted file mode 100644 index 5e2e3c3c6..000000000 --- a/apps/gateway/src/common/messageSchema.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { ContentType, MessageRole } from '@latitude-data/compiler' -import { z } from 'zod' - -const userContentSchema = z.array( - z - .object({ - type: z.literal(ContentType.text), - text: z.string(), - }) - .or( - z.object({ - type: z.literal(ContentType.image), - image: z - .string() - .or(z.instanceof(Uint8Array)) - .or(z.instanceof(Buffer)) - .or(z.instanceof(ArrayBuffer)) - .or(z.instanceof(URL)), - }), - ), -) - -// TODO: We could unify the way this schema is build. -// We could have this zod schema as base in a common package and -// infer compiler's types from it -export const messageSchema = z - .object({ - role: z.literal(MessageRole.system), - content: z.string(), - }) - .or( - z.object({ - role: z.literal(MessageRole.user), - name: z.string().optional(), - content: userContentSchema, - }), - ) - .or( - z.object({ - role: z.literal(MessageRole.assistant), - content: z.string().or( - z.array( - z.object({ - type: z.literal(ContentType.toolCall), - toolCallId: z.string(), - toolName: z.string(), - args: z.record(z.any()), - }), - ), - ), - toolCalls: z.array( - z.object({ - id: z.string(), - name: z.string(), - arguments: z.record(z.any()), - }), - ), - }), - ) - .or( - z.object({ - role: z.literal(MessageRole.tool), - content: z.array( - z.object({ - type: z.literal(ContentType.toolResult), - toolCallId: z.string(), - toolName: z.string(), - result: z.string(), - isError: z.boolean().optional(), - }), - ), - }), - ) diff --git a/apps/gateway/src/common/routes.ts b/apps/gateway/src/common/routes.ts deleted file mode 100644 index 61b659ecf..000000000 --- a/apps/gateway/src/common/routes.ts +++ /dev/null @@ -1,32 +0,0 @@ -const ROUTES = { - Base: '', - Api: { - Base: '/api', - V1: { - Base: '/v1', - Conversations: { - Base: '/conversations', - Chat: '/:conversationUuid/chat', - }, - Documents: { - Base: '/projects/:projectId/versions/:versionUuid/documents', - Get: '/:documentPath{.+}', - Run: '/run', - }, - }, - V2: { - Base: '/v2', - Conversations: { - Base: '/conversations', - Chat: '/:conversationUuid/chat', - }, - Documents: { - Base: '/projects/:projectId/versions/:versionUuid/documents', - Get: '/:documentPath{.+}', - Run: '/run', - }, - }, - }, -} - -export default ROUTES diff --git a/apps/gateway/src/routes/api/v1/conversations/[conversationUuid]/handlers/chat.ts b/apps/gateway/src/routes/api/v1/conversations/[conversationUuid]/handlers/chat.ts index dd5497b15..ef6c39af1 100644 --- a/apps/gateway/src/routes/api/v1/conversations/[conversationUuid]/handlers/chat.ts +++ b/apps/gateway/src/routes/api/v1/conversations/[conversationUuid]/handlers/chat.ts @@ -1,9 +1,8 @@ import { zValidator } from '@hono/zod-validator' -import { LogSources } from '@latitude-data/core/browser' +import { LogSources, messagesSchema } from '@latitude-data/core/browser' import { streamToGenerator } from '@latitude-data/core/lib/streamToGenerator' import { addMessages } from '@latitude-data/core/services/documentLogs/index' import { captureException } from '@sentry/node' -import { messageSchema } from '$/common/messageSchema' import { Factory } from 'hono/factory' import { streamSSE } from 'hono/streaming' import { z } from 'zod' @@ -13,7 +12,7 @@ import { chainEventPresenter } from '../../../projects/[projectId]/versions/[ver const factory = new Factory() const schema = z.object({ - messages: z.array(messageSchema), + messages: messagesSchema, __internal: z .object({ source: z.nativeEnum(LogSources).optional(), diff --git a/apps/gateway/src/routes/api/v1/conversations/[conversationUuid]/index.ts b/apps/gateway/src/routes/api/v1/conversations/[conversationUuid]/index.ts index 0097fb7db..fd5d47fb1 100644 --- a/apps/gateway/src/routes/api/v1/conversations/[conversationUuid]/index.ts +++ b/apps/gateway/src/routes/api/v1/conversations/[conversationUuid]/index.ts @@ -1,8 +1,7 @@ -import ROUTES from '$/common/routes' import { Hono } from 'hono' import { chatHandler } from './handlers/chat' export const chatsRouter = new Hono() -chatsRouter.post(ROUTES.Api.V1.Conversations.Chat, ...chatHandler) +chatsRouter.post('/:conversationUuid/chat', ...chatHandler) diff --git a/apps/gateway/src/routes/api/v1/projects/[projectId]/versions/[versionUuid]/documents/index.ts b/apps/gateway/src/routes/api/v1/projects/[projectId]/versions/[versionUuid]/documents/index.ts index 7f0cc80a5..d7b0488ce 100644 --- a/apps/gateway/src/routes/api/v1/projects/[projectId]/versions/[versionUuid]/documents/index.ts +++ b/apps/gateway/src/routes/api/v1/projects/[projectId]/versions/[versionUuid]/documents/index.ts @@ -1,12 +1,13 @@ -import ROUTES from '$/common/routes' import { Hono } from 'hono' import { getHandler } from './handlers/get' import { runHandler } from './handlers/run' +import { logsRouterV1 } from './logs' const router = new Hono() -router.get(ROUTES.Api.V1.Documents.Get, ...getHandler) -router.post(ROUTES.Api.V1.Documents.Run, ...runHandler) +router.get('/:documentPath{.+}', ...getHandler) +router.post('/run', ...runHandler) +router.route('/logs', logsRouterV1) export { router as documentsRouter } diff --git a/apps/gateway/src/routes/api/v1/projects/[projectId]/versions/[versionUuid]/documents/logs/index.ts b/apps/gateway/src/routes/api/v1/projects/[projectId]/versions/[versionUuid]/documents/logs/index.ts new file mode 100644 index 000000000..c86c159b8 --- /dev/null +++ b/apps/gateway/src/routes/api/v1/projects/[projectId]/versions/[versionUuid]/documents/logs/index.ts @@ -0,0 +1,8 @@ +import { postHandler } from '$/routes/api/v2/projects/[projectId]/versions/[versionUuid]/documents/logs/handlers/post' +import { Hono } from 'hono' + +const router = new Hono() + +router.post('/', ...postHandler) + +export { router as logsRouterV1 } diff --git a/apps/gateway/src/routes/api/v2/conversations/[conversationUuid]/index.ts b/apps/gateway/src/routes/api/v2/conversations/[conversationUuid]/index.ts index e36f7679c..1472edaa0 100644 --- a/apps/gateway/src/routes/api/v2/conversations/[conversationUuid]/index.ts +++ b/apps/gateway/src/routes/api/v2/conversations/[conversationUuid]/index.ts @@ -1,7 +1,6 @@ -import ROUTES from '$/common/routes' import { chatHandler } from '$/routes/api/v1/conversations/[conversationUuid]/handlers/chat' import { Hono } from 'hono' export const chatsRouter = new Hono() -chatsRouter.post(ROUTES.Api.V2.Conversations.Chat, ...chatHandler) +chatsRouter.post('/:conversationUuid/chat', ...chatHandler) diff --git a/apps/gateway/src/routes/api/v2/projects/[projectId]/versions/[versionUuid]/documents/index.ts b/apps/gateway/src/routes/api/v2/projects/[projectId]/versions/[versionUuid]/documents/index.ts index c1c08c800..0ac69e9c6 100644 --- a/apps/gateway/src/routes/api/v2/projects/[projectId]/versions/[versionUuid]/documents/index.ts +++ b/apps/gateway/src/routes/api/v2/projects/[projectId]/versions/[versionUuid]/documents/index.ts @@ -1,12 +1,13 @@ -import ROUTES from '$/common/routes' import { getHandler } from '$/routes/api/v1/projects/[projectId]/versions/[versionUuid]/documents/handlers/get' import { Hono } from 'hono' import { runHandler } from './handlers/run' +import { logsRouterV2 } from './logs' const router = new Hono() -router.get(ROUTES.Api.V2.Documents.Get, ...getHandler) -router.post(ROUTES.Api.V2.Documents.Run, ...runHandler) +router.get('/:documentPath{.+}', ...getHandler) +router.post('/run', ...runHandler) +router.route('/logs', logsRouterV2) export { router as documentsRouter } diff --git a/apps/gateway/src/routes/api/v2/projects/[projectId]/versions/[versionUuid]/documents/logs/handlers/post.ts b/apps/gateway/src/routes/api/v2/projects/[projectId]/versions/[versionUuid]/documents/logs/handlers/post.ts new file mode 100644 index 000000000..265b69c07 --- /dev/null +++ b/apps/gateway/src/routes/api/v2/projects/[projectId]/versions/[versionUuid]/documents/logs/handlers/post.ts @@ -0,0 +1,50 @@ +import { zValidator } from '@hono/zod-validator' +import { LogSources, messagesSchema } from '@latitude-data/core/browser' +import { generateUUIDIdentifier } from '@latitude-data/core/lib/generateUUID' +import { createDocumentLog } from '@latitude-data/core/services/documentLogs/create' +import { getData } from '$/routes/api/v1/projects/[projectId]/versions/[versionUuid]/documents/handlers/_shared' +import { Factory } from 'hono/factory' +import { z } from 'zod' + +const factory = new Factory() + +const schema = z.object({ + path: z.string(), + messages: messagesSchema, + response: z.string().optional(), +}) + +export const postHandler = factory.createHandlers( + zValidator('json', schema), + async (c) => { + const { projectId, versionUuid } = c.req.param() + const { path, messages, response } = c.req.valid('json') + const workspace = c.get('workspace') + const { document, commit } = await getData({ + workspace, + projectId: Number(projectId!), + commitUuid: versionUuid!, + documentPath: path!, + }).then((r) => r.unwrap()) + + const documentLog = await createDocumentLog({ + data: { + uuid: generateUUIDIdentifier(), + documentUuid: document.documentUuid, + resolvedContent: document.content, + source: LogSources.API, + parameters: {}, + providerLog: { + messages, + responseText: + response ?? + (messages[messages.length - 1].content?.text || + messages[messages.length - 1].content), + }, + }, + commit, + }).then((r) => r.unwrap()) + + return c.json(documentLog) + }, +) diff --git a/apps/gateway/src/routes/api/v2/projects/[projectId]/versions/[versionUuid]/documents/logs/index.ts b/apps/gateway/src/routes/api/v2/projects/[projectId]/versions/[versionUuid]/documents/logs/index.ts new file mode 100644 index 000000000..94d12e74d --- /dev/null +++ b/apps/gateway/src/routes/api/v2/projects/[projectId]/versions/[versionUuid]/documents/logs/index.ts @@ -0,0 +1,9 @@ +import { Hono } from 'hono' + +import { postHandler } from './handlers/post' + +const router = new Hono() + +router.post('/', ...postHandler) + +export { router as logsRouterV2 } diff --git a/apps/gateway/src/routes/app.ts b/apps/gateway/src/routes/app.ts index 3d614340f..5ec73525c 100644 --- a/apps/gateway/src/routes/app.ts +++ b/apps/gateway/src/routes/app.ts @@ -1,10 +1,8 @@ -import ROUTES from '$/common/routes' import authMiddleware from '$/middlewares/auth' import errorHandlerMiddleware from '$/middlewares/errorHandler' import rateLimitMiddleware from '$/middlewares/rateLimit' import { Hono } from 'hono' import { logger } from 'hono/logger' -import jetPaths from 'jet-paths' import { chatsRouter as chatsRouterV1 } from './api/v1/conversations/[conversationUuid]' import { documentsRouter as documentsRouterV1 } from './api/v1/projects/[projectId]/versions/[versionUuid]/documents' @@ -26,10 +24,19 @@ app.use(rateLimitMiddleware()) app.use(authMiddleware()) // Routers -app.route(jetPaths(ROUTES).Api.V1.Documents.Base, documentsRouterV1) -app.route(jetPaths(ROUTES).Api.V1.Conversations.Base, chatsRouterV1) -app.route(jetPaths(ROUTES).Api.V2.Documents.Base, documentsRouterV2) -app.route(jetPaths(ROUTES).Api.V2.Conversations.Base, chatsRouterV2) +// v1 +app.route( + '/api/v1/projects/:projectId/versions/:versionUuid/documents', + documentsRouterV1, +) +app.route('/api/v1/conversations', chatsRouterV1) + +// v2 +app.route( + '/api/v2/projects/:projectId/versions/:versionUuid/documents', + documentsRouterV2, +) +app.route('/api/v2/conversations', chatsRouterV2) // Must be the last one! app.onError(errorHandlerMiddleware) diff --git a/apps/gateway/test/support/paths.ts b/apps/gateway/test/support/paths.ts deleted file mode 100644 index c9c3e80af..000000000 --- a/apps/gateway/test/support/paths.ts +++ /dev/null @@ -1,6 +0,0 @@ -import ROUTES from '$/common/routes' -import jetPaths from 'jet-paths' - -const paths = jetPaths(ROUTES) - -export default paths diff --git a/apps/web/src/actions/datasets/create.ts b/apps/web/src/actions/datasets/create.ts index c4034acf6..62da648bf 100644 --- a/apps/web/src/actions/datasets/create.ts +++ b/apps/web/src/actions/datasets/create.ts @@ -1,27 +1,17 @@ 'use server' +import { + DELIMITER_VALUES, + DELIMITERS_KEYS, + MAX_SIZE, + MAX_UPLOAD_SIZE_IN_MB, +} from '@latitude-data/core/browser' import { DatasetsRepository } from '@latitude-data/core/repositories' import { createDataset } from '@latitude-data/core/services/datasets/create' import { z } from 'zod' import { authProcedure } from '../procedures' -const DELIMITERS_KEYS = [ - 'comma', - 'semicolon', - 'tab', - 'space', - 'custom', -] as const -const DELIMITER_VALUES = { - comma: ',', - semicolon: ';', - tab: '\t', - space: ' ', -} - -const MAX_SIZE = 15 -const MAX_UPLOAD_SIZE_IN_MB = MAX_SIZE * 1024 * 1024 export const createDatasetAction = authProcedure .createServerAction() .input( diff --git a/apps/web/src/actions/documentLogs/upload.ts b/apps/web/src/actions/documentLogs/upload.ts new file mode 100644 index 000000000..9473dea21 --- /dev/null +++ b/apps/web/src/actions/documentLogs/upload.ts @@ -0,0 +1,50 @@ +'use server' + +import { + DELIMITERS_KEYS, + MAX_SIZE, + MAX_UPLOAD_SIZE_IN_MB, +} from '@latitude-data/core/browser' +import { CommitsRepository } from '@latitude-data/core/repositories' +import { bulkUploadDocumentLogs } from '@latitude-data/core/services/documentLogs/bulkUpload' +import { z } from 'zod' + +import { withDocument } from '../procedures' + +export const uploadDocumentLogsAction = withDocument + .createServerAction() + .input( + z.object({ + csvDelimiter: z.enum(DELIMITERS_KEYS, { + message: 'Choose a valid delimiter option', + }), + logsFile: z + .instanceof(File) + .refine((file) => { + return !file || file.size <= MAX_UPLOAD_SIZE_IN_MB + }, `Your file must be less than ${MAX_SIZE}MB in size`) + .refine( + (file) => file.type === 'text/csv', + 'Your file must be a CSV file', + ), + }), + ) + .handler(async ({ ctx, input }) => { + const commitsScope = new CommitsRepository(ctx.workspace.id) + const commit = await commitsScope + .getCommitByUuid({ + projectId: ctx.project.id, + uuid: ctx.currentCommitUuid, + }) + .then((r) => r.unwrap()) + + await bulkUploadDocumentLogs({ + workspace: ctx.workspace, + document: ctx.document, + commit, + csvDelimiter: input.csvDelimiter, + logsFile: input.logsFile, + }) + + return { success: true } + }) diff --git a/apps/web/src/app/(private)/datasets/new/_components/DelimiterSelector/index.tsx b/apps/web/src/app/(private)/datasets/new/_components/DelimiterSelector/index.tsx index 59dac5e29..56788c781 100644 --- a/apps/web/src/app/(private)/datasets/new/_components/DelimiterSelector/index.tsx +++ b/apps/web/src/app/(private)/datasets/new/_components/DelimiterSelector/index.tsx @@ -13,16 +13,22 @@ export enum DelimiterEnum { } const DELIMITERS = [ - { value: DelimiterEnum.Comma, label: 'Comma (ex.: column1,column2,column3)' }, + { + value: DelimiterEnum.Comma, + label: 'Comma (e.g.: column1,column2,column3)', + }, { value: DelimiterEnum.Semicolon, - label: 'Semicolon (ex.: column1;column2;column3)', + label: 'Semicolon (e.g.: column1;column2;column3)', }, { value: DelimiterEnum.Tab, - label: 'Tab (ex.: column1 \\t column2 \\t column3)', + label: 'Tab (e.g.: column1 \\t column2 \\t column3)', + }, + { + value: DelimiterEnum.Space, + label: 'Space (e.g.: column1 column2 column3)', }, - { value: DelimiterEnum.Space, label: 'Space (ex.: column1 column2 column3)' }, { value: DelimiterEnum.Custom, label: 'Custom' }, ] export const DELIMITER_KEYS = DELIMITERS.map(({ value }) => value) @@ -32,15 +38,15 @@ export default function DelimiterSelector({ customDelimiterInputName, delimiterErrors, customDelimiterErrors, - delimiterValue, + delimiterDefaultValue, customDelimiterValue, }: { delimiterInputName: string - delimiterValue: string | undefined - delimiterErrors: string[] | undefined + delimiterDefaultValue?: string | undefined + delimiterErrors?: string[] | undefined customDelimiterInputName: string - customDelimiterValue: string - customDelimiterErrors: string[] | undefined + customDelimiterValue?: string + customDelimiterErrors?: string[] | undefined }) { const inputRef = useRef(null) const [isCustom, setIsCustom] = useState(false) @@ -60,7 +66,7 @@ export default function DelimiterSelector({ + + + +
+ +
+ + Upload a csv file with your prompt logs.{' '} + + Learn more + + +
+
+ + + + + ) +} diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/upload/page.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/upload/page.tsx new file mode 100644 index 000000000..906855598 --- /dev/null +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/upload/page.tsx @@ -0,0 +1,19 @@ +import UploadLogModal from './UploadLogModal' + +export default function UploadLogModalPage({ + params, +}: { + params: { + documentUuid: string + projectId: string + commitUuid: string + } +}) { + return ( + + ) +} diff --git a/apps/web/src/app/_lib/formatUtils.ts b/apps/web/src/app/_lib/formatUtils.ts index 4d1c473f6..39dc1c665 100644 --- a/apps/web/src/app/_lib/formatUtils.ts +++ b/apps/web/src/app/_lib/formatUtils.ts @@ -15,7 +15,8 @@ export function relativeTime(date: Date | null) { return format(date, 'PPpp') } -export function formatDuration(duration: number) { +export function formatDuration(duration?: number | null) { + if (!duration) return '-' if (duration < MINUTES) return `${(duration / SECONDS).toFixed(3)}s` const hours = Math.floor(duration / HOURS) diff --git a/apps/web/src/services/routes.ts b/apps/web/src/services/routes.ts index 443415e20..ab7addb85 100644 --- a/apps/web/src/services/routes.ts +++ b/apps/web/src/services/routes.ts @@ -134,6 +134,7 @@ export const ROUTES = { }, [DocumentRoutes.logs]: { root: `${root}/${DocumentRoutes.logs}`, + upload: `${root}/${DocumentRoutes.logs}/upload`, }, } }, diff --git a/docs/assets/upload_logs_1.png b/docs/assets/upload_logs_1.png new file mode 100644 index 0000000000000000000000000000000000000000..64897efcd7920babccd833ccb7d75b3efb805608 GIT binary patch literal 52748 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/guides/evaluations/running-evaluations.mdx b/docs/guides/evaluations/running-evaluations.mdx index 97a98a58b..9f5461458 100644 --- a/docs/guides/evaluations/running-evaluations.mdx +++ b/docs/guides/evaluations/running-evaluations.mdx @@ -42,4 +42,4 @@ Click the "Run evaluation" button to start the evaluation process. You'll see th Evaluations running in live mode will run on all new logs generated in your project. This is useful if you want to monitor the performance of your prompts in real-time. -We recommend keeping a few key evaluations running in live mode to spot degradations in response quality as soon as they happen. Sometimes new model releases or changes in parameters can lead to a drop in response quality, so this is a good way to catch those issues early. \ No newline at end of file +We recommend keeping a few key evaluations running in live mode to spot degradations in response quality as soon as they happen. Sometimes new model releases or changes in parameters can lead to a drop in response quality, so this is a good way to catch those issues early. diff --git a/docs/guides/logs/upload-logs.mdx b/docs/guides/logs/upload-logs.mdx new file mode 100644 index 000000000..53392b187 --- /dev/null +++ b/docs/guides/logs/upload-logs.mdx @@ -0,0 +1,44 @@ +--- +title: Upload logs +description: 'Learn how to upload logs to Latitude and start evaluating your production systems.' +--- + +## Upload logs to Latitude + +Most users use Latitude's SDK to run their prompts via Latitude's AI Gateway. However, if you're using a different method to run your prompts, you can still upload logs to Latitude and evalute them in order to assess the quality of your outputs. + +### Upload logs from Latitude's Dashboard + +Navigate to your prompt's logs section and click on the **Upload logs** button. The following screen will appear: + +![Upload logs](/images/guides/logs/upload_logs_1.png) + +You can upload a list of logs in a single csv file. The CSV file should be a single column csv where each row is a log, i.e a user-assitant interaction. [Here's an example](https://docs.google.com/spreadsheets/d/1uxmUW2XhcqRB_cK0SBmHzUfa9xMqVzKZ0eT8umO8pr8/edit?usp=sharing) of a valid CSV file: + +```csv +"[{""role"":""system"",""content"":""Tell me a joke about School""},{""role"": ""assistant"", ""content"": ""Why did the math book look sad? Because it had too many problems!""}]" +"[{""role"":""system"",""content"":""Tell me a joke about Travel""}],{""role"": ""assistant"", ""content"": ""Why did the hiker refuse to eat pizza on the way to the top of the mountain? Because he was afraid of heights and the pizza was too big!""}]" +"[{""role"":""system"",""content"":""Tell me a joke about Work""}],{""role"": ""assistant"", ""content"": ""Why did the worker get fired from his job? Because he was not properly training for it!""}]" +"[{""role"":""system"",""content"":""Tell me a joke about Sports""}],{""role"": ""assistant"", ""content"": ""Why did the sports star quit his job? Because he was not properly training for it!""}]" +``` + + + Logs follow OpenAI's [ChatCompletion + format](https://platform.openai.com/docs/guides/text-generation/building-prompts). + If you're using a different method to run your prompts, you'll need to format + your logs accordingly. + + +Do not include headers in your CSV file. + +Once you've uploaded your logs, they will quickly appear in your prompt's logs section. + +### Evaluating uploaded logs + +To evaluate your uploaded logs, make sure to first create and connect an evaluation to your prompt. Also, make sure the evaluation is configured to [evaluate live logs](/guides/evaluations/running-evaluations). + +Once you've correctly configured your evaluation, just upload your logs as described above and they will be automatically evaluated in real time. + +### Upload logs from the SDK + +You can push logs to Latitude with Latitude's SDK. Check out Latitude' SDK [`log` method](/guides/sdk/javascript-typescript-sdk). diff --git a/docs/guides/sdk/javascript-typescript-sdk.mdx b/docs/guides/sdk/javascript-typescript-sdk.mdx index aeb1778ba..1fe80ab46 100644 --- a/docs/guides/sdk/javascript-typescript-sdk.mdx +++ b/docs/guides/sdk/javascript-typescript-sdk.mdx @@ -122,7 +122,53 @@ onError: (error) => { } ``` -## More examples +## Pushing a log to Latitude + +You can push a log to Latitude in order to evaluate it, using the `log` method: + +```typescript +const sdk = new Latitude(process.env.LATITUDE_API_KEY, { + projectId: 1, +}) +const openai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, +}) + +const messages = [ + { + role: 'user' as MessageRole.user, + content: [ + { + type: 'text' as ContentType.text, + text: 'Please tell me a joke about doctors', + }, + ], + }, +] +const chatCompletion = await openai.chat.completions.create({ + messages, + model: 'gpt-4o-mini', +}) + +// Push the log to the live version of our prompt called 'joker' +sdk.log('joker', messages, { + response: chatCompletion.choices[0].message.content, +}) +``` + + + Logs follow OpenAI's + [format](https://platform.openai.com/docs/guides/text-generation/building-prompts). + If you're using a different method to run your prompts, you'll need to format + your logs accordingly. + + + + If you include the assistant response in the optional `response` parameter, + make sure to not include it in the log so it isn't included twice. + + +## Common examples ### Executing tool calls diff --git a/docs/mint.json b/docs/mint.json index f668311ea..2fa0074a0 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -64,7 +64,10 @@ }, { "group": "Logs", - "pages": ["guides/logs/overview"] + "pages": [ + "guides/logs/overview", + "guides/logs/upload-logs" + ] }, { "group": "Datasets", diff --git a/examples/sdks/typescript/package.json b/examples/sdks/typescript/package.json new file mode 100644 index 000000000..c9e72f6fd --- /dev/null +++ b/examples/sdks/typescript/package.json @@ -0,0 +1,17 @@ +{ + "name": "typescript-examples", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@latitude-data/sdk": "workspace:^", + "openai": "^4.68.4" + } +} diff --git a/examples/sdks/typescript/push_logs.ts b/examples/sdks/typescript/push_logs.ts new file mode 100644 index 000000000..6f232149b --- /dev/null +++ b/examples/sdks/typescript/push_logs.ts @@ -0,0 +1,30 @@ +import { ContentType, Latitude, MessageRole } from '@latitude-data/sdk' +import OpenAI from 'openai' + +const sdk = new Latitude(process.env.LATITUDE_API_KEY, { + projectId: 1, +}) +const openai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, +}) + +const messages = [ + { + role: 'user' as MessageRole.user, + content: [ + { + type: 'text' as ContentType.text, + text: 'Please tell me a joke about doctors', + }, + ], + }, +] +const chatCompletion = await openai.chat.completions.create({ + messages, + model: 'gpt-4o-mini', +}) + +// Push the log to the live version of our prompt called 'joker' +sdk.log('joker', messages, { + response: chatCompletion.choices[0].message.content, +}) diff --git a/examples/sdks/typescript/tsconfig.json b/examples/sdks/typescript/tsconfig.json new file mode 100644 index 000000000..41254be94 --- /dev/null +++ b/examples/sdks/typescript/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "module": "node16", + "moduleResolution": "node16" + } +} diff --git a/packages/core/drizzle/0082_sparkling_redwing.sql b/packages/core/drizzle/0082_sparkling_redwing.sql new file mode 100644 index 000000000..6e242d5c0 --- /dev/null +++ b/packages/core/drizzle/0082_sparkling_redwing.sql @@ -0,0 +1,6 @@ +ALTER TABLE "latitude"."provider_logs" ALTER COLUMN "provider_id" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "latitude"."provider_logs" ALTER COLUMN "finish_reason" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "latitude"."provider_logs" ALTER COLUMN "config" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "latitude"."provider_logs" ALTER COLUMN "messages" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "latitude"."provider_logs" ALTER COLUMN "duration" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "latitude"."provider_logs" ALTER COLUMN "generated_at" DROP NOT NULL; \ No newline at end of file diff --git a/packages/core/drizzle/0083_neat_johnny_storm.sql b/packages/core/drizzle/0083_neat_johnny_storm.sql new file mode 100644 index 000000000..231b3f26d --- /dev/null +++ b/packages/core/drizzle/0083_neat_johnny_storm.sql @@ -0,0 +1 @@ +ALTER TABLE "latitude"."provider_logs" ALTER COLUMN "tokens" DROP NOT NULL; \ No newline at end of file diff --git a/packages/core/drizzle/0084_special_whistler.sql b/packages/core/drizzle/0084_special_whistler.sql new file mode 100644 index 000000000..2b48a204e --- /dev/null +++ b/packages/core/drizzle/0084_special_whistler.sql @@ -0,0 +1 @@ +ALTER TABLE "latitude"."document_logs" ALTER COLUMN "duration" DROP NOT NULL; \ No newline at end of file diff --git a/packages/core/drizzle/0085_silky_warstar.sql b/packages/core/drizzle/0085_silky_warstar.sql new file mode 100644 index 000000000..a58eadb2f --- /dev/null +++ b/packages/core/drizzle/0085_silky_warstar.sql @@ -0,0 +1,2 @@ +ALTER TYPE "latitude"."log_source" ADD VALUE 'user';--> statement-breakpoint +ALTER TABLE "latitude"."provider_logs" ALTER COLUMN "messages" SET NOT NULL; \ No newline at end of file diff --git a/packages/core/drizzle/0086_polite_forgotten_one.sql b/packages/core/drizzle/0086_polite_forgotten_one.sql new file mode 100644 index 000000000..ed54411b0 --- /dev/null +++ b/packages/core/drizzle/0086_polite_forgotten_one.sql @@ -0,0 +1,8 @@ +ALTER TYPE "latitude"."log_source" ADD VALUE 'user';--> statement-breakpoint +ALTER TABLE "latitude"."document_logs" ALTER COLUMN "duration" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "latitude"."provider_logs" ALTER COLUMN "provider_id" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "latitude"."provider_logs" ALTER COLUMN "finish_reason" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "latitude"."provider_logs" ALTER COLUMN "config" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "latitude"."provider_logs" ALTER COLUMN "tokens" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "latitude"."provider_logs" ALTER COLUMN "duration" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "latitude"."provider_logs" ALTER COLUMN "generated_at" DROP NOT NULL; \ No newline at end of file diff --git a/packages/core/drizzle/meta/0086_snapshot.json b/packages/core/drizzle/meta/0086_snapshot.json new file mode 100644 index 000000000..05966161a --- /dev/null +++ b/packages/core/drizzle/meta/0086_snapshot.json @@ -0,0 +1,2989 @@ +{ + "id": "996c7c35-30de-44dc-be7b-168c6117d59d", + "prevId": "4939be1e-dbd6-4a33-8fed-e33835c84bb4", + "version": "7", + "dialect": "postgresql", + "tables": { + "latitude.users": { + "name": "users", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "admin": { + "name": "admin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + } + }, + "latitude.sessions": { + "name": "sessions", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "schemaTo": "latitude", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "latitude.workspaces": { + "name": "workspaces", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "current_subscription_id": { + "name": "current_subscription_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "creator_id": { + "name": "creator_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workspaces_current_subscription_id_subscriptions_id_fk": { + "name": "workspaces_current_subscription_id_subscriptions_id_fk", + "tableFrom": "workspaces", + "tableTo": "subscriptions", + "schemaTo": "latitude", + "columnsFrom": [ + "current_subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workspaces_creator_id_users_id_fk": { + "name": "workspaces_creator_id_users_id_fk", + "tableFrom": "workspaces", + "tableTo": "users", + "schemaTo": "latitude", + "columnsFrom": [ + "creator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "latitude.subscriptions": { + "name": "subscriptions", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "subscription_plans", + "typeSchema": "latitude", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "subscriptions_workspace_id_index": { + "name": "subscriptions_workspace_id_index", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "subscriptions_plan_index": { + "name": "subscriptions_plan_index", + "columns": [ + { + "expression": "plan", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "latitude.memberships": { + "name": "memberships", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invitation_token": { + "name": "invitation_token", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "memberships_workspace_id_user_id_index": { + "name": "memberships_workspace_id_user_id_index", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memberships_invitation_token_index": { + "name": "memberships_invitation_token_index", + "columns": [ + { + "expression": "invitation_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memberships_workspace_id_workspaces_id_fk": { + "name": "memberships_workspace_id_workspaces_id_fk", + "tableFrom": "memberships", + "tableTo": "workspaces", + "schemaTo": "latitude", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "memberships_user_id_users_id_fk": { + "name": "memberships_user_id_users_id_fk", + "tableFrom": "memberships", + "tableTo": "users", + "schemaTo": "latitude", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "memberships_invitation_token_unique": { + "name": "memberships_invitation_token_unique", + "nullsNotDistinct": false, + "columns": [ + "invitation_token" + ] + } + } + }, + "latitude.api_keys": { + "name": "api_keys", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workspace_id": { + "name": "workspace_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_id_idx": { + "name": "workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "api_keys_workspace_id_workspaces_id_fk": { + "name": "api_keys_workspace_id_workspaces_id_fk", + "tableFrom": "api_keys", + "tableTo": "workspaces", + "schemaTo": "latitude", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_token_unique": { + "name": "api_keys_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + } + }, + "latitude.claimed_rewards": { + "name": "claimed_rewards", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "creator_id": { + "name": "creator_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reward_type": { + "name": "reward_type", + "type": "reward_types", + "typeSchema": "latitude", + "primaryKey": false, + "notNull": true + }, + "reference": { + "name": "reference", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "is_valid": { + "name": "is_valid", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "claimed_rewards_workspace_id_idx": { + "name": "claimed_rewards_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "claimed_rewards_workspace_id_workspaces_id_fk": { + "name": "claimed_rewards_workspace_id_workspaces_id_fk", + "tableFrom": "claimed_rewards", + "tableTo": "workspaces", + "schemaTo": "latitude", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "claimed_rewards_creator_id_users_id_fk": { + "name": "claimed_rewards_creator_id_users_id_fk", + "tableFrom": "claimed_rewards", + "tableTo": "users", + "schemaTo": "latitude", + "columnsFrom": [ + "creator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "latitude.projects": { + "name": "projects", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_idx": { + "name": "workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "projects_deleted_at_idx": { + "name": "projects_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "projects_workspace_id_workspaces_id_fk": { + "name": "projects_workspace_id_workspaces_id_fk", + "tableFrom": "projects", + "tableTo": "workspaces", + "schemaTo": "latitude", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "latitude.commits": { + "name": "commits", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "title": { + "name": "title", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "merged_at": { + "name": "merged_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "project_commit_order_idx": { + "name": "project_commit_order_idx", + "columns": [ + { + "expression": "merged_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "unique_commit_version": { + "name": "unique_commit_version", + "columns": [ + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_idx": { + "name": "user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "merged_at_idx": { + "name": "merged_at_idx", + "columns": [ + { + "expression": "merged_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "project_id_idx": { + "name": "project_id_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "commits_deleted_at_indx": { + "name": "commits_deleted_at_indx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "commits_project_id_projects_id_fk": { + "name": "commits_project_id_projects_id_fk", + "tableFrom": "commits", + "tableTo": "projects", + "schemaTo": "latitude", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "commits_user_id_users_id_fk": { + "name": "commits_user_id_users_id_fk", + "tableFrom": "commits", + "tableTo": "users", + "schemaTo": "latitude", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "commits_uuid_unique": { + "name": "commits_uuid_unique", + "nullsNotDistinct": false, + "columns": [ + "uuid" + ] + } + } + }, + "latitude.document_versions": { + "name": "document_versions", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "document_uuid": { + "name": "document_uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "resolved_content": { + "name": "resolved_content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "commit_id": { + "name": "commit_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "document_versions_unique_document_uuid_commit_id": { + "name": "document_versions_unique_document_uuid_commit_id", + "columns": [ + { + "expression": "document_uuid", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "commit_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_versions_unique_path_commit_id_deleted_at": { + "name": "document_versions_unique_path_commit_id_deleted_at", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "commit_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_versions_commit_id_idx": { + "name": "document_versions_commit_id_idx", + "columns": [ + { + "expression": "commit_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_versions_deleted_at_idx": { + "name": "document_versions_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_versions_path_idx": { + "name": "document_versions_path_idx", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "document_versions_commit_id_commits_id_fk": { + "name": "document_versions_commit_id_commits_id_fk", + "tableFrom": "document_versions", + "tableTo": "commits", + "schemaTo": "latitude", + "columnsFrom": [ + "commit_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "latitude.provider_api_keys": { + "name": "provider_api_keys", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "provider", + "typeSchema": "latitude", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "provider_apikeys_workspace_id_idx": { + "name": "provider_apikeys_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "provider_api_keys_name_workspace_id_deleted_at_index": { + "name": "provider_api_keys_name_workspace_id_deleted_at_index", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "provider_apikeys_user_id_idx": { + "name": "provider_apikeys_user_id_idx", + "columns": [ + { + "expression": "author_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "provider_api_keys_token_provider_workspace_id_deleted_at_index": { + "name": "provider_api_keys_token_provider_workspace_id_deleted_at_index", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "provider_api_keys_author_id_users_id_fk": { + "name": "provider_api_keys_author_id_users_id_fk", + "tableFrom": "provider_api_keys", + "tableTo": "users", + "schemaTo": "latitude", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "provider_api_keys_workspace_id_workspaces_id_fk": { + "name": "provider_api_keys_workspace_id_workspaces_id_fk", + "tableFrom": "provider_api_keys", + "tableTo": "workspaces", + "schemaTo": "latitude", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "provider_api_keys_name_workspace_id_deleted_at_unique": { + "name": "provider_api_keys_name_workspace_id_deleted_at_unique", + "nullsNotDistinct": true, + "columns": [ + "name", + "workspace_id", + "deleted_at" + ] + }, + "provider_api_keys_token_provider_workspace_id_deleted_at_unique": { + "name": "provider_api_keys_token_provider_workspace_id_deleted_at_unique", + "nullsNotDistinct": true, + "columns": [ + "token", + "provider", + "workspace_id", + "deleted_at" + ] + } + } + }, + "latitude.document_logs": { + "name": "document_logs", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "document_uuid": { + "name": "document_uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "commit_id": { + "name": "commit_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "resolved_content": { + "name": "resolved_content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parameters": { + "name": "parameters", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "custom_identifier": { + "name": "custom_identifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "duration": { + "name": "duration", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "log_source", + "typeSchema": "latitude", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "document_log_own_uuid_idx": { + "name": "document_log_own_uuid_idx", + "columns": [ + { + "expression": "uuid", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_log_uuid_idx": { + "name": "document_log_uuid_idx", + "columns": [ + { + "expression": "document_uuid", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_logs_commit_id_idx": { + "name": "document_logs_commit_id_idx", + "columns": [ + { + "expression": "commit_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_logs_content_hash_idx": { + "name": "document_logs_content_hash_idx", + "columns": [ + { + "expression": "content_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_logs_created_at_idx": { + "name": "document_logs_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "document_logs_commit_id_commits_id_fk": { + "name": "document_logs_commit_id_commits_id_fk", + "tableFrom": "document_logs", + "tableTo": "commits", + "schemaTo": "latitude", + "columnsFrom": [ + "commit_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "document_logs_uuid_unique": { + "name": "document_logs_uuid_unique", + "nullsNotDistinct": false, + "columns": [ + "uuid" + ] + } + } + }, + "latitude.run_errors": { + "name": "run_errors", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "code": { + "name": "code", + "type": "run_error_code_enum", + "typeSchema": "latitude", + "primaryKey": false, + "notNull": true + }, + "errorable_type": { + "name": "errorable_type", + "type": "run_error_entity_enum", + "typeSchema": "latitude", + "primaryKey": false, + "notNull": true + }, + "errorable_uuid": { + "name": "errorable_uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "run_errors_errorable_entity_uuid_idx": { + "name": "run_errors_errorable_entity_uuid_idx", + "columns": [ + { + "expression": "errorable_uuid", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "errorable_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "latitude.provider_logs": { + "name": "provider_logs", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "document_log_uuid": { + "name": "document_log_uuid", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_id": { + "name": "provider_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "finish_reason": { + "name": "finish_reason", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'stop'" + }, + "config": { + "name": "config", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "messages": { + "name": "messages", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "response_object": { + "name": "response_object", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "response_text": { + "name": "response_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tool_calls": { + "name": "tool_calls", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'[]'::json" + }, + "tokens": { + "name": "tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "cost_in_millicents": { + "name": "cost_in_millicents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "duration": { + "name": "duration", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "log_source", + "typeSchema": "latitude", + "primaryKey": false, + "notNull": true + }, + "apiKeyId": { + "name": "apiKeyId", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "generated_at": { + "name": "generated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "provider_idx": { + "name": "provider_idx", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "provider_logs_created_at_idx": { + "name": "provider_logs_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "provider_logs_workspace_id_index": { + "name": "provider_logs_workspace_id_index", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "provider_logs_provider_id_provider_api_keys_id_fk": { + "name": "provider_logs_provider_id_provider_api_keys_id_fk", + "tableFrom": "provider_logs", + "tableTo": "provider_api_keys", + "schemaTo": "latitude", + "columnsFrom": [ + "provider_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + }, + "provider_logs_apiKeyId_api_keys_id_fk": { + "name": "provider_logs_apiKeyId_api_keys_id_fk", + "tableFrom": "provider_logs", + "tableTo": "api_keys", + "schemaTo": "latitude", + "columnsFrom": [ + "apiKeyId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "provider_logs_uuid_unique": { + "name": "provider_logs_uuid_unique", + "nullsNotDistinct": false, + "columns": [ + "uuid" + ] + } + } + }, + "latitude.datasets": { + "name": "datasets", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "csv_delimiter": { + "name": "csv_delimiter", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_key": { + "name": "file_key", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "file_metadata": { + "name": "file_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "datasets_workspace_idx": { + "name": "datasets_workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "datasets_author_idx": { + "name": "datasets_author_idx", + "columns": [ + { + "expression": "author_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "datasets_workspace_id_name_index": { + "name": "datasets_workspace_id_name_index", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "datasets_workspace_id_workspaces_id_fk": { + "name": "datasets_workspace_id_workspaces_id_fk", + "tableFrom": "datasets", + "tableTo": "workspaces", + "schemaTo": "latitude", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "datasets_author_id_users_id_fk": { + "name": "datasets_author_id_users_id_fk", + "tableFrom": "datasets", + "tableTo": "users", + "schemaTo": "latitude", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "latitude.evaluations": { + "name": "evaluations", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata_id": { + "name": "metadata_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "configuration": { + "name": "configuration", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "metadata_type": { + "name": "metadata_type", + "type": "metadata_type", + "typeSchema": "latitude", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "evaluation_workspace_idx": { + "name": "evaluation_workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "evaluation_metadata_idx": { + "name": "evaluation_metadata_idx", + "columns": [ + { + "expression": "metadata_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "metadata_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "evaluations_deleted_at_idx": { + "name": "evaluations_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "evaluations_workspace_id_workspaces_id_fk": { + "name": "evaluations_workspace_id_workspaces_id_fk", + "tableFrom": "evaluations", + "tableTo": "workspaces", + "schemaTo": "latitude", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "evaluations_uuid_unique": { + "name": "evaluations_uuid_unique", + "nullsNotDistinct": false, + "columns": [ + "uuid" + ] + } + } + }, + "latitude.llm_as_judge_evaluation_metadatas": { + "name": "llm_as_judge_evaluation_metadatas", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "metadata_type": { + "name": "metadata_type", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true, + "default": "'llm_as_judge'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "llm_as_judge_evaluation_metadatas_template_id_idx": { + "name": "llm_as_judge_evaluation_metadatas_template_id_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "llm_as_judge_evaluation_metadatas_template_id_evaluations_templates_id_fk": { + "name": "llm_as_judge_evaluation_metadatas_template_id_evaluations_templates_id_fk", + "tableFrom": "llm_as_judge_evaluation_metadatas", + "tableTo": "evaluations_templates", + "schemaTo": "latitude", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "latitude.connected_evaluations": { + "name": "connected_evaluations", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "live": { + "name": "live", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "document_uuid": { + "name": "document_uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "evaluation_id": { + "name": "evaluation_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "connected_evaluations_evaluation_idx": { + "name": "connected_evaluations_evaluation_idx", + "columns": [ + { + "expression": "evaluation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "connected_evaluations_evaluation_id_evaluations_id_fk": { + "name": "connected_evaluations_evaluation_id_evaluations_id_fk", + "tableFrom": "connected_evaluations", + "tableTo": "evaluations", + "schemaTo": "latitude", + "columnsFrom": [ + "evaluation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "connected_evaluations_unique_idx": { + "name": "connected_evaluations_unique_idx", + "nullsNotDistinct": false, + "columns": [ + "document_uuid", + "evaluation_id" + ] + } + } + }, + "latitude.evaluation_results": { + "name": "evaluation_results", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "evaluation_id": { + "name": "evaluation_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "document_log_id": { + "name": "document_log_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "provider_log_id": { + "name": "provider_log_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "resultable_type": { + "name": "resultable_type", + "type": "evaluation_result_types", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "resultable_id": { + "name": "resultable_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "log_source", + "typeSchema": "latitude", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "evaluation_idx": { + "name": "evaluation_idx", + "columns": [ + { + "expression": "evaluation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_log_idx": { + "name": "document_log_idx", + "columns": [ + { + "expression": "document_log_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "provider_log_idx": { + "name": "provider_log_idx", + "columns": [ + { + "expression": "provider_log_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "resultable_idx": { + "name": "resultable_idx", + "columns": [ + { + "expression": "resultable_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resultable_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "evaluation_results_created_at_idx": { + "name": "evaluation_results_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "evaluation_results_evaluation_id_evaluations_id_fk": { + "name": "evaluation_results_evaluation_id_evaluations_id_fk", + "tableFrom": "evaluation_results", + "tableTo": "evaluations", + "schemaTo": "latitude", + "columnsFrom": [ + "evaluation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "evaluation_results_document_log_id_document_logs_id_fk": { + "name": "evaluation_results_document_log_id_document_logs_id_fk", + "tableFrom": "evaluation_results", + "tableTo": "document_logs", + "schemaTo": "latitude", + "columnsFrom": [ + "document_log_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "evaluation_results_provider_log_id_provider_logs_id_fk": { + "name": "evaluation_results_provider_log_id_provider_logs_id_fk", + "tableFrom": "evaluation_results", + "tableTo": "provider_logs", + "schemaTo": "latitude", + "columnsFrom": [ + "provider_log_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "evaluation_results_uuid_unique": { + "name": "evaluation_results_uuid_unique", + "nullsNotDistinct": false, + "columns": [ + "uuid" + ] + } + } + }, + "latitude.evaluations_templates": { + "name": "evaluations_templates", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "configuration": { + "name": "configuration", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "evaluations_templates_category_evaluations_template_categories_id_fk": { + "name": "evaluations_templates_category_evaluations_template_categories_id_fk", + "tableFrom": "evaluations_templates", + "tableTo": "evaluations_template_categories", + "schemaTo": "latitude", + "columnsFrom": [ + "category" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "latitude.evaluations_template_categories": { + "name": "evaluations_template_categories", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "latitude.magic_link_tokens": { + "name": "magic_link_tokens", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "expired_at": { + "name": "expired_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "magic_link_tokens_user_id_users_id_fk": { + "name": "magic_link_tokens_user_id_users_id_fk", + "tableFrom": "magic_link_tokens", + "tableTo": "users", + "schemaTo": "latitude", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "magic_link_tokens_token_unique": { + "name": "magic_link_tokens_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + } + }, + "latitude.events": { + "name": "events", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "event_workspace_idx": { + "name": "event_workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "event_type_idx": { + "name": "event_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "events_workspace_id_workspaces_id_fk": { + "name": "events_workspace_id_workspaces_id_fk", + "tableFrom": "events", + "tableTo": "workspaces", + "schemaTo": "latitude", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "latitude.evaluation_resultable_numbers": { + "name": "evaluation_resultable_numbers", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "result": { + "name": "result", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "latitude.evaluation_resultable_texts": { + "name": "evaluation_resultable_texts", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "result": { + "name": "result", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "latitude.evaluation_resultable_booleans": { + "name": "evaluation_resultable_booleans", + "schema": "latitude", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "result": { + "name": "result", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": { + "latitude.subscription_plans": { + "name": "subscription_plans", + "schema": "latitude", + "values": [ + "hobby_v1", + "hobby_v2", + "team_v1", + "enterprise_v1" + ] + }, + "latitude.reward_types": { + "name": "reward_types", + "schema": "latitude", + "values": [ + "github_star", + "follow", + "post", + "github_issue", + "referral", + "signup_launch_day" + ] + }, + "latitude.provider": { + "name": "provider", + "schema": "latitude", + "values": [ + "openai", + "anthropic", + "groq", + "mistral", + "azure", + "google", + "custom" + ] + }, + "latitude.run_error_code_enum": { + "name": "run_error_code_enum", + "schema": "latitude", + "values": [ + "unknown_error", + "default_provider_exceeded_quota_error", + "document_config_error", + "missing_provider_error", + "chain_compile_error", + "ai_run_error", + "unsupported_provider_response_type_error", + "ai_provider_config_error", + "ev_run_missing_provider_log_error", + "ev_run_missing_workspace_error", + "ev_run_unsupported_result_type_error", + "ev_run_response_json_format_error", + "default_provider_invalid_model_error" + ] + }, + "latitude.run_error_entity_enum": { + "name": "run_error_entity_enum", + "schema": "latitude", + "values": [ + "document_log", + "evaluation_result" + ] + }, + "latitude.log_source": { + "name": "log_source", + "schema": "latitude", + "values": [ + "playground", + "api", + "evaluation", + "user" + ] + }, + "latitude.metadata_type": { + "name": "metadata_type", + "schema": "latitude", + "values": [ + "llm_as_judge" + ] + }, + "public.evaluation_result_types": { + "name": "evaluation_result_types", + "schema": "public", + "values": [ + "evaluation_resultable_booleans", + "evaluation_resultable_texts", + "evaluation_resultable_numbers" + ] + } + }, + "schemas": { + "latitude": "latitude" + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/core/drizzle/meta/_journal.json b/packages/core/drizzle/meta/_journal.json index f2e285b08..751c01272 100644 --- a/packages/core/drizzle/meta/_journal.json +++ b/packages/core/drizzle/meta/_journal.json @@ -596,6 +596,13 @@ "when": 1730106440291, "tag": "0085_tan_wilson_fisk", "breakpoints": true + }, + { + "idx": 86, + "version": "7", + "when": 1730113435380, + "tag": "0086_polite_forgotten_one", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts index 73296fd42..3d8a2946c 100644 --- a/packages/core/src/constants.ts +++ b/packages/core/src/constants.ts @@ -1,9 +1,9 @@ -import type { - AssistantMessage, - Message as CompilerMessage, - SystemMessage, - ToolCall, - UserMessage, +import { + type AssistantMessage, + type Message as CompilerMessage, + type SystemMessage, + type ToolCall, + type UserMessage, } from '@latitude-data/compiler' import { CoreTool, @@ -11,6 +11,7 @@ import { ObjectStreamPart, TextStreamPart, } from 'ai' +import { z } from 'zod' import { ProviderLog } from './browser' import { Config } from './services/ai' @@ -78,6 +79,7 @@ export enum LogSources { API = 'api', Playground = 'playground', Evaluation = 'evaluation', + User = 'user', } // NOTE: If you add a new error code, please add it to the pg enum in models/runErrors.ts @@ -279,8 +281,8 @@ export type SerializedProviderLog = { messages: SerializedConversation context: string response: string | null - config: object - duration: number + config: object | null + duration: number | null cost: number } @@ -311,3 +313,88 @@ export type SerializedEvaluationResult = Omit< } export const ULTRA_LARGE_PAGE_SIZE = 1000 +export const DELIMITER_VALUES = { + comma: ',', + semicolon: ';', + tab: '\t', + space: ' ', +} +export const DELIMITERS_KEYS = [ + 'comma', + 'semicolon', + 'tab', + 'space', + 'custom', +] as const +export const MAX_SIZE = 15 +export const MAX_UPLOAD_SIZE_IN_MB = MAX_SIZE * 1024 * 1024 + +const userContentSchema = z.array( + z + .object({ + type: z.literal('text'), + text: z.string(), + }) + .or( + z.object({ + type: z.literal('image'), + image: z + .string() + .or(z.instanceof(Uint8Array)) + .or(z.instanceof(Buffer)) + .or(z.instanceof(ArrayBuffer)) + .or(z.instanceof(URL)), + }), + ), +) + +export const messageSchema = z + .object({ + role: z.literal('system'), + content: z.string(), + }) + .or( + z.object({ + role: z.literal('user'), + name: z.string().optional(), + content: userContentSchema, + }), + ) + .or( + z.object({ + role: z.literal('assistant'), + content: z.string().or( + z.array( + z.object({ + type: z.literal('tool-call'), + toolCallId: z.string(), + toolName: z.string(), + args: z.record(z.any()), + }), + ), + ), + toolCalls: z.array( + z.object({ + id: z.string(), + name: z.string(), + arguments: z.record(z.any()), + }), + ), + }), + ) + .or( + z.object({ + role: z.literal('tool'), + content: z.array( + z.object({ + type: z.literal('tool-result'), + toolCallId: z.string(), + toolName: z.string(), + result: z.string(), + isError: z.boolean().optional(), + }), + ), + }), + ) + +export const messagesSchema = z.array(z.any(messageSchema)) diff --git a/packages/core/src/jobs/constants.ts b/packages/core/src/jobs/constants.ts index d5320fcad..c8056a99f 100644 --- a/packages/core/src/jobs/constants.ts +++ b/packages/core/src/jobs/constants.ts @@ -19,6 +19,7 @@ export enum Jobs { runEvaluationJob = 'runEvaluationJob', publishToAnalyticsJob = 'publishToAnalyticsJob', runLiveEvaluationJob = 'runLiveEvaluationJob', + uploadDocumentLogsJob = 'uploadDocumentLogsJob', } export const QUEUES = { @@ -31,6 +32,8 @@ export const QUEUES = { 'runDocumentInBatchJob', 'runDocumentJob', 'runEvaluationJob', + 'uploadDocumentLogsJob', + 'createDocumentLogJob', ], }, [Queues.eventsQueue]: { diff --git a/packages/core/src/jobs/job-definitions/documentLogs/createDocumentLogJob.ts b/packages/core/src/jobs/job-definitions/documentLogs/createDocumentLogJob.ts new file mode 100644 index 000000000..1ca954c37 --- /dev/null +++ b/packages/core/src/jobs/job-definitions/documentLogs/createDocumentLogJob.ts @@ -0,0 +1,46 @@ +import { Message } from '@latitude-data/compiler' +import { Job } from 'bullmq' + +import { Commit } from '../../../browser' +import { LogSources } from '../../../constants' +import { generateUUIDIdentifier } from '../../../lib' +import { DocumentVersionsRepository } from '../../../repositories' +import { createDocumentLog } from '../../../services/documentLogs' + +export type CreateDocumentLogJobProps = { + workspaceId: number + documentUuid: string + commit: Commit + source: LogSources + messages: Message[] + responseText: string +} + +export const createDocumentLogJob = async ( + job: Job, +) => { + const { workspaceId, documentUuid, commit, source, messages, responseText } = + job.data + const docsRepo = new DocumentVersionsRepository(workspaceId) + const document = await docsRepo + .getDocumentByUuid({ + documentUuid, + commit, + }) + .then((r) => r.unwrap()) + + await createDocumentLog({ + data: { + uuid: generateUUIDIdentifier(), + documentUuid: document.documentUuid, + resolvedContent: document.resolvedContent || '', + source, + parameters: {}, + providerLog: { + messages, + responseText, + }, + }, + commit, + }).then((r) => r.unwrap()) +} diff --git a/packages/core/src/jobs/job-definitions/documentLogs/uploadDocumentLogsJob.ts b/packages/core/src/jobs/job-definitions/documentLogs/uploadDocumentLogsJob.ts new file mode 100644 index 000000000..2e9906366 --- /dev/null +++ b/packages/core/src/jobs/job-definitions/documentLogs/uploadDocumentLogsJob.ts @@ -0,0 +1,42 @@ +import { Job } from 'bullmq' + +import { setupJobs } from '../..' +import { Commit } from '../../../browser' +import { LogSources, messagesSchema } from '../../../constants' + +export type UploadDocumentLogsJobData = { + workspaceId: number + documentUuid: string + commit: Commit + source: LogSources + csv: { data: { record: string[] }[] } +} + +export const uploadDocumentLogsJob = async ( + job: Job, +) => { + const { workspaceId, source, documentUuid, commit, csv } = job.data + const jobs = await setupJobs() + + csv.data.forEach((row) => { + const messages = JSON.parse(row.record[0]!) + const response = row.record[1] + const result = messagesSchema.safeParse(messages) + if (!result.success) { + // TODO: notify client of invalid log format + return + } + + jobs.defaultQueue.jobs.enqueueCreateDocumentLogJob({ + workspaceId, + documentUuid, + commit, + source, + messages: response ? messages : messages.slice(0, -1), + responseText: + response ?? + (messages[messages.length - 1].content?.text || + messages[messages.length - 1].content), + }) + }) +} diff --git a/packages/core/src/jobs/job-definitions/index.ts b/packages/core/src/jobs/job-definitions/index.ts index 2c5a3b329..968c6dff6 100644 --- a/packages/core/src/jobs/job-definitions/index.ts +++ b/packages/core/src/jobs/job-definitions/index.ts @@ -8,3 +8,5 @@ export * from '../../events/handlers/notifyToClientEvaluationResultCreatedJob' export * from '../../events/handlers/sendMagicLinkHandler' export * from '../../events/handlers/sendInvitationToUser' export * from '../../events/handlers/sendReferralInvitation' +export * from './documentLogs/uploadDocumentLogsJob' +export * from './documentLogs/createDocumentLogJob' diff --git a/packages/core/src/jobs/job-definitions/types.d.ts b/packages/core/src/jobs/job-definitions/types.d.ts index ee5a3a8dc..64465e5ba 100644 --- a/packages/core/src/jobs/job-definitions/types.d.ts +++ b/packages/core/src/jobs/job-definitions/types.d.ts @@ -9,6 +9,7 @@ import type { RunBatchEvaluationJobParams } from './batchEvaluations/runBatchEva import type { RunEvaluationJobData } from './batchEvaluations/runEvaluationJob' import type { RunDocumentInBatchJobProps } from './documents/runDocumentInBatchJob' import type { RunDocumentJobData } from './documents/runDocumentJob' +import type { UploadDocumentLogsJobData } from './documents/uploadDocumentLogsJob' import type { RunLiveEvaluationJobData } from './liveEvaluations/runLiveEvaluationJob' export type JobDataMap = { @@ -23,6 +24,7 @@ export type JobDataMap = { [Jobs.runEvaluationJob]: RunEvaluationJobData [Jobs.publishToAnalyticsJob]: LatitudeEvent [Jobs.runLiveEvaluationJob]: RunLiveEvaluationJobData + [Jobs.uploadDocumentLogsJob]: UploadDocumentLogsJobData } type JobData = J extends keyof JobDataMap diff --git a/packages/core/src/lib/readCsv.ts b/packages/core/src/lib/readCsv.ts index bcafbd424..93bc223bf 100644 --- a/packages/core/src/lib/readCsv.ts +++ b/packages/core/src/lib/readCsv.ts @@ -16,6 +16,7 @@ type ParseCsvOptions = { // https://csv.js.org/parse/options/to_line/ toLine?: number fromLine?: number + columns?: boolean } type ParseResult = { record: Record @@ -28,7 +29,7 @@ export type CsvParsedData = { } export async function syncReadCsv( file: File | string, - { delimiter, toLine, fromLine }: ParseCsvOptions, + { delimiter, toLine, fromLine, columns = true }: ParseCsvOptions, ) { try { const data = await getData(file) @@ -37,7 +38,7 @@ export async function syncReadCsv( relax_column_count: true, skip_empty_lines: true, relax_quotes: true, - columns: true, + columns, trim: true, info: true, } @@ -58,7 +59,8 @@ export async function syncReadCsv( return Result.ok({ headers: [], rowCount: 0, data: [] }) const firstRecord = records[0]! - const headers = firstRecord.info.columns.map((column) => column.name) + const headers = + firstRecord.info?.columns?.map?.((column) => column.name) ?? [] return Result.ok({ rowCount: records.length, headers, data: records }) } catch (e) { const error = e as CsvError diff --git a/packages/core/src/repositories/connectedEvaluationsRepository/getConnectedDocumentsWithMetadata.test.ts b/packages/core/src/repositories/connectedEvaluationsRepository/getConnectedDocumentsWithMetadata.test.ts index 9593dffef..424e87575 100644 --- a/packages/core/src/repositories/connectedEvaluationsRepository/getConnectedDocumentsWithMetadata.test.ts +++ b/packages/core/src/repositories/connectedEvaluationsRepository/getConnectedDocumentsWithMetadata.test.ts @@ -231,7 +231,8 @@ describe('getConnectedDocumentsWithMetadata', () => { ) const totalTokens = results.reduce( - (acc, r) => acc + r.providerLogs.reduce((acc2, l) => acc2 + l.tokens, 0), + (acc, r) => + acc + r.providerLogs.reduce((acc2, l) => acc2 + (l?.tokens ?? 0), 0), 0, ) const totalCost = results.reduce( diff --git a/packages/core/src/repositories/providerLogsRepository.ts b/packages/core/src/repositories/providerLogsRepository.ts index 1911cb405..2e79eac1e 100644 --- a/packages/core/src/repositories/providerLogsRepository.ts +++ b/packages/core/src/repositories/providerLogsRepository.ts @@ -2,12 +2,7 @@ import { asc, desc, eq, getTableColumns } from 'drizzle-orm' import { ProviderLog } from '../browser' import { NotFoundError, Result } from '../lib' -import { - documentLogs, - providerApiKeys, - providerLogs, - workspaces, -} from '../schema' +import { documentLogs, providerLogs } from '../schema' import { QueryOptions } from './repository' import Repository from './repositoryV2' @@ -18,11 +13,7 @@ export class ProviderLogsRepository extends Repository { return this.db .select(tt) .from(providerLogs) - .innerJoin( - providerApiKeys, - eq(providerApiKeys.id, providerLogs.providerId), - ) - .innerJoin(workspaces, eq(workspaces.id, providerApiKeys.workspaceId)) + .where(eq(providerLogs.workspaceId, this.workspaceId)) .$dynamic() } @@ -38,10 +29,6 @@ export class ProviderLogsRepository extends Repository { async findByDocumentUuid(documentUuid: string, opts: QueryOptions = {}) { const query = this.scope - .innerJoin( - documentLogs, - eq(documentLogs.uuid, providerLogs.documentLogUuid), - ) .where(eq(documentLogs.documentUuid, documentUuid)) .orderBy(asc(providerLogs.generatedAt)) diff --git a/packages/core/src/schema/messages.ts b/packages/core/src/schema/messages.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/core/src/schema/models/documentLogs.ts b/packages/core/src/schema/models/documentLogs.ts index e7db96f24..e5f97e7eb 100644 --- a/packages/core/src/schema/models/documentLogs.ts +++ b/packages/core/src/schema/models/documentLogs.ts @@ -28,7 +28,7 @@ export const documentLogs = latitudeSchema.table( contentHash: text('content_hash').notNull(), parameters: jsonb('parameters').$type>().notNull(), customIdentifier: text('custom_identifier'), - duration: bigint('duration', { mode: 'number' }).notNull(), + duration: bigint('duration', { mode: 'number' }), source: logSourcesEnum('source'), ...timestamps(), }, diff --git a/packages/core/src/schema/models/providerLogs.ts b/packages/core/src/schema/models/providerLogs.ts index 6137c87e8..c3f5d407b 100644 --- a/packages/core/src/schema/models/providerLogs.ts +++ b/packages/core/src/schema/models/providerLogs.ts @@ -23,6 +23,7 @@ export const logSourcesEnum = latitudeSchema.enum('log_source', [ LogSources.Playground, LogSources.API, LogSources.Evaluation, + LogSources.User, ]) export const providerLogs = latitudeSchema.table( @@ -32,22 +33,23 @@ export const providerLogs = latitudeSchema.table( workspaceId: bigint('workspace_id', { mode: 'number' }), uuid: uuid('uuid').notNull().unique(), documentLogUuid: uuid('document_log_uuid'), - providerId: bigint('provider_id', { mode: 'number' }) - .notNull() - .references(() => providerApiKeys.id, { + providerId: bigint('provider_id', { mode: 'number' }).references( + () => providerApiKeys.id, + { onDelete: 'restrict', onUpdate: 'cascade', - }), + }, + ), model: varchar('model'), - finishReason: varchar('finish_reason').notNull().default('stop'), - config: json('config').$type().notNull(), + finishReason: varchar('finish_reason').default('stop'), + config: json('config').$type(), messages: json('messages').$type().notNull(), responseObject: jsonb('response_object').$type(), responseText: text('response_text').$type(), toolCalls: json('tool_calls').$type().notNull().default([]), - tokens: bigint('tokens', { mode: 'number' }).notNull(), + tokens: bigint('tokens', { mode: 'number' }), costInMillicents: integer('cost_in_millicents').notNull().default(0), - duration: bigint('duration', { mode: 'number' }).notNull(), // in milliseconds! + duration: bigint('duration', { mode: 'number' }), // in milliseconds! source: logSourcesEnum('source').notNull(), apiKeyId: bigint('apiKeyId', { mode: 'number' }).references( () => apiKeys.id, @@ -56,7 +58,7 @@ export const providerLogs = latitudeSchema.table( onUpdate: 'cascade', }, ), - generatedAt: timestamp('generated_at', { mode: 'date' }).notNull(), + generatedAt: timestamp('generated_at', { mode: 'date' }), ...timestamps(), }, (table) => ({ diff --git a/packages/core/src/services/documentLogs/bulkUpload.ts b/packages/core/src/services/documentLogs/bulkUpload.ts new file mode 100644 index 000000000..380155dc7 --- /dev/null +++ b/packages/core/src/services/documentLogs/bulkUpload.ts @@ -0,0 +1,32 @@ +import { Commit, DocumentVersion, Workspace } from '../../browser' +import { LogSources } from '../../constants' +import { setupJobs } from '../../jobs' +import { syncReadCsv } from '../../lib/readCsv' + +export async function bulkUploadDocumentLogs({ + csvDelimiter, + logsFile, + workspace, + document, + commit, +}: { + csvDelimiter: string + logsFile: File + workspace: Workspace + document: DocumentVersion + commit: Commit +}) { + const csv = await syncReadCsv(logsFile, { + delimiter: csvDelimiter, + columns: false, + }).then((r) => r.unwrap()) + const queues = await setupJobs() + + queues.defaultQueue.jobs.enqueueUploadDocumentLogsJob({ + workspaceId: workspace.id, + documentUuid: document.documentUuid, + commit, + csv, + source: LogSources.User, + }) +} diff --git a/packages/core/src/services/documentLogs/computeDocumentLogWithMetadata.test.ts b/packages/core/src/services/documentLogs/computeDocumentLogWithMetadata.test.ts index 10dba00f0..37485e185 100644 --- a/packages/core/src/services/documentLogs/computeDocumentLogWithMetadata.test.ts +++ b/packages/core/src/services/documentLogs/computeDocumentLogWithMetadata.test.ts @@ -68,8 +68,14 @@ describe('computeDocumentLogWithMetadata', () => { id: documentLog.id, uuid: documentLog.uuid, documentUuid: documentLog.documentUuid, - tokens: totalProviderLogs.reduce((acc, log) => acc + log.tokens, 0), - duration: totalProviderLogs.reduce((acc, log) => acc + log.duration, 0), + tokens: totalProviderLogs.reduce( + (acc, log) => acc + (log?.tokens ?? 0), + 0, + ), + duration: totalProviderLogs.reduce( + (acc, log) => acc + (log?.duration ?? 0), + 0, + ), costInMillicents: totalProviderLogs.reduce( (acc, log) => acc + log.costInMillicents, 0, diff --git a/packages/core/src/services/documentLogs/create.ts b/packages/core/src/services/documentLogs/create.ts index 67a85d5fc..8bd23abce 100644 --- a/packages/core/src/services/documentLogs/create.ts +++ b/packages/core/src/services/documentLogs/create.ts @@ -1,8 +1,18 @@ +import { Message } from '@latitude-data/compiler' + import { Commit, DocumentLog, LogSources } from '../../browser' import { database } from '../../client' +import { findWorkspaceFromCommit } from '../../data-access' import { publisher } from '../../events/publisher' -import { hashContent, Result, Transaction } from '../../lib' +import { + generateUUIDIdentifier, + hashContent, + NotFoundError, + Result, + Transaction, +} from '../../lib' import { documentLogs } from '../../schema' +import { createProviderLog } from '../providerLogs' export type CreateDocumentLogProps = { commit: Commit @@ -11,10 +21,14 @@ export type CreateDocumentLogProps = { documentUuid: string parameters: Record resolvedContent: string - duration: number + duration?: number source: LogSources customIdentifier?: string createdAt?: Date + providerLog?: { + messages: Message[] + responseText?: string + } } } @@ -29,6 +43,7 @@ export async function createDocumentLog( duration, source, createdAt, + providerLog, }, commit, }: CreateDocumentLogProps, @@ -52,6 +67,22 @@ export async function createDocumentLog( .returning() const documentLog = inserts[0]! + if (providerLog) { + const workspace = await findWorkspaceFromCommit(commit, trx) + if (!workspace) { + throw new NotFoundError('Workspace not found') + } + + await createProviderLog({ + uuid: generateUUIDIdentifier(), + documentLogUuid: documentLog.uuid, + messages: providerLog.messages, + responseText: providerLog.responseText, + generatedAt: new Date(), + source, + workspace, + }).then((r) => r.unwrap()) + } publisher.publishLater({ type: 'documentLogCreated', diff --git a/packages/core/src/services/documentLogs/index.ts b/packages/core/src/services/documentLogs/index.ts index b90681ae4..b3db43969 100644 --- a/packages/core/src/services/documentLogs/index.ts +++ b/packages/core/src/services/documentLogs/index.ts @@ -3,3 +3,4 @@ export * from './addMessages' export * from './computeDocumentLogWithMetadata' export * from './computeDocumentLogsWithMetadata' export * from './fetchDocumentLogWithMetadata' +export * from './bulkUpload' diff --git a/packages/core/src/services/evaluations/run/handleEvaluationResponse.ts b/packages/core/src/services/evaluations/run/handleEvaluationResponse.ts index 626cefb6f..0fad9aa67 100644 --- a/packages/core/src/services/evaluations/run/handleEvaluationResponse.ts +++ b/packages/core/src/services/evaluations/run/handleEvaluationResponse.ts @@ -81,7 +81,7 @@ export async function handleEvaluationResponse({ if (!response.object) { error = new ChainError({ code: RunErrorCodes.EvaluationRunResponseJsonFormatError, - message: `Provider with model [${providerLog.config.model}] did not return a valid JSON object`, + message: `Provider with model [${providerLog?.config?.model ?? 'unknown'}] did not return a valid JSON object`, }) } else { result = response.object diff --git a/packages/core/src/services/providerLogs/addMessages.ts b/packages/core/src/services/providerLogs/addMessages.ts index 563fe13be..2df31f1ae 100644 --- a/packages/core/src/services/providerLogs/addMessages.ts +++ b/packages/core/src/services/providerLogs/addMessages.ts @@ -38,6 +38,21 @@ export async function addMessages({ messages: Message[] source: LogSources }) { + if (!providerLog.providerId) { + return Result.error( + new NotFoundError( + `Cannot add messages to a conversation that has no associated provider`, + ), + ) + } + if (!providerLog.config) { + return Result.error( + new NotFoundError( + `Cannot add messages to a conversation that has no associated configuration`, + ), + ) + } + const provider = await unsafelyFindProviderApiKey(providerLog.providerId) if (!provider) { return Result.error( @@ -78,7 +93,7 @@ export async function addMessages({ iterate({ workspace, source, - config: providerLog.config, + config: providerLog.config!, provider, controller, documentLogUuid: providerLog.documentLogUuid!, diff --git a/packages/core/src/services/providerLogs/create.ts b/packages/core/src/services/providerLogs/create.ts index 4b13c048d..925c6b1ee 100644 --- a/packages/core/src/services/providerLogs/create.ts +++ b/packages/core/src/services/providerLogs/create.ts @@ -17,13 +17,13 @@ export type CreateProviderLogProps = { workspace: Workspace uuid: string generatedAt: Date - providerId: number - providerType: Providers - model: string - config: PartialConfig + providerId?: number + providerType?: Providers + model?: string + config?: PartialConfig messages: Message[] - usage: LanguageModelUsage - duration: number + usage?: LanguageModelUsage + duration?: number source: LogSources finishReason?: StreamConsumeReturn['finishReason'] apiKeyId?: number @@ -60,10 +60,15 @@ export async function createProviderLog( return await Transaction.call(async (trx) => { const cost = costInMillicents ?? - Math.floor( - estimateCost({ provider: providerType, model, usage }) * - TO_MILLICENTS_FACTOR, - ) + (providerType && model && usage + ? Math.floor( + estimateCost({ + provider: providerType!, + model: model!, + usage: usage!, + }) * TO_MILLICENTS_FACTOR, + ) + : undefined) const inserts = await trx .insert(providerLogs) .values({ @@ -78,7 +83,11 @@ export async function createProviderLog( responseText, responseObject, toolCalls, - tokens: isNaN(usage.totalTokens) ? 0 : (usage.totalTokens ?? 0), + tokens: usage + ? isNaN(usage.totalTokens) + ? 0 + : (usage.totalTokens ?? 0) + : undefined, costInMillicents: cost, duration, source, @@ -88,8 +97,7 @@ export async function createProviderLog( .returning() const log = inserts[0]! as ProviderLog - await touchProviderApiKey(providerId, trx) - + if (providerId) await touchProviderApiKey(providerId, trx) if (apiKeyId) await touchApiKey(apiKeyId, trx) publisher.publishLater({ diff --git a/packages/core/src/tests/factories/documentLogs.ts b/packages/core/src/tests/factories/documentLogs.ts index 00f5e0d89..02a6e9cdc 100644 --- a/packages/core/src/tests/factories/documentLogs.ts +++ b/packages/core/src/tests/factories/documentLogs.ts @@ -120,7 +120,7 @@ export async function createDocumentLog({ const duration = Math.floor(Math.random() * 100) + - providerLogs.reduce((acc, log) => acc + log.duration, 0) + providerLogs.reduce((acc, log) => acc + (log?.duration ?? 0), 0) const documentLog = await ogCreateDocumentLog({ commit, diff --git a/packages/sdks/typescript/package.json b/packages/sdks/typescript/package.json index 1ff91f5f4..0e0c93799 100644 --- a/packages/sdks/typescript/package.json +++ b/packages/sdks/typescript/package.json @@ -19,16 +19,24 @@ "prettier": "prettier --write \"**/*.{ts,tsx,md}\"" }, "type": "module", - "files": [ - "dist" - ], + "files": ["dist"], + "main": "./dist/index.cjs", + "module": "./dist/index.js", "exports": { ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js", - "require": "./dist/index.cjs" + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.ts", + "default": "./dist/index.cjs" + } } }, + "types": { + "index.d.ts": "./dist/index.d.ts" + }, "dependencies": { "@t3-oss/env-core": "^0.10.1", "eventsource-parser": "^2.0.1", diff --git a/packages/sdks/typescript/src/index.ts b/packages/sdks/typescript/src/index.ts index 07b568746..4e2603278 100644 --- a/packages/sdks/typescript/src/index.ts +++ b/packages/sdks/typescript/src/index.ts @@ -1,6 +1,11 @@ import { Readable } from 'stream' -import type { Config, Message } from '@latitude-data/compiler' +import type { + Config, + ContentType, + Message, + MessageRole, +} from '@latitude-data/compiler' import type { ChainCallResponseDto, ChainEventDto, @@ -189,6 +194,43 @@ export class Latitude { } } + async log( + path: string, + messages: Message[], + { + response, + projectId, + versionUuid, + }: { + projectId?: number + versionUuid?: string + response?: string + } = {}, + ) { + projectId = projectId ?? this.projectId + if (!projectId) { + throw new Error('Project ID is required') + } + versionUuid = versionUuid ?? this.versionUuid + + const httpResponse = await this.request({ + method: 'POST', + handler: HandlerType.Log, + params: { projectId, versionUuid }, + body: { path, messages, response }, + }) + + if (!httpResponse.ok) { + throw new LatitudeApiError({ + status: httpResponse.status, + statusText: httpResponse.statusText, + serverResponse: await httpResponse.text(), + }) + } + + return await httpResponse.json() + } + async chat( uuid: string, messages: Message[], @@ -349,4 +391,4 @@ export class Latitude { } } -export type { ChainEventDto, Message } +export type { ChainEventDto, Message, MessageRole, ContentType } diff --git a/packages/sdks/typescript/src/utils/index.ts b/packages/sdks/typescript/src/utils/index.ts index 4e67203ff..6c5b58e9b 100644 --- a/packages/sdks/typescript/src/utils/index.ts +++ b/packages/sdks/typescript/src/utils/index.ts @@ -2,6 +2,7 @@ import { ChatUrlParams, GetDocumentUrlParams, HandlerType, + LogUrlParams, RunUrlParams, UrlParams, } from '$sdk/utils/types' @@ -46,6 +47,8 @@ export class RouteResolver { return this.conversations().chat( (params as ChatUrlParams).conversationUuid, ) + case HandlerType.Log: + return this.documents(params as LogUrlParams).log default: throw new Error(`Unknown handler: ${handler satisfies never}`) } @@ -63,6 +66,7 @@ export class RouteResolver { return { run: `${base}/run`, document: (path: string) => `${base}/${path}`, + log: `${base}/logs`, } } diff --git a/packages/sdks/typescript/src/utils/types.ts b/packages/sdks/typescript/src/utils/types.ts index 6b4d87671..5d6e320fb 100644 --- a/packages/sdks/typescript/src/utils/types.ts +++ b/packages/sdks/typescript/src/utils/types.ts @@ -21,6 +21,12 @@ type RunDocumentBodyParam = { type ChatBodyParams = { messages: Message[] } +export type LogUrlParams = RunUrlParams +type LogBodyParams = { + path: string + messages: Message[] + response?: string +} export type GetDocumentUrlParams = { projectId: number @@ -32,6 +38,7 @@ export enum HandlerType { Chat = 'chat', GetDocument = 'get-document', RunDocument = 'run-document', + Log = 'log', } export type UrlParams = T extends HandlerType.RunDocument @@ -40,11 +47,15 @@ export type UrlParams = T extends HandlerType.RunDocument ? GetDocumentUrlParams : T extends HandlerType.Chat ? ChatUrlParams - : never + : T extends HandlerType.Log + ? LogUrlParams + : never export type BodyParams = T extends HandlerType.RunDocument ? RunDocumentBodyParam : T extends HandlerType.Chat ? ChatBodyParams - : never + : T extends HandlerType.Log + ? LogBodyParams + : never diff --git a/packages/web-ui/src/ds/atoms/DropzoneInput/index.tsx b/packages/web-ui/src/ds/atoms/DropzoneInput/index.tsx index 979171007..eb71a14d3 100644 --- a/packages/web-ui/src/ds/atoms/DropzoneInput/index.tsx +++ b/packages/web-ui/src/ds/atoms/DropzoneInput/index.tsx @@ -56,7 +56,7 @@ export function DropzoneInput({
) { +}) { if (typeof children !== 'string') return children - return ( -

- {children} -

- ) + return {children} } function TooltipMessage({ error }: { error: string | undefined }) { @@ -155,10 +148,7 @@ function FormField({ {children} - {description && ( - {description} - )} - + {description && {description}} {errorStyle === 'inline' ? ( ) : null} diff --git a/packages/web-ui/src/ds/atoms/FormFieldGroup/index.tsx b/packages/web-ui/src/ds/atoms/FormFieldGroup/index.tsx index 9ac4ad069..20ca1d054 100644 --- a/packages/web-ui/src/ds/atoms/FormFieldGroup/index.tsx +++ b/packages/web-ui/src/ds/atoms/FormFieldGroup/index.tsx @@ -34,11 +34,7 @@ export function FormFieldGroup({ > {children}
- {description ? ( - - {description} - - ) : null} + {description ? {description} : null} ) } diff --git a/packages/web-ui/src/ds/atoms/Switch/index.tsx b/packages/web-ui/src/ds/atoms/Switch/index.tsx index 241462c84..ab9fde748 100644 --- a/packages/web-ui/src/ds/atoms/Switch/index.tsx +++ b/packages/web-ui/src/ds/atoms/Switch/index.tsx @@ -96,9 +96,7 @@ function SwitchInput({ ) : null} - {description && ( - {description} - )} + {description && {description}} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b8b996aae..73ea69bbf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -79,7 +79,7 @@ importers: version: 0.11.1(typescript@5.6.3)(zod@3.23.8) ai: specifier: ^3.2.42 - version: 3.4.10(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.5.12(typescript@5.6.3))(zod@3.23.8) + version: 3.4.10(openai@4.68.4(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.5.12(typescript@5.6.3))(zod@3.23.8) argon2: specifier: ^0.41.0 version: 0.41.1 @@ -244,9 +244,6 @@ importers: hono: specifier: ^4.5.3 version: 4.6.5 - jet-paths: - specifier: ^1.0.6 - version: 1.0.9 lodash-es: specifier: ^4.17.21 version: 4.17.21 @@ -347,7 +344,7 @@ importers: version: 0.10.1(typescript@5.6.3)(zod@3.23.8) ai: specifier: ^3.2.42 - version: 3.4.10(react@19.0.0-rc-5d19e1c8-20240923)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.5.12(typescript@5.6.3))(zod@3.23.8) + version: 3.4.10(openai@4.68.4(encoding@0.1.13)(zod@3.23.8))(react@19.0.0-rc-5d19e1c8-20240923)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.5.12(typescript@5.6.3))(zod@3.23.8) bullmq: specifier: ^5.8.5 version: 5.20.0 @@ -697,7 +694,7 @@ importers: version: 10.0.0 ai: specifier: ^3.4.7 - version: 3.4.10(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.5.12(typescript@5.6.3))(zod@3.23.8) + version: 3.4.10(openai@4.68.4(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.5.12(typescript@5.6.3))(zod@3.23.8) argon2: specifier: ^0.41.0 version: 0.41.1 @@ -2407,9 +2404,6 @@ packages: peerDependencies: drizzle-orm: '>= 0.29 <1' lucia: 3.x - peerDependenciesMeta: - drizzle-orm: - optional: true '@lukeed/ms@2.0.2': resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} @@ -4987,6 +4981,10 @@ packages: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -5022,6 +5020,10 @@ packages: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} + agentkeepalive@4.5.0: + resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} + engines: {node: '>= 8.0.0'} + aggregate-error@3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} @@ -6663,6 +6665,10 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} @@ -6839,6 +6845,9 @@ packages: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} + form-data-encoder@1.7.2: + resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} + form-data-encoder@2.1.4: resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} engines: {node: '>= 14.17'} @@ -6851,6 +6860,10 @@ packages: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} + formdata-node@4.4.1: + resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} + engines: {node: '>= 12.20'} + formdata-polyfill@4.0.10: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} @@ -7240,6 +7253,9 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -7587,9 +7603,6 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} - jet-paths@1.0.9: - resolution: {integrity: sha512-XxYKHYMwwhFr2azKPGXAV80RIdFwueJOilBTXH2ICwXSxD5vo6PUZsfufL+DVk+nZKf6Z2y+19kpwh8lJ81Gng==} - jiti@1.21.6: resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} hasBin: true @@ -8570,6 +8583,15 @@ packages: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} + openai@4.68.4: + resolution: {integrity: sha512-LRinV8iU9VQplkr25oZlyrsYGPGasIwYN8KFMAAFTHHLHjHhejtJ5BALuLFrkGzY4wfbKhOhuT+7lcHZ+F3iEA==} + hasBin: true + peerDependencies: + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true + openapi-types@12.1.3: resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} @@ -10554,6 +10576,10 @@ packages: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} + web-streams-polyfill@4.0.0-beta.3: + resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} + engines: {node: '>= 14'} + web-vitals@4.2.3: resolution: {integrity: sha512-/CFAm1mNxSmOj6i0Co+iGFJ58OS4NRGVP+AWS/l509uIK5a1bSoIVaHz/ZumpHTfHSZBpgrJ+wjfpAOrTHok5Q==} @@ -12280,9 +12306,8 @@ snapshots: '@lucia-auth/adapter-drizzle@1.1.0(drizzle-orm@0.33.0(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@18.3.0)(pg@8.13.0)(react@19.0.0-rc-5d19e1c8-20240923))(lucia@3.2.1)': dependencies: - lucia: 3.2.1 - optionalDependencies: drizzle-orm: 0.33.0(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@18.3.0)(pg@8.13.0)(react@19.0.0-rc-5d19e1c8-20240923) + lucia: 3.2.1 '@lukeed/ms@2.0.2': {} @@ -15952,6 +15977,11 @@ snapshots: abbrev@2.0.0: {} + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + optional: true + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -15985,6 +16015,11 @@ snapshots: transitivePeerDependencies: - supports-color + agentkeepalive@4.5.0: + dependencies: + humanize-ms: 1.2.1 + optional: true + aggregate-error@3.1.0: dependencies: clean-stack: 2.2.0 @@ -15995,7 +16030,7 @@ snapshots: clean-stack: 4.2.0 indent-string: 5.0.0 - ai@3.4.10(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.5.12(typescript@5.6.3))(zod@3.23.8): + ai@3.4.10(openai@4.68.4(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.5.12(typescript@5.6.3))(zod@3.23.8): dependencies: '@ai-sdk/provider': 0.0.24 '@ai-sdk/provider-utils': 1.0.20(zod@3.23.8) @@ -16012,6 +16047,7 @@ snapshots: secure-json-parse: 2.7.0 zod-to-json-schema: 3.23.2(zod@3.23.8) optionalDependencies: + openai: 4.68.4(encoding@0.1.13)(zod@3.23.8) react: 18.3.1 sswr: 2.1.0(svelte@4.2.19) svelte: 4.2.19 @@ -16020,7 +16056,7 @@ snapshots: - solid-js - vue - ai@3.4.10(react@19.0.0-rc-5d19e1c8-20240923)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.5.12(typescript@5.6.3))(zod@3.23.8): + ai@3.4.10(openai@4.68.4(encoding@0.1.13)(zod@3.23.8))(react@19.0.0-rc-5d19e1c8-20240923)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.5.12(typescript@5.6.3))(zod@3.23.8): dependencies: '@ai-sdk/provider': 0.0.24 '@ai-sdk/provider-utils': 1.0.20(zod@3.23.8) @@ -16037,6 +16073,7 @@ snapshots: secure-json-parse: 2.7.0 zod-to-json-schema: 3.23.2(zod@3.23.8) optionalDependencies: + openai: 4.68.4(encoding@0.1.13)(zod@3.23.8) react: 19.0.0-rc-5d19e1c8-20240923 sswr: 2.1.0(svelte@4.2.19) svelte: 4.2.19 @@ -17766,6 +17803,9 @@ snapshots: etag@1.8.1: {} + event-target-shim@5.0.1: + optional: true + eventemitter3@4.0.7: {} events@1.1.1: {} @@ -17985,6 +18025,9 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 + form-data-encoder@1.7.2: + optional: true + form-data-encoder@2.1.4: {} form-data@4.0.1: @@ -17995,6 +18038,12 @@ snapshots: format@0.2.2: {} + formdata-node@4.4.1: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 4.0.0-beta.3 + optional: true + formdata-polyfill@4.0.10: dependencies: fetch-blob: 3.2.0 @@ -18560,6 +18609,11 @@ snapshots: human-signals@5.0.0: {} + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + optional: true + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -18874,8 +18928,6 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jet-paths@1.0.9: {} - jiti@1.21.6: {} jju@1.4.0: {} @@ -20198,6 +20250,21 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 + openai@4.68.4(encoding@0.1.13)(zod@3.23.8): + dependencies: + '@types/node': 18.19.55 + '@types/node-fetch': 2.6.11 + abort-controller: 3.0.0 + agentkeepalive: 4.5.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0(encoding@0.1.13) + optionalDependencies: + zod: 3.23.8 + transitivePeerDependencies: + - encoding + optional: true + openapi-types@12.1.3: {} optionator@0.9.4: @@ -22833,6 +22900,9 @@ snapshots: web-streams-polyfill@3.3.3: {} + web-streams-polyfill@4.0.0-beta.3: + optional: true + web-vitals@4.2.3: {} webidl-conversions@3.0.1: {}