Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a829447410 | ||
|
|
53e7ff4e16 | ||
|
|
4a85b4a4f2 | ||
|
|
d2e6dca3d3 | ||
|
|
cdbdc0af67 | ||
|
|
2af9b52213 | ||
|
|
894a3d1b32 | ||
|
|
82f0134368 | ||
|
|
e52ba46b0e | ||
|
|
6aca4954ef | ||
|
|
9d5fa4e2f1 | ||
|
|
c1db242edd | ||
|
|
91a2c63c88 | ||
|
|
f7d9bd7297 | ||
|
|
d94e880520 | ||
|
|
74e9053f85 | ||
|
|
ae5260d808 | ||
|
|
f72f8b7e2c | ||
|
|
43108eccde | ||
|
|
c93c9560e1 | ||
|
|
ddad1afd7b | ||
|
|
2b70ae3742 | ||
|
|
b110fd7932 |
@@ -16,8 +16,17 @@ NEXTAUTH_SECRET="secret"
|
||||
|
||||
# Langfuse experimental features
|
||||
LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES="true"
|
||||
|
||||
# Salt for API key hashing
|
||||
SALT="salt"
|
||||
|
||||
# Email
|
||||
EMAIL_FROM_ADDRESS="" # Defines the email address to use as the from address.
|
||||
SMTP_CONNECTION_URL="" # Defines the connection url for smtp server.
|
||||
|
||||
# S3 storage
|
||||
S3_ENDPOINT=
|
||||
S3_ACCESS_KEY_ID=
|
||||
S3_SECRET_ACCESS_KEY=
|
||||
S3_BUCKET_NAME=
|
||||
S3_REGION=
|
||||
+1
-8
@@ -1,21 +1,14 @@
|
||||
# When adding additional environment variables, the schema in "/src/env.mjs"
|
||||
# should be updated accordingly.
|
||||
|
||||
# Prisma
|
||||
# https://www.prisma.io/docs/reference/database-reference/connection-urls#env
|
||||
DIRECT_URL="postgresql://postgres:postgres@db:5432/postgres"
|
||||
DATABASE_URL="postgresql://postgres:postgres@db:5432/postgres"
|
||||
# Next Auth
|
||||
# You can generate a new secret on the command line with:
|
||||
# openssl rand -base64 32
|
||||
# https://next-auth.js.org/configuration/options#secret
|
||||
NEXTAUTH_SECRET="secret"
|
||||
NEXTAUTH_URL="http://localhost:3000"
|
||||
|
||||
# feature flag to enable experimental features locally
|
||||
LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES="false"
|
||||
SALT="salt"
|
||||
|
||||
|
||||
# Email
|
||||
EMAIL_FROM_ADDRESS="" # Defines the email address to use as the from address.
|
||||
SMTP_CONNECTION_URL="" # Defines the connection url for smtp server.
|
||||
|
||||
+66
-34
@@ -1,32 +1,72 @@
|
||||
# Assuming deployment on Vercel with Postgres database on Supabase
|
||||
NEXT_PUBLIC_LANGFUSE_CLOUD_REGION="US"
|
||||
NEXTAUTH_COOKIE_DOMAIN=".langfuse.com"
|
||||
# More information: https://langfuse.com/docs/deployment/self-host
|
||||
|
||||
# When adding additional environment variables, the schema in "/src/env.mjs"
|
||||
# should be updated accordingly.
|
||||
|
||||
# Prisma
|
||||
|
||||
# https://www.prisma.io/docs/reference/database-reference/connection-urls#env
|
||||
DIRECT_URL="postgresql://postgres:[pw]@db.[db_id].supabase.co:5432/postgres"
|
||||
DATABASE_URL="postgres://postgres:[pw]@db.[db_id].supabase.co:6543/postgres?pgbouncer=true&connection_limit=1"
|
||||
# DATABASE_URL supports pooled connections, but then you need to set DIRECT_URL
|
||||
DATABASE_URL="postgresql://postgres:postgres@db:5432/postgres"
|
||||
# DIRECT_URL="postgresql://postgres:postgres@db:5432/postgres"
|
||||
# SHADOW_DATABASE_URL=
|
||||
|
||||
# Next Auth
|
||||
|
||||
# NEXTAUTH_URL does not need to be set when deploying on Vercel
|
||||
# NEXTAUTH_URL="http://localhost:3000"
|
||||
|
||||
# AUTH_REDIRECT_PROXY_URL used to proxy oauth callbacks on e.g. preview deployments. optional.
|
||||
# AUTH_REDIRECT_PROXY_URL="https://example.com/api/auth"
|
||||
NEXTAUTH_URL="http://localhost:3000"
|
||||
|
||||
# You can generate a new secret on the command line with:
|
||||
# openssl rand -base64 32
|
||||
# https://next-auth.js.org/configuration/options#secret
|
||||
NEXTAUTH_SECRET="secret"
|
||||
SALT="salt"
|
||||
# Sentry; set via Vercel integration
|
||||
# NEXT_PUBLIC_SENTRY_DSN=
|
||||
# NEXT_SENTRY_ORG=
|
||||
# NEXT_SENTRY_PROJECT=
|
||||
# SENTRY_AUTH_TOKEN=
|
||||
|
||||
# Docker only, optional
|
||||
# PORT=3000
|
||||
# HOSTNAME=localhost
|
||||
|
||||
# Default project, optional
|
||||
# LANGFUSE_DEFAULT_PROJECT_ID=
|
||||
# LANGFUSE_DEFAULT_PROJECT_ROLE=
|
||||
|
||||
# Enable experimental features, optional
|
||||
# NEXT_PUBLIC_ENABLE_EXPERIMENTAL_FEATURES
|
||||
|
||||
# Auth, optional configuration
|
||||
# AUTH_DOMAINS_WITH_SSO_ENFORCEMENT=domain1.com,domain2.com
|
||||
# AUTH_DISABLE_USERNAME_PASSWORD=true
|
||||
|
||||
# SSO, each group is optional
|
||||
# AUTH_GOOGLE_CLIENT_ID=
|
||||
# AUTH_GOOGLE_CLIENT_SECRET=
|
||||
# AUTH_GITHUB_CLIENT_ID=
|
||||
# AUTH_GITHUB_CLIENT_SECRET=
|
||||
# AUTH_AZURE_AD_CLIENT_ID=
|
||||
# AUTH_AZURE_AD_CLIENT_SECRET=
|
||||
# AUTH_AZURE_AD_TENANT_ID=
|
||||
|
||||
# Transactional email, optional
|
||||
# Defines the email address to use as the from address.
|
||||
# EMAIL_FROM_ADDRESS=
|
||||
# Defines the connection url for smtp server.
|
||||
# SMTP_CONNECTION_URL=
|
||||
|
||||
# S3 storage, optional, used for exports from the UI
|
||||
# S3_ENDPOINT=
|
||||
# S3_ACCESS_KEY_ID=
|
||||
# S3_SECRET_ACCESS_KEY=
|
||||
# S3_BUCKET_NAME=
|
||||
# S3_REGION=
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### START Langfuse Cloud Config
|
||||
# Used for Langfuse Cloud deployments
|
||||
# Not recommended for self-hosted deployments as these are NOT COVERED BY SEMVER
|
||||
|
||||
# NEXT_PUBLIC_LANGFUSE_CLOUD_REGION="US"
|
||||
# NEXTAUTH_COOKIE_DOMAIN=".langfuse.com"
|
||||
|
||||
# LANGFUSE_TEAM_SLACK_WEBHOOK=
|
||||
# LANGFUSE_NEW_USER_SIGNUP_WEBHOOK=
|
||||
@@ -35,24 +75,16 @@ SALT="salt"
|
||||
# NEXT_PUBLIC_POSTHOG_HOST=
|
||||
# NEXT_PUBLIC_POSTHOG_KEY=
|
||||
|
||||
# Id of demo project to automatically assign new users to
|
||||
# Sentry
|
||||
# NEXT_PUBLIC_SENTRY_DSN=
|
||||
# NEXT_SENTRY_ORG=
|
||||
# NEXT_SENTRY_PROJECT=
|
||||
# SENTRY_AUTH_TOKEN=
|
||||
|
||||
# Demo project that users can use to try the platform
|
||||
# NEXT_PUBLIC_DEMO_PROJECT_ID=
|
||||
|
||||
# Auth, each group is optional
|
||||
AUTH_GOOGLE_CLIENT_ID=
|
||||
AUTH_GOOGLE_CLIENT_SECRET=
|
||||
AUTH_GITHUB_CLIENT_ID=
|
||||
AUTH_GITHUB_CLIENT_SECRET=
|
||||
# AUTH_DOMAINS_WITH_SSO_ENFORCEMENT=domain1.com,domain2.com
|
||||
# AUTH_DISABLE_USERNAME_PASSWORD=true
|
||||
# Crisp chat
|
||||
# NEXT_PUBLIC_CRISP_WEBSITE_ID=
|
||||
|
||||
# Email
|
||||
EMAIL_FROM_ADDRESS="" # Defines the email address to use as the from address.
|
||||
SMTP_CONNECTION_URL="" # Defines the connection url for smtp server.
|
||||
|
||||
# S3 storage (eg for exports), necessary for serverless deployments
|
||||
S3_ENDPOINT=""
|
||||
S3_ACCESS_KEY_ID=""
|
||||
S3_SECRET_ACCESS_KEY=""
|
||||
S3_BUCKET_NAME=""
|
||||
S3_REGION=""
|
||||
### END Langfuse Cloud Config
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the feature or potential improvement
|
||||
description: Please describe the change as clear and concise as possible. Remember to add context as to why you believe this is needed.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: Add any other information related to the change here. If your idea is related to any issues or discussions, link them here.
|
||||
- type: checkboxes
|
||||
id: contribute
|
||||
attributes:
|
||||
label: Contribute
|
||||
description: Are you willing to contribute to the implementation of this idea?
|
||||
options:
|
||||
- label: Yes, I can implement this and raise a PR
|
||||
@@ -13,6 +13,14 @@
|
||||
</div>
|
||||
</br>
|
||||
<div>
|
||||
<a href="https://cloud.langfuse.com">
|
||||
<strong>Langfuse Cloud »</strong>
|
||||
</a>
|
||||
</br>
|
||||
<a href="https://langfuse.com/docs/deployment/self-host">
|
||||
<strong>Self Host Langfuse »</strong>
|
||||
</a>
|
||||
</br>
|
||||
<a href="https://discord.gg/7NXusRtqYU">
|
||||
<strong>Join the Langfuse Discord »</strong>
|
||||
</a>
|
||||
@@ -26,11 +34,12 @@
|
||||
<a href="https://github.com/langfuse/langfuse/issues/new?labels=%F0%9F%90%9E%E2%9D%94+unconfirmed+bug&projects=&template=bug_report.yml&title=bug%3A+">
|
||||
<strong>Report Bug</strong>
|
||||
</a> ·
|
||||
<a href="https://github.com/langfuse/langfuse/issues/new?assignees=&labels=%E2%9C%A8+enhancement&projects=&template=feature_request.yml&title=feat%3A+">
|
||||
<a href="https://github.com/orgs/langfuse/discussions/new?category=ideas">
|
||||
<strong>Feature Request</strong>
|
||||
</a>
|
||||
</div>
|
||||
</br>
|
||||
</br>
|
||||
<div>
|
||||
<img src="https://img.shields.io/badge/License-MIT-red.svg?style=flat-square" alt="MIT License">
|
||||
<a href="https://discord.gg/7NXusRtqYU"><img src="https://img.shields.io/discord/1111061815649124414?style=flat-square&logo=Discord&logoColor=white&label=Discord&color=%23434EE4" alt="Discord"></a>
|
||||
|
||||
@@ -132,6 +132,9 @@ types:
|
||||
enum:
|
||||
- CHARACTERS
|
||||
- TOKENS
|
||||
- MILLISECONDS
|
||||
- SECONDS
|
||||
- IMAGES
|
||||
ObservationLevel:
|
||||
enum:
|
||||
- DEBUG
|
||||
|
||||
@@ -1311,6 +1311,9 @@ components:
|
||||
enum:
|
||||
- CHARACTERS
|
||||
- TOKENS
|
||||
- MILLISECONDS
|
||||
- SECONDS
|
||||
- IMAGES
|
||||
ObservationLevel:
|
||||
title: ObservationLevel
|
||||
type: string
|
||||
|
||||
Generated
+433
-290
File diff suppressed because it is too large
Load Diff
+17
-15
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "langfuse-core",
|
||||
"version": "2.1.2",
|
||||
"version": "2.2.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"prebuild": "cp generated/openapi-client/openapi.yml public/openapi-client.yml && cp generated/openapi-server/openapi.yml public/openapi-server.yml",
|
||||
@@ -16,6 +16,8 @@
|
||||
"test": "jest --runInBand",
|
||||
"test:watch": "jest --watch --runInBand",
|
||||
"test:e2e": "playwright test",
|
||||
"infra:dev:up": "docker-compose -f docker-compose.dev.yml up -d",
|
||||
"infra:dev:down": "docker-compose -f docker-compose.dev.yml down",
|
||||
"db:migrate": "DISABLE_ERD=false npx prisma migrate dev",
|
||||
"db:reset": "npx prisma migrate reset",
|
||||
"db:seed": "npx prisma db seed",
|
||||
@@ -28,13 +30,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/tokenizer": "^0.0.4",
|
||||
"@aws-sdk/client-s3": "^3.501.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.501.0",
|
||||
"@aws-sdk/client-s3": "^3.507.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.507.0",
|
||||
"@headlessui/react": "^1.7.18",
|
||||
"@heroicons/react": "^2.1.1",
|
||||
"@hookform/resolvers": "^3.3.4",
|
||||
"@next-auth/prisma-adapter": "^1.0.7",
|
||||
"@prisma/client": "^5.8.1",
|
||||
"@prisma/client": "^5.9.1",
|
||||
"@radix-ui/react-accordion": "^1.1.2",
|
||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||
"@radix-ui/react-avatar": "^1.0.4",
|
||||
@@ -63,7 +65,7 @@
|
||||
"@t3-oss/env-nextjs": "^0.8.0",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"@tanstack/react-table": "^8.11.7",
|
||||
"@tanstack/react-table": "^8.11.8",
|
||||
"@tremor/react": "^3.11.1",
|
||||
"@trpc/client": "^10.45.0",
|
||||
"@trpc/next": "^10.45.0",
|
||||
@@ -81,17 +83,17 @@
|
||||
"exponential-backoff": "^3.1.1",
|
||||
"js-tiktoken": "^1.0.10",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-react": "^0.321.0",
|
||||
"lucide-react": "^0.323.0",
|
||||
"next": "^14.1.0",
|
||||
"next-auth": "^4.24.5",
|
||||
"next-query-params": "^5.0.0",
|
||||
"nodemailer": "^6.9.9",
|
||||
"posthog-js": "^1.103.1",
|
||||
"posthog-node": "^3.6.1",
|
||||
"posthog-js": "^1.104.4",
|
||||
"posthog-node": "^3.6.2",
|
||||
"react": "18.2.0",
|
||||
"react-day-picker": "^8.10.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "^7.49.3",
|
||||
"react-hook-form": "^7.50.1",
|
||||
"react-icons": "^5.0.1",
|
||||
"react-responsive": "^9.0.2",
|
||||
"react18-json-view": "^0.2.7",
|
||||
@@ -107,7 +109,7 @@
|
||||
"@mermaid-js/mermaid-cli": "^10.7.0",
|
||||
"@playwright/test": "^1.41.2",
|
||||
"@release-it/bumper": "^6.0.1",
|
||||
"@testing-library/jest-dom": "^6.4.1",
|
||||
"@testing-library/jest-dom": "^6.4.2",
|
||||
"@testing-library/react": "^14.2.1",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/cors": "^2.8.17",
|
||||
@@ -116,21 +118,21 @@
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/node": "20.10.5",
|
||||
"@types/nodemailer": "^6.4.14",
|
||||
"@types/react": "^18.2.51",
|
||||
"@types/react": "^18.2.55",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "^6.20.0",
|
||||
"@typescript-eslint/parser": "^6.20.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"autoprefixer": "^10.4.17",
|
||||
"dotenv-cli": "^7.3.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-next": "^14.1.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"postcss": "^8.4.33",
|
||||
"prettier": "^3.2.4",
|
||||
"postcss": "^8.4.34",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-tailwindcss": "^0.5.11",
|
||||
"prisma": "^5.8.1",
|
||||
"prisma": "^5.9.1",
|
||||
"prisma-erd-generator": "^1.11.2",
|
||||
"release-it": "^17.0.3",
|
||||
"tailwindcss": "^3.4.1",
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
-- This is an empty migration.
|
||||
|
||||
DELETE FROM models
|
||||
WHERE id in ('clrkwk4cb000308l5go4b6otm', 'clrntjt89000a08jw0gcdbd5a');
|
||||
|
||||
|
||||
|
||||
INSERT INTO models (
|
||||
id,
|
||||
project_id,
|
||||
model_name,
|
||||
match_pattern,
|
||||
start_date,
|
||||
input_price,
|
||||
output_price,
|
||||
total_price,
|
||||
unit,
|
||||
tokenizer_id,
|
||||
tokenizer_config
|
||||
)
|
||||
VALUES
|
||||
-- https://openai.com/blog/new-embedding-models-and-api-updates
|
||||
|
||||
|
||||
-- fix prices
|
||||
('clrkwk4cb000308l5go4b6otm', NULL, 'gpt-3.5-turbo-16k', '(?i)^(gpt-)(35|3.5)(-turbo-16k)$', NULL, 0.000003, 0.000004, NULL, 'TOKENS', 'openai', '{ "tokensPerMessage": 3, "tokensPerName": 1, "tokenizerModel": "gpt-3.5-turbo-16k" }'),
|
||||
('clrntjt89000a08jw0gcdbd5a', NULL, 'gpt-3.5-turbo-16k-0613', '(?i)^(gpt-)(35|3.5)(-turbo-16k-0613)$', NULL, 0.000003, 0.000004, NULL, 'TOKENS', 'openai', '{ "tokensPerMessage": 3, "tokensPerName": 1, "tokenizerModel": "gpt-3.5-turbo-16k-0613" }')
|
||||
@@ -440,7 +440,7 @@ model Model {
|
||||
inputPrice Decimal? @map("input_price")
|
||||
outputPrice Decimal? @map("output_price")
|
||||
totalPrice Decimal? @map("total_price")
|
||||
unit String // TOKENS or CHARACTERS
|
||||
unit String // TOKENS, CHARACTERS, MILLISECONDS, SECONDS, or IMAGES
|
||||
tokenizerId String? @map("tokenizer_id")
|
||||
tokenizerConfig Json? @map("tokenizer_config")
|
||||
|
||||
|
||||
+2
-1
@@ -5,6 +5,7 @@ import {
|
||||
} from "@/src/features/public-api/lib/apiKeys";
|
||||
import { hash } from "bcryptjs";
|
||||
import { parseArgs } from "node:util";
|
||||
import { ModelUsageUnit } from "@/src/constants";
|
||||
|
||||
const options = {
|
||||
environment: { type: "string" },
|
||||
@@ -487,7 +488,7 @@ async function main() {
|
||||
? { prompt: { connect: { id: prompt.id } } }
|
||||
: {}),
|
||||
},
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
});
|
||||
if (Math.random() > 0.6)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/** @jest-environment node */
|
||||
|
||||
import { pruneDatabase } from "@/src/__tests__/test-utils";
|
||||
import { ModelUsageUnit } from "@/src/constants";
|
||||
import { prisma } from "@/src/server/db";
|
||||
|
||||
describe("cost retrieval tests", () => {
|
||||
@@ -111,7 +112,7 @@ describe("cost retrieval tests", () => {
|
||||
projectId: null,
|
||||
startDate: new Date("2023-12-01"),
|
||||
tokenizerConfig: { tokensPerMessage: 3, tokensPerName: 1 },
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -130,7 +131,7 @@ describe("cost retrieval tests", () => {
|
||||
model: "gpt-3.5-turbo",
|
||||
internalModel: "gpt-3.5-turbo",
|
||||
startTime: new Date("2024-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
promptTokens: input.promptTokens,
|
||||
completionTokens: input.completionTokens,
|
||||
totalTokens: input.totalTokens,
|
||||
@@ -181,7 +182,7 @@ describe("cost retrieval tests", () => {
|
||||
projectId: null,
|
||||
startDate: new Date("2023-12-01"),
|
||||
tokenizerConfig: { tokensPerMessage: 3, tokensPerName: 1 },
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
});
|
||||
await prisma.model.create({
|
||||
@@ -194,7 +195,7 @@ describe("cost retrieval tests", () => {
|
||||
matchPattern: "(.*)(gpt-)(35|3.5)(-turbo)?(.*)",
|
||||
startDate: new Date("2023-12-01"),
|
||||
tokenizerConfig: { tokensPerMessage: 3, tokensPerName: 1 },
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
project: { connect: { id: "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a" } },
|
||||
},
|
||||
});
|
||||
@@ -214,7 +215,7 @@ describe("cost retrieval tests", () => {
|
||||
model: "gpt-3.5-turbo",
|
||||
internalModel: "gpt-3.5-turbo",
|
||||
startTime: new Date("2024-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
promptTokens: 200,
|
||||
completionTokens: 3000,
|
||||
totalTokens: undefined,
|
||||
@@ -252,7 +253,7 @@ describe("cost retrieval tests", () => {
|
||||
projectId: null,
|
||||
startDate: new Date("2023-12-01"),
|
||||
tokenizerConfig: { tokensPerMessage: 3, tokensPerName: 1 },
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
});
|
||||
await prisma.model.create({
|
||||
@@ -265,7 +266,7 @@ describe("cost retrieval tests", () => {
|
||||
matchPattern: "(.*)(gpt-)(35|3.5)(-turbo)?(.*)",
|
||||
startDate: new Date("2023-12-02"),
|
||||
tokenizerConfig: { tokensPerMessage: 3, tokensPerName: 1 },
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -284,7 +285,7 @@ describe("cost retrieval tests", () => {
|
||||
model: "gpt-3.5-turbo",
|
||||
internalModel: "gpt-3.5-turbo",
|
||||
startTime: new Date("2024-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
promptTokens: 200,
|
||||
completionTokens: 3000,
|
||||
totalTokens: undefined,
|
||||
@@ -317,7 +318,7 @@ describe("cost retrieval tests", () => {
|
||||
matchPattern: "(.*)(gpt-)(35|3.5)(-turbo)?(.*)",
|
||||
startDate: new Date("2023-12-02"),
|
||||
tokenizerConfig: { tokensPerMessage: 3, tokensPerName: 1 },
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
});
|
||||
await prisma.model.create({
|
||||
@@ -331,7 +332,7 @@ describe("cost retrieval tests", () => {
|
||||
startDate: new Date("2023-12-01"),
|
||||
tokenizerConfig: { tokensPerMessage: 3, tokensPerName: 1 },
|
||||
project: { connect: { id: "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a" } },
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -350,7 +351,7 @@ describe("cost retrieval tests", () => {
|
||||
model: "gpt-3.5-turbo",
|
||||
internalModel: "gpt-3.5-turbo",
|
||||
startTime: new Date("2024-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
promptTokens: 200,
|
||||
completionTokens: 3000,
|
||||
totalTokens: undefined,
|
||||
@@ -383,7 +384,7 @@ describe("cost retrieval tests", () => {
|
||||
matchPattern: "(.*)(gpt-)(35|3.5)(-turbo)?(.*)",
|
||||
startDate: new Date("2023-12-02"),
|
||||
tokenizerConfig: { tokensPerMessage: 3, tokensPerName: 1 },
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
});
|
||||
await prisma.model.create({
|
||||
@@ -397,7 +398,7 @@ describe("cost retrieval tests", () => {
|
||||
startDate: new Date("2023-12-01"),
|
||||
tokenizerConfig: { tokensPerMessage: 3, tokensPerName: 1 },
|
||||
project: { connect: { id: "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a" } },
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -416,7 +417,7 @@ describe("cost retrieval tests", () => {
|
||||
model: "gpt-3.5-turbo",
|
||||
internalModel: "gpt-3.5-turbo",
|
||||
startTime: new Date("2024-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
promptTokens: 200,
|
||||
completionTokens: 3000,
|
||||
totalTokens: undefined,
|
||||
|
||||
@@ -1,27 +1,8 @@
|
||||
import { orderByToPrismaSql } from "@/src/features/orderBy/server/orderByToPrisma";
|
||||
import { tracesTableCols } from "@/src/server/api/definitions/tracesTable";
|
||||
import { Prisma } from "@prisma/client";
|
||||
|
||||
// The test for the orderByToPrisma function
|
||||
describe("orderByToPrisma (Convert orderBy to Prisma.sql)", () => {
|
||||
test("orderByToPrisma returns default sql when orderBy=null", () => {
|
||||
expect(orderByToPrismaSql(null, tracesTableCols)).toStrictEqual(
|
||||
Prisma.sql`ORDER BY t.timestamp DESC`,
|
||||
);
|
||||
});
|
||||
|
||||
test("orderByToPrisma returns correct clause for orderBy column included in column defs", () => {
|
||||
expect(
|
||||
orderByToPrismaSql(
|
||||
{
|
||||
column: "latency",
|
||||
order: "ASC",
|
||||
},
|
||||
tracesTableCols,
|
||||
),
|
||||
).toStrictEqual(Prisma.sql`ORDER BY tl.latency ASC`);
|
||||
});
|
||||
|
||||
test("orderByToPrisma throws error for orderBy column not included in column defs", () => {
|
||||
expect(() =>
|
||||
orderByToPrismaSql(
|
||||
@@ -33,4 +14,16 @@ describe("orderByToPrisma (Convert orderBy to Prisma.sql)", () => {
|
||||
),
|
||||
).toThrow(/Invalid filter column: InvalidCol/);
|
||||
});
|
||||
|
||||
test("orderByToPrisma throws error for orderBy order that is not valid", () => {
|
||||
expect(() =>
|
||||
orderByToPrismaSql(
|
||||
{
|
||||
column: "latency",
|
||||
order: "test" as "ASC" | "DESC",
|
||||
},
|
||||
tracesTableCols,
|
||||
),
|
||||
).toThrow(/Invalid order: test/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
/** @jest-environment node */
|
||||
|
||||
import { prisma } from "@/src/server/db";
|
||||
import { makeAPICall, pruneDatabase } from "@/src/__tests__/test-utils";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { makeAPICall, pruneDatabase } from "@/src/__tests__/test-utils";
|
||||
import { ModelUsageUnit } from "@/src/constants";
|
||||
import { prisma } from "@/src/server/db";
|
||||
|
||||
describe("/api/public/generations API Endpoint", () => {
|
||||
beforeEach(async () => await pruneDatabase());
|
||||
|
||||
@@ -13,9 +15,9 @@ describe("/api/public/generations API Endpoint", () => {
|
||||
input: 100,
|
||||
output: 200,
|
||||
total: 100,
|
||||
unit: "CHARACTERS",
|
||||
unit: ModelUsageUnit.Characters,
|
||||
},
|
||||
expectedUnit: "CHARACTERS",
|
||||
expectedUnit: ModelUsageUnit.Characters,
|
||||
expectedPromptTokens: 100,
|
||||
expectedCompletionTokens: 200,
|
||||
expectedTotalTokens: 100,
|
||||
@@ -23,9 +25,9 @@ describe("/api/public/generations API Endpoint", () => {
|
||||
{
|
||||
usage: {
|
||||
total: 100,
|
||||
unit: "CHARACTERS",
|
||||
unit: ModelUsageUnit.Characters,
|
||||
},
|
||||
expectedUnit: "CHARACTERS",
|
||||
expectedUnit: ModelUsageUnit.Characters,
|
||||
expectedPromptTokens: 0,
|
||||
expectedCompletionTokens: 0,
|
||||
expectedTotalTokens: 100,
|
||||
@@ -48,7 +50,7 @@ describe("/api/public/generations API Endpoint", () => {
|
||||
expectedPromptTokens: 100,
|
||||
expectedCompletionTokens: 200,
|
||||
expectedTotalTokens: 100,
|
||||
expectedUnit: "TOKENS",
|
||||
expectedUnit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
{
|
||||
usage: {
|
||||
@@ -57,7 +59,7 @@ describe("/api/public/generations API Endpoint", () => {
|
||||
expectedPromptTokens: 0,
|
||||
expectedCompletionTokens: 0,
|
||||
expectedTotalTokens: 100,
|
||||
expectedUnit: "TOKENS",
|
||||
expectedUnit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
{
|
||||
usage: undefined,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
/** @jest-environment node */
|
||||
|
||||
import { v4 } from "uuid";
|
||||
|
||||
import { makeAPICall, pruneDatabase } from "@/src/__tests__/test-utils";
|
||||
import { ModelUsageUnit } from "@/src/constants";
|
||||
import { cleanEvent } from "@/src/pages/api/public/ingestion";
|
||||
import { prisma } from "@/src/server/db";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
describe("/api/public/ingestion API Endpoint", () => {
|
||||
beforeEach(async () => await pruneDatabase());
|
||||
@@ -14,12 +16,12 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
input: 100,
|
||||
output: 200,
|
||||
total: 100,
|
||||
unit: "CHARACTERS",
|
||||
unit: ModelUsageUnit.Characters,
|
||||
inputCost: 123,
|
||||
outputCost: 456,
|
||||
totalCost: 789,
|
||||
},
|
||||
expectedUnit: "CHARACTERS",
|
||||
expectedUnit: ModelUsageUnit.Characters,
|
||||
expectedPromptTokens: 100,
|
||||
expectedCompletionTokens: 200,
|
||||
expectedTotalTokens: 100,
|
||||
@@ -27,13 +29,45 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
{
|
||||
usage: {
|
||||
total: 100,
|
||||
unit: "CHARACTERS",
|
||||
unit: ModelUsageUnit.Characters,
|
||||
},
|
||||
expectedUnit: "CHARACTERS",
|
||||
expectedUnit: ModelUsageUnit.Characters,
|
||||
expectedPromptTokens: 0,
|
||||
expectedCompletionTokens: 0,
|
||||
expectedTotalTokens: 100,
|
||||
},
|
||||
{
|
||||
usage: {
|
||||
total: 100,
|
||||
unit: ModelUsageUnit.Milliseconds,
|
||||
},
|
||||
expectedUnit: ModelUsageUnit.Milliseconds,
|
||||
expectedPromptTokens: 0,
|
||||
expectedCompletionTokens: 0,
|
||||
expectedTotalTokens: 100,
|
||||
},
|
||||
{
|
||||
usage: {
|
||||
input: 1,
|
||||
output: 2,
|
||||
unit: ModelUsageUnit.Images,
|
||||
},
|
||||
expectedUnit: ModelUsageUnit.Images,
|
||||
expectedPromptTokens: 1,
|
||||
expectedCompletionTokens: 2,
|
||||
expectedTotalTokens: 3,
|
||||
},
|
||||
{
|
||||
usage: {
|
||||
input: 30,
|
||||
output: 10,
|
||||
unit: ModelUsageUnit.Seconds,
|
||||
},
|
||||
expectedUnit: ModelUsageUnit.Seconds,
|
||||
expectedPromptTokens: 30,
|
||||
expectedCompletionTokens: 10,
|
||||
expectedTotalTokens: 40,
|
||||
},
|
||||
{
|
||||
usage: {
|
||||
total: 100,
|
||||
@@ -52,7 +86,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
expectedPromptTokens: 100,
|
||||
expectedCompletionTokens: 200,
|
||||
expectedTotalTokens: 100,
|
||||
expectedUnit: "TOKENS",
|
||||
expectedUnit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
{
|
||||
usage: {
|
||||
@@ -61,7 +95,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
expectedPromptTokens: 0,
|
||||
expectedCompletionTokens: 0,
|
||||
expectedTotalTokens: 100,
|
||||
expectedUnit: "TOKENS",
|
||||
expectedUnit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
{
|
||||
usage: undefined,
|
||||
@@ -256,7 +290,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
{
|
||||
observationExternalModel: "gpt-3.5",
|
||||
observationStartTime: new Date("2021-01-01T00:00:00.000Z"),
|
||||
modelUnit: "TOKENS",
|
||||
modelUnit: ModelUsageUnit.Tokens,
|
||||
expectedInternalModel: "gpt-3.5-turbo",
|
||||
expectedPromptTokens: 5,
|
||||
expectedCompletionTokens: 7,
|
||||
@@ -265,7 +299,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
modelName: "gpt-3.5-turbo",
|
||||
matchPattern: "(?i)^(gpt-)(35|3.5)(-turbo)?$",
|
||||
startDate: new Date("2021-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerId: "openai",
|
||||
},
|
||||
],
|
||||
@@ -273,7 +307,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
{
|
||||
observationExternalModel: "gpt-3.5",
|
||||
observationStartTime: new Date("2021-01-01T00:00:00.000Z"),
|
||||
modelUnit: "TOKENS",
|
||||
modelUnit: ModelUsageUnit.Tokens,
|
||||
expectedInternalModel: "gpt-3.5-turbo",
|
||||
expectedPromptTokens: 5,
|
||||
expectedCompletionTokens: 7,
|
||||
@@ -282,7 +316,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
modelName: "gpt-3.5-turbo",
|
||||
matchPattern: "(?i)^(gpt-)(35|3.5)(-turbo)?$",
|
||||
startDate: null,
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerId: "openai",
|
||||
},
|
||||
],
|
||||
@@ -290,7 +324,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
{
|
||||
observationExternalModel: "GPT-3.5",
|
||||
observationStartTime: new Date("2021-01-01T00:00:00.000Z"),
|
||||
modelUnit: "TOKENS",
|
||||
modelUnit: ModelUsageUnit.Tokens,
|
||||
expectedInternalModel: "gpt-3.5-turbo",
|
||||
expectedPromptTokens: 5,
|
||||
expectedCompletionTokens: 7,
|
||||
@@ -299,7 +333,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
modelName: "gpt-3.5-turbo",
|
||||
matchPattern: "(?i)^(gpt-)(35|3.5)(-turbo)?$",
|
||||
startDate: new Date("2021-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerId: "openai",
|
||||
},
|
||||
],
|
||||
@@ -307,7 +341,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
{
|
||||
observationExternalModel: "GPT-3.5",
|
||||
observationStartTime: new Date("2021-01-01T00:00:00.000Z"),
|
||||
modelUnit: "TOKENS",
|
||||
modelUnit: ModelUsageUnit.Tokens,
|
||||
expectedInternalModel: "gpt-3.5-turbo",
|
||||
expectedPromptTokens: 5,
|
||||
expectedCompletionTokens: 7,
|
||||
@@ -316,14 +350,14 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
modelName: "gpt-3.5-turbo",
|
||||
matchPattern: "(?i)^(gpt-)(35|3.5)(-turbo)?$",
|
||||
startDate: new Date("2021-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerId: "openai",
|
||||
},
|
||||
{
|
||||
modelName: "gpt-3.5-turbo-new",
|
||||
matchPattern: "(?i)^(gpt-)(35|3.5)(-turbo)?$",
|
||||
startDate: new Date("2021-01-01T10:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerId: "openai",
|
||||
},
|
||||
],
|
||||
@@ -331,7 +365,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
{
|
||||
observationExternalModel: "GPT-3.5",
|
||||
observationStartTime: new Date("2021-01-02T00:00:00.000Z"),
|
||||
modelUnit: "TOKENS",
|
||||
modelUnit: ModelUsageUnit.Tokens,
|
||||
expectedInternalModel: "gpt-3.5-turbo",
|
||||
expectedPromptTokens: 5,
|
||||
expectedCompletionTokens: 7,
|
||||
@@ -340,14 +374,14 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
modelName: "gpt-3.5-turbo-new",
|
||||
matchPattern: "(?i)^(gpt-)(35|3.5)(-turbo)?$",
|
||||
startDate: new Date("2021-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerId: "openai",
|
||||
},
|
||||
{
|
||||
modelName: "gpt-3.5-turbo",
|
||||
matchPattern: "(?i)^(gpt-)(35|3.5)(-turbo)?$",
|
||||
startDate: new Date("2021-01-01T10:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerId: "openai",
|
||||
tokenizerModel: "gpt-3.5-turbo",
|
||||
},
|
||||
@@ -356,7 +390,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
{
|
||||
observationExternalModel: "ft:gpt-3.5-turbo-1106:my-org:custom_suffix:id",
|
||||
observationStartTime: new Date("2022-01-01T10:00:00.000Z"),
|
||||
modelUnit: "TOKENS",
|
||||
modelUnit: ModelUsageUnit.Tokens,
|
||||
expectedInternalModel: "ft:gpt-3.5-turbo-1106",
|
||||
expectedPromptTokens: 0,
|
||||
expectedCompletionTokens: 0,
|
||||
@@ -365,7 +399,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
modelName: "ft:gpt-3.5-turbo-1106",
|
||||
matchPattern: "(?i)^(ft:)(gpt-3.5-turbo-1106:)(.+)(:)(.*)(:)(.+)$",
|
||||
startDate: new Date("2022-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerId: "openai",
|
||||
},
|
||||
],
|
||||
@@ -373,7 +407,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
{
|
||||
observationExternalModel: "ft:babbage-002:my-org#2:custom_suffix-2:id",
|
||||
observationStartTime: new Date("2022-01-01T10:00:00.000Z"),
|
||||
modelUnit: "TOKENS",
|
||||
modelUnit: ModelUsageUnit.Tokens,
|
||||
expectedInternalModel: "ft:babbage-002",
|
||||
expectedPromptTokens: 0,
|
||||
expectedCompletionTokens: 0,
|
||||
@@ -382,7 +416,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
modelName: "ft:babbage-002",
|
||||
matchPattern: "(?i)^(ft:)(babbage-002:)(.+)(:)(.*)(:)(.+)$",
|
||||
startDate: new Date("2022-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerId: "openai",
|
||||
},
|
||||
],
|
||||
@@ -390,7 +424,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
{
|
||||
observationExternalModel: "GPT-4",
|
||||
observationStartTime: new Date("2021-01-01T00:00:00.000Z"),
|
||||
modelUnit: "TOKENS",
|
||||
modelUnit: ModelUsageUnit.Tokens,
|
||||
expectedInternalModel: null,
|
||||
expectedPromptTokens: 0,
|
||||
expectedCompletionTokens: 0,
|
||||
@@ -399,7 +433,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
modelName: "gpt-3.5-turbo",
|
||||
matchPattern: "(?i)^(gpt-)(35|3.5)(-turbo)?$",
|
||||
startDate: new Date("2021-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerId: "openai",
|
||||
},
|
||||
],
|
||||
@@ -407,7 +441,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
{
|
||||
observationExternalModel: "GPT-3",
|
||||
observationStartTime: new Date("2021-01-01T00:00:00.000Z"),
|
||||
modelUnit: "CHARACTERS",
|
||||
modelUnit: ModelUsageUnit.Characters,
|
||||
expectedInternalModel: null,
|
||||
expectedPromptTokens: 0,
|
||||
expectedCompletionTokens: 0,
|
||||
@@ -416,7 +450,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
modelName: "gpt-3.5-turbo",
|
||||
matchPattern: "(?i)^(gpt-)(35|3.5)(-turbo)?$",
|
||||
startDate: new Date("2021-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerId: "openai",
|
||||
},
|
||||
],
|
||||
@@ -886,7 +920,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
modelName: "gpt-3.5",
|
||||
matchPattern: "(?i)^(gpt-)(35|3.5)(-turbo)?$",
|
||||
startDate: new Date("2021-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerId: "openai",
|
||||
tokenizerConfig: {
|
||||
tokensPerMessage: 3,
|
||||
@@ -968,7 +1002,7 @@ describe("/api/public/ingestion API Endpoint", () => {
|
||||
modelName: "gpt-3.5",
|
||||
matchPattern: "(?i)^(gpt-)(35|3.5)(-turbo)?$",
|
||||
startDate: new Date("2021-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerId: "openai",
|
||||
tokenizerConfig: {
|
||||
tokensPerMessage: 3,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { modelMatch } from "@/scripts/model-match";
|
||||
import { pruneDatabase } from "@/src/__tests__/test-utils";
|
||||
import { ModelUsageUnit } from "@/src/constants";
|
||||
import { prisma } from "@/src/server/db";
|
||||
|
||||
describe("model match", () => {
|
||||
@@ -17,7 +18,7 @@ describe("model match", () => {
|
||||
totalPrice: "0.1",
|
||||
matchPattern: "(.*)(gpt-3.5-turbo)?(.*)",
|
||||
projectId: null,
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerConfig: {
|
||||
tokensPerMessage: 3,
|
||||
tokensPerName: 1,
|
||||
@@ -35,7 +36,7 @@ describe("model match", () => {
|
||||
totalPrice: "0.1",
|
||||
matchPattern: "(.*)(claude-1.3)?(.*)",
|
||||
projectId: null,
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerId: "claude",
|
||||
},
|
||||
});
|
||||
@@ -48,7 +49,7 @@ describe("model match", () => {
|
||||
projectId: "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
|
||||
model: "gpt-3.5-turbo",
|
||||
startTime: new Date("2024-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
promptTokens: 200,
|
||||
completionTokens: 3000,
|
||||
input: "I am a prompt",
|
||||
@@ -59,7 +60,7 @@ describe("model match", () => {
|
||||
projectId: "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
|
||||
model: "claude-1.3",
|
||||
startTime: new Date("2024-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
input: "I am a prompt",
|
||||
output: "I am a completion",
|
||||
},
|
||||
@@ -68,7 +69,7 @@ describe("model match", () => {
|
||||
projectId: "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
|
||||
model: "claude-1.3",
|
||||
startTime: new Date("2024-01-01T00:00:00.000Z"),
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
input: "I am a prompt",
|
||||
output: "I am a completion",
|
||||
},
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/** @jest-environment node */
|
||||
|
||||
import { prisma } from "@/src/server/db";
|
||||
import { makeAPICall, pruneDatabase } from "@/src/__tests__/test-utils";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { makeAPICall, pruneDatabase } from "@/src/__tests__/test-utils";
|
||||
import { ModelUsageUnit } from "@/src/constants";
|
||||
import { prisma } from "@/src/server/db";
|
||||
import { type ObservationView } from "@prisma/client";
|
||||
|
||||
describe("/api/public/observations API Endpoint", () => {
|
||||
@@ -35,7 +37,7 @@ describe("/api/public/observations API Endpoint", () => {
|
||||
totalPrice: "0.1",
|
||||
matchPattern: "(.*)(gpt-)(35|3.5)(-turbo)?(.*)",
|
||||
projectId: null,
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -59,7 +61,7 @@ describe("/api/public/observations API Endpoint", () => {
|
||||
connect: { id: "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a" },
|
||||
},
|
||||
internalModel: "gpt-3.5-turbo",
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -120,7 +122,7 @@ describe("/api/public/observations API Endpoint", () => {
|
||||
totalPrice: "0.1",
|
||||
matchPattern: "(.*)(gpt-)(35|3.5)(-turbo)?(.*)",
|
||||
projectId: null,
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -140,7 +142,7 @@ describe("/api/public/observations API Endpoint", () => {
|
||||
completionTokens: 20,
|
||||
totalTokens: 30,
|
||||
version: "2.0.0",
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
type: "GENERATION",
|
||||
project: {
|
||||
connect: { id: "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a" },
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ModelUsageUnit } from "@/src/constants";
|
||||
import { tokenCount } from "@/src/features/ingest/lib/usage";
|
||||
|
||||
describe("Token Count Functions", () => {
|
||||
@@ -19,7 +20,7 @@ describe("Token Count Functions", () => {
|
||||
inputPrice: null,
|
||||
outputPrice: null,
|
||||
totalPrice: null,
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { VERSION } from "@/src/constants/VERSION";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
|
||||
import { VERSION } from "@/src/constants";
|
||||
import { env } from "@/src/env.mjs";
|
||||
import { cn } from "@/src/utils/tailwind";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
|
||||
export const LangfuseIcon = ({
|
||||
size = 32,
|
||||
|
||||
@@ -35,7 +35,7 @@ const modelConfigDescriptions = {
|
||||
outputPrice: "Price per unit of output",
|
||||
totalPrice:
|
||||
"Price per unit, for models that don't have input/output specific prices",
|
||||
unit: "Unit of measurement for model, can be TOKENS or CHARACTERS.",
|
||||
unit: "Unit of measurement for generative model, can be TOKENS, CHARACTERS, SECONDS, MILLISECONDS, or IMAGES.",
|
||||
tokenizerId:
|
||||
"Tokenizer used for this model to calculate token counts if none are ingested. Pick from list of supported tokenizers.",
|
||||
config:
|
||||
|
||||
@@ -1 +1 @@
|
||||
export const VERSION = "v2.1.2";
|
||||
export const VERSION = "v2.2.0";
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export { VERSION } from "./VERSION";
|
||||
|
||||
export enum ModelUsageUnit {
|
||||
Characters = "CHARACTERS",
|
||||
Tokens = "TOKENS",
|
||||
Seconds = "SECONDS",
|
||||
Milliseconds = "MILLISECONDS",
|
||||
Images = "IMAGES",
|
||||
}
|
||||
@@ -68,6 +68,8 @@ export function BaseTimeSeriesChart(props: {
|
||||
noDataText="No data"
|
||||
showLegend={props.showLegend}
|
||||
showAnimation={true}
|
||||
onValueChange={() => {}}
|
||||
enableLegendSlider={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
import * as z from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { usePostHog } from "posthog-js/react";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import JsonView from "react18-json-view";
|
||||
import * as z from "zod";
|
||||
|
||||
import { DatePicker } from "@/src/components/date-picker";
|
||||
import Header from "@/src/components/layouts/header";
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
@@ -11,6 +16,7 @@ import {
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/src/components/ui/form";
|
||||
import { Input } from "@/src/components/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -18,14 +24,10 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/src/components/ui/select";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { useState } from "react";
|
||||
import { usePostHog } from "posthog-js/react";
|
||||
import { Input } from "@/src/components/ui/input";
|
||||
import { DatePicker } from "@/src/components/date-picker";
|
||||
import Header from "@/src/components/layouts/header";
|
||||
import { ModelUsageUnit } from "@/src/constants";
|
||||
import { AutoComplete } from "@/src/features/prompts/components/auto-complete";
|
||||
import JsonView from "react18-json-view";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
|
||||
const formSchema = z.object({
|
||||
modelName: z.string().min(1),
|
||||
@@ -49,7 +51,7 @@ const formSchema = z.object({
|
||||
message: "Price needs to be numeric",
|
||||
})
|
||||
.optional(),
|
||||
unit: z.enum(["TOKENS", "CHARACTERS"]),
|
||||
unit: z.nativeEnum(ModelUsageUnit),
|
||||
tokenizerId: z.enum(["openai", "claude", "None"]),
|
||||
tokenizerConfig: z.string().refine(
|
||||
(value) => {
|
||||
@@ -81,7 +83,7 @@ export const NewModelForm = (props: {
|
||||
inputPrice: "",
|
||||
outputPrice: "",
|
||||
totalPrice: "",
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
tokenizerId: "None",
|
||||
tokenizerConfig: "{}",
|
||||
},
|
||||
@@ -233,7 +235,7 @@ export const NewModelForm = (props: {
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{["TOKENS", "CHARACTERS"].map((unit) => (
|
||||
{Object.values(ModelUsageUnit).map((unit) => (
|
||||
<SelectItem value={unit} key={unit}>
|
||||
{unit}
|
||||
</SelectItem>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type OrderByState } from "@/src/features/orderBy/types";
|
||||
import { type ColumnDefinition } from "@/src/server/api/interfaces/tableDefinition";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Convert orderBy to SQL ORDER BY clause
|
||||
@@ -13,7 +14,7 @@ export function orderByToPrismaSql(
|
||||
tableColumns: ColumnDefinition[],
|
||||
): Prisma.Sql {
|
||||
if (!orderBy) {
|
||||
return Prisma.sql([`ORDER BY t.timestamp DESC`]);
|
||||
return Prisma.sql`ORDER BY t.timestamp DESC`;
|
||||
}
|
||||
// Get column definition to map column to internal name, e.g. "t.id"
|
||||
const col = tableColumns.find(
|
||||
@@ -21,10 +22,20 @@ export function orderByToPrismaSql(
|
||||
// It's less error-prone & decouples data fetching from the human-readable UI labels
|
||||
(c) => c.name === orderBy.column || c.id === orderBy.column,
|
||||
);
|
||||
|
||||
if (!col) {
|
||||
console.log("Invalid filter column", orderBy.column);
|
||||
throw new Error("Invalid filter column: " + orderBy.column);
|
||||
}
|
||||
|
||||
return Prisma.sql([`ORDER BY ${col.internal} ${orderBy.order}`]);
|
||||
// Assert that orderBy.order is either "asc" or "desc"
|
||||
const orderByOrder = z.enum(["ASC", "DESC"]);
|
||||
const order = orderByOrder.safeParse(orderBy.order);
|
||||
if (!order.success) {
|
||||
console.log("Invalid order", orderBy.order);
|
||||
throw new Error("Invalid order: " + orderBy.order);
|
||||
}
|
||||
|
||||
// Both column and order are safe, can use raw SQL
|
||||
return Prisma.raw(`ORDER BY ${col.internal} ${order.data}`);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,9 @@ export async function verifyAuthHeaderAndReturnScope(
|
||||
console.error("Error verifying auth header: ", error);
|
||||
return {
|
||||
validKey: false,
|
||||
error: error instanceof Error ? error.message : "Authorization error",
|
||||
error:
|
||||
(error instanceof Error ? error.message : "Authorization error") +
|
||||
". Confirm that you've configured the correct host.",
|
||||
};
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { ObservationLevel } from "@prisma/client";
|
||||
import { jsonSchema } from "@/src/utils/zod";
|
||||
import { z } from "zod";
|
||||
import lodash from "lodash";
|
||||
import { z } from "zod";
|
||||
|
||||
import { ModelUsageUnit } from "@/src/constants";
|
||||
import { jsonSchema } from "@/src/utils/zod";
|
||||
import { ObservationLevel } from "@prisma/client";
|
||||
|
||||
export const Usage = z.object({
|
||||
input: z.number().int().nullish(),
|
||||
output: z.number().int().nullish(),
|
||||
total: z.number().int().nullish(),
|
||||
unit: z.enum(["TOKENS", "CHARACTERS"]).nullish(),
|
||||
unit: z.nativeEnum(ModelUsageUnit).nullish(),
|
||||
inputCost: z.number().nullish(),
|
||||
outputCost: z.number().nullish(),
|
||||
totalCost: z.number().nullish(),
|
||||
@@ -17,7 +19,7 @@ const MixedUsage = z.object({
|
||||
input: z.number().int().nullish(),
|
||||
output: z.number().int().nullish(),
|
||||
total: z.number().int().nullish(),
|
||||
unit: z.enum(["TOKENS", "CHARACTERS"]).nullish(),
|
||||
unit: z.nativeEnum(ModelUsageUnit).nullish(),
|
||||
promptTokens: z.number().int().nullish(),
|
||||
completionTokens: z.number().int().nullish(),
|
||||
totalTokens: z.number().int().nullish(),
|
||||
@@ -40,7 +42,7 @@ export const usage = MixedUsage.nullish()
|
||||
input: v.promptTokens,
|
||||
output: v.completionTokens,
|
||||
total: v.totalTokens,
|
||||
unit: "TOKENS",
|
||||
unit: ModelUsageUnit.Tokens,
|
||||
};
|
||||
}
|
||||
// if we get the new generic format, we do not set a default
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { VERSION } from "@/src/constants/VERSION";
|
||||
import { VERSION } from "@/src/constants";
|
||||
import { prisma } from "@/src/server/db";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { PostHog } from "posthog-node";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { VERSION } from "@/src/constants/VERSION";
|
||||
import { VERSION } from "@/src/constants";
|
||||
import { cors, runMiddleware } from "@/src/features/public-api/server/cors";
|
||||
import { telemetry } from "@/src/features/telemetry";
|
||||
import { prisma } from "@/src/server/db";
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ModelUsageUnit } from "@/src/constants";
|
||||
import { throwIfNoAccess } from "@/src/features/rbac/utils/checkAccess";
|
||||
import {
|
||||
createTRPCRouter,
|
||||
protectedProjectProcedure,
|
||||
} from "@/src/server/api/trpc";
|
||||
import { paginationZod } from "@/src/utils/zod";
|
||||
import { throwIfNoAccess } from "@/src/features/rbac/utils/checkAccess";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
@@ -93,7 +94,7 @@ export const modelRouter = createTRPCRouter({
|
||||
inputPrice: z.number().nonnegative().optional(),
|
||||
outputPrice: z.number().nonnegative().optional(),
|
||||
totalPrice: z.number().nonnegative().optional(),
|
||||
unit: z.enum(["TOKENS", "CHARACTERS"]),
|
||||
unit: z.nativeEnum(ModelUsageUnit),
|
||||
tokenizerId: z.enum(["openai", "claude"]).optional(),
|
||||
tokenizerConfig: z.record(z.union([z.string(), z.number()])).optional(),
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user