├── .nvmrc ├── generated ├── .gitignore └── openapi-client │ └── openapi.yml ├── entrypoint.sh ├── fern ├── fern.config.json ├── client │ ├── definition │ │ ├── api.yml │ │ ├── commons.yml │ │ └── score.yml │ └── generators.yml └── server │ ├── definition │ ├── api.yml │ ├── event.yml │ ├── utils │ │ └── pagination.yml │ ├── observations.yml │ ├── span.yml │ ├── generations.yml │ ├── score.yml │ ├── trace.yml │ └── commons.yml │ └── generators.yml ├── demo_thumbnail.jpg ├── public └── favicon.ico ├── src ├── features │ ├── featureFlags │ │ ├── availableFlags.ts │ │ ├── types.ts │ │ ├── utils.ts │ │ └── components │ │ │ └── FeatureFlagToggle.tsx │ ├── publicApi │ │ ├── server │ │ │ ├── types.ts │ │ │ ├── cors.ts │ │ │ ├── apiScope.ts │ │ │ ├── apiAuth.ts │ │ │ └── apiKeyRouter.ts │ │ ├── lib │ │ │ └── apiKeys.ts │ │ └── components │ │ │ └── CreateApiKeyButton.tsx │ ├── support-chat │ │ ├── index.tsx │ │ └── chat.tsx │ ├── auth │ │ └── lib │ │ │ ├── signupSchema.ts │ │ │ └── emailPassword.ts │ ├── feedback │ │ └── server │ │ │ ├── corsMiddleware.ts │ │ │ └── feedbackHandler.ts │ ├── rbac │ │ ├── constants │ │ │ └── roleAccessRights.ts │ │ ├── utils │ │ │ └── checkAccess.ts │ │ ├── server │ │ │ └── projectMembersRouter.ts │ │ └── components │ │ │ └── ProjectMembersTable.tsx │ ├── projects │ │ ├── server │ │ │ └── projectsRouter.ts │ │ └── components │ │ │ └── NewProjectButton.tsx │ ├── dashboard │ │ ├── lib │ │ │ └── timeseriesAggregation.ts │ │ └── components │ │ │ └── BaseTimeSeriesChart.tsx │ └── ingest │ │ └── lib │ │ └── usage.ts ├── utils │ ├── string.ts │ ├── numbers.ts │ ├── tailwind.ts │ ├── types.ts │ ├── tanstack.d.ts │ └── api.ts ├── pages │ ├── api │ │ ├── feedback.ts │ │ ├── auth │ │ │ ├── [...nextauth].ts │ │ │ └── signup.ts │ │ ├── public │ │ │ ├── doc.ts │ │ │ ├── health.ts │ │ │ ├── observations │ │ │ │ └── [observationId].ts │ │ │ └── traces │ │ │ │ └── [traceId].ts │ │ ├── trpc │ │ │ └── [trpc].ts │ │ └── cron │ │ │ └── telemetry.ts │ ├── project │ │ └── [projectId] │ │ │ ├── traces.tsx │ │ │ ├── scores.tsx │ │ │ ├── index.tsx │ │ │ ├── traces │ │ │ └── [traceId].tsx │ │ │ └── settings.tsx │ └── auth │ │ └── sign-in.tsx ├── components │ ├── table │ │ ├── types.ts │ │ ├── table-link.tsx │ │ ├── data-table-pagination.tsx │ │ └── data-table-toolbar.tsx │ ├── ui │ │ ├── collapsible.tsx │ │ ├── label.tsx │ │ ├── input.tsx │ │ ├── separator.tsx │ │ ├── textarea.tsx │ │ ├── popover.tsx │ │ ├── slider.tsx │ │ ├── badge.tsx │ │ ├── avatar.tsx │ │ ├── description-lists.tsx │ │ ├── alert.tsx │ │ ├── tabs.tsx │ │ ├── card.tsx │ │ ├── button.tsx │ │ └── table.tsx │ ├── stats-cards.tsx │ ├── layouts │ │ ├── routes.ts │ │ └── header.tsx │ ├── grouped-score-badge.tsx │ ├── token-usage-badge.tsx │ └── trace │ │ ├── index.tsx │ │ └── TracePreview.tsx ├── server │ ├── db.ts │ └── api │ │ ├── root.ts │ │ └── routers │ │ └── generations.ts ├── middleware.ts ├── styles │ └── globals.css ├── __tests__ │ ├── test-utils.ts │ └── traces.servertest.ts └── env.mjs ├── .dockerignore ├── .codespellrc ├── readme_walkthrough_thumbnail.png ├── prisma └── migrations │ ├── 20230529140133_user_add_pw │ └── migration.sql │ ├── 20230910164603_cron_add_state │ └── migration.sql │ ├── 20230710200816_scores_add_comment │ └── migration.sql │ ├── 20230814184705_add_viewer_membership_role │ └── migration.sql │ ├── 20230522094431_endtime_optional │ └── migration.sql │ ├── 20230707133415_user_add_email_index │ └── migration.sql │ ├── 20230707132314_traces_project_id_index │ └── migration.sql │ ├── 20230731162154_score_value_float │ └── migration.sql │ ├── 20230711110517_memberships_add_userid_index │ └── migration.sql │ ├── 20230810191452_traceid_nullable_on_observations │ └── migration.sql │ ├── 20230717190411_users_feature_flags │ └── migration.sql │ ├── migration_lock.toml │ ├── 20230706195819_add_completion_start_time │ └── migration.sql │ ├── 20230907225603_projects_updated_at │ └── migration.sql │ ├── 20230626095337_external_trace_id │ └── migration.sql │ ├── 20230720162550_tokens │ └── migration.sql │ ├── 20230810191453_project_id_not_null │ └── migration.sql │ ├── 20230907204921_add_cron_jobs_table │ └── migration.sql │ ├── 20230803093326_add_release_and_version │ └── migration.sql │ ├── 20230809093636_remove_foreign_keys │ └── migration.sql │ ├── 20230711104810_traces_add_index │ └── migration.sql │ ├── 20230721111651_drop_usage_json │ └── migration.sql │ ├── 20230710114928_traces_add_user_id │ └── migration.sql │ ├── 20230720164603_migrate_tokens │ └── migration.sql │ ├── 20230623172401_observation_add_level_and_status_message │ └── migration.sql │ ├── 20230907225604_api_keys_publishable_to_public │ └── migration.sql │ ├── 20230710105741_added_indices │ └── migration.sql │ ├── 20230618125818_remove_status_from_trace │ └── migration.sql │ ├── 20230705160335_add_created_updated_timestamps │ └── migration.sql │ ├── 20230622114254_new_oservations │ └── migration.sql │ ├── 20230522131516_default_timestamp │ └── migration.sql │ ├── 20230901155252_add_pricings_table │ └── migration.sql │ ├── 20230711112235_fix_indices │ └── migration.sql │ ├── 20230518193521_changes │ └── migration.sql │ ├── 20230809132331_add_project_id_to_observations │ └── migration.sql │ ├── 20230620181114_restructure │ └── migration.sql │ ├── 20230720172051_tokens_non_null │ └── migration.sql │ ├── 20230518193415_add_observaionts_and_traces │ └── migration.sql │ ├── 20230523084523_rename_to_score │ └── migration.sql │ ├── 20230523082455_rename_metrics_to_gradings │ └── migration.sql │ ├── 20230522092340_add_metrics_and_observation_types │ └── migration.sql │ └── 20230518191501_init │ └── migration.sql ├── vercel.json ├── postcss.config.cjs ├── prettier.config.cjs ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── ee └── Readme.md ├── cron.js ├── components.json ├── .github ├── workflows │ ├── codespell.yml │ ├── ci.yml.template │ └── pipeline.yml └── ISSUE_TEMPLATE │ ├── feature_request.yml │ └── bug_report.yml ├── docker-compose.dev.yml ├── .env.local.example ├── .env.dev.example ├── sentry.edge.config.ts ├── tsconfig.json ├── .gitignore ├── jest.config.mjs ├── docker-compose.build.yml ├── .eslintrc.cjs ├── .env.cloud.example ├── docker-compose.yml ├── sentry.client.config.ts ├── sentry.server.config.ts ├── types └── next-auth.d.ts ├── LICENSE ├── next.config.mjs ├── Dockerfile └── CONTRIBUTING.md /.nvmrc: -------------------------------------------------------------------------------- 1 | v18.16.0 -------------------------------------------------------------------------------- /generated/.gitignore: -------------------------------------------------------------------------------- 1 | python/ -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | node cron.js & 2 | node server.js 3 | -------------------------------------------------------------------------------- /fern/fern.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "organization": "finto", 3 | "version": "0.12.0" 4 | } -------------------------------------------------------------------------------- /demo_thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StephenGrider/langfuse/main/demo_thumbnail.jpg -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StephenGrider/langfuse/main/public/favicon.ico -------------------------------------------------------------------------------- /src/features/featureFlags/availableFlags.ts: -------------------------------------------------------------------------------- 1 | export const availableFlags = ["analytics-alpha"] as const; 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .dockerignore 3 | node_modules 4 | npm-debug.log 5 | README.md 6 | .next 7 | .git -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | skip = .git,*.pdf,*.svg,package-lock.json,*.prisma 3 | # 4 | # ignore-words-list = 5 | -------------------------------------------------------------------------------- /readme_walkthrough_thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StephenGrider/langfuse/main/readme_walkthrough_thumbnail.png -------------------------------------------------------------------------------- /prisma/migrations/20230529140133_user_add_pw/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "User" ADD COLUMN "password" TEXT; 3 | -------------------------------------------------------------------------------- /src/utils/string.ts: -------------------------------------------------------------------------------- 1 | export function lastCharacters(str: string, n: number) { 2 | return str.substring(str.length - n); 3 | } 4 | -------------------------------------------------------------------------------- /prisma/migrations/20230910164603_cron_add_state/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "cron_jobs" ADD COLUMN "state" TEXT; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20230710200816_scores_add_comment/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "scores" ADD COLUMN "comment" TEXT; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20230814184705_add_viewer_membership_role/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "MembershipRole" ADD VALUE 'VIEWER'; 3 | -------------------------------------------------------------------------------- /src/features/publicApi/server/types.ts: -------------------------------------------------------------------------------- 1 | export type ApiAccessScope = { 2 | projectId: string; 3 | accessLevel: "all" | "scores"; 4 | }; 5 | -------------------------------------------------------------------------------- /src/utils/numbers.ts: -------------------------------------------------------------------------------- 1 | export const numberFormatter = (number: number) => { 2 | return Intl.NumberFormat("us").format(number).toString(); 3 | }; 4 | -------------------------------------------------------------------------------- /prisma/migrations/20230522094431_endtime_optional/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "observations" ALTER COLUMN "end_time" DROP NOT NULL; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20230707133415_user_add_email_index/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateIndex 2 | CREATE INDEX "Account_user_id_idx" ON "Account"("user_id"); 3 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "crons": [ 3 | { 4 | "path": "/api/cron/ingestion_metrics", 5 | "schedule": "*/5 * * * *" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | 8 | module.exports = config; 9 | -------------------------------------------------------------------------------- /prisma/migrations/20230707132314_traces_project_id_index/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateIndex 2 | CREATE INDEX "traces_project_id_idx" ON "traces"("project_id"); 3 | -------------------------------------------------------------------------------- /src/pages/api/feedback.ts: -------------------------------------------------------------------------------- 1 | import feedbackApiHandler from "@/src/features/feedback/server/feedbackHandler"; 2 | 3 | export default feedbackApiHandler; 4 | -------------------------------------------------------------------------------- /prisma/migrations/20230731162154_score_value_float/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "scores" ALTER COLUMN "value" SET DATA TYPE DOUBLE PRECISION; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20230711110517_memberships_add_userid_index/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateIndex 2 | CREATE INDEX "memberships_user_id_idx" ON "memberships"("user_id"); 3 | -------------------------------------------------------------------------------- /prisma/migrations/20230810191452_traceid_nullable_on_observations/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "observations" ALTER COLUMN "trace_id" DROP NOT NULL; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20230717190411_users_feature_flags/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "users" ADD COLUMN "feature_flags" TEXT[] DEFAULT ARRAY[]::TEXT[]; 3 | -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /src/pages/api/auth/[...nextauth].ts: -------------------------------------------------------------------------------- 1 | import NextAuth from "next-auth"; 2 | import { authOptions } from "@/src/server/auth"; 3 | 4 | export default NextAuth(authOptions); 5 | -------------------------------------------------------------------------------- /prisma/migrations/20230706195819_add_completion_start_time/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "observations" ADD COLUMN "completion_start_time" TIMESTAMP(3); 3 | -------------------------------------------------------------------------------- /prisma/migrations/20230907225603_projects_updated_at/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "projects" ADD COLUMN "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; 3 | -------------------------------------------------------------------------------- /prettier.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("prettier").Config} */ 2 | const config = { 3 | plugins: [require.resolve("prettier-plugin-tailwindcss")], 4 | }; 5 | 6 | module.exports = config; 7 | -------------------------------------------------------------------------------- /src/features/support-chat/index.tsx: -------------------------------------------------------------------------------- 1 | import dynamic from "next/dynamic"; 2 | 3 | export { chatSetUser } from "./chat"; 4 | 5 | export const CrispWidget = dynamic(() => import("./chat"), { 6 | ssr: false, 7 | }); 8 | -------------------------------------------------------------------------------- /src/utils/tailwind.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /src/components/table/types.ts: -------------------------------------------------------------------------------- 1 | import { type LucideIcon } from "lucide-react"; 2 | 3 | export type TableRowOptions = { 4 | columnId: string; 5 | options: { label: string; value: number; icon?: LucideIcon }[]; 6 | }; 7 | -------------------------------------------------------------------------------- /prisma/migrations/20230626095337_external_trace_id/migration.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "traces" ADD COLUMN "external_id" TEXT; 2 | CREATE UNIQUE INDEX "traces_project_id_external_id_key" ON "traces"("project_id", "external_id"); 3 | -------------------------------------------------------------------------------- /prisma/migrations/20230720162550_tokens/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "observations" ADD COLUMN "completion_tokens" INTEGER, 3 | ADD COLUMN "prompt_tokens" INTEGER, 4 | ADD COLUMN "total_tokens" INTEGER; 5 | -------------------------------------------------------------------------------- /prisma/migrations/20230810191453_project_id_not_null/migration.sql: -------------------------------------------------------------------------------- 1 | -- Applied in separate migration after application release to minimize ingestion downtime 2 | ALTER TABLE "observations" 3 | ALTER COLUMN "project_id" SET NOT NULL; 4 | -------------------------------------------------------------------------------- /prisma/migrations/20230907204921_add_cron_jobs_table/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "cron_jobs" ( 3 | "name" TEXT NOT NULL, 4 | "last_run" TIMESTAMP(3), 5 | 6 | CONSTRAINT "cron_jobs_pkey" PRIMARY KEY ("name") 7 | ); 8 | -------------------------------------------------------------------------------- /src/features/featureFlags/types.ts: -------------------------------------------------------------------------------- 1 | import { type availableFlags } from "./availableFlags"; 2 | 3 | export type Flag = (typeof availableFlags)[number]; 4 | export type Flags = { 5 | [key in (typeof availableFlags)[number]]: boolean; 6 | }; 7 | -------------------------------------------------------------------------------- /prisma/migrations/20230803093326_add_release_and_version/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "observations" ADD COLUMN "version" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "traces" ADD COLUMN "release" TEXT, 6 | ADD COLUMN "version" TEXT; 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | "dbaeumer.vscode-eslint", 5 | "bradlc.vscode-tailwindcss", 6 | "unifiedjs.vscode-mdx", 7 | "yoavbls.pretty-ts-errors", 8 | "Prisma.prisma" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /prisma/migrations/20230809093636_remove_foreign_keys/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "observations" DROP CONSTRAINT "observations_parent_observation_id_fkey"; 3 | 4 | -- DropForeignKey 5 | ALTER TABLE "observations" DROP CONSTRAINT "observations_trace_id_fkey"; 6 | -------------------------------------------------------------------------------- /prisma/migrations/20230711104810_traces_add_index/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropIndex 2 | DROP INDEX "traces_project_id_name_user_id_idx"; 3 | 4 | -- CreateIndex 5 | CREATE INDEX "traces_project_id_name_user_id_external_id_idx" ON "traces"("project_id", "name", "user_id", "external_id"); 6 | -------------------------------------------------------------------------------- /prisma/migrations/20230721111651_drop_usage_json/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `usage` on the `observations` table. All the data in the column will be lost. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "observations" DROP COLUMN "usage"; 9 | -------------------------------------------------------------------------------- /src/features/auth/lib/signupSchema.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod"; 2 | 3 | export const signupSchema = z.object({ 4 | name: z.string(), 5 | email: z.string().email(), 6 | password: z.string().min(8, { 7 | message: "Password must be at least 8 characters long", 8 | }), 9 | }); 10 | -------------------------------------------------------------------------------- /fern/client/definition/api.yml: -------------------------------------------------------------------------------- 1 | name: langfuse 2 | error-discrimination: 3 | strategy: status-code 4 | auth: bearer 5 | imports: 6 | commons: commons.yml 7 | errors: 8 | - commons.Error 9 | - commons.UnauthorizedError 10 | - commons.AccessDeniedError 11 | - commons.MethodNotAllowedError 12 | -------------------------------------------------------------------------------- /ee/Readme.md: -------------------------------------------------------------------------------- 1 | # Enterprise Edition 2 | 3 | While we are currently working 100% on the open source version, we consider financing the project by developing some premium features as part of an enterprise version under a commercial license. Please reach out to us if you have specific requirements: enterprise@langfuse.com 4 | -------------------------------------------------------------------------------- /prisma/migrations/20230710114928_traces_add_user_id/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropIndex 2 | DROP INDEX "traces_project_id_name_idx"; 3 | 4 | -- AlterTable 5 | ALTER TABLE "traces" ADD COLUMN "user_id" TEXT; 6 | 7 | -- CreateIndex 8 | CREATE INDEX "traces_project_id_name_user_id_idx" ON "traces"("project_id", "name", "user_id"); 9 | -------------------------------------------------------------------------------- /prisma/migrations/20230720164603_migrate_tokens/migration.sql: -------------------------------------------------------------------------------- 1 | -- This is an empty migration. 2 | UPDATE observations 3 | SET prompt_tokens = CAST(usage::json->>'promptTokens' AS INTEGER), 4 | completion_tokens = CAST(usage::json->>'completionTokens' AS INTEGER), 5 | total_tokens = CAST(usage::json->>'totalTokens' AS INTEGER); -------------------------------------------------------------------------------- /fern/client/definition/commons.yml: -------------------------------------------------------------------------------- 1 | errors: 2 | Error: 3 | status-code: 400 4 | type: string 5 | UnauthorizedError: 6 | status-code: 401 7 | type: string 8 | AccessDeniedError: 9 | status-code: 403 10 | type: string 11 | MethodNotAllowedError: 12 | status-code: 405 13 | type: string 14 | -------------------------------------------------------------------------------- /prisma/migrations/20230623172401_observation_add_level_and_status_message/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "ObservationLevel" AS ENUM ('DEBUG', 'DEFAULT', 'WARNING', 'ERROR'); 3 | 4 | -- AlterTable 5 | ALTER TABLE "observations" ADD COLUMN "level" "ObservationLevel" NOT NULL DEFAULT 'DEFAULT', 6 | ADD COLUMN "status_message" TEXT; 7 | -------------------------------------------------------------------------------- /src/pages/api/public/doc.ts: -------------------------------------------------------------------------------- 1 | import { withSwagger } from "next-swagger-doc"; 2 | 3 | const swaggerHandler = withSwagger({ 4 | definition: { 5 | openapi: "3.0.0", 6 | info: { 7 | title: "NextJS Swagger", 8 | version: "0.1.0", 9 | }, 10 | }, 11 | apiFolder: "src/pages/api/public", 12 | }); 13 | export default swaggerHandler(); 14 | -------------------------------------------------------------------------------- /cron.js: -------------------------------------------------------------------------------- 1 | var CronJob = require('cron').CronJob; 2 | 3 | var job = new CronJob( 4 | '*/5 * * * *', // Every 5 minutes 5 | function () { 6 | fetch("http://host.docker.internal:3000/api/cron/telemetry").catch(err => console.log(err)); 7 | }, 8 | ); 9 | 10 | if (process.env.TELEMETRY_ENABLED === undefined || process.env.TELEMETRY_ENABLED === "true") 11 | job.start() 12 | -------------------------------------------------------------------------------- /src/features/featureFlags/utils.ts: -------------------------------------------------------------------------------- 1 | import { availableFlags } from "./availableFlags"; 2 | import { type Flags } from "./types"; 3 | 4 | export const parseFlags = (dbFlags: string[]): Flags => { 5 | const parsedFlags = {} as Flags; 6 | 7 | availableFlags.forEach((flag) => { 8 | parsedFlags[flag] = dbFlags.includes(flag); 9 | }); 10 | 11 | return parsedFlags; 12 | }; 13 | -------------------------------------------------------------------------------- /prisma/migrations/20230907225604_api_keys_publishable_to_public/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropIndex 2 | DROP INDEX "api_keys_publishable_key_key"; 3 | 4 | -- RENAME column from "publishable_key" to "public_key" on table "api_keys" 5 | ALTER TABLE "api_keys" rename column "publishable_key" to "public_key"; 6 | 7 | -- CreateIndex 8 | CREATE UNIQUE INDEX "api_keys_public_key_key" ON "api_keys"("public_key"); 9 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tailwind": { 6 | "config": "tailwind.config.ts", 7 | "css": "src/styles/globals.css", 8 | "baseColor": "slate", 9 | "cssVariables": true 10 | }, 11 | "aliases": { 12 | "components": "@/src/components", 13 | "utils": "@/src/utils/tailwind" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/components/ui/collapsible.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"; 4 | 5 | const Collapsible = CollapsiblePrimitive.Root; 6 | 7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger; 8 | 9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent; 10 | 11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }; 12 | -------------------------------------------------------------------------------- /prisma/migrations/20230710105741_added_indices/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropIndex 2 | DROP INDEX "traces_project_id_idx"; 3 | 4 | -- CreateIndex 5 | CREATE INDEX "observations_trace_id_type_idx" ON "observations"("trace_id", "type"); 6 | 7 | -- CreateIndex 8 | CREATE INDEX "scores_value_idx" ON "scores"("value"); 9 | 10 | -- CreateIndex 11 | CREATE INDEX "traces_project_id_name_idx" ON "traces"("project_id", "name"); 12 | -------------------------------------------------------------------------------- /fern/server/definition/api.yml: -------------------------------------------------------------------------------- 1 | name: langfuse 2 | error-discrimination: 3 | strategy: status-code 4 | auth: basic 5 | imports: 6 | commons: commons.yml 7 | errors: 8 | - commons.Error 9 | - commons.UnauthorizedError 10 | - commons.AccessDeniedError 11 | - commons.MethodNotAllowedError 12 | - commons.NotFoundError 13 | headers: 14 | X-Langfuse-Sdk-Name: optional 15 | X-Langfuse-Sdk-Version: optional 16 | -------------------------------------------------------------------------------- /prisma/migrations/20230618125818_remove_status_from_trace/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `status` on the `traces` table. All the data in the column will be lost. 5 | - You are about to drop the column `status_message` on the `traces` table. All the data in the column will be lost. 6 | 7 | */ 8 | -- AlterTable 9 | ALTER TABLE "traces" DROP COLUMN "status", 10 | DROP COLUMN "status_message"; 11 | -------------------------------------------------------------------------------- /fern/server/definition/event.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json 2 | imports: 3 | commons: ./commons.yml 4 | service: 5 | auth: true 6 | base-path: /api/public 7 | endpoints: 8 | create: 9 | docs: Add an event to the database 10 | method: POST 11 | path: /events 12 | request: commons.CreateEventRequest 13 | response: commons.Observation 14 | -------------------------------------------------------------------------------- /prisma/migrations/20230705160335_add_created_updated_timestamps/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "memberships" ADD COLUMN "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 3 | ADD COLUMN "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; 4 | 5 | -- AlterTable 6 | ALTER TABLE "users" ADD COLUMN "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 7 | ADD COLUMN "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; 8 | -------------------------------------------------------------------------------- /.github/workflows/codespell.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Codespell 3 | 4 | on: 5 | push: 6 | branches: [main] 7 | pull_request: 8 | branches: [main] 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | codespell: 15 | name: Check for spelling errors 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v3 21 | - name: Codespell 22 | uses: codespell-project/actions-codespell@v2 23 | -------------------------------------------------------------------------------- /docker-compose.dev.yml: -------------------------------------------------------------------------------- 1 | version: "3.5" 2 | 3 | services: 4 | db: 5 | image: postgres 6 | restart: always 7 | command: ["postgres", "-c", "log_statement=all"] 8 | environment: 9 | - POSTGRES_USER=postgres 10 | - POSTGRES_PASSWORD=postgres 11 | - POSTGRES_DB=postgres 12 | ports: 13 | - 5432:5432 14 | volumes: 15 | - database_data:/var/lib/postgresql/data 16 | 17 | volumes: 18 | database_data: 19 | driver: local 20 | -------------------------------------------------------------------------------- /src/server/db.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | 3 | import { env } from "@/src/env.mjs"; 4 | 5 | const globalForPrisma = globalThis as unknown as { 6 | prisma: PrismaClient | undefined; 7 | }; 8 | 9 | export const prisma = 10 | globalForPrisma.prisma ?? 11 | new PrismaClient({ 12 | log: 13 | env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"], 14 | }); 15 | 16 | if (env.NODE_ENV !== "production") globalForPrisma.prisma = prisma; 17 | -------------------------------------------------------------------------------- /prisma/migrations/20230622114254_new_oservations/migration.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | ALTER TABLE "observations" 4 | RENAME COLUMN "prompt" TO "input"; 5 | 6 | ALTER TABLE "observations" 7 | ADD COLUMN "output_temp" JSONB; 8 | 9 | UPDATE "observations" 10 | SET "output_temp" = json_build_object('completion', "observations"."completion"); 11 | 12 | ALTER TABLE "observations" 13 | DROP COLUMN "completion"; 14 | 15 | ALTER TABLE "observations" 16 | RENAME COLUMN "output_temp" TO "output"; 17 | 18 | COMMIT; -------------------------------------------------------------------------------- /src/pages/project/[projectId]/traces.tsx: -------------------------------------------------------------------------------- 1 | import Header from "@/src/components/layouts/header"; 2 | import { useRouter } from "next/router"; 3 | import TracesTable from "@/src/components/table/use-cases/traces"; 4 | 5 | export default function Traces() { 6 | const router = useRouter(); 7 | const projectId = router.query.projectId as string; 8 | 9 | return ( 10 |
11 |
12 | 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /fern/server/definition/utils/pagination.yml: -------------------------------------------------------------------------------- 1 | types: 2 | MetaResponse: 3 | properties: 4 | page: 5 | type: integer 6 | docs: current page number 7 | limit: 8 | type: integer 9 | docs: number of items per page 10 | totalItems: 11 | type: integer 12 | docs: number of total items given the current filters/selection (if any) 13 | totalPages: 14 | type: integer 15 | docs: number of total pages given the current limit 16 | -------------------------------------------------------------------------------- /src/pages/project/[projectId]/scores.tsx: -------------------------------------------------------------------------------- 1 | import Header from "@/src/components/layouts/header"; 2 | 3 | import { useRouter } from "next/router"; 4 | import ScoresTable from "@/src/components/table/use-cases/scores"; 5 | 6 | export default function ScoresPage() { 7 | const router = useRouter(); 8 | const projectId = router.query.projectId as string; 9 | 10 | return ( 11 |
12 |
13 | 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /prisma/migrations/20230522131516_default_timestamp/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `createdAt` on the `metrics` table. All the data in the column will be lost. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "metrics" DROP COLUMN "createdAt", 9 | ADD COLUMN "timestamp" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; 10 | 11 | -- AlterTable 12 | ALTER TABLE "observations" ALTER COLUMN "start_time" SET DEFAULT CURRENT_TIMESTAMP; 13 | 14 | -- AlterTable 15 | ALTER TABLE "traces" ALTER COLUMN "timestamp" SET DEFAULT CURRENT_TIMESTAMP; 16 | -------------------------------------------------------------------------------- /.env.local.example: -------------------------------------------------------------------------------- 1 | # When adding additional environment variables, the schema in "/src/env.mjs" 2 | # should be updated accordingly. 3 | # Prisma 4 | # https://www.prisma.io/docs/reference/database-reference/connection-urls#env 5 | DIRECT_URL="postgresql://postgres:postgres@db:5432/postgres" 6 | DATABASE_URL="postgresql://postgres:postgres@db:5432/postgres" 7 | # Next Auth 8 | # You can generate a new secret on the command line with: 9 | # openssl rand -base64 32 10 | # https://next-auth.js.org/configuration/options#secret 11 | NEXTAUTH_SECRET="secret" 12 | NEXTAUTH_URL="http://localhost:3000" -------------------------------------------------------------------------------- /src/pages/api/public/health.ts: -------------------------------------------------------------------------------- 1 | import { cors, runMiddleware } from "@/src/features/publicApi/server/cors"; 2 | import { prisma } from "@/src/server/db"; 3 | import { type NextApiRequest, type NextApiResponse } from "next"; 4 | 5 | export default async function handler( 6 | req: NextApiRequest, 7 | res: NextApiResponse, 8 | ) { 9 | await runMiddleware(req, res, cors); 10 | try { 11 | await prisma.$queryRaw`SELECT 1;`; 12 | } catch (e) { 13 | return res.status(500).json({ status: "Database not available" }); 14 | } 15 | return res.status(200).json({ status: "OK" }); 16 | } 17 | -------------------------------------------------------------------------------- /fern/server/definition/observations.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json 2 | imports: 3 | commons: ./commons.yml 4 | service: 5 | auth: true 6 | base-path: /api/public 7 | endpoints: 8 | get: 9 | docs: Get a specific observation 10 | method: GET 11 | path: /observations/{observationId} 12 | path-parameters: 13 | observationId: 14 | type: string 15 | docs: The unique langfuse identifier of an observation, can be an event, span or generation 16 | response: commons.Observation 17 | -------------------------------------------------------------------------------- /prisma/migrations/20230901155252_add_pricings_table/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "PricingUnit" AS ENUM ('PER_1000_TOKENS'); 3 | 4 | -- CreateEnum 5 | CREATE TYPE "TokenType" AS ENUM ('PROMPT', 'COMPLETION', 'TOTAL'); 6 | 7 | -- CreateTable 8 | CREATE TABLE "pricings" ( 9 | "id" TEXT NOT NULL, 10 | "model_name" TEXT NOT NULL, 11 | "pricing_unit" "PricingUnit" NOT NULL DEFAULT 'PER_1000_TOKENS', 12 | "price" DECIMAL(65,30) NOT NULL, 13 | "currency" TEXT NOT NULL DEFAULT 'USD', 14 | "token_type" "TokenType" NOT NULL, 15 | 16 | CONSTRAINT "pricings_pkey" PRIMARY KEY ("id") 17 | ); 18 | -------------------------------------------------------------------------------- /.env.dev.example: -------------------------------------------------------------------------------- 1 | # When adding additional environment variables, the schema in "/src/env.mjs" 2 | # should be updated accordingly. 3 | 4 | # Prisma 5 | # https://www.prisma.io/docs/reference/database-reference/connection-urls#env 6 | DIRECT_URL="postgresql://postgres:postgres@localhost:5432/postgres" 7 | DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" 8 | 9 | # Next Auth 10 | # You can generate a new secret on the command line with: 11 | # openssl rand -base64 32 12 | # https://next-auth.js.org/configuration/options#secret 13 | # NEXTAUTH_SECRET="" 14 | NEXTAUTH_URL="http://localhost:3000" 15 | NEXTAUTH_SECRET="secret" 16 | -------------------------------------------------------------------------------- /sentry.edge.config.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from "@sentry/nextjs"; 2 | 3 | if (process.env.NEXT_PUBLIC_SENTRY_DSN) 4 | Sentry.init({ 5 | dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, 6 | 7 | // Set tracesSampleRate to 1.0 to capture 100% 8 | // of transactions for performance monitoring. 9 | // We recommend adjusting this value in production 10 | tracesSampleRate: 1.0, 11 | 12 | // ... 13 | 14 | // Note: if you want to override the automatic release value, do not set a 15 | // `release` value here - use the environment variable `SENTRY_RELEASE`, so 16 | // that it will also get attached to your source maps 17 | }); 18 | -------------------------------------------------------------------------------- /src/pages/api/trpc/[trpc].ts: -------------------------------------------------------------------------------- 1 | import { createNextApiHandler } from "@trpc/server/adapters/next"; 2 | 3 | import { env } from "@/src/env.mjs"; 4 | import { createTRPCContext } from "@/src/server/api/trpc"; 5 | import { appRouter } from "@/src/server/api/root"; 6 | 7 | // export API handler 8 | export default createNextApiHandler({ 9 | router: appRouter, 10 | createContext: createTRPCContext, 11 | onError: 12 | env.NODE_ENV === "development" 13 | ? ({ path, error }) => { 14 | console.error( 15 | `❌ tRPC failed on ${path ?? ""}: ${error.message}`, 16 | ); 17 | } 18 | : undefined, 19 | }); 20 | -------------------------------------------------------------------------------- /fern/client/generators.yml: -------------------------------------------------------------------------------- 1 | default-group: local 2 | groups: 3 | local: 4 | generators: 5 | # - name: fernapi/fern-typescript-browser-sdk 6 | # version: 0.7.1 7 | # output: 8 | # location: npm 9 | # url: npm.buildwithfern.com 10 | # package-name: "@finto-fern/react-client" 11 | # config: 12 | # namespaceExport: Langfuse 13 | # allowCustomFetcher: true 14 | - name: fernapi/fern-openapi 15 | version: 0.0.26 16 | output: 17 | location: local-file-system 18 | path: ../../generated/openapi-client 19 | config: 20 | namespaceExport: Langfuse 21 | allowCustomFetcher: true 22 | -------------------------------------------------------------------------------- /src/features/publicApi/server/cors.ts: -------------------------------------------------------------------------------- 1 | import { type NextApiRequest, type NextApiResponse } from "next"; 2 | import Cors from "cors"; 3 | 4 | export function runMiddleware( 5 | req: NextApiRequest, 6 | res: NextApiResponse, 7 | // eslint-disable-next-line @typescript-eslint/ban-types 8 | fn: Function, 9 | ) { 10 | return new Promise((resolve, reject) => { 11 | fn(req, res, (result: unknown) => { 12 | if (result instanceof Error) { 13 | return reject(result); 14 | } 15 | 16 | return resolve(result); 17 | }); 18 | }); 19 | } 20 | 21 | export const cors = Cors({ 22 | origin: true, 23 | //update: or "origin: true," if you don't wanna add a specific one 24 | credentials: false, 25 | }); 26 | -------------------------------------------------------------------------------- /src/features/featureFlags/components/FeatureFlagToggle.tsx: -------------------------------------------------------------------------------- 1 | import { type Flag } from "@/src/features/featureFlags/types"; 2 | import { useSession } from "next-auth/react"; 3 | 4 | export const FeatureFlagToggle = (props: { 5 | featureFlag: Flag; 6 | whenEnabled?: React.ReactNode; 7 | whenDisabled?: React.ReactNode; 8 | whenLoading?: React.ReactNode; 9 | }) => { 10 | const session = useSession(); 11 | const flags = session.data?.user?.featureFlags; 12 | const isEnabled = flags !== undefined && flags[props.featureFlag]; 13 | 14 | if (session.status === "loading") { 15 | return props.whenLoading ??
Loading ...
; 16 | } 17 | 18 | return isEnabled ? props.whenEnabled ?? <> : props.whenDisabled ?? <>; 19 | }; 20 | -------------------------------------------------------------------------------- /prisma/migrations/20230711112235_fix_indices/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropIndex 2 | DROP INDEX "observations_trace_id_type_idx"; 3 | 4 | -- DropIndex 5 | DROP INDEX "traces_project_id_name_user_id_external_id_idx"; 6 | 7 | -- CreateIndex 8 | CREATE INDEX "observations_trace_id_idx" ON "observations"("trace_id"); 9 | 10 | -- CreateIndex 11 | CREATE INDEX "observations_type_idx" ON "observations"("type"); 12 | 13 | -- CreateIndex 14 | CREATE INDEX IF NOT EXISTS "traces_project_id_idx" ON "traces"("project_id"); 15 | 16 | -- CreateIndex 17 | CREATE INDEX "traces_name_idx" ON "traces"("name"); 18 | 19 | -- CreateIndex 20 | CREATE INDEX "traces_user_id_idx" ON "traces"("user_id"); 21 | 22 | -- CreateIndex 23 | CREATE INDEX "traces_external_id_idx" ON "traces"("external_id"); 24 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Next.js: debug server-side", 6 | "type": "node-terminal", 7 | "request": "launch", 8 | "command": "npm run dev" 9 | }, 10 | { 11 | "name": "Next.js: debug client-side", 12 | "type": "chrome", 13 | "request": "launch", 14 | "url": "http://localhost:3000" 15 | }, 16 | { 17 | "name": "Next.js: debug full stack", 18 | "type": "node-terminal", 19 | "request": "launch", 20 | "command": "npm run dev", 21 | "serverReadyAction": { 22 | "pattern": "started server on .+, url: (https?://.+)", 23 | "uriFormat": "%s", 24 | "action": "debugWithChrome" 25 | } 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /src/components/table/table-link.tsx: -------------------------------------------------------------------------------- 1 | import { useRouter } from "next/router"; 2 | 3 | export type TableLinkProps = { 4 | path: string; 5 | value: string; 6 | truncateAt?: number; 7 | }; 8 | 9 | export default function TableLink({ 10 | path, 11 | value, 12 | truncateAt = 7, 13 | }: TableLinkProps) { 14 | const router = useRouter(); 15 | 16 | return ( 17 |
18 | 29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/features/feedback/server/corsMiddleware.ts: -------------------------------------------------------------------------------- 1 | import { type NextApiRequest, type NextApiResponse } from "next"; 2 | import Cors from "cors"; 3 | 4 | // You can read more about the available options here: https://github.com/expressjs/cors#configuration-options 5 | const cors = Cors({ 6 | methods: ["POST", "GET", "HEAD"], 7 | origin: "*", 8 | }); 9 | 10 | // Helper method to wait for a middleware to execute before continuing 11 | // And to throw an error when an error happens in a middleware 12 | export function runFeedbackCorsMiddleware( 13 | req: NextApiRequest, 14 | res: NextApiResponse, 15 | ) { 16 | return new Promise((resolve, reject) => { 17 | cors(req, res, (result: unknown) => { 18 | if (result instanceof Error) { 19 | return reject(result); 20 | } 21 | 22 | return resolve(result); 23 | }); 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /prisma/migrations/20230518193521_changes/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `parentObservationId` on the `observations` table. All the data in the column will be lost. 5 | - Added the required column `type` to the `observations` table without a default value. This is not possible if the table is not empty. 6 | 7 | */ 8 | -- DropForeignKey 9 | ALTER TABLE "observations" DROP CONSTRAINT "observations_parentObservationId_fkey"; 10 | 11 | -- AlterTable 12 | ALTER TABLE "observations" DROP COLUMN "parentObservationId", 13 | ADD COLUMN "parent_observation_id" TEXT, 14 | ADD COLUMN "type" TEXT NOT NULL; 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "observations" ADD CONSTRAINT "observations_parent_observation_id_fkey" FOREIGN KEY ("parent_observation_id") REFERENCES "observations"("id") ON DELETE SET NULL ON UPDATE CASCADE; 18 | -------------------------------------------------------------------------------- /src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as LabelPrimitive from "@radix-ui/react-label"; 5 | import { cva, type VariantProps } from "class-variance-authority"; 6 | import { cn } from "@/src/utils/tailwind"; 7 | 8 | const labelVariants = cva( 9 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", 10 | ); 11 | 12 | const Label = React.forwardRef< 13 | React.ElementRef, 14 | React.ComponentPropsWithoutRef & 15 | VariantProps 16 | >(({ className, ...props }, ref) => ( 17 | 22 | )); 23 | Label.displayName = LabelPrimitive.Root.displayName; 24 | 25 | export { Label }; 26 | -------------------------------------------------------------------------------- /prisma/migrations/20230809132331_add_project_id_to_observations/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - Added the required column `project_id` to the `observations` table without a default value. This is not possible if the table is not empty. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "observations" ADD COLUMN "project_id" TEXT; 9 | 10 | -- Backfill data in the new column 11 | UPDATE "observations" o 12 | SET "project_id" = t."project_id" 13 | FROM "traces" t 14 | WHERE o."trace_id" = t."id"; 15 | 16 | -- To be applied in separate migration after application release to minimize ingestion downtime 17 | -- ALTER TABLE "observations" 18 | -- ALTER COLUMN "project_id" SET NOT NULL; 19 | 20 | 21 | -- AddForeignKey 22 | ALTER TABLE "observations" ADD CONSTRAINT "observations_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "checkJs": true, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "incremental": true, 18 | "noUncheckedIndexedAccess": true, 19 | "baseUrl": ".", 20 | "types": ["jest", "node"], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": [ 26 | ".eslintrc.cjs", 27 | "next-env.d.ts", 28 | "**/*.ts", 29 | "**/*.tsx", 30 | "**/*.cjs", 31 | "**/*.mjs" 32 | ], 33 | "exclude": ["node_modules", "sdk"] 34 | } 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # database 12 | /prisma/db.sqlite 13 | /prisma/db.sqlite-journal 14 | 15 | # next.js 16 | /.next/ 17 | /out/ 18 | next-env.d.ts 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # local env files 34 | # do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables 35 | .env 36 | .env.local 37 | .env*.local 38 | 39 | # vercel 40 | .vercel 41 | 42 | # typescript 43 | *.tsbuildinfo 44 | 45 | /generated/typescript-server 46 | 47 | # openapi spec that is copied during build 48 | /public/openapi*.yml -------------------------------------------------------------------------------- /jest.config.mjs: -------------------------------------------------------------------------------- 1 | // jest.config.mjs 2 | import nextJest from "next/jest.js"; 3 | 4 | const createJestConfig = nextJest({ 5 | // Provide the path to your Next.js app to load next.config.js and .env files in your test environment 6 | dir: "./", 7 | }); 8 | 9 | const clientTestConfig = { 10 | displayName: "client", 11 | testMatch: ["/**/*.clienttest.[jt]s?(x)"], 12 | testEnvironment: "jest-environment-jsdom", 13 | }; 14 | 15 | const serverTestConfig = { 16 | displayName: "server", 17 | testMatch: ["/**/*.servertest.[jt]s?(x)"], 18 | testEnvironment: "jest-environment-node", 19 | }; 20 | 21 | // Add any custom config to be passed to Jest 22 | /** @type {import('jest').Config} */ 23 | const config = { 24 | // Add more setup options before each test is run 25 | projects: [ 26 | await createJestConfig(clientTestConfig)(), 27 | await createJestConfig(serverTestConfig)(), 28 | ], 29 | }; 30 | 31 | export default config; 32 | -------------------------------------------------------------------------------- /src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/src/utils/tailwind"; 2 | import * as React from "react"; 3 | 4 | export type InputProps = React.InputHTMLAttributes; 5 | 6 | const Input = React.forwardRef( 7 | ({ className, type, ...props }, ref) => { 8 | return ( 9 | 18 | ); 19 | }, 20 | ); 21 | Input.displayName = "Input"; 22 | 23 | export { Input }; 24 | -------------------------------------------------------------------------------- /src/components/stats-cards.tsx: -------------------------------------------------------------------------------- 1 | interface Statistic { 2 | name: string; 3 | stat: string; 4 | } 5 | 6 | export default function StatsCards({ stats }: { stats: Statistic[] }) { 7 | return ( 8 |
9 |

10 | Model configuration 11 |

12 |
13 | {stats.map((item) => ( 14 |
18 |
19 | {item.name} 20 |
21 |
22 | {item.stat} 23 |
24 |
25 | ))} 26 |
27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as SeparatorPrimitive from "@radix-ui/react-separator"; 5 | 6 | import { cn } from "@/src/utils/tailwind"; 7 | 8 | const Separator = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >( 12 | ( 13 | { className, orientation = "horizontal", decorative = true, ...props }, 14 | ref, 15 | ) => ( 16 | 27 | ), 28 | ); 29 | Separator.displayName = SeparatorPrimitive.Root.displayName; 30 | 31 | export { Separator }; 32 | -------------------------------------------------------------------------------- /src/features/support-chat/chat.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect } from "react"; 4 | import { Crisp } from "crisp-sdk-web"; 5 | 6 | const CrispChat = () => { 7 | useEffect(() => { 8 | if (process.env.NEXT_PUBLIC_CRISP_WEBSITE_ID) 9 | Crisp.configure(process.env.NEXT_PUBLIC_CRISP_WEBSITE_ID); 10 | }); 11 | 12 | return null; 13 | }; 14 | 15 | export default CrispChat; 16 | 17 | export const chatSetUser = ({ 18 | name, 19 | email, 20 | data, 21 | }: { 22 | name: string; 23 | email: string; 24 | data: object; 25 | }) => { 26 | if (process.env.NEXT_PUBLIC_CRISP_WEBSITE_ID) { 27 | Crisp.user.setEmail(email); 28 | Crisp.user.setNickname(name); 29 | Crisp.session.setData(data); 30 | } 31 | }; 32 | 33 | type Trigger = "after-project-creation"; 34 | 35 | export const chatRunTrigger = (trigger: Trigger) => { 36 | if (process.env.NEXT_PUBLIC_CRISP_WEBSITE_ID) { 37 | Crisp.trigger.run(trigger); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@/src/utils/tailwind"; 4 | 5 | // eslint-disable-next-line @typescript-eslint/no-empty-interface 6 | export interface TextareaProps 7 | extends React.TextareaHTMLAttributes {} 8 | 9 | const Textarea = React.forwardRef( 10 | ({ className, ...props }, ref) => { 11 | return ( 12 |