Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf42dd726d | ||
|
|
8aeec63c07 | ||
|
|
0cd3fcfbeb | ||
|
|
a880bdb3b2 | ||
|
|
5c6d3e14f2 | ||
|
|
6ccc57312c | ||
|
|
c4831d1b25 | ||
|
|
cc08408f0a | ||
|
|
6a41aadc98 | ||
|
|
9d6efbae19 | ||
|
|
74be1e87c1 | ||
|
|
dd823da1da | ||
|
|
992f0affbc | ||
|
|
b8c192324c | ||
|
|
1416e760ff | ||
|
|
4266b46dd9 | ||
|
|
1933f8f201 | ||
|
|
1e35eb3a66 | ||
|
|
5478c5e317 | ||
|
|
140452c89e | ||
|
|
7ac92e83da | ||
|
|
8a65bad7d4 | ||
|
|
6c6348bfbe | ||
|
|
9ff7fc942d | ||
|
|
a3f59e7e95 | ||
|
|
445bdcb65c | ||
|
|
8230aa79b3 | ||
|
|
57ca7fb478 | ||
|
|
2db88101e1 | ||
|
|
669167002a | ||
|
|
2f6ade9354 | ||
|
|
86f44df5dc | ||
|
|
34da790d5e | ||
|
|
c16d86f7e5 | ||
|
|
1b2b8cc43a | ||
|
|
3903790086 | ||
|
|
6da1280c43 | ||
|
|
85343bf26c | ||
|
|
e3b4dc1928 | ||
|
|
a4939bacd8 | ||
|
|
4441942cf5 | ||
|
|
6597b02c09 | ||
|
|
0df3ad27dc | ||
|
|
696d458a27 | ||
|
|
fd18ab605d | ||
|
|
d47d14eecd | ||
|
|
24a81e1589 | ||
|
|
ac0c13f3e5 | ||
|
|
a7b90fa32c | ||
|
|
9eaf2a2e0f | ||
|
|
61e3cc33bf | ||
|
|
2be198400d | ||
|
|
830df682f9 | ||
|
|
2a420b2e90 | ||
|
|
53127c34ab | ||
|
|
9c57ff4853 | ||
|
|
a71176eda4 | ||
|
|
f59a85f412 | ||
|
|
ec59436522 | ||
|
|
53a3d58a2f | ||
|
|
c1162a3a8b | ||
|
|
43d8c4ec99 | ||
|
|
3a88315ac5 | ||
|
|
e06b96a5ee | ||
|
|
307f54ef8a | ||
|
|
196dc1bf00 | ||
|
|
ee2c0fb27b | ||
|
|
27623de650 | ||
|
|
61c8f6c269 | ||
|
|
cc9e21ccb3 | ||
|
|
078ceb7ff1 | ||
|
|
9436c33025 | ||
|
|
88b3e1bea6 | ||
|
|
da042d8284 | ||
|
|
50264701f6 | ||
|
|
3ee8a2e026 | ||
|
|
ed04357815 | ||
|
|
fa26275634 | ||
|
|
7587c75459 | ||
|
|
a956d0115c | ||
|
|
cb82835489 | ||
|
|
d1c1e60eca |
+1
-1
@@ -47,5 +47,5 @@ REDIS_PORT=6379
|
||||
REDIS_AUTH="myredissecret"
|
||||
|
||||
LANGFUSE_WORKER_PASSWORD=mybasicauthsecret
|
||||
# openssl rand -base64 32 used only here
|
||||
# openssl rand -hex 32 used only here
|
||||
ENCRYPTION_KEY=6c16874e5c0f0cc74ddec00425fa99fbe9ffbe412b7d5d906a4b00005df91403
|
||||
@@ -40,19 +40,27 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Build Web Docker image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: ./web/Dockerfile
|
||||
push: false
|
||||
- name: Build and run both images from compose
|
||||
run: |
|
||||
docker compose -f docker-compose.build.yml up -d
|
||||
sleep 5 # Wait for PostgreSQL to accept connections
|
||||
|
||||
- name: Build Worker Docker image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: ./worker/Dockerfile
|
||||
push: false
|
||||
- name: Check server health
|
||||
run: |
|
||||
timeout 10 bash -c 'until curl -f http://localhost:3000/api/public/health; do sleep 2; done'
|
||||
|
||||
- name: Check worker health
|
||||
run: |
|
||||
timeout 10 bash -c 'until curl -f http://localhost:3030/api/health; do sleep 2; done'
|
||||
|
||||
- name: Ensure no unhealthy status
|
||||
run: |
|
||||
if docker-compose ps | grep "(unhealthy)"; then
|
||||
echo "One or more services are unhealthy"
|
||||
exit 1
|
||||
else
|
||||
echo "All services are healthy"
|
||||
fi
|
||||
|
||||
tests-web:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
+13
-1
@@ -191,6 +191,16 @@ Requirements
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
7. Open the web app in the browser:
|
||||
|
||||
http://localhost:3000
|
||||
|
||||
8. Log in as a test user (after you ran `db:seed` command):
|
||||
|
||||
Username: demo@langfuse.com
|
||||
|
||||
Password: password
|
||||
|
||||
## Monorepo quickstart
|
||||
|
||||
- Available packages and their dependencies
|
||||
@@ -358,7 +368,9 @@ The background color of the following component will be `hsl(var(--primary))` an
|
||||
|
||||
By following these guidelines, you can ensure that any contributions to our theme are consistent, maintainable, and aligned with our design system.
|
||||
|
||||
## Using secrets stored in 1Password
|
||||
## Maintainers
|
||||
|
||||
### Using secrets stored in 1Password
|
||||
|
||||
When applying changes to non-local environments, you may need to use secrets stored in 1Password. We use the 1Password CLI for this purpose.
|
||||
|
||||
|
||||
@@ -106,22 +106,26 @@ See the [→ Quickstart](https://langfuse.com/docs/get-started) to integrate Lan
|
||||
|
||||
### Integrations
|
||||
|
||||
| Integration | Supports | Description |
|
||||
| ---------------------------------------------------------------- | ------------- | ----------------------------------------------------------- |
|
||||
| [**SDK** - _recommended_](https://langfuse.com/docs/sdk) | Python, JS/TS | Manual instrumentation using the SDKs for full flexibility. |
|
||||
| [OpenAI SDK](https://langfuse.com/docs/integrations/openai) | Python, JS/TS | Automated instrumentation of OpenAI SDK. |
|
||||
| [Langchain](https://langfuse.com/docs/integrations/langchain) | Python, JS/TS | Instrumentation via Langchain callbacks. |
|
||||
| [LlamaIndex](https://langfuse.com/docs/integrations/llama-index) | Python | Automated instrumentation via LlamaIndex callback system. |
|
||||
| [API](https://langfuse.com/docs/api) | | Directly call the public API. OpenAPI spec available. |
|
||||
| Integration | Supports | Description |
|
||||
| -------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| [SDK](/docs/sdk) | Python, JS/TS | Manual instrumentation using the SDKs for full flexibility. |
|
||||
| [OpenAI](/docs/integrations/openai) | Python, JS/TS | Automated instrumentation using drop-in replacement of OpenAI SDK. |
|
||||
| [Langchain](/docs/integrations/langchain) | Python, JS/TS | Automated instrumentation by passing callback handler to Langchain application. |
|
||||
| [LlamaIndex](/docs/integrations/llama-index/get-started) | Python | Automated instrumentation via LlamaIndex callback system. |
|
||||
| [Haystack](/docs/integrations/haystack) | Python | Automated instrumentation via Haystack content tracing system. |
|
||||
| [LiteLLM](/docs/integrations/litellm) | Python, JS/TS (proxy only) | Use any LLM as a drop in replacement for GPT. Use Azure, OpenAI, Cohere, Anthropic, Ollama, VLLM, Sagemaker, HuggingFace, Replicate (100+ LLMs). |
|
||||
| [API](/docs/api) | | Directly call the public API. OpenAPI spec available. |
|
||||
|
||||
External projects/packages that integrate with Langfuse:
|
||||
Packages that integrate with Langfuse:
|
||||
|
||||
| Name | Description |
|
||||
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| [LiteLLM](https://langfuse.com/docs/integrations/litellm) | Use any LLM as a drop in replacement for GPT. Use Azure, OpenAI, Cohere, Anthropic, Ollama, VLLM, Sagemaker, HuggingFace, Replicate (100+ LLMs). |
|
||||
| [Flowise](https://langfuse.com/docs/integrations/flowise) | JS/TS no-code builder for customized LLM flows. |
|
||||
| [Langflow](https://langfuse.com/docs/integrations/langflow) | Python-based UI for LangChain, designed with react-flow to provide an effortless way to experiment and prototype flows. |
|
||||
| [Superagent](https://langfuse.com/docs/integrations/superagent) | Open Source AI Assistant Framework & API for prototyping and deployment of agents. |
|
||||
| Name | Description |
|
||||
| ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Instructor](/docs/integrations/instructor) | Library to get structured LLM outputs (JSON, Pydantic) |
|
||||
| [Mirascope](/docs/integrations/mirascope) | Python toolkit for building LLM applications. |
|
||||
| [AI SDK by Vercel](/docs/sdk/typescript/example-vercel-ai) | Typescript SDK that makes streaming LLM outputs super easy. |
|
||||
| [Flowise](/docs/integrations/flowise) | JS/TS no-code builder for customized LLM flows. |
|
||||
| [Langflow](/docs/integrations/langflow) | Python-based UI for LangChain, designed with react-flow to provide an effortless way to experiment and prototype flows. |
|
||||
| [Superagent](/docs/integrations/superagent) | Open Source AI Assistant Framework & API for prototyping and deployment of agents. |
|
||||
|
||||
## Questions and feedback
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ Join us in scaling Langfuse in Berlin, Germany. We are an open source company, w
|
||||
|
||||
_Open Roles_
|
||||
|
||||
- Backend Engineer, 70-110k EUR, 0.25-0.75% Equity, https://www.ycombinator.com/companies/langfuse/jobs/mnrdwla-backend-engineer
|
||||
- Product Engineer, 70-110k EUR, 0.25-0.75% Equity, https://www.ycombinator.com/companies/langfuse/jobs/aAvmoFB-product-engineer
|
||||
- Developer Advocate, 60-100k EUR, 0.25-0.5% Equity, https://www.ycombinator.com/companies/langfuse/jobs/uHysbKH-developer-advocate-devrel
|
||||
- Backend Engineer, 70-130k EUR, 0.25-0.75% Equity, https://www.ycombinator.com/companies/langfuse/jobs/mnrdwla-backend-engineer
|
||||
- Product Engineer, 70-130k EUR, 0.25-0.75% Equity, https://www.ycombinator.com/companies/langfuse/jobs/aAvmoFB-product-engineer
|
||||
- Developer Advocate, 60-110k EUR, 0.25-0.5% Equity, https://www.ycombinator.com/companies/langfuse/jobs/uHysbKH-developer-advocate-devrel
|
||||
|
||||
_More Info_
|
||||
|
||||
|
||||
@@ -20,6 +20,11 @@ services:
|
||||
- LANGFUSE_WORKER_HOST=${LANGFUSE_WORKER_HOST:-worker}
|
||||
- LANGFUSE_WORKER_PASSWORD=${LANGFUSE_WORKER_PASSWORD:-mybasicauthsecret}
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/api/public/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
worker:
|
||||
build:
|
||||
@@ -41,6 +46,11 @@ services:
|
||||
- REDIS_AUTH=${REDIS_AUTH:-myredissecret}
|
||||
- LANGFUSE_WORKER_PASSWORD=${LANGFUSE_WORKER_PASSWORD:-mybasicauthsecret}
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3030/api/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
redis:
|
||||
image: redis:7.2.4
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@
|
||||
"@repo/eslint-config": "*",
|
||||
"@repo/typescript-config": "*",
|
||||
"@types/node": "^20.11.29",
|
||||
"@typescript-eslint/parser": "^7.7.0",
|
||||
"@typescript-eslint/parser": "^7.12.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
|
||||
+24
-2
@@ -6,7 +6,7 @@ import CognitoProvider from "next-auth/providers/cognito";
|
||||
import Auth0Provider from "next-auth/providers/auth0";
|
||||
import AzureADProvider from "next-auth/providers/azure-ad";
|
||||
import { isEeAvailable } from "..";
|
||||
import { prisma } from "@langfuse/shared/src/db";
|
||||
import { type SsoConfig, prisma } from "@langfuse/shared/src/db";
|
||||
import { encrypt, decrypt } from "@langfuse/shared/encryption";
|
||||
import { SsoProviderSchema } from "./types";
|
||||
import { type NextApiRequest, type NextApiResponse } from "next";
|
||||
@@ -26,14 +26,36 @@ let cachedSsoConfigs: {
|
||||
async function getSsoConfigs(): Promise<SsoProviderSchema[]> {
|
||||
if (!isEeAvailable) return [];
|
||||
const CACHE_TTL = 60 * 1000; // 1 minute
|
||||
const DB_MAX_WAIT = 2000; // 2 seconds
|
||||
const DB_TIMEOUT = 3000; // 3 seconds
|
||||
|
||||
// Set/refresh the cache if it's empty or expired
|
||||
if (
|
||||
cachedSsoConfigs === null ||
|
||||
Date.now() - cachedSsoConfigs.timestamp > CACHE_TTL
|
||||
) {
|
||||
// findMany with custom timeout via $transaction
|
||||
let dbConfigs: SsoConfig[] = [];
|
||||
try {
|
||||
dbConfigs = await prisma.$transaction(
|
||||
async (prisma) => prisma.ssoConfig.findMany(),
|
||||
{
|
||||
maxWait: DB_MAX_WAIT,
|
||||
timeout: DB_TIMEOUT,
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
// cache empty array to prevent repeated DB calls on error
|
||||
cachedSsoConfigs = {
|
||||
data: [],
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
// caught and logged in the caller
|
||||
throw e;
|
||||
}
|
||||
|
||||
// transform into zod object
|
||||
const dbConfigs = await prisma.ssoConfig.findMany();
|
||||
const parsedSsoConfigs = dbConfigs
|
||||
.map((v) => {
|
||||
try {
|
||||
|
||||
@@ -48,12 +48,25 @@ types:
|
||||
date: date
|
||||
countTraces: integer
|
||||
countObservations: integer
|
||||
totalCost: double
|
||||
totalCost:
|
||||
type: double
|
||||
docs: Total model cost in USD
|
||||
usage: list<UsageByModel>
|
||||
UsageByModel:
|
||||
docs: Daily usage of a given model. Usage corresponds to the unit set for the specific model (e.g. tokens).
|
||||
properties:
|
||||
model: optional<string>
|
||||
inputUsage: integer
|
||||
outputUsage: integer
|
||||
totalUsage: integer
|
||||
inputUsage:
|
||||
type: integer
|
||||
docs: Total number of generation input units (e.g. tokens)
|
||||
outputUsage:
|
||||
type: integer
|
||||
docs: Total number of generation output units (e.g. tokens)
|
||||
totalUsage:
|
||||
type: integer
|
||||
docs: Total number of generation total units (e.g. tokens)
|
||||
countTraces: integer
|
||||
countObservations: integer
|
||||
totalCost:
|
||||
type: double
|
||||
docs: Total model cost in USD
|
||||
|
||||
@@ -70,14 +70,24 @@ types:
|
||||
name: string
|
||||
prompt: list<ChatMessage>
|
||||
config: optional<unknown>
|
||||
labels: optional<list<string>>
|
||||
labels:
|
||||
type: optional<list<string>>
|
||||
docs: List of deployment labels of this prompt version.
|
||||
tags:
|
||||
type: optional<list<string>>
|
||||
docs: List of tags to apply to all versions of this prompt.
|
||||
|
||||
CreateTextPromptRequest:
|
||||
properties:
|
||||
name: string
|
||||
prompt: string
|
||||
config: optional<unknown>
|
||||
labels: optional<list<string>>
|
||||
labels:
|
||||
type: optional<list<string>>
|
||||
docs: List of deployment labels of this prompt version.
|
||||
tags:
|
||||
type: optional<list<string>>
|
||||
docs: List of tags to apply to all versions of this prompt.
|
||||
|
||||
Prompt:
|
||||
union:
|
||||
@@ -89,7 +99,12 @@ types:
|
||||
name: string
|
||||
version: integer
|
||||
config: unknown
|
||||
labels: list<string>
|
||||
labels:
|
||||
type: list<string>
|
||||
docs: List of deployment labels of this prompt version.
|
||||
tags:
|
||||
type: list<string>
|
||||
docs: List of tags. Used to filter via UI and API. The same across versions of a prompt.
|
||||
|
||||
ChatMessage:
|
||||
properties:
|
||||
|
||||
@@ -7,7 +7,7 @@ service:
|
||||
base-path: /api/public
|
||||
endpoints:
|
||||
get:
|
||||
docs: Get a session
|
||||
docs: Get a session. Please note that `traces` on this endpoint are not paginated, if you plan to fetch large sessions, consider `GET /api/public/traces?sessionId=<sessionId>`
|
||||
method: GET
|
||||
path: /sessions/{sessionId}
|
||||
path-parameters:
|
||||
|
||||
@@ -30,6 +30,7 @@ service:
|
||||
docs: Limit of items per page. If you encounter api issues due to too large page sizes, try to reduce the limit.
|
||||
userId: optional<string>
|
||||
name: optional<string>
|
||||
sessionId: optional<string>
|
||||
fromTimestamp:
|
||||
type: optional<datetime>
|
||||
docs: Retrieve only traces newer than this datetime (ISO 8601).
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "langfuse",
|
||||
"version": "2.45.1",
|
||||
"version": "2.49.2",
|
||||
"author": "engineering@langfuse.com",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
],
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
||||
"@typescript-eslint/parser": "^7.7.0",
|
||||
"@typescript-eslint/parser": "^7.12.0",
|
||||
"@vercel/style-guide": "^6.0.0",
|
||||
"eslint-config-next": "^14.2.3",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"bcryptjs": "^2.4.3",
|
||||
"kysely": "^0.27.3",
|
||||
"langchain": "^0.1.36",
|
||||
"lodash": "^4.17.21",
|
||||
"prisma-extension-kysely": "^2.1.0",
|
||||
"zod": "^3.22.4",
|
||||
"zod-to-json-schema": "^3.22.5"
|
||||
@@ -63,7 +64,7 @@
|
||||
"@types/node": "^20.11.29",
|
||||
"@types/pg": "^8.11.6",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@typescript-eslint/parser": "^7.7.0",
|
||||
"@typescript-eslint/parser": "^7.12.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
|
||||
@@ -32,20 +32,10 @@ export const ScoreSource = {
|
||||
export type ScoreSource = (typeof ScoreSource)[keyof typeof ScoreSource];
|
||||
export const ScoreDataType = {
|
||||
CATEGORICAL: "CATEGORICAL",
|
||||
NUMERIC: "NUMERIC"
|
||||
NUMERIC: "NUMERIC",
|
||||
BOOLEAN: "BOOLEAN"
|
||||
} as const;
|
||||
export type ScoreDataType = (typeof ScoreDataType)[keyof typeof ScoreDataType];
|
||||
export const PricingUnit = {
|
||||
PER_1000_TOKENS: "PER_1000_TOKENS",
|
||||
PER_1000_CHARS: "PER_1000_CHARS"
|
||||
} as const;
|
||||
export type PricingUnit = (typeof PricingUnit)[keyof typeof PricingUnit];
|
||||
export const TokenType = {
|
||||
PROMPT: "PROMPT",
|
||||
COMPLETION: "COMPLETION",
|
||||
TOTAL: "TOTAL"
|
||||
} as const;
|
||||
export type TokenType = (typeof TokenType)[keyof typeof TokenType];
|
||||
export const DatasetStatus = {
|
||||
ACTIVE: "ACTIVE",
|
||||
ARCHIVED: "ARCHIVED"
|
||||
@@ -251,6 +241,7 @@ export type Observation = {
|
||||
status_message: string | null;
|
||||
version: string | null;
|
||||
created_at: Generated<Timestamp>;
|
||||
updated_at: Generated<Timestamp>;
|
||||
model: string | null;
|
||||
internal_model: string | null;
|
||||
modelParameters: unknown | null;
|
||||
@@ -308,14 +299,6 @@ export type PosthogIntegration = {
|
||||
enabled: boolean;
|
||||
created_at: Generated<Timestamp>;
|
||||
};
|
||||
export type Pricing = {
|
||||
id: string;
|
||||
model_name: string;
|
||||
pricing_unit: Generated<PricingUnit>;
|
||||
price: string;
|
||||
currency: Generated<string>;
|
||||
token_type: TokenType;
|
||||
};
|
||||
export type Project = {
|
||||
id: string;
|
||||
created_at: Generated<Timestamp>;
|
||||
@@ -358,6 +341,8 @@ export type Score = {
|
||||
observation_id: string | null;
|
||||
config_id: string | null;
|
||||
string_value: string | null;
|
||||
created_at: Generated<Timestamp>;
|
||||
updated_at: Generated<Timestamp>;
|
||||
data_type: Generated<ScoreDataType>;
|
||||
};
|
||||
export type ScoreConfig = {
|
||||
@@ -467,7 +452,6 @@ export type DB = {
|
||||
observations: Observation;
|
||||
observations_view: ObservationView;
|
||||
posthog_integrations: PosthogIntegration;
|
||||
pricings: Pricing;
|
||||
project_memberships: ProjectMembership;
|
||||
projects: Project;
|
||||
prompts: Prompt;
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "observations" ADD COLUMN "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "scores" ADD COLUMN "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
ADD COLUMN "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "audit_logs_updated_at_idx" ON "audit_logs"("updated_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "dataset_items_created_at_idx" ON "dataset_items"("created_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "dataset_items_updated_at_idx" ON "dataset_items"("updated_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "dataset_run_items_created_at_idx" ON "dataset_run_items"("created_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "dataset_run_items_updated_at_idx" ON "dataset_run_items"("updated_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "dataset_runs_created_at_idx" ON "dataset_runs"("created_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "dataset_runs_updated_at_idx" ON "dataset_runs"("updated_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "datasets_created_at_idx" ON "datasets"("created_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "datasets_updated_at_idx" ON "datasets"("updated_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "job_executions_updated_at_idx" ON "job_executions"("updated_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "observations_updated_at_idx" ON "observations"("updated_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "prompts_created_at_idx" ON "prompts"("created_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "prompts_updated_at_idx" ON "prompts"("updated_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "score_configs_created_at_idx" ON "score_configs"("created_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "score_configs_updated_at_idx" ON "score_configs"("updated_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "scores_created_at_idx" ON "scores"("created_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "scores_updated_at_idx" ON "scores"("updated_at");
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "trace_sessions_updated_at_idx" ON "trace_sessions"("updated_at");
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- Google Vertex uses @ to separate model name and version
|
||||
|
||||
UPDATE "public"."models" SET "match_pattern" = '(?i)^(claude-3-haiku(-|@)?20240307)$' WHERE "id" = 'cltr0w45b000008k1407o9qv1';
|
||||
|
||||
UPDATE "public"."models" SET "match_pattern" = '(?i)^(claude-3-opus(-|@)?20240229)$' WHERE "id" = 'cltgy0iuw000008le3vod1hhy';
|
||||
|
||||
UPDATE "public"."models" SET "match_pattern" = '(?i)^(claude-3-sonnet(-|@)?20240229)$' WHERE "id" = 'cltgy0pp6000108le56se7bl3';
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "dataset_items_source_trace_id_idx" ON "dataset_items" USING HASH ("source_trace_id");
|
||||
@@ -0,0 +1,2 @@
|
||||
-- CreateIndex
|
||||
CREATE INDEX "scores_project_id_name_idx" ON "scores"("project_id", "name");
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- AlterEnum
|
||||
ALTER TYPE "ScoreDataType" ADD VALUE 'BOOLEAN';
|
||||
@@ -0,0 +1,50 @@
|
||||
-- Create manual-score config for any project with manual scores and link config_id to scores
|
||||
BEGIN;
|
||||
WITH project_configs AS (
|
||||
INSERT INTO score_configs (id,
|
||||
project_id,
|
||||
name,
|
||||
data_type,
|
||||
is_archived,
|
||||
min_value,
|
||||
max_value,
|
||||
description)
|
||||
SELECT
|
||||
gen_random_uuid () AS id,
|
||||
s.project_id,
|
||||
'manual-score',
|
||||
'NUMERIC',
|
||||
FALSE,
|
||||
- 1,
|
||||
1,
|
||||
'Langfuse legacy annotation score.'
|
||||
FROM ( SELECT DISTINCT
|
||||
project_id
|
||||
FROM
|
||||
scores
|
||||
WHERE
|
||||
name = 'manual-score'
|
||||
AND source = 'ANNOTATION') s
|
||||
WHERE
|
||||
NOT EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
score_configs sc
|
||||
WHERE
|
||||
sc.name = 'manual-score'
|
||||
AND sc.project_id = s.project_id)
|
||||
RETURNING
|
||||
id,
|
||||
project_id
|
||||
)
|
||||
UPDATE
|
||||
scores
|
||||
SET
|
||||
config_id = pc.id
|
||||
FROM
|
||||
project_configs pc
|
||||
WHERE
|
||||
scores.project_id = pc.project_id
|
||||
AND scores.name = 'manual-score';
|
||||
COMMIT;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
-- does not include pricing yet, will be added as soon as it is calculated at ingestion time
|
||||
|
||||
INSERT INTO "public"."models" ("id", "model_name", "match_pattern", "unit") VALUES ('clx30djsn0000w9mzebiv41we', 'gemini-1.5-flash', '(?i)^(gemini-1.5-flash)(@[a-zA-Z0-9]+)?$', 'CHARACTERS');
|
||||
|
||||
INSERT INTO "public"."models" ("id", "model_name", "match_pattern", "unit") VALUES ('clx30hkrx0000w9mz7lqi0ial', 'gemini-1.5-pro', '(?i)^(gemini-1.5-pro)(@[a-zA-Z0-9]+)?$', 'CHARACTERS');
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the `pricings` table. If the table is not empty, all the data it contains will be lost.
|
||||
|
||||
*/
|
||||
-- DropTable
|
||||
DROP TABLE "pricings";
|
||||
|
||||
-- DropEnum
|
||||
DROP TYPE "PricingUnit";
|
||||
|
||||
-- DropEnum
|
||||
DROP TYPE "TokenType";
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "dataset_run_items" DROP CONSTRAINT "dataset_run_items_trace_id_fkey";
|
||||
ALTER TABLE "dataset_run_items" DROP CONSTRAINT "dataset_run_items_observation_id_fkey";
|
||||
@@ -206,6 +206,7 @@ model TraceSession {
|
||||
@@id([id, projectId])
|
||||
@@index([projectId])
|
||||
@@index([createdAt])
|
||||
@@index([updatedAt])
|
||||
@@map("trace_sessions")
|
||||
}
|
||||
|
||||
@@ -232,9 +233,8 @@ model Trace {
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||
|
||||
DatasetRunItems DatasetRunItems[]
|
||||
DatasetItem DatasetItem[]
|
||||
JobExecution JobExecution[]
|
||||
DatasetItem DatasetItem[]
|
||||
JobExecution JobExecution[]
|
||||
|
||||
@@index([projectId])
|
||||
@@index([sessionId])
|
||||
@@ -293,24 +293,24 @@ model Observation {
|
||||
statusMessage String? @map("status_message")
|
||||
version String?
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||
|
||||
// GENERATION ONLY
|
||||
model String?
|
||||
internalModel String? @map("internal_model")
|
||||
internalModel String? @map("internal_model")
|
||||
modelParameters Json?
|
||||
input Json?
|
||||
output Json?
|
||||
promptTokens Int @default(0) @map("prompt_tokens")
|
||||
completionTokens Int @default(0) @map("completion_tokens")
|
||||
totalTokens Int @default(0) @map("total_tokens")
|
||||
promptTokens Int @default(0) @map("prompt_tokens")
|
||||
completionTokens Int @default(0) @map("completion_tokens")
|
||||
totalTokens Int @default(0) @map("total_tokens")
|
||||
unit String?
|
||||
inputCost Decimal? @map("input_cost")
|
||||
outputCost Decimal? @map("output_cost")
|
||||
totalCost Decimal? @map("total_cost")
|
||||
completionStartTime DateTime? @map("completion_start_time")
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
inputCost Decimal? @map("input_cost")
|
||||
outputCost Decimal? @map("output_cost")
|
||||
totalCost Decimal? @map("total_cost")
|
||||
completionStartTime DateTime? @map("completion_start_time")
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
derivedDatasetItems DatasetItem[]
|
||||
datasetRunItems DatasetRunItems[]
|
||||
|
||||
promptId String? @map("prompt_id")
|
||||
prompt Prompt? @relation(fields: [promptId], onDelete: SetNull, references: [id])
|
||||
@@ -324,6 +324,7 @@ model Observation {
|
||||
@@index([type])
|
||||
@@index(startTime)
|
||||
@@index(createdAt)
|
||||
@@index(updatedAt)
|
||||
@@index(projectId)
|
||||
@@index(parentObservationId)
|
||||
@@index(model)
|
||||
@@ -406,6 +407,8 @@ model Score {
|
||||
observationId String? @map("observation_id")
|
||||
configId String? @map("config_id")
|
||||
stringValue String? @map("string_value")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||
dataType ScoreDataType @default(NUMERIC) @map("data_type")
|
||||
JobExecution JobExecution[]
|
||||
scoreConfig ScoreConfig? @relation(fields: [configId], references: [id], onDelete: SetNull)
|
||||
@@ -414,11 +417,14 @@ model Score {
|
||||
@@index(timestamp)
|
||||
@@index([value])
|
||||
@@index([projectId])
|
||||
@@index([projectId, name])
|
||||
@@index([authorUserId])
|
||||
@@index([configId])
|
||||
@@index([traceId], type: Hash)
|
||||
@@index([observationId], type: Hash)
|
||||
@@index([source])
|
||||
@@index([createdAt])
|
||||
@@index([updatedAt])
|
||||
@@map("scores")
|
||||
}
|
||||
|
||||
@@ -448,35 +454,15 @@ model ScoreConfig {
|
||||
@@index([isArchived])
|
||||
@@index([projectId])
|
||||
@@index([categories])
|
||||
@@index([createdAt])
|
||||
@@index([updatedAt])
|
||||
@@map("score_configs")
|
||||
}
|
||||
|
||||
enum ScoreDataType {
|
||||
CATEGORICAL
|
||||
NUMERIC
|
||||
}
|
||||
|
||||
enum PricingUnit {
|
||||
PER_1000_TOKENS
|
||||
PER_1000_CHARS
|
||||
}
|
||||
|
||||
enum TokenType {
|
||||
PROMPT
|
||||
COMPLETION
|
||||
TOTAL
|
||||
}
|
||||
|
||||
model Pricing {
|
||||
id String @id @default(cuid())
|
||||
modelName String @map("model_name")
|
||||
pricingUnit PricingUnit @default(PER_1000_TOKENS) @map("pricing_unit")
|
||||
price Decimal
|
||||
currency String @default("USD")
|
||||
tokenType TokenType @map("token_type")
|
||||
|
||||
@@index(modelName)
|
||||
@@map("pricings")
|
||||
BOOLEAN
|
||||
}
|
||||
|
||||
model CronJobs {
|
||||
@@ -502,6 +488,8 @@ model Dataset {
|
||||
|
||||
@@unique([projectId, name])
|
||||
@@index([projectId], type: Hash)
|
||||
@@index([createdAt])
|
||||
@@index([updatedAt])
|
||||
@@map("datasets")
|
||||
}
|
||||
|
||||
@@ -521,8 +509,11 @@ model DatasetItem {
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||
datasetRunItems DatasetRunItems[]
|
||||
|
||||
@@index([sourceTraceId], type: Hash)
|
||||
@@index([sourceObservationId], type: Hash)
|
||||
@@index([datasetId], type: Hash)
|
||||
@@index([createdAt])
|
||||
@@index([updatedAt])
|
||||
@@map("dataset_items")
|
||||
}
|
||||
|
||||
@@ -544,26 +535,28 @@ model DatasetRuns {
|
||||
|
||||
@@unique([datasetId, name])
|
||||
@@index([datasetId], type: Hash)
|
||||
@@index([createdAt])
|
||||
@@index([updatedAt])
|
||||
@@map("dataset_runs")
|
||||
}
|
||||
|
||||
model DatasetRunItems {
|
||||
id String @id @default(cuid())
|
||||
datasetRunId String @map("dataset_run_id")
|
||||
datasetRun DatasetRuns @relation(fields: [datasetRunId], references: [id], onDelete: Cascade)
|
||||
datasetItemId String @map("dataset_item_id")
|
||||
datasetItem DatasetItem @relation(fields: [datasetItemId], references: [id], onDelete: Cascade)
|
||||
traceId String @map("trace_id")
|
||||
trace Trace @relation(fields: [traceId], references: [id], onDelete: Cascade)
|
||||
observationId String? @map("observation_id")
|
||||
observation Observation? @relation(fields: [observationId], references: [id], onDelete: Cascade)
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||
id String @id @default(cuid())
|
||||
datasetRunId String @map("dataset_run_id")
|
||||
datasetRun DatasetRuns @relation(fields: [datasetRunId], references: [id], onDelete: Cascade)
|
||||
datasetItemId String @map("dataset_item_id")
|
||||
datasetItem DatasetItem @relation(fields: [datasetItemId], references: [id], onDelete: Cascade)
|
||||
traceId String @map("trace_id")
|
||||
observationId String? @map("observation_id")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||
|
||||
@@index([datasetRunId], type: Hash)
|
||||
@@index([datasetItemId], type: Hash)
|
||||
@@index([observationId], type: Hash)
|
||||
@@index([traceId])
|
||||
@@index([createdAt])
|
||||
@@index([updatedAt])
|
||||
@@map("dataset_run_items")
|
||||
}
|
||||
|
||||
@@ -606,6 +599,8 @@ model Prompt {
|
||||
@@index([projectId, name, version])
|
||||
@@index([projectId, id])
|
||||
@@index([projectId])
|
||||
@@index([createdAt])
|
||||
@@index([updatedAt])
|
||||
@@index([tags(ops: ArrayOps)], type: Gin)
|
||||
@@map("prompts")
|
||||
}
|
||||
@@ -653,6 +648,7 @@ model AuditLog {
|
||||
|
||||
@@index([projectId])
|
||||
@@index([createdAt])
|
||||
@@index([updatedAt])
|
||||
@@map("audit_logs")
|
||||
}
|
||||
|
||||
@@ -750,6 +746,7 @@ model JobExecution {
|
||||
@@index([jobOutputScoreId])
|
||||
@@index([jobInputTraceId])
|
||||
@@index([createdAt])
|
||||
@@index([updatedAt])
|
||||
@@map("job_executions")
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
type Prisma,
|
||||
ObservationType,
|
||||
ScoreSource,
|
||||
ScoreDataType,
|
||||
} from "../src/index";
|
||||
import { hash } from "bcryptjs";
|
||||
import { parseArgs } from "node:util";
|
||||
@@ -12,6 +13,7 @@ import { chunk } from "lodash";
|
||||
import { v4 } from "uuid";
|
||||
import { ModelUsageUnit } from "../src";
|
||||
import { getDisplaySecretKey, hashSecretKey } from "../src/server/auth";
|
||||
import { encrypt } from "../src/encryption";
|
||||
|
||||
const LOAD_TRACE_VOLUME = 10_000;
|
||||
|
||||
@@ -38,6 +40,7 @@ async function main() {
|
||||
name: "Demo User",
|
||||
email: "demo@langfuse.com",
|
||||
password: await hash("password", 12),
|
||||
image: "https://static.langfuse.com/langfuse-dev%2Fexample-avatar.png",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -143,6 +146,11 @@ async function main() {
|
||||
});
|
||||
}
|
||||
|
||||
const configIdsAndNames = await generateConfigsForProject([
|
||||
project1,
|
||||
project2,
|
||||
]);
|
||||
|
||||
const promptIds = await generatePromptsForProject([project1, project2]);
|
||||
|
||||
const envTags = [null, "development", "staging", "production"];
|
||||
@@ -156,7 +164,8 @@ async function main() {
|
||||
colorTags,
|
||||
project1,
|
||||
project2,
|
||||
promptIds
|
||||
promptIds,
|
||||
configIdsAndNames
|
||||
);
|
||||
|
||||
console.log(
|
||||
@@ -165,6 +174,24 @@ async function main() {
|
||||
|
||||
await uploadObjects(traces, observations, scores, sessions, events);
|
||||
|
||||
// If openai key is in environment, add it to the projects LLM API keys
|
||||
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
|
||||
|
||||
if (OPENAI_API_KEY) {
|
||||
await prisma.llmApiKeys.create({
|
||||
data: {
|
||||
projectId: project1.id,
|
||||
secretKey: encrypt(OPENAI_API_KEY),
|
||||
displaySecretKey: getDisplaySecretKey(OPENAI_API_KEY),
|
||||
provider: "openai",
|
||||
},
|
||||
});
|
||||
} else {
|
||||
console.warn(
|
||||
"No OPENAI_API_KEY found in environment. Skipping seeding LLM API key."
|
||||
);
|
||||
}
|
||||
|
||||
// add eval objects
|
||||
const evalTemplate = await prisma.evalTemplate.upsert({
|
||||
where: {
|
||||
@@ -434,13 +461,15 @@ function createObjects(
|
||||
colorTags: (string | null)[],
|
||||
project1: Project,
|
||||
project2: Project,
|
||||
promptIds: Map<string, string[]>
|
||||
promptIds: Map<string, string[]>,
|
||||
configIdsAndNames: Map<string, { name: string; id: string }[]>
|
||||
) {
|
||||
const traces: Prisma.TraceCreateManyInput[] = [];
|
||||
const observations: Prisma.ObservationCreateManyInput[] = [];
|
||||
const scores: Prisma.ScoreCreateManyInput[] = [];
|
||||
const sessions: Prisma.TraceSessionCreateManyInput[] = [];
|
||||
const events: Prisma.ObservationCreateManyInput[] = [];
|
||||
const configs: Prisma.ScoreConfigCreateManyInput[] = [];
|
||||
|
||||
for (let i = 0; i < traceVolume; i++) {
|
||||
// print progress to console with a progress bar that refreshes every 10 iterations
|
||||
@@ -492,17 +521,28 @@ function createObjects(
|
||||
|
||||
traces.push(trace);
|
||||
|
||||
const configArray = configIdsAndNames.get(projectId) ?? [];
|
||||
const randomIndex = Math.floor(Math.random() * 3);
|
||||
const config =
|
||||
configArray.length >= randomIndex - 1 && configArray[randomIndex];
|
||||
const { name: annotationScoreName, id: configId } = config || {
|
||||
name: "manual-score",
|
||||
id: undefined,
|
||||
};
|
||||
|
||||
const traceScores = [
|
||||
...(Math.random() > 0.5
|
||||
? [
|
||||
{
|
||||
traceId: trace.id,
|
||||
name: "manual-score",
|
||||
name: annotationScoreName,
|
||||
value: Math.floor(Math.random() * 3) - 1,
|
||||
timestamp: traceTs,
|
||||
source: ScoreSource.ANNOTATION,
|
||||
projectId,
|
||||
authorUserId: `user-${i}`,
|
||||
dataType: ScoreDataType.NUMERIC,
|
||||
...(configId ? { configId } : {}),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
@@ -515,6 +555,7 @@ function createObjects(
|
||||
timestamp: traceTs,
|
||||
source: ScoreSource.API,
|
||||
projectId,
|
||||
dataType: ScoreDataType.NUMERIC,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
@@ -753,6 +794,7 @@ function createObjects(
|
||||
traces,
|
||||
observations,
|
||||
scores,
|
||||
configs,
|
||||
sessions: uniqueSessions,
|
||||
events,
|
||||
};
|
||||
@@ -935,3 +977,80 @@ async function generatePrompts(project: Project) {
|
||||
}
|
||||
return promptIds;
|
||||
}
|
||||
|
||||
async function generateConfigsForProject(projects: Project[]) {
|
||||
const projectIdsToConfigs: Map<string, { name: string; id: string }[]> =
|
||||
new Map();
|
||||
|
||||
await Promise.all(
|
||||
projects.map(async (project) => {
|
||||
const configNameAndId = await generateConfigs(project);
|
||||
projectIdsToConfigs.set(project.id, configNameAndId);
|
||||
})
|
||||
);
|
||||
return projectIdsToConfigs;
|
||||
}
|
||||
|
||||
async function generateConfigs(project: Project) {
|
||||
const configNameAndId: { name: string; id: string }[] = [];
|
||||
|
||||
const configs = [
|
||||
{
|
||||
id: `config-${v4()}`,
|
||||
name: "manual-score",
|
||||
dataType: ScoreDataType.NUMERIC,
|
||||
projectId: project.id,
|
||||
isArchived: false,
|
||||
},
|
||||
{
|
||||
id: `config-${v4()}`,
|
||||
projectId: project.id,
|
||||
name: "Accuracy",
|
||||
dataType: ScoreDataType.CATEGORICAL,
|
||||
categories: [
|
||||
{ label: "Incorrect", value: 0 },
|
||||
{ label: "Partially Correct", value: 1 },
|
||||
{ label: "Correct", value: 2 },
|
||||
],
|
||||
isArchived: false,
|
||||
},
|
||||
{
|
||||
id: `config-${v4()}`,
|
||||
projectId: project.id,
|
||||
name: "Toxicity",
|
||||
dataType: ScoreDataType.BOOLEAN,
|
||||
categories: [
|
||||
{ label: "True", value: 1 },
|
||||
{ label: "False", value: 0 },
|
||||
],
|
||||
description:
|
||||
"Used to indicate if text was harmful or offensive in nature.",
|
||||
isArchived: false,
|
||||
},
|
||||
];
|
||||
|
||||
for (const config of configs) {
|
||||
await prisma.scoreConfig.upsert({
|
||||
where: {
|
||||
id_projectId: {
|
||||
projectId: config.projectId,
|
||||
id: config.id,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
id: config.id,
|
||||
projectId: config.projectId,
|
||||
name: config.name,
|
||||
dataType: config.dataType,
|
||||
categories: config.categories,
|
||||
isArchived: config.isArchived,
|
||||
},
|
||||
update: {
|
||||
id: config.id,
|
||||
},
|
||||
});
|
||||
configNameAndId.push({ name: config.name, id: config.id });
|
||||
}
|
||||
|
||||
return configNameAndId;
|
||||
}
|
||||
|
||||
+8
-8
@@ -1,9 +1,9 @@
|
||||
import lodash from "lodash";
|
||||
import { z } from "zod";
|
||||
|
||||
import { ModelUsageUnit } from "@langfuse/shared";
|
||||
import { NonEmptyString, jsonSchema } from "@/src/utils/zod";
|
||||
import { ObservationLevel } from "@langfuse/shared";
|
||||
import { NonEmptyString, jsonSchema } from "../../utils/zod";
|
||||
import { ModelUsageUnit } from "../../constants";
|
||||
import { ObservationLevel } from "@prisma/client";
|
||||
|
||||
export const Usage = z.object({
|
||||
input: z.number().int().nullish(),
|
||||
@@ -109,7 +109,7 @@ export const CreateGenerationBody = CreateSpanBody.extend({
|
||||
z.string(),
|
||||
z
|
||||
.union([z.string(), z.number(), z.boolean(), z.array(z.string())])
|
||||
.nullish(),
|
||||
.nullish()
|
||||
)
|
||||
.nullish(),
|
||||
usage: usage,
|
||||
@@ -131,7 +131,7 @@ export const UpdateGenerationBody = UpdateSpanBody.extend({
|
||||
z.string(),
|
||||
z
|
||||
.union([z.string(), z.number(), z.boolean(), z.array(z.string())])
|
||||
.nullish(),
|
||||
.nullish()
|
||||
)
|
||||
.nullish(),
|
||||
usage: usage,
|
||||
@@ -195,7 +195,7 @@ export const LegacyGenerationsCreateSchema = z.object({
|
||||
modelParameters: z
|
||||
.record(
|
||||
z.string(),
|
||||
z.union([z.string(), z.number(), z.boolean()]).nullish(),
|
||||
z.union([z.string(), z.number(), z.boolean()]).nullish()
|
||||
)
|
||||
.nullish(),
|
||||
prompt: jsonSchema.nullish(),
|
||||
@@ -219,7 +219,7 @@ export const LegacyGenerationPatchSchema = z.object({
|
||||
modelParameters: z
|
||||
.record(
|
||||
z.string(),
|
||||
z.union([z.string(), z.number(), z.boolean()]).nullish(),
|
||||
z.union([z.string(), z.number(), z.boolean()]).nullish()
|
||||
)
|
||||
.nullish(),
|
||||
prompt: jsonSchema.nullish(),
|
||||
@@ -243,7 +243,7 @@ export const LegacyObservationBody = z.object({
|
||||
modelParameters: z
|
||||
.record(
|
||||
z.string(),
|
||||
z.union([z.string(), z.number(), z.boolean()]).nullish(),
|
||||
z.union([z.string(), z.number(), z.boolean()]).nullish()
|
||||
)
|
||||
.nullish(),
|
||||
input: jsonSchema.nullish(),
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import {
|
||||
ColumnDefinition,
|
||||
type TableNames as TableName,
|
||||
type TableNames,
|
||||
} from "./interfaces/tableDefinition";
|
||||
import { FilterState } from "./types";
|
||||
import { filterOperators } from "./interfaces/filters";
|
||||
@@ -27,7 +27,7 @@ const arrayOperatorReplacements = {
|
||||
export function tableColumnsToSqlFilterAndPrefix(
|
||||
filters: FilterState,
|
||||
tableColumns: ColumnDefinition[],
|
||||
table: TableName
|
||||
table: TableNames
|
||||
): Prisma.Sql {
|
||||
const sql = tableColumnsToSqlFilter(filters, tableColumns, table);
|
||||
if (sql === Prisma.empty) {
|
||||
@@ -43,7 +43,7 @@ export function tableColumnsToSqlFilterAndPrefix(
|
||||
export function tableColumnsToSqlFilter(
|
||||
filters: FilterState,
|
||||
tableColumns: ColumnDefinition[],
|
||||
table: TableName
|
||||
table: TableNames
|
||||
): Prisma.Sql {
|
||||
const internalFilters = filters.map((filter) => {
|
||||
// Get column definition to map column to internal name, e.g. "t.id"
|
||||
@@ -153,7 +153,7 @@ export function tableColumnsToSqlFilter(
|
||||
|
||||
const castValueToPostgresTypes = (
|
||||
column: ColumnDefinition,
|
||||
table: TableName
|
||||
table: TableNames
|
||||
) => {
|
||||
return column.name === "type" &&
|
||||
(table === "observations" ||
|
||||
|
||||
@@ -9,6 +9,9 @@ export * from "./filterToPrisma";
|
||||
export * from "./tracesTable";
|
||||
export * from "./server/auth";
|
||||
export * from "./observationsTable";
|
||||
export * from "./features/ingestion/types";
|
||||
export * from "./utils/zod";
|
||||
export * from "./utils/json";
|
||||
export { env } from "./env";
|
||||
|
||||
// llm api
|
||||
|
||||
@@ -42,6 +42,7 @@ export const tableNames = [
|
||||
"traces_parent_observation_scores",
|
||||
"sessions",
|
||||
"prompts",
|
||||
"users",
|
||||
] as const;
|
||||
|
||||
export type TableNames = (typeof tableNames)[number];
|
||||
|
||||
@@ -30,4 +30,5 @@ export type TableName =
|
||||
| "sessions"
|
||||
| "scores"
|
||||
| "prompts"
|
||||
| "dashboard";
|
||||
| "dashboard"
|
||||
| "users";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { type jsonSchema } from "@/src/utils/zod";
|
||||
import { type z } from "zod";
|
||||
import lodash from "lodash";
|
||||
import { jsonSchema } from "./zod";
|
||||
|
||||
export const parseJson = (input: string) => {
|
||||
try {
|
||||
@@ -37,7 +37,7 @@ export function deepParseJson(json: unknown): unknown {
|
||||
// Ensure we only iterate over the object's own properties
|
||||
if (Object.prototype.hasOwnProperty.call(json, key)) {
|
||||
(json as Record<string, unknown>)[key] = deepParseJson(
|
||||
(json as Record<string, unknown>)[key],
|
||||
(json as Record<string, unknown>)[key]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ export function deepParseJson(json: unknown): unknown {
|
||||
|
||||
export const mergeJson = (
|
||||
json1?: z.infer<typeof jsonSchema>,
|
||||
json2?: z.infer<typeof jsonSchema>,
|
||||
json2?: z.infer<typeof jsonSchema>
|
||||
) => {
|
||||
if (json1 === undefined) {
|
||||
return json2;
|
||||
@@ -26,7 +26,7 @@ const jsonSchemaNullable: z.ZodType<JsonNested> = z.lazy(() =>
|
||||
nestedLiteralSchema,
|
||||
z.array(jsonSchemaNullable),
|
||||
z.record(jsonSchemaNullable),
|
||||
]),
|
||||
])
|
||||
);
|
||||
|
||||
// Root schema that does not allow nulls at the root level
|
||||
@@ -35,17 +35,17 @@ export const jsonSchema: z.ZodType<Json> = z.lazy(() =>
|
||||
rootLiteralSchema,
|
||||
z.array(jsonSchemaNullable),
|
||||
z.record(jsonSchemaNullable),
|
||||
]),
|
||||
])
|
||||
);
|
||||
|
||||
export const paginationZod = {
|
||||
page: z.preprocess(
|
||||
(x) => (x === "" ? undefined : x),
|
||||
z.coerce.number().default(1),
|
||||
z.coerce.number().default(1)
|
||||
),
|
||||
limit: z.preprocess(
|
||||
(x) => (x === "" ? undefined : x),
|
||||
z.coerce.number().lte(100).default(50),
|
||||
z.coerce.number().lte(100).default(50)
|
||||
),
|
||||
};
|
||||
|
||||
Generated
+214
-411
@@ -49,8 +49,8 @@ importers:
|
||||
specifier: ^20.11.29
|
||||
version: 20.11.29
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^7.7.0
|
||||
version: 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
specifier: ^7.12.0
|
||||
version: 7.12.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
eslint:
|
||||
specifier: ^8.57.0
|
||||
version: 8.57.0
|
||||
@@ -80,10 +80,10 @@ importers:
|
||||
devDependencies:
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^7.1.0
|
||||
version: 7.3.1(@typescript-eslint/parser@7.7.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
version: 7.3.1(@typescript-eslint/parser@7.12.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^7.7.0
|
||||
version: 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
specifier: ^7.12.0
|
||||
version: 7.12.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@vercel/style-guide':
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.5)
|
||||
@@ -130,7 +130,10 @@ importers:
|
||||
version: 0.27.3
|
||||
langchain:
|
||||
specifier: ^0.1.36
|
||||
version: 0.1.36
|
||||
version: 0.1.36(@aws-sdk/client-s3@3.550.0)(@aws-sdk/credential-provider-node@3.549.0)(lodash@4.17.21)
|
||||
lodash:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
prisma-extension-kysely:
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.0(@prisma/client@5.13.0)
|
||||
@@ -160,8 +163,8 @@ importers:
|
||||
specifier: ^9.0.8
|
||||
version: 9.0.8
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^7.7.0
|
||||
version: 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
specifier: ^7.12.0
|
||||
version: 7.12.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
eslint:
|
||||
specifier: ^8.57.0
|
||||
version: 8.57.0
|
||||
@@ -222,6 +225,9 @@ importers:
|
||||
'@codemirror/lang-json':
|
||||
specifier: ^6.0.1
|
||||
version: 6.0.1
|
||||
'@codemirror/lint':
|
||||
specifier: ^6.8.0
|
||||
version: 6.8.0
|
||||
'@headlessui/react':
|
||||
specifier: 1.7.18
|
||||
version: 1.7.18(react-dom@18.2.0)(react@18.2.0)
|
||||
@@ -312,6 +318,9 @@ importers:
|
||||
'@radix-ui/react-toggle':
|
||||
specifier: ^1.0.3
|
||||
version: 1.0.3(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-toggle-group':
|
||||
specifier: ^1.0.4
|
||||
version: 1.0.4(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-tooltip':
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0)
|
||||
@@ -354,9 +363,6 @@ importers:
|
||||
'@tanstack/react-table':
|
||||
specifier: ^8.11.8
|
||||
version: 8.13.2(react-dom@18.2.0)(react@18.2.0)
|
||||
'@tanstack/react-virtual':
|
||||
specifier: ^3.5.0
|
||||
version: 3.5.0(react-dom@18.2.0)(react@18.2.0)
|
||||
'@tremor/react':
|
||||
specifier: 3.16.2
|
||||
version: 3.16.2(react-dom@18.2.0)(react@18.2.0)(tailwindcss@3.4.3)
|
||||
@@ -380,7 +386,7 @@ importers:
|
||||
version: 4.22.1(@codemirror/language@6.10.1)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3)
|
||||
'@uiw/react-codemirror':
|
||||
specifier: ^4.21.25
|
||||
version: 4.21.25(@babel/runtime@7.24.5)(@codemirror/autocomplete@6.16.0)(@codemirror/language@6.10.1)(@codemirror/lint@6.7.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.26.3)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 4.21.25(@babel/runtime@7.24.5)(@codemirror/autocomplete@6.16.0)(@codemirror/language@6.10.1)(@codemirror/lint@6.8.0)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.26.3)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0)
|
||||
ai:
|
||||
specifier: ^3.0.23
|
||||
version: 3.0.23(react@18.2.0)(solid-js@1.8.17)(svelte@4.2.17)(vue@3.4.27)(zod@3.22.4)
|
||||
@@ -492,6 +498,9 @@ importers:
|
||||
uuid:
|
||||
specifier: ^9.0.1
|
||||
version: 9.0.1
|
||||
vaul:
|
||||
specifier: ^0.9.1
|
||||
version: 0.9.1(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0)
|
||||
zod:
|
||||
specifier: ^3.22.4
|
||||
version: 3.22.4
|
||||
@@ -553,10 +562,10 @@ importers:
|
||||
version: 9.0.8
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^6.21.0
|
||||
version: 6.21.0(@typescript-eslint/parser@7.7.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
version: 6.21.0(@typescript-eslint/parser@7.12.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^7.7.0
|
||||
version: 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
specifier: ^7.12.0
|
||||
version: 7.12.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
autoprefixer:
|
||||
specifier: ^10.4.19
|
||||
version: 10.4.19(postcss@8.4.38)
|
||||
@@ -605,6 +614,9 @@ importers:
|
||||
|
||||
worker:
|
||||
dependencies:
|
||||
'@anthropic-ai/tokenizer':
|
||||
specifier: ^0.0.4
|
||||
version: 0.0.4
|
||||
'@langfuse/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/shared
|
||||
@@ -644,6 +656,9 @@ importers:
|
||||
ioredis:
|
||||
specifier: ^5.4.1
|
||||
version: 5.4.1
|
||||
js-tiktoken:
|
||||
specifier: ^1.0.12
|
||||
version: 1.0.12
|
||||
kysely:
|
||||
specifier: ^0.27.3
|
||||
version: 0.27.3
|
||||
@@ -662,6 +677,9 @@ importers:
|
||||
pino-pretty:
|
||||
specifier: ^10.3.1
|
||||
version: 10.3.1
|
||||
tiktoken:
|
||||
specifier: ^1.0.15
|
||||
version: 1.0.15
|
||||
uuid:
|
||||
specifier: ^9.0.1
|
||||
version: 9.0.1
|
||||
@@ -694,8 +712,8 @@ importers:
|
||||
specifier: ^8.11.6
|
||||
version: 8.11.6
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^7.7.0
|
||||
version: 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
specifier: ^7.12.0
|
||||
version: 7.12.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
eslint:
|
||||
specifier: ^8.57.0
|
||||
version: 8.57.0
|
||||
@@ -821,7 +839,7 @@ packages:
|
||||
resolution: {integrity: sha512-EHRKbxlxlc8W4KCBEseByJ7YwyYCmgu9OyN59H9+IYIGPoKv8tXyQXinkeGDI+cI8Tiuz9wk2jZb/kK7AyvL7g==}
|
||||
dependencies:
|
||||
'@types/node': 18.19.25
|
||||
tiktoken: 1.0.13
|
||||
tiktoken: 1.0.15
|
||||
dev: false
|
||||
|
||||
/@aws-crypto/crc32@3.0.0:
|
||||
@@ -2076,8 +2094,8 @@ packages:
|
||||
style-mod: 4.1.2
|
||||
dev: false
|
||||
|
||||
/@codemirror/lint@6.7.1:
|
||||
resolution: {integrity: sha512-rELba6QJD20/bNXWP/cKTGLrwVEcpa2ViwULCV03ONcY1Je85++7sczVRUlnE4TJMjatx3IJTz6HX4NXi+moXw==}
|
||||
/@codemirror/lint@6.8.0:
|
||||
resolution: {integrity: sha512-lsFofvaw0lnPRJlQylNsC4IRt/1lI4OD/yYslrSGVndOJfStc58v+8p9dgGiD90ktOfL7OhBWns1ZETYgz0EJA==}
|
||||
dependencies:
|
||||
'@codemirror/state': 6.4.1
|
||||
'@codemirror/view': 6.26.3
|
||||
@@ -3335,7 +3353,7 @@ packages:
|
||||
ansi-styles: 5.2.0
|
||||
camelcase: 6.3.0
|
||||
decamelize: 1.2.0
|
||||
js-tiktoken: 1.0.11
|
||||
js-tiktoken: 1.0.12
|
||||
langsmith: 0.1.14
|
||||
ml-distance: 4.0.1
|
||||
mustache: 4.2.0
|
||||
@@ -4245,7 +4263,7 @@ packages:
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
'@babel/runtime': 7.24.5
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.79)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.79)(react@18.2.0)
|
||||
@@ -4830,6 +4848,33 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-toggle-group@1.0.4(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.5
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.79)(react@18.2.0)
|
||||
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.79)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-toggle': 1.0.3(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.79)(react@18.2.0)
|
||||
'@types/react': 18.2.79
|
||||
'@types/react-dom': 18.2.25
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-toggle@1.0.3(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-Pkqg3+Bc98ftZGsl60CLANXQBBQ4W3mTFS9EJvNxKMZ7magklKV69/id1mlAlOFDDfHvlCms0fx8fA4CMKDJHg==}
|
||||
peerDependencies:
|
||||
@@ -6699,7 +6744,7 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@7.7.0)(eslint@8.57.0)(typescript@5.4.5):
|
||||
/@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@7.12.0)(eslint@8.57.0)(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==}
|
||||
engines: {node: ^16.0.0 || >=18.0.0}
|
||||
peerDependencies:
|
||||
@@ -6711,7 +6756,7 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.10.0
|
||||
'@typescript-eslint/parser': 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/parser': 7.12.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/scope-manager': 6.21.0
|
||||
'@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
@@ -6728,7 +6773,7 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.7.0)(eslint@8.57.0)(typescript@5.4.5):
|
||||
/@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.12.0)(eslint@8.57.0)(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-STEDMVQGww5lhCuNXVSQfbfuNII5E08QWkvAw5Qwf+bj2WT+JkG1uc+5/vXA3AOYMDHVOSpL+9rcbEUiHIm2dw==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
peerDependencies:
|
||||
@@ -6740,7 +6785,7 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.10.0
|
||||
'@typescript-eslint/parser': 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/parser': 7.12.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/scope-manager': 7.3.1
|
||||
'@typescript-eslint/type-utils': 7.3.1(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/utils': 7.3.1(eslint@8.57.0)(typescript@5.4.5)
|
||||
@@ -6757,6 +6802,27 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-dm/J2UDY3oV3TKius2OUZIFHsomQmpHtsV0FTh1WO8EKgHLQ1QCADUqscPgTpU+ih1e21FQSRjXckHn3txn6kQ==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.56.0
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 7.12.0
|
||||
'@typescript-eslint/types': 7.12.0
|
||||
'@typescript-eslint/typescript-estree': 7.12.0(typescript@5.4.5)
|
||||
'@typescript-eslint/visitor-keys': 7.12.0
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
eslint: 8.57.0
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==}
|
||||
engines: {node: ^16.0.0 || >=18.0.0}
|
||||
@@ -6778,27 +6844,6 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-fNcDm3wSwVM8QYL4HKVBggdIPAy9Q41vcvC/GtDobw3c4ndVT3K6cqudUmjHPw8EAp4ufax0o58/xvWaP2FmTg==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.56.0
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 7.7.0
|
||||
'@typescript-eslint/types': 7.7.0
|
||||
'@typescript-eslint/typescript-estree': 7.7.0(typescript@5.4.5)
|
||||
'@typescript-eslint/visitor-keys': 7.7.0
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
eslint: 8.57.0
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/scope-manager@5.62.0:
|
||||
resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
@@ -6815,6 +6860,14 @@ packages:
|
||||
'@typescript-eslint/visitor-keys': 6.21.0
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/scope-manager@7.12.0:
|
||||
resolution: {integrity: sha512-itF1pTnN6F3unPak+kutH9raIkL3lhH1YRPGgt7QQOh43DQKVJXmWkpb+vpc/TiDHs6RSd9CTbDsc/Y+Ygq7kg==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 7.12.0
|
||||
'@typescript-eslint/visitor-keys': 7.12.0
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/scope-manager@7.2.0:
|
||||
resolution: {integrity: sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==}
|
||||
engines: {node: ^16.0.0 || >=18.0.0}
|
||||
@@ -6831,14 +6884,6 @@ packages:
|
||||
'@typescript-eslint/visitor-keys': 7.3.1
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/scope-manager@7.7.0:
|
||||
resolution: {integrity: sha512-/8INDn0YLInbe9Wt7dK4cXLDYp0fNHP5xKLHvZl3mOT5X17rK/YShXaiNmorl+/U4VKCVIjJnx4Ri5b0y+HClw==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 7.7.0
|
||||
'@typescript-eslint/visitor-keys': 7.7.0
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/type-utils@6.21.0(eslint@8.57.0)(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==}
|
||||
engines: {node: ^16.0.0 || >=18.0.0}
|
||||
@@ -6889,6 +6934,11 @@ packages:
|
||||
engines: {node: ^16.0.0 || >=18.0.0}
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/types@7.12.0:
|
||||
resolution: {integrity: sha512-o+0Te6eWp2ppKY3mLCU+YA9pVJxhUJE15FV7kxuD9jgwIAa+w/ycGJBMrYDTpVGUM/tgpa9SeMOugSabWFq7bg==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/types@7.2.0:
|
||||
resolution: {integrity: sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==}
|
||||
engines: {node: ^16.0.0 || >=18.0.0}
|
||||
@@ -6899,11 +6949,6 @@ packages:
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/types@7.7.0:
|
||||
resolution: {integrity: sha512-G01YPZ1Bd2hn+KPpIbrAhEWOn5lQBrjxkzHkWvP6NucMXFtfXoevK82hzQdpfuQYuhkvFDeQYbzXCjR1z9Z03w==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
@@ -6947,6 +6992,28 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/typescript-estree@7.12.0(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-5bwqLsWBULv1h6pn7cMW5dXX/Y2amRqLaKqsASVwbBHMZSnHqE/HN4vT4fE0aFsiwxYvr98kqOWh1a8ZKXalCQ==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 7.12.0
|
||||
'@typescript-eslint/visitor-keys': 7.12.0
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
globby: 11.1.0
|
||||
is-glob: 4.0.3
|
||||
minimatch: 9.0.4
|
||||
semver: 7.6.2
|
||||
ts-api-utils: 1.3.0(typescript@5.4.5)
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/typescript-estree@7.2.0(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==}
|
||||
engines: {node: ^16.0.0 || >=18.0.0}
|
||||
@@ -6991,28 +7058,6 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/typescript-estree@7.7.0(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-8p71HQPE6CbxIBy2kWHqM1KGrC07pk6RJn40n0DSc6bMOBBREZxSDJ+BmRzc8B5OdaMh1ty3mkuWRg4sCFiDQQ==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 7.7.0
|
||||
'@typescript-eslint/visitor-keys': 7.7.0
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
globby: 11.1.0
|
||||
is-glob: 4.0.3
|
||||
minimatch: 9.0.4
|
||||
semver: 7.6.0
|
||||
ts-api-utils: 1.3.0(typescript@5.4.5)
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.4.5):
|
||||
resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
@@ -7087,6 +7132,14 @@ packages:
|
||||
eslint-visitor-keys: 3.4.3
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/visitor-keys@7.12.0:
|
||||
resolution: {integrity: sha512-uZk7DevrQLL3vSnfFl5bj4sL75qC9D6EdjemIdbtkuUmIheWpuiiylSY01JxJE7+zGrOWDZrp1WxOuDntvKrHQ==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 7.12.0
|
||||
eslint-visitor-keys: 3.4.3
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/visitor-keys@7.2.0:
|
||||
resolution: {integrity: sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==}
|
||||
engines: {node: ^16.0.0 || >=18.0.0}
|
||||
@@ -7103,15 +7156,7 @@ packages:
|
||||
eslint-visitor-keys: 3.4.3
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/visitor-keys@7.7.0:
|
||||
resolution: {integrity: sha512-h0WHOj8MhdhY8YWkzIF30R379y0NqyOHExI9N9KCzvmu05EgG4FumeYa3ccfKUSphyWkWQE1ybVrgz/Pbam6YA==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 7.7.0
|
||||
eslint-visitor-keys: 3.4.3
|
||||
dev: true
|
||||
|
||||
/@uiw/codemirror-extensions-basic-setup@4.21.25(@codemirror/autocomplete@6.16.0)(@codemirror/commands@6.3.3)(@codemirror/language@6.10.1)(@codemirror/lint@6.7.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3):
|
||||
/@uiw/codemirror-extensions-basic-setup@4.21.25(@codemirror/autocomplete@6.16.0)(@codemirror/commands@6.3.3)(@codemirror/language@6.10.1)(@codemirror/lint@6.8.0)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3):
|
||||
resolution: {integrity: sha512-eeUKlmEE8aSoSgelS8OR2elcPGntpRo669XinAqPCLa0eKorT2B0d3ts+AE+njAeGk744tiyAEbHb2n+6OQmJw==}
|
||||
peerDependencies:
|
||||
'@codemirror/autocomplete': '>=6.0.0'
|
||||
@@ -7125,7 +7170,7 @@ packages:
|
||||
'@codemirror/autocomplete': 6.16.0(@codemirror/language@6.10.1)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3)(@lezer/common@1.2.1)
|
||||
'@codemirror/commands': 6.3.3
|
||||
'@codemirror/language': 6.10.1
|
||||
'@codemirror/lint': 6.7.1
|
||||
'@codemirror/lint': 6.8.0
|
||||
'@codemirror/search': 6.5.6
|
||||
'@codemirror/state': 6.4.1
|
||||
'@codemirror/view': 6.26.3
|
||||
@@ -7175,7 +7220,7 @@ packages:
|
||||
'@codemirror/view': 6.26.3
|
||||
dev: false
|
||||
|
||||
/@uiw/react-codemirror@4.21.25(@babel/runtime@7.24.5)(@codemirror/autocomplete@6.16.0)(@codemirror/language@6.10.1)(@codemirror/lint@6.7.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.26.3)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0):
|
||||
/@uiw/react-codemirror@4.21.25(@babel/runtime@7.24.5)(@codemirror/autocomplete@6.16.0)(@codemirror/language@6.10.1)(@codemirror/lint@6.8.0)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.26.3)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-mBrCoiffQ+hbTqV1JoixFEcH7BHXkS3PjTyNH7dE8Gzf3GSBRazhtSM5HrAFIiQ5FIRGFs8Gznc4UAdhtevMmw==}
|
||||
peerDependencies:
|
||||
'@babel/runtime': '>=7.11.0'
|
||||
@@ -7191,7 +7236,7 @@ packages:
|
||||
'@codemirror/state': 6.4.1
|
||||
'@codemirror/theme-one-dark': 6.1.2
|
||||
'@codemirror/view': 6.26.3
|
||||
'@uiw/codemirror-extensions-basic-setup': 4.21.25(@codemirror/autocomplete@6.16.0)(@codemirror/commands@6.3.3)(@codemirror/language@6.10.1)(@codemirror/lint@6.7.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3)
|
||||
'@uiw/codemirror-extensions-basic-setup': 4.21.25(@codemirror/autocomplete@6.16.0)(@codemirror/commands@6.3.3)(@codemirror/language@6.10.1)(@codemirror/lint@6.8.0)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3)
|
||||
codemirror: 6.0.1(@lezer/common@1.2.1)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
@@ -7227,14 +7272,14 @@ packages:
|
||||
'@babel/core': 7.24.3
|
||||
'@babel/eslint-parser': 7.24.1(@babel/core@7.24.3)(eslint@8.57.0)
|
||||
'@rushstack/eslint-patch': 1.7.2
|
||||
'@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.7.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/parser': 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.12.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/parser': 7.12.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
eslint: 8.57.0
|
||||
eslint-config-prettier: 9.1.0(eslint@8.57.0)
|
||||
eslint-import-resolver-alias: 1.1.2(eslint-plugin-import@2.29.1)
|
||||
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
|
||||
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.12.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
|
||||
eslint-plugin-eslint-comments: 3.2.0(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.12.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-jest: 27.9.0(@typescript-eslint/eslint-plugin@7.3.1)(eslint@8.57.0)(typescript@5.4.5)
|
||||
eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0)
|
||||
eslint-plugin-playwright: 1.5.4(eslint-plugin-jest@27.9.0)(eslint@8.57.0)
|
||||
@@ -8409,7 +8454,7 @@ packages:
|
||||
'@codemirror/autocomplete': 6.16.0(@codemirror/language@6.10.1)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3)(@lezer/common@1.2.1)
|
||||
'@codemirror/commands': 6.5.0
|
||||
'@codemirror/language': 6.10.1
|
||||
'@codemirror/lint': 6.7.1
|
||||
'@codemirror/lint': 6.8.0
|
||||
'@codemirror/search': 6.5.6
|
||||
'@codemirror/state': 6.4.1
|
||||
'@codemirror/view': 6.26.3
|
||||
@@ -9510,7 +9555,7 @@ packages:
|
||||
eslint: 8.57.0
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.12.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0)
|
||||
eslint-plugin-react: 7.34.1(eslint@8.57.0)
|
||||
eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0)
|
||||
@@ -9539,7 +9584,7 @@ packages:
|
||||
eslint-plugin-promise: ^6.0.0
|
||||
dependencies:
|
||||
eslint: 8.57.0
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.12.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-n: 16.6.2(eslint@8.57.0)
|
||||
eslint-plugin-promise: 6.1.1(eslint@8.57.0)
|
||||
dev: true
|
||||
@@ -9559,7 +9604,7 @@ packages:
|
||||
peerDependencies:
|
||||
eslint-plugin-import: '>=1.4.0'
|
||||
dependencies:
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.12.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
dev: true
|
||||
|
||||
/eslint-import-resolver-node@0.3.9:
|
||||
@@ -9572,6 +9617,29 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.12.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0):
|
||||
resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
eslint: '*'
|
||||
eslint-plugin-import: '*'
|
||||
dependencies:
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
enhanced-resolve: 5.16.0
|
||||
eslint: 8.57.0
|
||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.12.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.12.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
fast-glob: 3.3.2
|
||||
get-tsconfig: 4.7.3
|
||||
is-core-module: 2.13.1
|
||||
is-glob: 4.0.3
|
||||
transitivePeerDependencies:
|
||||
- '@typescript-eslint/parser'
|
||||
- eslint-import-resolver-node
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0):
|
||||
resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
@@ -9583,7 +9651,7 @@ packages:
|
||||
enhanced-resolve: 5.16.0
|
||||
eslint: 8.57.0
|
||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.12.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
fast-glob: 3.3.2
|
||||
get-tsconfig: 4.7.3
|
||||
is-core-module: 2.13.1
|
||||
@@ -9595,26 +9663,33 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.7.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0):
|
||||
resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
/eslint-module-utils@2.8.1(@typescript-eslint/parser@7.12.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
|
||||
resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==}
|
||||
engines: {node: '>=4'}
|
||||
peerDependencies:
|
||||
'@typescript-eslint/parser': '*'
|
||||
eslint: '*'
|
||||
eslint-plugin-import: '*'
|
||||
eslint-import-resolver-node: '*'
|
||||
eslint-import-resolver-typescript: '*'
|
||||
eslint-import-resolver-webpack: '*'
|
||||
peerDependenciesMeta:
|
||||
'@typescript-eslint/parser':
|
||||
optional: true
|
||||
eslint:
|
||||
optional: true
|
||||
eslint-import-resolver-node:
|
||||
optional: true
|
||||
eslint-import-resolver-typescript:
|
||||
optional: true
|
||||
eslint-import-resolver-webpack:
|
||||
optional: true
|
||||
dependencies:
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
enhanced-resolve: 5.16.0
|
||||
'@typescript-eslint/parser': 7.12.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
debug: 3.2.7
|
||||
eslint: 8.57.0
|
||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
fast-glob: 3.3.2
|
||||
get-tsconfig: 4.7.3
|
||||
is-core-module: 2.13.1
|
||||
is-glob: 4.0.3
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.12.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
|
||||
transitivePeerDependencies:
|
||||
- '@typescript-eslint/parser'
|
||||
- eslint-import-resolver-node
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
@@ -9648,36 +9723,6 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-module-utils@2.8.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
|
||||
resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==}
|
||||
engines: {node: '>=4'}
|
||||
peerDependencies:
|
||||
'@typescript-eslint/parser': '*'
|
||||
eslint: '*'
|
||||
eslint-import-resolver-node: '*'
|
||||
eslint-import-resolver-typescript: '*'
|
||||
eslint-import-resolver-webpack: '*'
|
||||
peerDependenciesMeta:
|
||||
'@typescript-eslint/parser':
|
||||
optional: true
|
||||
eslint:
|
||||
optional: true
|
||||
eslint-import-resolver-node:
|
||||
optional: true
|
||||
eslint-import-resolver-typescript:
|
||||
optional: true
|
||||
eslint-import-resolver-webpack:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
debug: 3.2.7
|
||||
eslint: 8.57.0
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-es-x@7.6.0(eslint@8.57.0):
|
||||
resolution: {integrity: sha512-I0AmeNgevgaTR7y2lrVCJmGYF0rjoznpDvqV/kIkZSZbZ8Rw3eu4cGlvBBULScfkSOCzqKbff5LR4CNrV7mZHA==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
@@ -9701,7 +9746,7 @@ packages:
|
||||
ignore: 5.3.1
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
|
||||
/eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.12.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
|
||||
resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==}
|
||||
engines: {node: '>=4'}
|
||||
peerDependencies:
|
||||
@@ -9711,7 +9756,7 @@ packages:
|
||||
'@typescript-eslint/parser':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 7.7.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/parser': 7.12.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
array-includes: 3.1.7
|
||||
array.prototype.findlastindex: 1.2.4
|
||||
array.prototype.flat: 1.3.2
|
||||
@@ -9720,7 +9765,7 @@ packages:
|
||||
doctrine: 2.1.0
|
||||
eslint: 8.57.0
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.7.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.12.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.13.1
|
||||
is-glob: 4.0.3
|
||||
@@ -9749,7 +9794,7 @@ packages:
|
||||
jest:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.7.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.12.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
eslint: 8.57.0
|
||||
transitivePeerDependencies:
|
||||
@@ -9957,7 +10002,7 @@ packages:
|
||||
vitest:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.7.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.12.0)(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/utils': 7.3.1(eslint@8.57.0)(typescript@5.4.5)
|
||||
eslint: 8.57.0
|
||||
transitivePeerDependencies:
|
||||
@@ -12196,12 +12241,6 @@ packages:
|
||||
engines: {node: '>=14'}
|
||||
dev: false
|
||||
|
||||
/js-tiktoken@1.0.11:
|
||||
resolution: {integrity: sha512-PajXFLq2vx7/8jllQZ43vzNpAai/0MOVdJjW/UrNyJorNQRTjHrqdGJG/mjHVy7h9M6dW6CaG43eNLMYFkTh6w==}
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
dev: false
|
||||
|
||||
/js-tiktoken@1.0.12:
|
||||
resolution: {integrity: sha512-L7wURW1fH9Qaext0VzaUDpFGVQgjkdE3Dgsy9/+yXyGEpBKnylTd0mU0bfbNkKDlXRb6TEsZkwuflu1B8uQbJQ==}
|
||||
dependencies:
|
||||
@@ -12407,256 +12446,6 @@ packages:
|
||||
resolution: {integrity: sha512-lG03Ru+XyOJFsjH3OMY6R/9U38IjDPfnOfDgO3ynhbDr+Dz8fak+X6L62vqu3iybQnj+lG84OttBuU9KY3L9kA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
/langchain@0.1.36:
|
||||
resolution: {integrity: sha512-NTbnCL/jKWIeEI//Nm1oG8nhW3vkYWvEMr1MPotmTThTfeKfO87eV/OAzAyh6Ruy6GFs/qofRgQZGIe6XvXTNQ==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@aws-sdk/client-s3': ^3.310.0
|
||||
'@aws-sdk/client-sagemaker-runtime': ^3.310.0
|
||||
'@aws-sdk/client-sfn': ^3.310.0
|
||||
'@aws-sdk/credential-provider-node': ^3.388.0
|
||||
'@azure/storage-blob': ^12.15.0
|
||||
'@gomomento/sdk': ^1.51.1
|
||||
'@gomomento/sdk-core': ^1.51.1
|
||||
'@gomomento/sdk-web': ^1.51.1
|
||||
'@google-ai/generativelanguage': ^0.2.1
|
||||
'@google-cloud/storage': ^6.10.1 || ^7.7.0
|
||||
'@mendable/firecrawl-js': ^0.0.13
|
||||
'@notionhq/client': ^2.2.10
|
||||
'@pinecone-database/pinecone': '*'
|
||||
'@supabase/supabase-js': ^2.10.0
|
||||
'@vercel/kv': ^0.2.3
|
||||
'@xata.io/client': ^0.28.0
|
||||
apify-client: ^2.7.1
|
||||
assemblyai: ^4.0.0
|
||||
axios: '*'
|
||||
cheerio: ^1.0.0-rc.12
|
||||
chromadb: '*'
|
||||
convex: ^1.3.1
|
||||
couchbase: ^4.3.0
|
||||
d3-dsv: ^2.0.0
|
||||
epub2: ^3.0.1
|
||||
faiss-node: '*'
|
||||
fast-xml-parser: '*'
|
||||
google-auth-library: ^8.9.0
|
||||
handlebars: ^4.7.8
|
||||
html-to-text: ^9.0.5
|
||||
ignore: ^5.2.0
|
||||
ioredis: ^5.3.2
|
||||
jsdom: '*'
|
||||
mammoth: ^1.6.0
|
||||
mongodb: '>=5.2.0'
|
||||
node-llama-cpp: '*'
|
||||
notion-to-md: ^3.1.0
|
||||
officeparser: ^4.0.4
|
||||
pdf-parse: 1.1.1
|
||||
peggy: ^3.0.2
|
||||
playwright: ^1.32.1
|
||||
puppeteer: ^19.7.2
|
||||
pyodide: ^0.24.1
|
||||
redis: ^4.6.4
|
||||
sonix-speech-recognition: ^2.1.1
|
||||
srt-parser-2: ^1.2.3
|
||||
typeorm: ^0.3.12
|
||||
weaviate-ts-client: '*'
|
||||
web-auth-library: ^1.0.3
|
||||
ws: ^8.14.2
|
||||
youtube-transcript: ^1.0.6
|
||||
youtubei.js: ^9.1.0
|
||||
peerDependenciesMeta:
|
||||
'@aws-sdk/client-s3':
|
||||
optional: true
|
||||
'@aws-sdk/client-sagemaker-runtime':
|
||||
optional: true
|
||||
'@aws-sdk/client-sfn':
|
||||
optional: true
|
||||
'@aws-sdk/credential-provider-node':
|
||||
optional: true
|
||||
'@azure/storage-blob':
|
||||
optional: true
|
||||
'@gomomento/sdk':
|
||||
optional: true
|
||||
'@gomomento/sdk-core':
|
||||
optional: true
|
||||
'@gomomento/sdk-web':
|
||||
optional: true
|
||||
'@google-ai/generativelanguage':
|
||||
optional: true
|
||||
'@google-cloud/storage':
|
||||
optional: true
|
||||
'@mendable/firecrawl-js':
|
||||
optional: true
|
||||
'@notionhq/client':
|
||||
optional: true
|
||||
'@pinecone-database/pinecone':
|
||||
optional: true
|
||||
'@supabase/supabase-js':
|
||||
optional: true
|
||||
'@vercel/kv':
|
||||
optional: true
|
||||
'@xata.io/client':
|
||||
optional: true
|
||||
apify-client:
|
||||
optional: true
|
||||
assemblyai:
|
||||
optional: true
|
||||
axios:
|
||||
optional: true
|
||||
cheerio:
|
||||
optional: true
|
||||
chromadb:
|
||||
optional: true
|
||||
convex:
|
||||
optional: true
|
||||
couchbase:
|
||||
optional: true
|
||||
d3-dsv:
|
||||
optional: true
|
||||
epub2:
|
||||
optional: true
|
||||
faiss-node:
|
||||
optional: true
|
||||
fast-xml-parser:
|
||||
optional: true
|
||||
google-auth-library:
|
||||
optional: true
|
||||
handlebars:
|
||||
optional: true
|
||||
html-to-text:
|
||||
optional: true
|
||||
ignore:
|
||||
optional: true
|
||||
ioredis:
|
||||
optional: true
|
||||
jsdom:
|
||||
optional: true
|
||||
mammoth:
|
||||
optional: true
|
||||
mongodb:
|
||||
optional: true
|
||||
node-llama-cpp:
|
||||
optional: true
|
||||
notion-to-md:
|
||||
optional: true
|
||||
officeparser:
|
||||
optional: true
|
||||
pdf-parse:
|
||||
optional: true
|
||||
peggy:
|
||||
optional: true
|
||||
playwright:
|
||||
optional: true
|
||||
puppeteer:
|
||||
optional: true
|
||||
pyodide:
|
||||
optional: true
|
||||
redis:
|
||||
optional: true
|
||||
sonix-speech-recognition:
|
||||
optional: true
|
||||
srt-parser-2:
|
||||
optional: true
|
||||
typeorm:
|
||||
optional: true
|
||||
weaviate-ts-client:
|
||||
optional: true
|
||||
web-auth-library:
|
||||
optional: true
|
||||
ws:
|
||||
optional: true
|
||||
youtube-transcript:
|
||||
optional: true
|
||||
youtubei.js:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@anthropic-ai/sdk': 0.9.1
|
||||
'@langchain/community': 0.0.47(@aws-sdk/credential-provider-node@3.549.0)(lodash@4.17.21)
|
||||
'@langchain/core': 0.1.61
|
||||
'@langchain/openai': 0.0.33
|
||||
'@langchain/textsplitters': 0.0.0
|
||||
binary-extensions: 2.3.0
|
||||
js-tiktoken: 1.0.11
|
||||
js-yaml: 4.1.0
|
||||
jsonpointer: 5.0.1
|
||||
langchainhub: 0.0.8
|
||||
langsmith: 0.1.14
|
||||
ml-distance: 4.0.1
|
||||
openapi-types: 12.1.3
|
||||
p-retry: 4.6.2
|
||||
uuid: 9.0.1
|
||||
yaml: 2.4.1
|
||||
zod: 3.22.4
|
||||
zod-to-json-schema: 3.22.5(zod@3.22.4)
|
||||
transitivePeerDependencies:
|
||||
- '@aws-crypto/sha256-js'
|
||||
- '@aws-sdk/client-bedrock-agent-runtime'
|
||||
- '@aws-sdk/client-bedrock-runtime'
|
||||
- '@aws-sdk/client-dynamodb'
|
||||
- '@aws-sdk/client-kendra'
|
||||
- '@aws-sdk/client-lambda'
|
||||
- '@azure/search-documents'
|
||||
- '@clickhouse/client'
|
||||
- '@cloudflare/ai'
|
||||
- '@datastax/astra-db-ts'
|
||||
- '@elastic/elasticsearch'
|
||||
- '@getmetal/metal-sdk'
|
||||
- '@getzep/zep-js'
|
||||
- '@gradientai/nodejs-sdk'
|
||||
- '@huggingface/inference'
|
||||
- '@mozilla/readability'
|
||||
- '@opensearch-project/opensearch'
|
||||
- '@planetscale/database'
|
||||
- '@premai/prem-sdk'
|
||||
- '@qdrant/js-client-rest'
|
||||
- '@raycast/api'
|
||||
- '@rockset/client'
|
||||
- '@smithy/eventstream-codec'
|
||||
- '@smithy/protocol-http'
|
||||
- '@smithy/signature-v4'
|
||||
- '@smithy/util-utf8'
|
||||
- '@supabase/postgrest-js'
|
||||
- '@tensorflow-models/universal-sentence-encoder'
|
||||
- '@tensorflow/tfjs-converter'
|
||||
- '@tensorflow/tfjs-core'
|
||||
- '@upstash/redis'
|
||||
- '@upstash/vector'
|
||||
- '@vercel/postgres'
|
||||
- '@writerai/writer-sdk'
|
||||
- '@xenova/transformers'
|
||||
- '@zilliz/milvus2-sdk-node'
|
||||
- better-sqlite3
|
||||
- cassandra-driver
|
||||
- cborg
|
||||
- closevector-common
|
||||
- closevector-node
|
||||
- closevector-web
|
||||
- cohere-ai
|
||||
- discord.js
|
||||
- dria
|
||||
- duck-duck-scrape
|
||||
- encoding
|
||||
- firebase-admin
|
||||
- googleapis
|
||||
- hnswlib-node
|
||||
- interface-datastore
|
||||
- it-all
|
||||
- jsonwebtoken
|
||||
- llmonitor
|
||||
- lodash
|
||||
- lunary
|
||||
- mysql2
|
||||
- neo4j-driver
|
||||
- pg
|
||||
- pg-copy-streams
|
||||
- pickleparser
|
||||
- portkey-ai
|
||||
- replicate
|
||||
- typesense
|
||||
- usearch
|
||||
- vectordb
|
||||
- voy-search
|
||||
dev: false
|
||||
|
||||
/langchain@0.1.36(@aws-sdk/client-s3@3.550.0)(@aws-sdk/credential-provider-node@3.549.0)(lodash@4.17.21):
|
||||
resolution: {integrity: sha512-NTbnCL/jKWIeEI//Nm1oG8nhW3vkYWvEMr1MPotmTThTfeKfO87eV/OAzAyh6Ruy6GFs/qofRgQZGIe6XvXTNQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -16419,8 +16208,8 @@ packages:
|
||||
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
|
||||
dev: true
|
||||
|
||||
/tiktoken@1.0.13:
|
||||
resolution: {integrity: sha512-JaL9ZnvTbGFMDIBeGdVkLt4qWTeCPw+n7Ock+wceAGRenuHA6nOOvMJFliNDyXsjg2osGKJWsXtO2xc74VxyDw==}
|
||||
/tiktoken@1.0.15:
|
||||
resolution: {integrity: sha512-sCsrq/vMWUSEW29CJLNmPvWxlVp7yh2tlkAjpJltIKqp5CKf98ZNpdeHRmAlPVFlGEbswDc6SmI8vz64W/qErw==}
|
||||
dev: false
|
||||
|
||||
/tiny-invariant@1.3.3:
|
||||
@@ -17039,6 +16828,20 @@ packages:
|
||||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/vaul@0.9.1(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-fAhd7i4RNMinx+WEm6pF3nOl78DFkAazcN04ElLPFF9BMCNGbY/kou8UMhIcicm0rJCNePJP0Yyza60gGOD0Jw==}
|
||||
peerDependencies:
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||
dependencies:
|
||||
'@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- '@types/react-dom'
|
||||
dev: false
|
||||
|
||||
/victory-vendor@36.9.2:
|
||||
resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==}
|
||||
dependencies:
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ FROM node:20-alpine AS alpine
|
||||
RUN apk update && apk upgrade --no-cache libcrypto3 libssl3 libc6-compat
|
||||
|
||||
FROM alpine AS base
|
||||
RUN npm install turbo --global
|
||||
RUN npm install turbo@^1.13.3 --global
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN corepack enable
|
||||
|
||||
+5
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "web",
|
||||
"version": "2.45.1",
|
||||
"version": "2.49.2",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -25,6 +25,7 @@
|
||||
"@aws-sdk/lib-storage": "^3.568.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.554.0",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/lint": "^6.8.0",
|
||||
"@headlessui/react": "1.7.18",
|
||||
"@headlessui/tailwindcss": "0.2.0",
|
||||
"@heroicons/react": "^2.1.3",
|
||||
@@ -55,6 +56,7 @@
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@radix-ui/react-toggle": "^1.0.3",
|
||||
"@radix-ui/react-toggle-group": "^1.0.4",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@react-email/components": "^0.0.18",
|
||||
"@react-email/render": "^0.0.14",
|
||||
@@ -69,7 +71,6 @@
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"@tanstack/react-table": "^8.11.8",
|
||||
"@tanstack/react-virtual": "^3.5.0",
|
||||
"@tremor/react": "3.16.2",
|
||||
"@trpc/client": "^10.45.0",
|
||||
"@trpc/next": "^10.45.0",
|
||||
@@ -115,6 +116,7 @@
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"use-query-params": "^2.2.1",
|
||||
"uuid": "^9.0.1",
|
||||
"vaul": "^0.9.1",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -136,7 +138,7 @@
|
||||
"@types/react-dom": "^18.2.25",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/parser": "^7.7.0",
|
||||
"@typescript-eslint/parser": "^7.12.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"dotenv-cli": "^7.4.2",
|
||||
"eslint": "^8.56.0",
|
||||
|
||||
@@ -1137,7 +1137,10 @@ paths:
|
||||
security: *ref_0
|
||||
/api/public/sessions/{sessionId}:
|
||||
get:
|
||||
description: Get a session
|
||||
description: >-
|
||||
Get a session. Please note that `traces` on this endpoint are not
|
||||
paginated, if you plan to fetch large sessions, consider `GET
|
||||
/api/public/traces?sessionId=<sessionId>`
|
||||
operationId: sessions_get
|
||||
tags:
|
||||
- Sessions
|
||||
@@ -1262,6 +1265,12 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
nullable: true
|
||||
- name: sessionId
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
nullable: true
|
||||
- name: fromTimestamp
|
||||
in: query
|
||||
description: Retrieve only traces newer than this datetime (ISO 8601).
|
||||
@@ -2499,6 +2508,7 @@ components:
|
||||
totalCost:
|
||||
type: number
|
||||
format: double
|
||||
description: Total model cost in USD
|
||||
usage:
|
||||
type: array
|
||||
items:
|
||||
@@ -2521,14 +2531,28 @@ components:
|
||||
nullable: true
|
||||
inputUsage:
|
||||
type: integer
|
||||
description: Total number of generation input units (e.g. tokens)
|
||||
outputUsage:
|
||||
type: integer
|
||||
description: Total number of generation output units (e.g. tokens)
|
||||
totalUsage:
|
||||
type: integer
|
||||
description: Total number of generation total units (e.g. tokens)
|
||||
countTraces:
|
||||
type: integer
|
||||
countObservations:
|
||||
type: integer
|
||||
totalCost:
|
||||
type: number
|
||||
format: double
|
||||
description: Total model cost in USD
|
||||
required:
|
||||
- inputUsage
|
||||
- outputUsage
|
||||
- totalUsage
|
||||
- countTraces
|
||||
- countObservations
|
||||
- totalCost
|
||||
Observations:
|
||||
title: Observations
|
||||
type: object
|
||||
@@ -2654,6 +2678,13 @@ components:
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
description: List of deployment labels of this prompt version.
|
||||
tags:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
description: List of tags to apply to all versions of this prompt.
|
||||
required:
|
||||
- name
|
||||
- prompt
|
||||
@@ -2672,6 +2703,13 @@ components:
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
description: List of deployment labels of this prompt version.
|
||||
tags:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
description: List of tags to apply to all versions of this prompt.
|
||||
required:
|
||||
- name
|
||||
- prompt
|
||||
@@ -2713,11 +2751,20 @@ components:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: List of deployment labels of this prompt version.
|
||||
tags:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: >-
|
||||
List of tags. Used to filter via UI and API. The same across
|
||||
versions of a prompt.
|
||||
required:
|
||||
- name
|
||||
- version
|
||||
- config
|
||||
- labels
|
||||
- tags
|
||||
ChatMessage:
|
||||
title: ChatMessage
|
||||
type: object
|
||||
|
||||
@@ -699,7 +699,7 @@
|
||||
"auth": null,
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"type\": \"chat\",\n \"name\": \"example\",\n \"prompt\": [\n {\n \"role\": \"example\",\n \"content\": \"example\"\n }\n ],\n \"config\": \"UNKNOWN\",\n \"labels\": [\n \"example\"\n ]\n}",
|
||||
"raw": "{\n \"type\": \"chat\",\n \"name\": \"example\",\n \"prompt\": [\n {\n \"role\": \"example\",\n \"content\": \"example\"\n }\n ],\n \"config\": \"UNKNOWN\",\n \"labels\": [\n \"example\"\n ],\n \"tags\": [\n \"example\"\n ]\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
@@ -890,7 +890,7 @@
|
||||
"_type": "endpoint",
|
||||
"name": "Get",
|
||||
"request": {
|
||||
"description": "Get a session",
|
||||
"description": "Get a session. Please note that `traces` on this endpoint are not paginated, if you plan to fetch large sessions, consider `GET /api/public/traces?sessionId=<sessionId>`",
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/public/sessions/:sessionId",
|
||||
"host": [
|
||||
@@ -963,7 +963,7 @@
|
||||
"request": {
|
||||
"description": "Get list of traces.",
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/public/traces?page=&limit=&userId=&name=&fromTimestamp=&orderBy=&tags=",
|
||||
"raw": "{{baseUrl}}/api/public/traces?page=&limit=&userId=&name=&sessionId=&fromTimestamp=&orderBy=&tags=",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
@@ -993,6 +993,11 @@
|
||||
"value": "",
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"key": "sessionId",
|
||||
"value": "",
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"key": "fromTimestamp",
|
||||
"value": "",
|
||||
|
||||
@@ -2,7 +2,7 @@ import { prisma } from "@langfuse/shared/src/db";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import { ProfilingIntegration } from "@sentry/profiling-node";
|
||||
import type { SamplingContext, TransactionEvent } from "@sentry/types";
|
||||
import { jsonSchema } from "@/src/utils/zod";
|
||||
import { jsonSchema } from "@langfuse/shared";
|
||||
|
||||
if (process.env.NEXT_PUBLIC_SENTRY_DSN)
|
||||
Sentry.init({
|
||||
@@ -18,7 +18,7 @@ if (process.env.NEXT_PUBLIC_SENTRY_DSN)
|
||||
samplingContext.request.url &&
|
||||
samplingContext.request.url.includes("api/trpc")
|
||||
) {
|
||||
return 0.3;
|
||||
return 0.1;
|
||||
}
|
||||
if (
|
||||
samplingContext.request &&
|
||||
@@ -27,12 +27,12 @@ if (process.env.NEXT_PUBLIC_SENTRY_DSN)
|
||||
samplingContext.transactionContext.status !== "ok" &&
|
||||
samplingContext.transactionContext.status !== "unauthenticated"
|
||||
) {
|
||||
return 1;
|
||||
return 0.1;
|
||||
}
|
||||
return 0.1;
|
||||
return 0.01;
|
||||
},
|
||||
|
||||
profilesSampleRate: 0.2, // Profiling sample rate is relative to tracesSampleRate
|
||||
profilesSampleRate: 0.1,
|
||||
integrations: [
|
||||
// Add profiling integration to list of integrations
|
||||
new ProfilingIntegration(),
|
||||
|
||||
@@ -68,12 +68,18 @@ describe("/api/public/metrics/daily API Endpoint", () => {
|
||||
inputUsage: 333,
|
||||
outputUsage: 0,
|
||||
totalUsage: 333,
|
||||
countObservations: 1,
|
||||
countTraces: 1,
|
||||
totalCost: 0,
|
||||
},
|
||||
{
|
||||
model: "modelC",
|
||||
inputUsage: 666,
|
||||
outputUsage: 777,
|
||||
totalUsage: 1443,
|
||||
countObservations: 1,
|
||||
countTraces: 1,
|
||||
totalCost: 1024.22,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -88,6 +94,9 @@ describe("/api/public/metrics/daily API Endpoint", () => {
|
||||
inputUsage: 100,
|
||||
outputUsage: 200,
|
||||
totalUsage: 300,
|
||||
countObservations: 1,
|
||||
countTraces: 1,
|
||||
totalCost: 0,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -680,6 +680,71 @@ describe("/api/public/v2/prompts API Endpoint", () => {
|
||||
expect(fetchedPrompt.body.createdBy).toBe("API");
|
||||
expect(fetchedPrompt.body.config).toEqual({});
|
||||
});
|
||||
|
||||
it("should update tags across versions", async () => {
|
||||
const promptName = "prompt-name" + nanoid();
|
||||
|
||||
const createPromptVersion = async (tags?: string[]) => {
|
||||
await makeAPICall("POST", baseURI, {
|
||||
name: promptName,
|
||||
prompt: "This is a test prompt",
|
||||
type: PromptType.Text,
|
||||
...(tags !== undefined && { tags: tags }),
|
||||
});
|
||||
};
|
||||
|
||||
const fetchPromptVersion = async (version: number) => {
|
||||
const fetchedPrompt = await makeAPICall(
|
||||
"GET",
|
||||
`${baseURI}/${promptName}?version=${version}`,
|
||||
undefined,
|
||||
);
|
||||
expect(fetchedPrompt.status).toBe(200);
|
||||
if (!isPrompt(fetchedPrompt.body)) {
|
||||
throw new Error("Expected body to be a prompt");
|
||||
}
|
||||
return fetchedPrompt.body;
|
||||
};
|
||||
|
||||
// Create version 1 with ["tag"]
|
||||
await createPromptVersion(["tag"]);
|
||||
let fetchedPrompt1 = await fetchPromptVersion(1);
|
||||
expect(fetchedPrompt1.tags).toEqual(["tag"]);
|
||||
expect(fetchedPrompt1.version).toBe(1);
|
||||
|
||||
// Create version 2 with no tags provided (should use tags from version 1)
|
||||
await createPromptVersion();
|
||||
let fetchedPrompt2 = await fetchPromptVersion(2);
|
||||
expect(fetchedPrompt2.tags).toEqual(["tag"]);
|
||||
expect(fetchedPrompt2.version).toBe(2);
|
||||
|
||||
// Create version 3 with ["tag1", "tag2", "tag3"] (should update tags across versions)
|
||||
await createPromptVersion(["tag1", "tag2", "tag3"]);
|
||||
fetchedPrompt1 = await fetchPromptVersion(1);
|
||||
fetchedPrompt2 = await fetchPromptVersion(2);
|
||||
let fetchedPrompt3 = await fetchPromptVersion(3);
|
||||
expect(fetchedPrompt1.tags).toEqual(["tag1", "tag2", "tag3"]);
|
||||
expect(fetchedPrompt1.version).toBe(1);
|
||||
expect(fetchedPrompt2.tags).toEqual(["tag1", "tag2", "tag3"]);
|
||||
expect(fetchedPrompt2.version).toBe(2);
|
||||
expect(fetchedPrompt3.tags).toEqual(["tag1", "tag2", "tag3"]);
|
||||
expect(fetchedPrompt3.version).toBe(3);
|
||||
|
||||
// remove tags
|
||||
await createPromptVersion([]);
|
||||
fetchedPrompt1 = await fetchPromptVersion(1);
|
||||
fetchedPrompt2 = await fetchPromptVersion(2);
|
||||
fetchedPrompt3 = await fetchPromptVersion(3);
|
||||
let fetchedPrompt4 = await fetchPromptVersion(4);
|
||||
expect(fetchedPrompt1.tags).toEqual([]);
|
||||
expect(fetchedPrompt1.version).toBe(1);
|
||||
expect(fetchedPrompt2.tags).toEqual([]);
|
||||
expect(fetchedPrompt2.version).toBe(2);
|
||||
expect(fetchedPrompt3.tags).toEqual([]);
|
||||
expect(fetchedPrompt3.version).toBe(3);
|
||||
expect(fetchedPrompt4.tags).toEqual([]);
|
||||
expect(fetchedPrompt4.version).toBe(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when fetching a prompt list", () => {
|
||||
|
||||
@@ -50,7 +50,7 @@ describe("Build valid SQL queries", () => {
|
||||
table: "traces_scores",
|
||||
values: ["project-id"],
|
||||
strings: [
|
||||
' FROM traces t JOIN scores s ON t.id = s.trace_id AND t.project_id = s.project_id WHERE t."project_id" = ',
|
||||
` FROM traces t JOIN scores s ON t.id = s.trace_id AND s.data_type != 'CATEGORICAL' AND t.project_id = s.project_id WHERE t."project_id" = `,
|
||||
";",
|
||||
],
|
||||
} as const,
|
||||
|
||||
@@ -238,4 +238,62 @@ describe("/api/public/traces API Endpoint", () => {
|
||||
expect(trace.body.htmlPath).toContain(`/traces/${traceId}`);
|
||||
expect(trace.body.htmlPath).toContain(`/project/`); // do not know the projectId
|
||||
});
|
||||
|
||||
it("should filter traces by session ID", async () => {
|
||||
const sessionId = "test-session-id";
|
||||
const anotherSessionId = "another-session-id";
|
||||
|
||||
// Create traces with different session IDs
|
||||
await makeAPICall("POST", "/api/public/traces", {
|
||||
id: "trace-1",
|
||||
name: "test-trace-1",
|
||||
sessionId,
|
||||
userId: "user-1",
|
||||
projectId: "project-1",
|
||||
metadata: { key: "value" },
|
||||
release: "1.0.0",
|
||||
version: "1.0.0",
|
||||
});
|
||||
|
||||
await makeAPICall("POST", "/api/public/traces", {
|
||||
id: "trace-2",
|
||||
name: "test-trace-2",
|
||||
sessionId: anotherSessionId,
|
||||
userId: "user-2",
|
||||
projectId: "project-1",
|
||||
metadata: { key: "value" },
|
||||
release: "1.0.0",
|
||||
version: "1.0.0",
|
||||
});
|
||||
|
||||
// Filter by session ID
|
||||
const tracesBySessionId = await makeAPICall<GetTracesAPIResponse>(
|
||||
"GET",
|
||||
`/api/public/traces?sessionId=${sessionId}`,
|
||||
);
|
||||
|
||||
expect(tracesBySessionId.status).toBe(200);
|
||||
expect(tracesBySessionId.body.data).toHaveLength(1);
|
||||
expect(tracesBySessionId.body.data[0].id).toBe("trace-1");
|
||||
|
||||
// Filter by another session ID
|
||||
const tracesByAnotherSessionId = await makeAPICall<GetTracesAPIResponse>(
|
||||
"GET",
|
||||
`/api/public/traces?sessionId=${anotherSessionId}`,
|
||||
);
|
||||
|
||||
expect(tracesByAnotherSessionId.status).toBe(200);
|
||||
expect(tracesByAnotherSessionId.body.data).toHaveLength(1);
|
||||
expect(tracesByAnotherSessionId.body.data[0].id).toBe("trace-2");
|
||||
|
||||
// Filter by non-existent session ID
|
||||
const tracesByNonExistentSessionId =
|
||||
await makeAPICall<GetTracesAPIResponse>(
|
||||
"GET",
|
||||
`/api/public/traces?sessionId=non-existent-session-id`,
|
||||
);
|
||||
|
||||
expect(tracesByNonExistentSessionId.status).toBe(200);
|
||||
expect(tracesByNonExistentSessionId.body.data).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { paginationZod } from "@/src/utils/zod";
|
||||
import { paginationZod } from "@langfuse/shared";
|
||||
import { ZodError } from "zod";
|
||||
|
||||
// Create test cases
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import chatCompletionHandler from "@/src/ee/features/playground/server/chatCompletionHandler";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
export const maxDuration = 60;
|
||||
|
||||
export const POST = chatCompletionHandler;
|
||||
@@ -1,8 +1,105 @@
|
||||
import { AlertTriangle, Check } from "lucide-react";
|
||||
import {
|
||||
AlertTriangle,
|
||||
Check,
|
||||
Github,
|
||||
HardDriveDownload,
|
||||
Newspaper,
|
||||
} from "lucide-react";
|
||||
|
||||
import { VERSION } from "@/src/constants";
|
||||
import { env } from "@/src/env.mjs";
|
||||
import { cn } from "@/src/utils/tailwind";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
} from "@/src/components/ui/dropdown-menu";
|
||||
import { ArrowUp } from "lucide-react";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
import Link from "next/link";
|
||||
|
||||
const VersionLabel = ({ className }: { className?: string }) => {
|
||||
const checkUpdate = api.public.checkUpdate.useQuery(undefined, {
|
||||
refetchOnMount: false,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
enabled: !env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION, // do not check for updates on Langfuse Cloud
|
||||
onError: (error) => console.error("checkUpdate error", error), // do not render default error message
|
||||
});
|
||||
|
||||
const hasUpdate =
|
||||
!env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION &&
|
||||
checkUpdate.data &&
|
||||
checkUpdate.data.updateType;
|
||||
|
||||
const color =
|
||||
checkUpdate.data?.updateType === "major"
|
||||
? "text-dark-red"
|
||||
: checkUpdate.data?.updateType === "minor"
|
||||
? "text-dark-yellow"
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="xs" className={className}>
|
||||
{VERSION}
|
||||
{hasUpdate && <ArrowUp className={`ml-1 h-3 w-3 ${color}`} />}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
{hasUpdate ? (
|
||||
<>
|
||||
<DropdownMenuLabel>
|
||||
New {checkUpdate.data?.updateType} version:{" "}
|
||||
{checkUpdate.data?.latestRelease}
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
</>
|
||||
) : !env.NEXT_PUBLIC_LANGFUSE_CLOUD_REGION ? (
|
||||
<>
|
||||
<DropdownMenuLabel>This is the latest release</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href="https://github.com/langfuse/langfuse/releases"
|
||||
target="_blank"
|
||||
>
|
||||
<Github size={16} className="mr-2" />
|
||||
Releases
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href="https://langfuse.com/changelog" target="_blank">
|
||||
<Newspaper size={16} className="mr-2" />
|
||||
Changelog
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
{hasUpdate && (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href="https://langfuse.com/docs/deployment/self-host#update"
|
||||
target="_blank"
|
||||
>
|
||||
<HardDriveDownload size={16} className="mr-2" />
|
||||
Update
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export const LangfuseIcon = ({
|
||||
size = 32,
|
||||
@@ -65,22 +162,12 @@ export const LangfuseLogo = ({
|
||||
<span
|
||||
className={cn(
|
||||
"ml-2 font-mono font-semibold",
|
||||
size === "sm" ? "text-sm" : "text-xl",
|
||||
size === "sm" ? "text-sm" : "text-lg",
|
||||
)}
|
||||
>
|
||||
Langfuse
|
||||
</span>
|
||||
{version && (
|
||||
<a
|
||||
href="https://github.com/langfuse/langfuse/releases"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
title="View releases on GitHub"
|
||||
className="ml-2 text-xs text-muted-foreground"
|
||||
>
|
||||
{VERSION}
|
||||
</a>
|
||||
)}
|
||||
{version && <VersionLabel className="ml-2" />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import Link from "next/link";
|
||||
|
||||
import { Label } from "@/src/components/ui/label";
|
||||
import { useHasAccess } from "@/src/features/rbac/utils/checkAccess";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { supportedModels, type UIModelParams } from "@langfuse/shared";
|
||||
import { ArrowTopRightIcon } from "@radix-ui/react-icons";
|
||||
|
||||
export const LLMApiKeyComponent = (p: {
|
||||
projectId: string;
|
||||
modelParams: UIModelParams;
|
||||
}) => {
|
||||
const hasAccess = useHasAccess({
|
||||
projectId: p.projectId,
|
||||
scope: "llmApiKeys:read",
|
||||
});
|
||||
|
||||
if (!hasAccess) {
|
||||
return (
|
||||
<div>
|
||||
<Label className="text-xs font-semibold">API key</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
LLM API Key only visible to Owner and Admin roles.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const apiKeys = api.llmApiKey.all.useQuery({
|
||||
projectId: p.projectId,
|
||||
});
|
||||
|
||||
if (apiKeys.isLoading) {
|
||||
return (
|
||||
<div>
|
||||
<Label className="text-xs font-semibold">API key</Label>
|
||||
<p className="text-sm text-muted-foreground">Loading...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const model = p.modelParams.model.value;
|
||||
const modelProvider = Object.entries(supportedModels).find((providerData) =>
|
||||
(providerData[1] as any as string[]).includes(model),
|
||||
)?.[0];
|
||||
|
||||
const apiKey = apiKeys.data?.data.find((k) => k.provider === modelProvider);
|
||||
|
||||
return (
|
||||
<div className="space-y-2 text-xs">
|
||||
<Label className="text-xs font-semibold">API key</Label>
|
||||
<div>
|
||||
{apiKey ? (
|
||||
<span className="mr-2 rounded-sm bg-input p-1 text-xs">
|
||||
{apiKey.displaySecretKey}
|
||||
</span>
|
||||
) : undefined}
|
||||
</div>
|
||||
{/* Custom form message to include a link to the already existing prompt */}
|
||||
{!apiKey ? (
|
||||
<div className="flex flex-col font-medium text-destructive">
|
||||
{`No LLM API key found for provider ${modelProvider}.`}
|
||||
|
||||
<Link
|
||||
href={`/project/${p.projectId}/settings`}
|
||||
className="flex flex-row"
|
||||
>
|
||||
Create a new LLM API key here. <ArrowTopRightIcon />
|
||||
</Link>
|
||||
</div>
|
||||
) : undefined}
|
||||
<p className="text-muted-foreground">
|
||||
The LLM API key is used for each execution and will incur costs.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
+9
-1
@@ -8,6 +8,7 @@ import {
|
||||
} from "@/src/components/ui/select";
|
||||
import { Slider } from "@/src/components/ui/slider";
|
||||
import { Switch } from "@/src/components/ui/switch";
|
||||
import useProjectIdFromURL from "@/src/hooks/useProjectIdFromURL";
|
||||
import { cn } from "@/src/utils/tailwind";
|
||||
import {
|
||||
ModelProvider,
|
||||
@@ -15,6 +16,8 @@ import {
|
||||
type UIModelParams,
|
||||
} from "@langfuse/shared";
|
||||
|
||||
import { LLMApiKeyComponent } from "./LLMApiKeyComponent";
|
||||
|
||||
export type ModelParamsContext = {
|
||||
modelParams: UIModelParams;
|
||||
availableModels?: UIModelParams[];
|
||||
@@ -33,10 +36,14 @@ export const ModelParameters: React.FC<ModelParamsContext> = ({
|
||||
setModelParamEnabled,
|
||||
formDisabled = false,
|
||||
}) => {
|
||||
const projectId = useProjectIdFromURL();
|
||||
|
||||
if (!projectId) return null;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-4">
|
||||
<p className="font-semibold">Model</p>
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
<ModelParamsSelect
|
||||
title="Provider"
|
||||
modelParamsKey="provider"
|
||||
@@ -104,6 +111,7 @@ export const ModelParameters: React.FC<ModelParamsContext> = ({
|
||||
tooltip="An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both."
|
||||
updateModelParam={updateModelParamValue}
|
||||
/>
|
||||
<LLMApiKeyComponent {...{ projectId, modelParams }} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -8,14 +8,7 @@ import {
|
||||
CardTitle,
|
||||
} from "@/src/components/ui/card";
|
||||
import { chatAvailable, openChat } from "@/src/features/support-chat/chat";
|
||||
import {
|
||||
Book,
|
||||
Calendar,
|
||||
Github,
|
||||
Mail,
|
||||
MessageSquare,
|
||||
Slack,
|
||||
} from "lucide-react";
|
||||
import { Book, Github, Mail, MessageSquare, Slack } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { SiDiscord } from "react-icons/si";
|
||||
|
||||
@@ -30,7 +23,7 @@ const supportChannels = [
|
||||
},
|
||||
{
|
||||
icon: Github,
|
||||
title: "GitHub",
|
||||
title: "GitHub Issues",
|
||||
description:
|
||||
"Create an issue on Github to report bugs or request new features.",
|
||||
href: "https://github.com/langfuse/langfuse/issues/new/choose",
|
||||
@@ -38,12 +31,11 @@ const supportChannels = [
|
||||
primary: true,
|
||||
},
|
||||
{
|
||||
icon: SiDiscord,
|
||||
title: "Discord",
|
||||
description:
|
||||
"Get support from community and maintainers. Follow announcements to stay up to date with new features.",
|
||||
href: "https://langfuse.com/discord",
|
||||
buttonText: "Join Discord",
|
||||
icon: Github,
|
||||
title: "GitHub Support",
|
||||
description: "Create a support ticket via GitHub discussions.",
|
||||
href: "https://github.com/orgs/langfuse/discussions/categories/support",
|
||||
buttonText: "Submit question",
|
||||
primary: true,
|
||||
},
|
||||
{
|
||||
@@ -57,24 +49,25 @@ const supportChannels = [
|
||||
{
|
||||
icon: MessageSquare,
|
||||
title: "Chat",
|
||||
description: "Get quick support directly from the team.",
|
||||
description: "Get support directly from the team.",
|
||||
onClick: () => openChat(),
|
||||
available: chatAvailable,
|
||||
buttonText: "Launch Chat",
|
||||
},
|
||||
{
|
||||
icon: Calendar,
|
||||
title: "Schedule call",
|
||||
description: "Schedule a call with one of the founders.",
|
||||
href: "https://cal.com/marc-kl/office-hours",
|
||||
buttonText: "Schedule Call",
|
||||
icon: SiDiscord,
|
||||
title: "Discord",
|
||||
description:
|
||||
"Get support from community. Follow announcements to stay up to date with new features.",
|
||||
href: "https://langfuse.com/discord",
|
||||
buttonText: "Join Discord",
|
||||
},
|
||||
{
|
||||
icon: Slack,
|
||||
title: "Slack Connect",
|
||||
description: "Get a dedicated support channel for you and your team.",
|
||||
href: "mailto:help@langfuse.com?subject=Slack%20Connect%20Request&body=I'd%20like%20to%20request%20a%20dedicated%20Slack%20Connect%20channel%20for%20me%20and%20my%20team.%0D%0A%0D%0AUsers%20(emails)%20to%20include%20besides%20mine%3A%0D%0A%0D%0A",
|
||||
buttonText: "Request Slack Connect (via Email)",
|
||||
buttonText: "Request via Email",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -53,16 +53,13 @@ export function DeleteButton({
|
||||
},
|
||||
});
|
||||
|
||||
if (!hasAccess) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover key={itemId}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant={isTableAction ? "ghost" : "outline"}
|
||||
size={isTableAction ? "xs" : "icon"}
|
||||
disabled={!hasAccess}
|
||||
onClick={() =>
|
||||
type === "trace"
|
||||
? capture("trace:delete_form_open", {
|
||||
|
||||
@@ -4,9 +4,13 @@ import {
|
||||
HoverCardContent,
|
||||
HoverCardTrigger,
|
||||
} from "@/src/components/ui/hover-card";
|
||||
import {
|
||||
isBooleanDataType,
|
||||
isCategoricalDataType,
|
||||
} from "@/src/features/manual-scoring/lib/helpers";
|
||||
import { type ScoreSimplified } from "@/src/server/api/routers/generations/getAllQuery";
|
||||
import { cn } from "@/src/utils/tailwind";
|
||||
import { MessageCircle } from "lucide-react";
|
||||
import { MessageCircleMore } from "lucide-react";
|
||||
|
||||
export const GroupedScoreBadges = ({
|
||||
scores,
|
||||
@@ -34,11 +38,13 @@ export const GroupedScoreBadges = ({
|
||||
<div className={cn("text-xs", props.className)}>
|
||||
{props.scores.map((s, i) => (
|
||||
<span key={i} className="group/score ml-1 first:ml-0">
|
||||
{s.value.toFixed(2)}
|
||||
{isCategoricalDataType(s.dataType) || isBooleanDataType(s.dataType)
|
||||
? s.stringValue
|
||||
: s.value.toFixed(2)}
|
||||
{s.comment && (
|
||||
<HoverCard>
|
||||
<HoverCardTrigger className="ml-1 inline-block cursor-pointer">
|
||||
<MessageCircle size={12} />
|
||||
<MessageCircleMore size={12} />
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent className="overflow-hidden whitespace-normal break-normal">
|
||||
<p>{s.comment}</p>
|
||||
@@ -73,9 +79,12 @@ export const GroupedScoreBadges = ({
|
||||
<Badge
|
||||
variant="outline"
|
||||
key={name}
|
||||
className="break-all font-normal"
|
||||
className="grid grid-cols-[1fr,auto] gap-1 font-normal"
|
||||
>
|
||||
{name}: <ScoresOfGroup scores={scores} className="ml-2" />
|
||||
<p className="truncate" title={name}>
|
||||
{name}:
|
||||
</p>
|
||||
<ScoresOfGroup scores={scores} />
|
||||
</Badge>
|
||||
))}
|
||||
</>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import CodeMirror, { EditorView } from "@uiw/react-codemirror";
|
||||
import { githubLight } from "@uiw/codemirror-theme-github";
|
||||
import { tokyoNight } from "@uiw/codemirror-theme-tokyo-night";
|
||||
import { json } from "@codemirror/lang-json";
|
||||
import { json, jsonParseLinter } from "@codemirror/lang-json";
|
||||
import { linter } from "@codemirror/lint";
|
||||
import { useTheme } from "next-themes";
|
||||
import { cn } from "@/src/utils/tailwind";
|
||||
import { useState } from "react";
|
||||
|
||||
// todo: add json linting
|
||||
|
||||
@@ -20,8 +22,13 @@ export function JsonEditor({
|
||||
lineWrapping?: boolean;
|
||||
className?: string;
|
||||
}) {
|
||||
const { theme } = useTheme();
|
||||
const codeMirrorTheme = theme === "dark" ? tokyoNight : githubLight;
|
||||
const { resolvedTheme } = useTheme();
|
||||
const codeMirrorTheme = resolvedTheme === "dark" ? tokyoNight : githubLight;
|
||||
|
||||
// used to disable linter when field is empty
|
||||
const [linterEnabled, setLinterEnabled] = useState<boolean>(
|
||||
!!defaultValue && defaultValue !== "",
|
||||
);
|
||||
return (
|
||||
<CodeMirror
|
||||
value={defaultValue}
|
||||
@@ -30,9 +37,16 @@ export function JsonEditor({
|
||||
foldGutter: true,
|
||||
}}
|
||||
lang={"json"}
|
||||
extensions={[json(), ...(lineWrapping ? [EditorView.lineWrapping] : [])]}
|
||||
extensions={[
|
||||
json(),
|
||||
...(linterEnabled ? [linter(jsonParseLinter())] : []),
|
||||
...(lineWrapping ? [EditorView.lineWrapping] : []),
|
||||
]}
|
||||
defaultValue={defaultValue}
|
||||
onChange={onChange}
|
||||
onChange={(c) => {
|
||||
if (onChange) onChange(c);
|
||||
setLinterEnabled(c !== "");
|
||||
}}
|
||||
className={cn("overflow-hidden rounded-md border", className)}
|
||||
editable={editable}
|
||||
/>
|
||||
|
||||
@@ -521,9 +521,9 @@ const MainNavigation: React.FC<{
|
||||
onNavitemClick?: () => void;
|
||||
className?: string;
|
||||
}> = ({ nav, onNavitemClick, className }) => {
|
||||
const [isOpen, setIsOpen] = useLocalStorage(
|
||||
"sidebar-tracing-default-open",
|
||||
false,
|
||||
const [isOpen, setIsOpen] = useLocalStorage<Record<string, boolean>>(
|
||||
"sidebar-item-default-open",
|
||||
{},
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -572,14 +572,20 @@ const MainNavigation: React.FC<{
|
||||
<Disclosure
|
||||
as="div"
|
||||
defaultOpen={
|
||||
item.children.some((child) => child.current) || isOpen
|
||||
item.children.some((child) => child.current) ||
|
||||
isOpen[item.name]
|
||||
}
|
||||
>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button
|
||||
className="group flex w-full items-center gap-x-3 rounded-md p-2 text-left text-sm font-semibold hover:bg-primary-foreground hover:text-primary-accent"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
onClick={() =>
|
||||
setIsOpen((prev) => ({
|
||||
...prev,
|
||||
[item.name]: !prev[item.name],
|
||||
}))
|
||||
}
|
||||
>
|
||||
{item.icon && (
|
||||
<item.icon
|
||||
|
||||
@@ -7,38 +7,24 @@ import { IOPreview } from "@/src/components/trace/IOPreview";
|
||||
import { JsonSkeleton } from "@/src/components/ui/CodeJsonViewer";
|
||||
import { Badge } from "@/src/components/ui/badge";
|
||||
import { Card } from "@/src/components/ui/card";
|
||||
import { ManualScoreButton } from "@/src/features/manual-scoring/components/ManualScoreButton";
|
||||
import { DetailPageNav } from "@/src/features/navigate-detail-pages/DetailPageNav";
|
||||
import { useDetailPageLists } from "@/src/features/navigate-detail-pages/context";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { usdFormatter } from "@/src/utils/numbers";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useWindowVirtualizer } from "@tanstack/react-virtual";
|
||||
import { type RouterOutput } from "@/src/utils/types";
|
||||
import {
|
||||
DataTableRowHeightSwitch,
|
||||
type RowHeight,
|
||||
useRowHeightLocalStorage,
|
||||
} from "@/src/components/table/data-table-row-height-switch";
|
||||
import { ScrollArea } from "@/src/components/ui/scroll-area";
|
||||
import { useEffect, useState } from "react";
|
||||
import { AnnotateButton } from "@/src/features/manual-scoring/components/AnnotateButton";
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
|
||||
// do not use the usual table row heights here
|
||||
const rowHeightMapping: Record<RowHeight, number> = {
|
||||
s: 200,
|
||||
m: 350,
|
||||
l: 700,
|
||||
};
|
||||
// some projects have thousands of traces in a sessions, paginate to avoid rendering all at once
|
||||
const PAGE_SIZE = 50;
|
||||
|
||||
export const SessionPage: React.FC<{
|
||||
sessionId: string;
|
||||
projectId: string;
|
||||
}> = ({ sessionId, projectId }) => {
|
||||
const { setDetailPageList } = useDetailPageLists();
|
||||
const [rowHeight, setRowHeight] = useRowHeightLocalStorage(
|
||||
"single-session",
|
||||
"m",
|
||||
);
|
||||
const [visibleTraces, setVisibleTraces] = useState(PAGE_SIZE);
|
||||
const session = api.sessions.byId.useQuery(
|
||||
{
|
||||
sessionId,
|
||||
@@ -96,11 +82,6 @@ export const SessionPage: React.FC<{
|
||||
}
|
||||
listKey="sessions"
|
||||
/>,
|
||||
<DataTableRowHeightSwitch
|
||||
rowHeight={rowHeight}
|
||||
setRowHeight={setRowHeight}
|
||||
key="height"
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
@@ -121,97 +102,49 @@ export const SessionPage: React.FC<{
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
{session.data && (
|
||||
<TraceCardList
|
||||
session={session.data}
|
||||
projectId={projectId}
|
||||
rowHeight={rowHeightMapping[rowHeight]}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const TraceCardList = ({
|
||||
session,
|
||||
projectId,
|
||||
rowHeight,
|
||||
}: {
|
||||
session: RouterOutput["sessions"]["byId"];
|
||||
projectId: string;
|
||||
rowHeight: number;
|
||||
}) => {
|
||||
const listVirtualizationRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const virtualizer = useWindowVirtualizer({
|
||||
count: session.traces.length,
|
||||
estimateSize: () => rowHeight,
|
||||
overscan: 5,
|
||||
scrollMargin: listVirtualizationRef.current?.offsetTop ?? 0,
|
||||
gap: 10,
|
||||
});
|
||||
useEffect(() => {
|
||||
// re-measure when rowHeight changes to update the virtualizer
|
||||
virtualizer.measure();
|
||||
}, [rowHeight, virtualizer]);
|
||||
return (
|
||||
<div className="mt-5 border-t pt-5">
|
||||
<div
|
||||
ref={listVirtualizationRef}
|
||||
style={{
|
||||
height: `${virtualizer.getTotalSize()}px`,
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
{virtualizer
|
||||
.getVirtualItems()
|
||||
.map((virtualItem) => ({
|
||||
virtualItem,
|
||||
trace: session.traces[virtualItem.index],
|
||||
}))
|
||||
.map(({ virtualItem, trace }) => (
|
||||
<Card
|
||||
className="group grid w-full gap-3 overflow-hidden border-border p-2 shadow-none hover:border-ring md:grid-cols-3"
|
||||
key={virtualItem.key}
|
||||
data-index={virtualItem.index}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
height: `${rowHeight}px`,
|
||||
transform: `translateY(${virtualItem.start - virtualizer.options.scrollMargin}px)`,
|
||||
}}
|
||||
>
|
||||
<ScrollArea className="col-span-2 pr-3">
|
||||
<SessionIO traceId={trace.id} />
|
||||
</ScrollArea>
|
||||
<ScrollArea>
|
||||
<div className="-mt-1 overflow-y-auto p-1 opacity-50 transition-opacity group-hover:opacity-100">
|
||||
<Link
|
||||
href={`/project/${projectId}/traces/${trace.id}`}
|
||||
className="text-xs hover:underline"
|
||||
>
|
||||
Trace: {trace.name} ({trace.id}) ↗
|
||||
</Link>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{trace.timestamp.toLocaleString()}
|
||||
</div>
|
||||
<div className="mb-1 mt-2 text-xs text-muted-foreground">
|
||||
Scores
|
||||
</div>
|
||||
<div className="flex flex-wrap content-start items-start gap-1">
|
||||
<GroupedScoreBadges scores={trace.scores} />
|
||||
</div>
|
||||
<ManualScoreButton
|
||||
projectId={projectId}
|
||||
traceId={trace.id}
|
||||
scores={trace.scores}
|
||||
variant="badge"
|
||||
/>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</Card>
|
||||
))}
|
||||
<div className="mt-5 flex flex-col gap-2 border-t pt-5">
|
||||
{session.data?.traces.slice(0, visibleTraces).map((trace) => (
|
||||
<Card
|
||||
className="group grid gap-3 border-border p-2 shadow-none hover:border-ring md:grid-cols-3"
|
||||
key={trace.id}
|
||||
>
|
||||
<SessionIO traceId={trace.id} />
|
||||
<div className="-mt-1 p-1 opacity-50 transition-opacity group-hover:opacity-100">
|
||||
<Link
|
||||
href={`/project/${projectId}/traces/${trace.id}`}
|
||||
className="text-xs hover:underline"
|
||||
>
|
||||
Trace: {trace.name} ({trace.id}) ↗
|
||||
</Link>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{trace.timestamp.toLocaleString()}
|
||||
</div>
|
||||
<div className="mb-1 mt-2 text-xs text-muted-foreground">
|
||||
Scores
|
||||
</div>
|
||||
<div className="mb-1 flex flex-wrap content-start items-start gap-1">
|
||||
<GroupedScoreBadges scores={trace.scores} />
|
||||
</div>
|
||||
<AnnotateButton
|
||||
projectId={projectId}
|
||||
traceId={trace.id}
|
||||
scores={trace.scores}
|
||||
variant="badge"
|
||||
type="session"
|
||||
source="SessionDetail"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
{session.data?.traces && session.data.traces.length > visibleTraces && (
|
||||
<Button
|
||||
onClick={() => setVisibleTraces((prev) => prev + PAGE_SIZE)}
|
||||
variant="ghost"
|
||||
className="self-center"
|
||||
>
|
||||
{`Load ${Math.min(session.data.traces.length - visibleTraces, PAGE_SIZE)} More`}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -230,9 +163,8 @@ const SessionIO = ({ traceId }: { traceId: string }) => {
|
||||
refetchOnMount: false, // prevents refetching loops
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2 overflow-x-hidden overflow-y-scroll p-0">
|
||||
<div className="col-span-2 flex flex-col gap-2 p-0">
|
||||
{!trace.data ? (
|
||||
<JsonSkeleton
|
||||
className="h-full w-full overflow-hidden px-2 py-1"
|
||||
|
||||
@@ -7,6 +7,7 @@ import { cn } from "@/src/utils/tailwind";
|
||||
import { type RouterOutput, type RouterInput } from "@/src/utils/types";
|
||||
import { useState } from "react";
|
||||
import { usePostHogClientCapture } from "@/src/features/posthog-analytics/usePostHogClientCapture";
|
||||
import { trpcErrorToast } from "@/src/utils/trpcErrorToast";
|
||||
|
||||
export function StarToggle({
|
||||
value,
|
||||
@@ -76,7 +77,7 @@ export function StarTraceToggle({
|
||||
onError: (err, _newTodo, context) => {
|
||||
setIsLoading(false);
|
||||
// Rollback to the previous value if mutation fails
|
||||
console.log("error", err);
|
||||
trpcErrorToast(err);
|
||||
utils.traces.all.setData(tracesFilter, context?.prev);
|
||||
},
|
||||
onSettled: () => {
|
||||
@@ -157,7 +158,7 @@ export function StarTraceDetailsToggle({
|
||||
},
|
||||
onError: (err, _newTodo, context) => {
|
||||
setIsLoading(false);
|
||||
console.log("error", err);
|
||||
trpcErrorToast(err);
|
||||
// Rollback to the previous value if mutation fails
|
||||
utils.traces.byId.setData({ traceId }, context?.prevData);
|
||||
},
|
||||
|
||||
@@ -50,6 +50,8 @@ interface DataTableProps<TData, TValue> {
|
||||
help?: { description: string; href: string };
|
||||
rowHeight?: RowHeight;
|
||||
className?: string;
|
||||
paginationClassName?: string;
|
||||
isBorderless?: boolean;
|
||||
}
|
||||
|
||||
export interface AsyncTableData<T> {
|
||||
@@ -71,6 +73,9 @@ export function DataTable<TData extends object, TValue>({
|
||||
orderBy,
|
||||
setOrderBy,
|
||||
rowHeight,
|
||||
className,
|
||||
paginationClassName,
|
||||
isBorderless = false,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
||||
const rowheighttw = getRowHeightTailwindClass(rowHeight);
|
||||
@@ -105,8 +110,18 @@ export function DataTable<TData extends object, TValue>({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex w-full max-w-full flex-1 flex-col gap-1 overflow-auto">
|
||||
<div className="w-full overflow-auto rounded-md border">
|
||||
<div
|
||||
className={cn(
|
||||
"flex w-full max-w-full flex-1 flex-col gap-1 overflow-auto",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"w-full overflow-auto",
|
||||
isBorderless ? "" : "rounded-md border",
|
||||
)}
|
||||
>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
@@ -242,7 +257,12 @@ export function DataTable<TData extends object, TValue>({
|
||||
<div className="grow"></div>
|
||||
</div>
|
||||
{pagination !== undefined ? (
|
||||
<div className="bg:background sticky bottom-0 z-10 flex w-full justify-end font-medium">
|
||||
<div
|
||||
className={cn(
|
||||
"sticky bottom-0 z-10 flex w-full justify-end bg-background font-medium",
|
||||
paginationClassName,
|
||||
)}
|
||||
>
|
||||
<DataTablePagination
|
||||
table={table}
|
||||
paginationOptions={pagination.options}
|
||||
|
||||
@@ -21,11 +21,7 @@ import {
|
||||
withDefault,
|
||||
} from "use-query-params";
|
||||
import { useQueryFilterState } from "@/src/features/filters/hooks/useFilterState";
|
||||
import {
|
||||
formatIntervalSeconds,
|
||||
intervalInSeconds,
|
||||
utcDateOffsetByDays,
|
||||
} from "@/src/utils/dates";
|
||||
import { formatIntervalSeconds, utcDateOffsetByDays } from "@/src/utils/dates";
|
||||
import useColumnVisibility from "@/src/features/column-visibility/hooks/useColumnVisibility";
|
||||
import { type LangfuseColumnDef } from "@/src/components/table/types";
|
||||
import {
|
||||
@@ -119,7 +115,7 @@ export default function GenerationsTable({
|
||||
type: "datetime",
|
||||
operator: ">",
|
||||
value: utcDateOffsetByDays(
|
||||
session.data?.environment.defaultTableDateTimeOffset ?? -14,
|
||||
session.data?.environment.defaultTableDateTimeOffset ?? -7,
|
||||
),
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { DataTable } from "@/src/components/table/data-table";
|
||||
import { type LangfuseColumnDef } from "@/src/components/table/types";
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/src/components/ui/popover";
|
||||
import { useState } from "react";
|
||||
import useColumnVisibility from "@/src/features/column-visibility/hooks/useColumnVisibility";
|
||||
import { usePostHogClientCapture } from "@/src/features/posthog-analytics/usePostHogClientCapture";
|
||||
import { useHasAccess } from "@/src/features/rbac/utils/checkAccess";
|
||||
@@ -10,6 +16,7 @@ import { type Prisma, type Model } from "@langfuse/shared/src/db";
|
||||
import Decimal from "decimal.js";
|
||||
import { Trash } from "lucide-react";
|
||||
import { useQueryParams, withDefault, NumberParam } from "use-query-params";
|
||||
import { cn } from "@/src/utils/tailwind";
|
||||
|
||||
export type ModelTableRow = {
|
||||
modelId: string;
|
||||
@@ -222,13 +229,12 @@ export default function ModelTable({ projectId }: { projectId: string }) {
|
||||
accessorKey: "actions",
|
||||
header: "Actions",
|
||||
cell: ({ row }) => {
|
||||
return row.original.maintainer === "User" ? (
|
||||
return (
|
||||
<DeleteModelButton
|
||||
projectId={projectId}
|
||||
modelId={row.original.modelId}
|
||||
isBuiltIn={row.original.maintainer === "Langfuse"}
|
||||
/>
|
||||
) : (
|
||||
<div className="h-6" />
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -287,10 +293,13 @@ export default function ModelTable({ projectId }: { projectId: string }) {
|
||||
const DeleteModelButton = ({
|
||||
modelId,
|
||||
projectId,
|
||||
isBuiltIn,
|
||||
}: {
|
||||
modelId: string;
|
||||
projectId: string;
|
||||
isBuiltIn?: boolean;
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const utils = api.useUtils();
|
||||
const capture = usePostHogClientCapture();
|
||||
const mut = api.models.delete.useMutation({
|
||||
@@ -304,32 +313,48 @@ const DeleteModelButton = ({
|
||||
scope: "models:CUD",
|
||||
});
|
||||
|
||||
if (!hasAccess) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
size="xs"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
const confirmDelete = window.confirm(
|
||||
"Are you sure you want to delete this model?",
|
||||
);
|
||||
if (confirmDelete) {
|
||||
capture("models:delete_button_click");
|
||||
mut
|
||||
.mutateAsync({
|
||||
projectId,
|
||||
modelId,
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Trash size={14} />
|
||||
</Button>
|
||||
<Popover open={isOpen} onOpenChange={() => setIsOpen(!isOpen)}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
disabled={!hasAccess || isBuiltIn}
|
||||
title={
|
||||
isBuiltIn ? "Built-in models cannot be deleted" : "Delete model"
|
||||
}
|
||||
className={cn(
|
||||
isBuiltIn &&
|
||||
"disabled:pointer-events-auto disabled:cursor-not-allowed",
|
||||
)}
|
||||
>
|
||||
<Trash className="h-4 w-4" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<h2 className="text-md mb-3 font-semibold">Please confirm</h2>
|
||||
<p className="mb-3 text-sm">
|
||||
This action permanently deletes this model definition.
|
||||
</p>
|
||||
<div className="flex justify-end space-x-4">
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
loading={mut.isLoading}
|
||||
onClick={() => {
|
||||
capture("models:delete_button_click");
|
||||
mut.mutateAsync({
|
||||
projectId,
|
||||
modelId,
|
||||
});
|
||||
|
||||
setIsOpen(false);
|
||||
}}
|
||||
>
|
||||
Delete Model
|
||||
</Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
import React from "react";
|
||||
import { Card } from "@/src/components/ui/card";
|
||||
import { useRowHeightLocalStorage } from "@/src/components/table/data-table-row-height-switch";
|
||||
import useColumnVisibility from "@/src/features/column-visibility/hooks/useColumnVisibility";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { type LangfuseColumnDef } from "@/src/components/table/types";
|
||||
import { DataTableToolbar } from "@/src/components/table/data-table-toolbar";
|
||||
import { DataTable } from "@/src/components/table/data-table";
|
||||
import { type ScoreDataType, type Prisma } from "@langfuse/shared";
|
||||
import { IOTableCell } from "@/src/components/ui/CodeJsonViewer";
|
||||
import { NumberParam, useQueryParams, withDefault } from "use-query-params";
|
||||
import { isNumericDataType } from "@/src/features/manual-scoring/lib/helpers";
|
||||
|
||||
type ScoreConfigTableRow = {
|
||||
id: string;
|
||||
name: string;
|
||||
dataType: ScoreDataType;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
range: {
|
||||
maxValue?: number | null;
|
||||
minValue?: number | null;
|
||||
categories?: Prisma.JsonValue | null;
|
||||
};
|
||||
description?: string | null;
|
||||
};
|
||||
|
||||
function getConfigRange(
|
||||
originalRow: ScoreConfigTableRow,
|
||||
): Prisma.JsonValue | undefined {
|
||||
const { range, dataType } = originalRow;
|
||||
if (isNumericDataType(dataType)) {
|
||||
return [
|
||||
{ minValue: range.minValue ?? "-∞", maxValue: range.maxValue ?? "∞" },
|
||||
];
|
||||
}
|
||||
return range.categories;
|
||||
}
|
||||
|
||||
export function ScoreConfigsTable({ projectId }: { projectId: string }) {
|
||||
const [paginationState, setPaginationState] = useQueryParams({
|
||||
pageIndex: withDefault(NumberParam, 0),
|
||||
pageSize: withDefault(NumberParam, 50),
|
||||
});
|
||||
|
||||
const [rowHeight, setRowHeight] = useRowHeightLocalStorage(
|
||||
"scoreConfigs",
|
||||
"s",
|
||||
);
|
||||
|
||||
const configs = api.scoreConfigs.all.useQuery({
|
||||
projectId,
|
||||
page: paginationState.pageIndex,
|
||||
limit: paginationState.pageSize,
|
||||
});
|
||||
|
||||
const totalCount = configs.data?.totalCount ?? 0;
|
||||
|
||||
const columns: LangfuseColumnDef<ScoreConfigTableRow>[] = [
|
||||
{
|
||||
accessorKey: "name",
|
||||
id: "name",
|
||||
header: "Name",
|
||||
enableHiding: true,
|
||||
},
|
||||
{
|
||||
accessorKey: "dataType",
|
||||
id: "dataType",
|
||||
header: "Data Type",
|
||||
enableHiding: true,
|
||||
},
|
||||
{
|
||||
accessorKey: "range",
|
||||
id: "range",
|
||||
header: "Range",
|
||||
enableHiding: true,
|
||||
cell: ({ row }) => {
|
||||
const range = getConfigRange(row.original);
|
||||
|
||||
return !!range ? (
|
||||
<IOTableCell data={range} singleLine={rowHeight === "s"} />
|
||||
) : null;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "description",
|
||||
id: "description",
|
||||
header: "Description",
|
||||
enableHiding: true,
|
||||
cell: ({ row }) => {
|
||||
const value = row.original.description;
|
||||
|
||||
return !!value ? (
|
||||
<IOTableCell data={value} singleLine={rowHeight === "s"} />
|
||||
) : null;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "id",
|
||||
id: "id",
|
||||
header: "Config ID",
|
||||
enableHiding: true,
|
||||
defaultHidden: true,
|
||||
},
|
||||
{
|
||||
accessorKey: "createdAt",
|
||||
id: "createdAt",
|
||||
header: "Created At",
|
||||
enableHiding: true,
|
||||
defaultHidden: true,
|
||||
},
|
||||
];
|
||||
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
useColumnVisibility<ScoreConfigTableRow>(
|
||||
"scoreConfigsColumnVisibility",
|
||||
columns,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DataTableToolbar
|
||||
columns={columns}
|
||||
columnVisibility={columnVisibility}
|
||||
setColumnVisibility={setColumnVisibility}
|
||||
rowHeight={rowHeight}
|
||||
setRowHeight={setRowHeight}
|
||||
/>
|
||||
<Card className="mb-4 flex max-h-[calc(100dvh-40rem)] flex-col overflow-hidden">
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={
|
||||
configs.isLoading
|
||||
? { isLoading: true, isError: false }
|
||||
: configs.isError
|
||||
? {
|
||||
isLoading: false,
|
||||
isError: true,
|
||||
error: configs.error.message,
|
||||
}
|
||||
: {
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
data: configs.data?.configs.map((config) => ({
|
||||
id: config.id,
|
||||
name: config.name,
|
||||
dataType: config.dataType,
|
||||
description: config.description,
|
||||
createdAt: config.createdAt.toLocaleString(),
|
||||
updatedAt: config.updatedAt.toLocaleString(),
|
||||
range: {
|
||||
maxValue: config.maxValue,
|
||||
minValue: config.minValue,
|
||||
categories: config.categories,
|
||||
},
|
||||
})),
|
||||
}
|
||||
}
|
||||
pagination={{
|
||||
pageCount: Math.ceil(totalCount / paginationState.pageSize),
|
||||
onChange: setPaginationState,
|
||||
state: paginationState,
|
||||
}}
|
||||
columnVisibility={columnVisibility}
|
||||
onColumnVisibilityChange={setColumnVisibility}
|
||||
rowHeight={rowHeight}
|
||||
className="gap-2"
|
||||
paginationClassName="-mx-2 mb-2"
|
||||
isBorderless
|
||||
/>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -4,16 +4,20 @@ import { DataTableToolbar } from "@/src/components/table/data-table-toolbar";
|
||||
import TableLink from "@/src/components/table/table-link";
|
||||
import { type LangfuseColumnDef } from "@/src/components/table/types";
|
||||
import { IOTableCell } from "@/src/components/ui/CodeJsonViewer";
|
||||
import { Avatar, AvatarImage } from "@/src/components/ui/avatar";
|
||||
import useColumnVisibility from "@/src/features/column-visibility/hooks/useColumnVisibility";
|
||||
import { useQueryFilterState } from "@/src/features/filters/hooks/useFilterState";
|
||||
import { isNumericDataType } from "@/src/features/manual-scoring/lib/helpers";
|
||||
import { useOrderByState } from "@/src/features/orderBy/hooks/useOrderByState";
|
||||
import {
|
||||
type ScoreOptions,
|
||||
scoresTableColsWithOptions,
|
||||
} from "@/src/server/api/definitions/scoresTable";
|
||||
import { api } from "@/src/utils/api";
|
||||
import { utcDateOffsetByDays } from "@/src/utils/dates";
|
||||
import type { RouterOutput, RouterInput } from "@/src/utils/types";
|
||||
import type { FilterState } from "@langfuse/shared";
|
||||
import type { FilterState, ScoreDataType } from "@langfuse/shared";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useQueryParams, withDefault, NumberParam } from "use-query-params";
|
||||
|
||||
export type ScoresTableRow = {
|
||||
@@ -22,7 +26,12 @@ export type ScoresTableRow = {
|
||||
timestamp: string;
|
||||
source: string;
|
||||
name: string;
|
||||
value: number;
|
||||
dataType: ScoreDataType;
|
||||
value: string;
|
||||
author: {
|
||||
image?: string;
|
||||
name?: string;
|
||||
};
|
||||
comment?: string;
|
||||
observationId?: string;
|
||||
traceName?: string;
|
||||
@@ -72,11 +81,20 @@ export default function ScoresTable({
|
||||
pageIndex: withDefault(NumberParam, 0),
|
||||
pageSize: withDefault(NumberParam, 50),
|
||||
});
|
||||
|
||||
const session = useSession();
|
||||
const [rowHeight, setRowHeight] = useRowHeightLocalStorage("scores", "s");
|
||||
|
||||
const [userFilterState, setUserFilterState] = useQueryFilterState(
|
||||
[],
|
||||
[
|
||||
{
|
||||
column: "Timestamp",
|
||||
type: "datetime",
|
||||
operator: ">",
|
||||
value: utcDateOffsetByDays(
|
||||
session.data?.environment.defaultTableDateTimeOffset ?? -7,
|
||||
),
|
||||
},
|
||||
],
|
||||
"scores",
|
||||
);
|
||||
|
||||
@@ -161,6 +179,29 @@ export default function ScoresTable({
|
||||
) : undefined;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "userId",
|
||||
header: "Trace User ID",
|
||||
id: "userId",
|
||||
headerTooltip: {
|
||||
description: "The user ID associated with the trace.",
|
||||
href: "https://langfuse.com/docs/tracing-features/users",
|
||||
},
|
||||
enableHiding: true,
|
||||
enableSorting: true,
|
||||
cell: ({ row }) => {
|
||||
const value = row.getValue("userId");
|
||||
return typeof value === "string" ? (
|
||||
<>
|
||||
<TableLink
|
||||
path={`/project/${projectId}/users/${value}`}
|
||||
value={value}
|
||||
truncateAt={40}
|
||||
/>
|
||||
</>
|
||||
) : undefined;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "timestamp",
|
||||
header: "Timestamp",
|
||||
@@ -182,38 +223,39 @@ export default function ScoresTable({
|
||||
enableHiding: true,
|
||||
enableSorting: true,
|
||||
},
|
||||
{
|
||||
accessorKey: "dataType",
|
||||
header: "Data Type",
|
||||
id: "dataType",
|
||||
enableHiding: true,
|
||||
enableSorting: true,
|
||||
},
|
||||
{
|
||||
accessorKey: "value",
|
||||
header: "Value",
|
||||
id: "value",
|
||||
enableHiding: true,
|
||||
enableSorting: true,
|
||||
cell: ({ row }) => {
|
||||
const value: number = row.getValue("value");
|
||||
return value % 1 === 0 ? value : value.toFixed(4);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "userId",
|
||||
header: "User ID",
|
||||
id: "userId",
|
||||
headerTooltip: {
|
||||
description: "The user ID associated with the trace.",
|
||||
href: "https://langfuse.com/docs/tracing-features/users",
|
||||
},
|
||||
enableHiding: true,
|
||||
enableSorting: true,
|
||||
accessorKey: "author",
|
||||
id: "author",
|
||||
header: "Author",
|
||||
cell: ({ row }) => {
|
||||
const value = row.getValue("userId");
|
||||
return typeof value === "string" ? (
|
||||
<>
|
||||
<TableLink
|
||||
path={`/project/${projectId}/users/${value}`}
|
||||
value={value}
|
||||
truncateAt={40}
|
||||
/>
|
||||
</>
|
||||
) : undefined;
|
||||
const { name, image } = row.getValue(
|
||||
"author",
|
||||
) as ScoresTableRow["author"];
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Avatar className="h-7 w-7">
|
||||
<AvatarImage
|
||||
src={image ?? undefined}
|
||||
alt={name ?? "User Avatar"}
|
||||
/>
|
||||
</Avatar>
|
||||
<span>{name}</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -268,12 +310,21 @@ export default function ScoresTable({
|
||||
timestamp: score.timestamp.toLocaleString(),
|
||||
source: score.source,
|
||||
name: score.name,
|
||||
value: score.value,
|
||||
dataType: score.dataType,
|
||||
value: isNumericDataType(score.dataType)
|
||||
? score.value % 1 === 0
|
||||
? String(score.value)
|
||||
: score.value.toFixed(4)
|
||||
: score.stringValue ?? "",
|
||||
author: {
|
||||
image: score.authorUserImage ?? undefined,
|
||||
name: score.authorUserName ?? undefined,
|
||||
},
|
||||
comment: score.comment ?? undefined,
|
||||
observationId: score.observationId ?? undefined,
|
||||
traceId: score.traceId,
|
||||
traceName: score.traceName ?? undefined,
|
||||
userId: score.userId ?? undefined,
|
||||
userId: score.traceUserId ?? undefined,
|
||||
jobConfigurationId: score.jobConfigurationId ?? undefined,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -55,7 +55,7 @@ export default function SessionsTable({
|
||||
type: "datetime",
|
||||
operator: ">",
|
||||
value: utcDateOffsetByDays(
|
||||
session.data?.environment.defaultTableDateTimeOffset ?? -14,
|
||||
session.data?.environment.defaultTableDateTimeOffset ?? -7,
|
||||
),
|
||||
},
|
||||
],
|
||||
|
||||
@@ -96,7 +96,7 @@ export default function TracesTable({
|
||||
type: "datetime",
|
||||
operator: ">",
|
||||
value: utcDateOffsetByDays(
|
||||
session.data?.environment.defaultTableDateTimeOffset ?? -14,
|
||||
session.data?.environment.defaultTableDateTimeOffset ?? -7,
|
||||
),
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { JSONView } from "@/src/components/ui/CodeJsonViewer";
|
||||
import { z } from "zod";
|
||||
import { deepParseJson } from "@/src/utils/json";
|
||||
import { deepParseJson } from "@langfuse/shared";
|
||||
import { cn } from "@/src/utils/tailwind";
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
CardTitle,
|
||||
} from "@/src/components/ui/card";
|
||||
import { Badge } from "@/src/components/ui/badge";
|
||||
import { ManualScoreButton } from "@/src/features/manual-scoring/components/ManualScoreButton";
|
||||
import { NewDatasetItemFromTrace } from "@/src/features/datasets/components/NewDatasetItemFromObservationButton";
|
||||
import { type ObservationReturnType } from "@/src/server/api/routers/traces";
|
||||
import { api } from "@/src/utils/api";
|
||||
@@ -22,6 +21,7 @@ import { withDefault, StringParam, useQueryParam } from "use-query-params";
|
||||
import ScoresTable from "@/src/components/table/use-cases/scores";
|
||||
import { ScoresPreview } from "@/src/components/trace/ScoresPreview";
|
||||
import { JumpToPlaygroundButton } from "@/src/ee/features/playground/page/components/JumpToPlaygroundButton";
|
||||
import { AnnotateButton } from "@/src/features/manual-scoring/components/AnnotateButton";
|
||||
|
||||
export const ObservationPreview = (props: {
|
||||
observations: Array<ObservationReturnType>;
|
||||
@@ -154,11 +154,12 @@ export const ObservationPreview = (props: {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<ManualScoreButton
|
||||
<AnnotateButton
|
||||
projectId={props.projectId}
|
||||
traceId={preloadedObservation.traceId}
|
||||
observationId={preloadedObservation.id}
|
||||
scores={props.scores}
|
||||
type="observation"
|
||||
/>
|
||||
{observationWithInputAndOutput.data?.type === "GENERATION" && (
|
||||
<JumpToPlaygroundButton
|
||||
@@ -237,7 +238,7 @@ const PromptBadge = (props: { promptId: string; projectId: string }) => {
|
||||
if (prompt.isLoading || !prompt.data) return null;
|
||||
return (
|
||||
<Link
|
||||
href={`/project/${props.projectId}/prompts/${prompt.data.name}?version=${prompt.data.version}`}
|
||||
href={`/project/${props.projectId}/prompts/${encodeURIComponent(prompt.data.name)}?version=${prompt.data.version}`}
|
||||
>
|
||||
<Badge>
|
||||
Prompt: {prompt.data.name}
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
CardTitle,
|
||||
} from "@/src/components/ui/card";
|
||||
import { TraceAggUsageBadge } from "@/src/components/token-usage-badge";
|
||||
import { ManualScoreButton } from "@/src/features/manual-scoring/components/ManualScoreButton";
|
||||
import { Badge } from "@/src/components/ui/badge";
|
||||
import { type ObservationReturnType } from "@/src/server/api/routers/traces";
|
||||
import { IOPreview } from "@/src/components/trace/IOPreview";
|
||||
@@ -19,6 +18,7 @@ import { Tabs, TabsList, TabsTrigger } from "@/src/components/ui/tabs";
|
||||
import { withDefault, StringParam, useQueryParam } from "use-query-params";
|
||||
import ScoresTable from "@/src/components/table/use-cases/scores";
|
||||
import { ScoresPreview } from "@/src/components/trace/ScoresPreview";
|
||||
import { AnnotateButton } from "@/src/features/manual-scoring/components/AnnotateButton";
|
||||
|
||||
export const TracePreview = ({
|
||||
trace,
|
||||
@@ -54,13 +54,13 @@ export const TracePreview = ({
|
||||
<TabsList className="bg-background py-0">
|
||||
<TabsTrigger
|
||||
value="preview"
|
||||
className="data-[state=active]:border-primary-accent h-full rounded-none border-b-4 border-transparent data-[state=active]:shadow-none"
|
||||
className="h-full rounded-none border-b-4 border-transparent data-[state=active]:border-primary-accent data-[state=active]:shadow-none"
|
||||
>
|
||||
Preview
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="scores"
|
||||
className="data-[state=active]:border-primary-accent h-full rounded-none border-b-4 border-transparent data-[state=active]:shadow-none"
|
||||
className="h-full rounded-none border-b-4 border-transparent data-[state=active]:border-primary-accent data-[state=active]:shadow-none"
|
||||
>
|
||||
Scores
|
||||
</TabsTrigger>
|
||||
@@ -95,7 +95,7 @@ export const TracePreview = ({
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<ManualScoreButton
|
||||
<AnnotateButton
|
||||
projectId={trace.projectId}
|
||||
traceId={trace.id}
|
||||
scores={scores}
|
||||
|
||||
@@ -277,6 +277,7 @@ export function TracePage({ traceId }: { traceId: string }) {
|
||||
availableTags={allTags}
|
||||
traceId={trace.data.id}
|
||||
projectId={trace.data.projectId}
|
||||
className="flex-wrap"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Check, ChevronsDownUp, ChevronsUpDown, Copy } from "lucide-react";
|
||||
import { cn } from "@/src/utils/tailwind";
|
||||
import { default as React18JsonView } from "react18-json-view";
|
||||
import "react18-json-view/src/dark.css";
|
||||
import { deepParseJson } from "@/src/utils/json";
|
||||
import { deepParseJson } from "@langfuse/shared";
|
||||
import { Skeleton } from "@/src/components/ui/skeleton";
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
@@ -17,7 +17,7 @@ export function JSONView(props: {
|
||||
}) {
|
||||
// some users ingest stringified json nested in json, parse it
|
||||
const parsedJson = deepParseJson(props.json);
|
||||
const { theme } = useTheme();
|
||||
const { resolvedTheme } = useTheme();
|
||||
return (
|
||||
<div className={cn("rounded-md border", props.className)}>
|
||||
{props.title ? (
|
||||
@@ -44,7 +44,7 @@ export function JSONView(props: {
|
||||
<React18JsonView
|
||||
src={parsedJson}
|
||||
theme="github"
|
||||
dark={theme === "dark"}
|
||||
dark={resolvedTheme === "dark"}
|
||||
collapseObjectsAfterLength={20}
|
||||
collapseStringsAfterLength={500}
|
||||
displaySize={"collapsed"}
|
||||
|
||||
@@ -19,6 +19,8 @@ const buttonVariants = cva(
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
errorNotification:
|
||||
"bg-destructive-foreground/90 text-destructive hover:bg-destructive-foreground/80",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
|
||||
@@ -42,7 +42,7 @@ const CommandInput = React.forwardRef<
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
className="flex items-center rounded-lg border px-3"
|
||||
className="flex items-center rounded-md border px-3"
|
||||
cmdk-input-wrapper=""
|
||||
>
|
||||
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { Drawer as DrawerPrimitive } from "vaul";
|
||||
|
||||
import { cn } from "@/src/utils/tailwind";
|
||||
import { useMediaQuery } from "react-responsive";
|
||||
|
||||
// https://tailwindcss.com/docs/responsive-design
|
||||
const TAILWIND_MD_MEDIA_QUERY = 768;
|
||||
|
||||
const Drawer = ({
|
||||
shouldScaleBackground = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Root>) => {
|
||||
const isMediumScreen = useMediaQuery({
|
||||
query: `(min-width: ${TAILWIND_MD_MEDIA_QUERY}px)`,
|
||||
});
|
||||
const direction = isMediumScreen ? "right" : "bottom";
|
||||
|
||||
return (
|
||||
<DrawerPrimitive.Root
|
||||
shouldScaleBackground={shouldScaleBackground}
|
||||
direction={direction}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
Drawer.displayName = "Drawer";
|
||||
|
||||
const DrawerTrigger = DrawerPrimitive.Trigger;
|
||||
|
||||
const DrawerPortal = DrawerPrimitive.Portal;
|
||||
|
||||
const DrawerClose = DrawerPrimitive.Close;
|
||||
|
||||
const DrawerOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DrawerPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn("fixed inset-0 z-50 bg-primary/20", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
|
||||
|
||||
const DrawerContent = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DrawerPortal>
|
||||
<DrawerOverlay />
|
||||
<DrawerPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background md:inset-x-auto md:inset-y-0 md:right-0 md:mt-0 md:h-full md:w-2/5 md:rounded-l-[10px] md:rounded-t-[0px] lg:w-1/3 xl:w-1/4 2xl:w-1/4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</DrawerPrimitive.Content>
|
||||
</DrawerPortal>
|
||||
));
|
||||
DrawerContent.displayName = "DrawerContent";
|
||||
|
||||
const DrawerHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
DrawerHeader.displayName = "DrawerHeader";
|
||||
|
||||
const DrawerFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
DrawerFooter.displayName = "DrawerFooter";
|
||||
|
||||
const DrawerTitle = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DrawerPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-lg font-semibold leading-none tracking-tight",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
|
||||
|
||||
const DrawerDescription = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DrawerPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
|
||||
|
||||
export {
|
||||
Drawer,
|
||||
DrawerPortal,
|
||||
DrawerOverlay,
|
||||
DrawerTrigger,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerHeader,
|
||||
DrawerFooter,
|
||||
DrawerTitle,
|
||||
DrawerDescription,
|
||||
};
|
||||
@@ -28,4 +28,6 @@ const PopoverContent = React.forwardRef<
|
||||
));
|
||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
||||
|
||||
export { Popover, PopoverTrigger, PopoverContent };
|
||||
const PopoverClose = PopoverPrimitive.Close;
|
||||
|
||||
export { Popover, PopoverTrigger, PopoverContent, PopoverClose };
|
||||
|
||||
@@ -74,7 +74,7 @@ const TableHead = React.forwardRef<
|
||||
<th
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"sticky top-0 z-10 border-b bg-background px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
|
||||
"sticky top-0 z-10 rounded-t-md border-b bg-background px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
|
||||
import { type VariantProps } from "class-variance-authority";
|
||||
import { cn } from "@/src/utils/tailwind";
|
||||
import { toggleVariants } from "@/src/components/ui/toggle";
|
||||
|
||||
const ToggleGroupContext = React.createContext<
|
||||
VariantProps<typeof toggleVariants>
|
||||
>({
|
||||
size: "default",
|
||||
variant: "default",
|
||||
});
|
||||
|
||||
const ToggleGroup = React.forwardRef<
|
||||
React.ElementRef<typeof ToggleGroupPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &
|
||||
VariantProps<typeof toggleVariants>
|
||||
>(({ className, variant, size, children, ...props }, ref) => (
|
||||
<ToggleGroupPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn("flex items-center justify-center gap-1", className)}
|
||||
{...props}
|
||||
>
|
||||
<ToggleGroupContext.Provider value={{ variant, size }}>
|
||||
{children}
|
||||
</ToggleGroupContext.Provider>
|
||||
</ToggleGroupPrimitive.Root>
|
||||
));
|
||||
|
||||
ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName;
|
||||
|
||||
const ToggleGroupItem = React.forwardRef<
|
||||
React.ElementRef<typeof ToggleGroupPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &
|
||||
VariantProps<typeof toggleVariants>
|
||||
>(({ className, children, variant, size, ...props }, ref) => {
|
||||
const context = React.useContext(ToggleGroupContext);
|
||||
|
||||
return (
|
||||
<ToggleGroupPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
toggleVariants({
|
||||
variant: context.variant || variant,
|
||||
size: context.size || size,
|
||||
}),
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</ToggleGroupPrimitive.Item>
|
||||
);
|
||||
});
|
||||
|
||||
ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName;
|
||||
|
||||
export { ToggleGroup, ToggleGroupItem };
|
||||
@@ -1 +1 @@
|
||||
export const VERSION = "v2.45.1";
|
||||
export const VERSION = "v2.49.2";
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
PopoverTrigger,
|
||||
} from "@/src/components/ui/popover";
|
||||
import { useState } from "react";
|
||||
import { Trash2 } from "lucide-react";
|
||||
import { Trash } from "lucide-react";
|
||||
import {
|
||||
Tabs,
|
||||
TabsContent,
|
||||
@@ -156,7 +156,7 @@ export function DeactivateConfig({
|
||||
disabled={!hasAccess || config?.status !== "ACTIVE"}
|
||||
loading={isLoading}
|
||||
>
|
||||
<Trash2 className="h-5 w-5" />
|
||||
<Trash className="h-5 w-5" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
|
||||
@@ -33,8 +33,6 @@ import {
|
||||
type ModelParams,
|
||||
} from "@langfuse/shared";
|
||||
import { PromptDescription } from "@/src/features/prompts/components/prompt-description";
|
||||
import Link from "next/dist/client/link";
|
||||
import { ArrowTopRightIcon } from "@radix-ui/react-icons";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -43,8 +41,6 @@ import {
|
||||
SelectValue,
|
||||
} from "@/src/components/ui/select";
|
||||
import { TEMPLATES } from "@/src/ee/features/evals/components/templates";
|
||||
import { Label } from "@/src/components/ui/label";
|
||||
import { useHasAccess } from "@/src/features/rbac/utils/checkAccess";
|
||||
import { usePostHogClientCapture } from "@/src/features/posthog-analytics/usePostHogClientCapture";
|
||||
import { getFinalModelParams } from "@/src/ee/utils/getFinalModelParams";
|
||||
|
||||
@@ -439,10 +435,6 @@ export const InnerEvalTemplateForm = (props: {
|
||||
availableModels={[...evalLLMModels]}
|
||||
formDisabled={!props.isEditing}
|
||||
/>
|
||||
<LLMApiKeyComponent
|
||||
projectId={props.projectId}
|
||||
modelParams={modelParams}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -465,78 +457,6 @@ export const InnerEvalTemplateForm = (props: {
|
||||
);
|
||||
};
|
||||
|
||||
export const LLMApiKeyComponent = (p: {
|
||||
projectId: string;
|
||||
modelParams: UIModelParams;
|
||||
}) => {
|
||||
const hasAccess = useHasAccess({
|
||||
projectId: p.projectId,
|
||||
scope: "llmApiKeys:read",
|
||||
});
|
||||
|
||||
if (!hasAccess) {
|
||||
return (
|
||||
<div>
|
||||
<Label>API key</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
LLM API Key only visible to Owner and Admin roles.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const apiKeys = api.llmApiKey.all.useQuery({
|
||||
projectId: p.projectId,
|
||||
});
|
||||
|
||||
if (apiKeys.isLoading) {
|
||||
return (
|
||||
<div>
|
||||
<Label>API key</Label>
|
||||
<p className="text-sm text-muted-foreground">Loading...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const getModelProvider = (model: string) => {
|
||||
return evalLLMModels.find((m) => m.model.value === model)?.provider.value;
|
||||
};
|
||||
|
||||
const getApiKeyForModel = (model: string) => {
|
||||
const modelProvider = getModelProvider(model);
|
||||
return apiKeys.data?.data.find((k) => k.provider === modelProvider);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Label>API key</Label>
|
||||
<div>
|
||||
{getApiKeyForModel(p.modelParams.model.value) ? (
|
||||
<span className="mr-2 rounded-sm bg-input p-1 text-xs">
|
||||
{getApiKeyForModel(p.modelParams.model.value)?.displaySecretKey}
|
||||
</span>
|
||||
) : undefined}
|
||||
</div>
|
||||
{/* Custom form message to include a link to the already existing prompt */}
|
||||
{!getApiKeyForModel(p.modelParams.model.value) ? (
|
||||
<div className="flex flex-col text-sm font-medium text-destructive">
|
||||
{"No LLM API key found."}
|
||||
|
||||
<Link
|
||||
href={`/project/${p.projectId}/settings`}
|
||||
className="flex flex-row"
|
||||
>
|
||||
Create a new API key here. <ArrowTopRightIcon />
|
||||
</Link>
|
||||
</div>
|
||||
) : undefined}
|
||||
<p className="text-sm text-muted-foreground">
|
||||
The API key is used for each evaluation and will incur costs.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function getModelParamsWithEnabledFlag(
|
||||
evalPreFill?: EvalTemplateFormPreFill,
|
||||
): UIModelParams {
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import { Terminal } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createEmptyMessage } from "@/src/components/ChatMessages/utils/createEmptyMessage";
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
import usePlaygroundCache from "@/src/ee/features/playground/page/hooks/usePlaygroundCache";
|
||||
import { type PlaygroundCache } from "@/src/ee/features/playground/page/types";
|
||||
import { getIsCloudEnvironment } from "@/src/ee/utils/getIsCloudEnvironment";
|
||||
import { usePostHogClientCapture } from "@/src/features/posthog-analytics/usePostHogClientCapture";
|
||||
import { PromptType } from "@/src/features/prompts/server/utils/validation";
|
||||
import useProjectIdFromURL from "@/src/hooks/useProjectIdFromURL";
|
||||
import {
|
||||
ChatMessageRole,
|
||||
supportedModels as playgroundSupportedModels,
|
||||
type Observation,
|
||||
type Prompt,
|
||||
supportedModels as playgroundSupportedModels,
|
||||
type UIModelParams,
|
||||
ZodModelConfig,
|
||||
} from "@langfuse/shared";
|
||||
import { useEffect, useState } from "react";
|
||||
import { PromptType } from "@/src/features/prompts/server/utils/validation";
|
||||
import { ChatMessageListSchema } from "@/src/features/prompts/components/NewPromptForm/validation";
|
||||
import { createEmptyMessage } from "@/src/components/ChatMessages/utils/createEmptyMessage";
|
||||
import { getIsCloudEnvironment } from "@/src/ee/utils/getIsCloudEnvironment";
|
||||
|
||||
type JumpToPlaygroundButtonProps = (
|
||||
| {
|
||||
@@ -76,9 +76,19 @@ export const JumpToPlaygroundButton: React.FC<JumpToPlaygroundButtonProps> = (
|
||||
);
|
||||
};
|
||||
|
||||
const ParsedChatMessageListSchema = z.array(
|
||||
z.object({
|
||||
role: z.nativeEnum(ChatMessageRole),
|
||||
content: z.union([
|
||||
z.string(),
|
||||
z.any().transform((v) => JSON.stringify(v, null, 2)),
|
||||
]),
|
||||
}),
|
||||
);
|
||||
|
||||
const parsePrompt = (prompt: Prompt): PlaygroundCache => {
|
||||
if (prompt.type === PromptType.Chat) {
|
||||
const parsedMessages = ChatMessageListSchema.safeParse(prompt.prompt);
|
||||
const parsedMessages = ParsedChatMessageListSchema.safeParse(prompt.prompt);
|
||||
|
||||
return parsedMessages.success ? { messages: parsedMessages.data } : null;
|
||||
} else {
|
||||
@@ -109,14 +119,16 @@ const parseGeneration = (generation: Observation): PlaygroundCache => {
|
||||
}
|
||||
|
||||
if (typeof input === "object") {
|
||||
const parsedMessages = ChatMessageListSchema.safeParse(input);
|
||||
const parsedMessages = ParsedChatMessageListSchema.safeParse(input);
|
||||
|
||||
if (parsedMessages.success)
|
||||
return { messages: parsedMessages.data, modelParams };
|
||||
}
|
||||
|
||||
if (typeof input === "object" && "messages" in input) {
|
||||
const parsedMessages = ChatMessageListSchema.safeParse(input["messages"]);
|
||||
const parsedMessages = ParsedChatMessageListSchema.safeParse(
|
||||
input["messages"],
|
||||
);
|
||||
|
||||
if (parsedMessages.success)
|
||||
return { messages: parsedMessages.data, modelParams };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type ChangeEvent } from "react";
|
||||
import { CheckCircle2, Circle, Trash2Icon } from "lucide-react";
|
||||
import { CheckCircle2, Circle, TrashIcon } from "lucide-react";
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
import { Textarea } from "@/src/components/ui/textarea";
|
||||
import { type PromptVariable } from "@langfuse/shared";
|
||||
@@ -46,7 +46,7 @@ export const PromptVariableComponent: React.FC<{
|
||||
onClick={handleDeleteVariable}
|
||||
className="p-0"
|
||||
>
|
||||
{!isUsed && <Trash2Icon size={16} />}
|
||||
{!isUsed && <TrashIcon size={16} />}
|
||||
</Button>
|
||||
</div>
|
||||
<Textarea
|
||||
|
||||
@@ -49,8 +49,7 @@ export const SaveToPromptButton: React.FC = () => {
|
||||
)
|
||||
.data?.prompts.filter((prompt) => prompt.type === PromptType.Chat)
|
||||
.map((prompt) => ({
|
||||
label:
|
||||
prompt.name.slice(0, 20) + (prompt.name.length > 25 ? "..." : ""),
|
||||
label: prompt.name,
|
||||
value: prompt.id,
|
||||
})) ?? [];
|
||||
|
||||
@@ -126,7 +125,9 @@ export const SaveToPromptButton: React.FC = () => {
|
||||
: "opacity-0",
|
||||
)}
|
||||
/>
|
||||
{promptName.label}
|
||||
<span className="overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{promptName.label}
|
||||
</span>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandList>
|
||||
|
||||
@@ -14,6 +14,7 @@ import useCommandEnter from "@/src/ee/features/playground/page/hooks/useCommandE
|
||||
import usePlaygroundCache from "@/src/ee/features/playground/page/hooks/usePlaygroundCache";
|
||||
import { getFinalModelParams } from "@/src/ee/utils/getFinalModelParams";
|
||||
import { usePostHogClientCapture } from "@/src/features/posthog-analytics/usePostHogClientCapture";
|
||||
import useProjectIdFromURL from "@/src/hooks/useProjectIdFromURL";
|
||||
import { extractVariables } from "@/src/utils/string";
|
||||
import {
|
||||
ChatMessageRole,
|
||||
@@ -57,6 +58,7 @@ export const PlaygroundProvider: React.FC<PropsWithChildren> = ({
|
||||
children,
|
||||
}) => {
|
||||
const capture = usePostHogClientCapture();
|
||||
const projectId = useProjectIdFromURL();
|
||||
const { playgroundCache, setPlaygroundCache } = usePlaygroundCache();
|
||||
const [promptVariables, setPromptVariables] = useState<PromptVariable[]>([]);
|
||||
const [output, setOutput] = useState("");
|
||||
@@ -172,6 +174,7 @@ export const PlaygroundProvider: React.FC<PropsWithChildren> = ({
|
||||
}
|
||||
|
||||
const completionStream = getChatCompletionStream(
|
||||
projectId,
|
||||
finalMessages,
|
||||
modelParams,
|
||||
);
|
||||
@@ -263,10 +266,17 @@ export const PlaygroundProvider: React.FC<PropsWithChildren> = ({
|
||||
};
|
||||
|
||||
async function* getChatCompletionStream(
|
||||
projectId: string | undefined,
|
||||
messages: ChatMessageWithId[],
|
||||
modelParams: UIModelParams,
|
||||
) {
|
||||
if (!projectId) {
|
||||
console.error("Project ID is not set");
|
||||
return;
|
||||
}
|
||||
|
||||
const body = JSON.stringify({
|
||||
projectId,
|
||||
messages,
|
||||
modelParams: getFinalModelParams(modelParams),
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user