Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
375edf2f2c | ||
|
|
2171218bd8 | ||
|
|
b5ff3120dc | ||
|
|
c923b5c768 | ||
|
|
146805dae2 | ||
|
|
4904478c97 | ||
|
|
f2132b4db1 | ||
|
|
58f6603060 | ||
|
|
025c2e1661 | ||
|
|
69915ae392 | ||
|
|
20e3d3f119 | ||
|
|
7973976fea | ||
|
|
89e6342854 | ||
|
|
17ed4b95d4 | ||
|
|
6a69683589 | ||
|
|
39ef81aeef | ||
|
|
90201a972d | ||
|
|
9f5b4cf787 | ||
|
|
bddadaa8e7 | ||
|
|
3fd287e827 | ||
|
|
31500dfe03 | ||
|
|
b78d7a97aa | ||
|
|
b384eb5a2b | ||
|
|
51e10e464e | ||
|
|
54970c00ff | ||
|
|
5fd2919f99 | ||
|
|
e0b984b7c8 | ||
|
|
0dc66d99b3 | ||
|
|
64a7338dd7 | ||
|
|
87b968db84 | ||
|
|
77a55c3c78 | ||
|
|
2e5fc5bc45 | ||
|
|
84598cfbbf | ||
|
|
a1f28bff2b | ||
|
|
f5f5a0dc0b | ||
|
|
f75c970ea6 | ||
|
|
d053ccc5b1 | ||
|
|
710131525f | ||
|
|
cbde3b7998 | ||
|
|
20f59f5f0a | ||
|
|
846cb22744 | ||
|
|
6d0323e9c4 | ||
|
|
e0d2e1df40 | ||
|
|
b65afdba26 | ||
|
|
67759bb965 | ||
|
|
ef6ee43a81 | ||
|
|
982cc04663 | ||
|
|
6d4b085433 | ||
|
|
37dc8c83e0 | ||
|
|
745aeb1673 | ||
|
|
a4d90e99f6 | ||
|
|
72f95c705b | ||
|
|
6123cd140c | ||
|
|
adbb9474a4 | ||
|
|
53bfe18801 | ||
|
|
90fc4c3a37 | ||
|
|
bbc252ffde | ||
|
|
c43484cb75 | ||
|
|
fa2979bba4 | ||
|
|
942f70105a | ||
|
|
b49c152d85 | ||
|
|
d07cb39ef6 | ||
|
|
0e59668ad9 | ||
|
|
75c7d0e448 | ||
|
|
094599a424 | ||
|
|
eca06ca08e | ||
|
|
13295f997c | ||
|
|
a9048a64a1 | ||
|
|
fff0734af3 | ||
|
|
7dd6f31edb | ||
|
|
c32da046de | ||
|
|
bd704157ed | ||
|
+3 |
2eca294a67 | ||
|
|
e8efbb5e8f | ||
|
|
3f2e65ce61 | ||
|
|
218db7a5d7 | ||
|
|
69c6a74110 | ||
|
|
66f55e94fe | ||
|
|
0becc56477 | ||
|
|
6a6bbf7524 | ||
|
|
a369cf0941 | ||
|
|
cf4ed6bb2e | ||
|
|
be00006ae2 | ||
|
|
b88a41443a | ||
|
|
9843c79f57 | ||
|
|
8fcac23083 | ||
|
|
b171db153e | ||
|
|
fba07bdff0 | ||
|
|
ee7473855c | ||
|
|
53b90fed02 | ||
|
|
0ed2c53b6c | ||
|
|
2b6420b78d | ||
|
|
5e5f198aae | ||
|
|
01e4c57a55 | ||
|
|
1d7c0ae9ab | ||
|
|
25d428dc93 | ||
|
|
aedee34611 | ||
|
|
72d7a99ff2 | ||
|
|
8cb3d26e54 | ||
|
|
dee644d8cb | ||
|
|
d3388aac23 | ||
|
|
09f709627a | ||
|
|
69c730f1a0 | ||
|
|
33cfd9b4f9 | ||
|
|
3290a3381e | ||
|
|
cf40ce5093 | ||
|
|
9b53418780 | ||
|
|
e0568b0c5e | ||
|
|
d03ff3d6a9 | ||
|
|
689dc16307 | ||
|
|
32e6026f24 | ||
|
|
d9460e684d | ||
|
|
30726d6150 | ||
|
|
28adf5b129 | ||
|
|
dae07f5c93 | ||
|
|
6a18e6615c | ||
|
|
07c125bcc1 | ||
|
|
446fbe484c | ||
|
|
77733d7774 | ||
|
|
7119201dcf | ||
|
|
1354e0937f | ||
|
|
a2da9a51b3 | ||
|
|
3ce6c51009 | ||
|
|
f1564c3049 | ||
|
|
f2c764c1d7 | ||
|
|
71c7b30516 | ||
|
|
e639e866e1 | ||
|
|
304f9999dd | ||
|
|
bee927e159 | ||
|
|
288b6d4ff2 | ||
|
|
a021f99e99 | ||
|
|
2c3362de5c | ||
|
|
c6199bc931 | ||
|
|
07352dbf94 | ||
|
|
3dd795b095 | ||
|
|
c519fe1737 | ||
|
|
506d670625 | ||
|
|
4ee723d9ed | ||
|
|
d3f35d1f22 | ||
|
|
1a0a2b1b53 | ||
|
|
5bb5b105c4 | ||
|
|
7377a313e5 | ||
|
|
a1e7867a35 | ||
|
|
6081187466 | ||
|
|
adb40d1bb1 | ||
|
|
f1601bb38d | ||
|
|
466d7e32c5 | ||
|
|
819fa50f2e | ||
|
|
2d000cb826 | ||
|
|
d953606e2f | ||
|
|
3611fc3b4e | ||
|
|
fedfe24ec4 | ||
|
|
9842d842d8 | ||
|
|
6cd98d2cd8 | ||
|
|
23300f9db8 | ||
|
|
5427d3f74f | ||
|
|
a43fbc8a74 | ||
|
|
8d9c059572 | ||
|
|
37002f39bd | ||
|
|
c7de984fb1 | ||
|
|
1d3ad16710 | ||
|
|
17a358a1c2 | ||
|
|
7e76fbfcb4 | ||
|
|
aa5b5fb8a9 | ||
|
|
c084d19c2d | ||
|
|
23e765fc35 | ||
|
|
f60286f13d | ||
|
|
0f43240c35 | ||
|
|
dc4e94a68b | ||
|
|
fd98cd4cd7 | ||
|
|
a1f09a2e30 | ||
|
|
decac0cd5a | ||
|
|
17e600f65e | ||
|
|
0540828e21 | ||
|
|
4f9d7b0e60 | ||
|
|
a940fa40d9 | ||
|
|
ab2007a627 | ||
|
|
9b8868ac97 | ||
|
|
4f6e917cd7 | ||
|
|
da51c01f35 | ||
|
|
4aeea98b56 | ||
|
|
917e35a1b3 | ||
|
|
2e0a548e6f | ||
|
|
f9222c526f | ||
|
|
5cb61d6a96 | ||
|
|
463ada23ca | ||
|
|
b2d22a0720 | ||
|
|
fd1f1786da | ||
|
|
56598597df | ||
|
|
ea71c40ecb | ||
|
|
20cc20f908 | ||
|
|
6326a6d788 | ||
|
|
286f7ea29f | ||
|
|
700921f69f | ||
|
|
9e3379f05f | ||
|
|
bc178010c1 | ||
|
|
47a1cb24a3 | ||
|
|
312d97f509 | ||
|
|
7776462c9b | ||
|
|
aa8eabbc19 | ||
|
|
f4caacabdb | ||
|
|
7892ff521d | ||
|
|
45215cb4ff | ||
|
|
5404119473 | ||
|
|
7769cb9d2a | ||
|
|
0b68c410cf | ||
|
|
efa6fe7e6a | ||
|
|
16f41f809a | ||
|
|
a7d59d7bb2 | ||
|
|
291abf3205 | ||
|
|
5df5a7cd48 | ||
|
|
ac2936d316 | ||
|
|
0335dcd18d | ||
|
|
27cfbb14a1 | ||
|
|
4c5828dade | ||
|
|
2c9afcc58b | ||
|
|
d401d42377 | ||
|
|
322c767a33 | ||
|
|
53726d58bf | ||
|
|
63feb943a4 | ||
|
|
6379c7c54b | ||
|
|
69b3b81771 | ||
|
|
668d1b6933 | ||
|
|
d7069df433 | ||
|
|
83d3009331 | ||
|
|
688659c597 | ||
|
|
63ec00efe9 | ||
|
|
a11b5ffa92 | ||
|
|
72bf6a75c1 | ||
|
|
92b11374d9 | ||
|
|
828c3ff7be | ||
|
|
7b35cb0e99 | ||
|
|
8d5c445dc5 | ||
|
|
c752af2201 | ||
|
|
03f6204797 | ||
|
|
c33708e5db | ||
|
|
4c2c3d21c7 | ||
|
|
9a09fa2ad0 | ||
|
|
43e0644c64 | ||
|
|
c5be145a53 | ||
|
|
3a002f03d5 | ||
|
|
c785156220 | ||
|
|
6f8627ed86 | ||
|
|
6a7034a5ad | ||
|
|
29443bcdbd | ||
|
|
9f066a285f | ||
|
|
664bd3596e | ||
|
|
bad27d2625 | ||
|
|
4da02c1ebe | ||
|
|
65bd0bf5c1 | ||
|
|
b1ee21fc34 | ||
|
|
b894e8a620 | ||
|
|
aa39af94fb | ||
|
|
b1880ddb82 | ||
|
|
19e9da214e | ||
|
|
2e698c8ec0 | ||
|
|
46feb262ec | ||
|
|
23b830074a | ||
|
|
fa5908a749 | ||
|
|
26045d089c | ||
|
|
edfb15c010 | ||
|
|
1f76ae650f | ||
|
|
36dc608b58 | ||
|
|
bc0735729f | ||
|
|
88e553734e | ||
|
|
bd5495321e | ||
|
|
e8ebfb8e3e | ||
|
|
8c076b6b50 | ||
|
|
2cdc3cab7f | ||
|
|
729c2132af | ||
|
|
d3627bf225 | ||
|
|
05b87199ec | ||
|
|
a618d53451 | ||
|
|
9907c39861 | ||
|
|
4c8594e47a | ||
|
|
a69609229a | ||
|
|
217ae648d6 | ||
|
|
72e5b51398 | ||
|
|
0cbbf94568 | ||
|
|
61f4c3ac95 | ||
|
|
cc8f521741 | ||
|
|
540f8622e3 | ||
|
|
a7e3dec6f0 | ||
|
|
5760d51c08 | ||
|
|
59a75ce36c | ||
|
|
9a6a79f458 | ||
|
|
2598f44036 | ||
|
|
f7e7b132dd | ||
|
|
521be4e0b6 | ||
|
|
a13b5d0d32 | ||
|
|
ebe557881c | ||
|
|
5e8a9e3e80 | ||
|
|
ab115cdc19 | ||
|
|
18deee6434 | ||
|
|
977724a801 | ||
|
|
434c9be8a6 | ||
|
|
06bb3bbf43 | ||
|
|
736f4762bc | ||
|
|
4341b37ef3 | ||
|
|
95e07f2eb0 | ||
|
|
242e33c2f6 | ||
|
|
01aa0b9ecd | ||
|
|
3c0a4c863a | ||
|
|
d9954d14b1 | ||
|
|
7a2c5188cd | ||
|
|
c7f62805c4 | ||
|
|
c23d1521ab | ||
|
|
53b44454f6 | ||
|
|
8bf6044b24 | ||
|
|
a28877b9d9 | ||
|
|
902d8fc2f9 | ||
|
|
869851b974 | ||
|
|
f4118e451e | ||
|
|
a8471adcea | ||
|
|
2922387793 | ||
|
|
94124ac60c | ||
|
|
69c91c5988 | ||
|
|
dbb97ca511 | ||
|
|
3e23f0c58e | ||
|
|
e5d3659b64 | ||
|
|
7b6b8b176b | ||
|
|
98151fa706 | ||
|
|
0840ac1a9f | ||
|
|
7ad3818008 | ||
|
|
3cbe3bd1bc | ||
|
|
0781cfb194 | ||
|
|
8c40cfc97d | ||
|
|
6bc996806b | ||
|
|
b9e54d2a19 | ||
|
|
99fd7ca6ab | ||
|
|
3e083656fa | ||
|
|
ee80b102bc | ||
|
|
7dcd2ad30f | ||
|
|
43a98141a8 | ||
|
|
cf3505805d | ||
|
|
63fa75cc61 | ||
|
|
a02ef20deb | ||
|
|
2deb588a57 | ||
|
|
f04fd76f6c | ||
|
|
d022a07263 | ||
|
|
94e8f0b592 | ||
|
|
5c8e4d641a | ||
|
|
2eebca4e42 | ||
|
|
426d4b77b6 | ||
|
|
1e3f345f5b | ||
|
|
2d6e58136d | ||
|
|
cf15ee5066 | ||
|
|
816abfb59e | ||
|
|
702dd6ac27 | ||
|
|
14fbeb1788 | ||
|
|
fbf6110acf | ||
|
|
ef950ae381 |
+1
-1
@@ -1,4 +1,4 @@
|
||||
[codespell]
|
||||
skip = .git,*.pdf,*.svg,package-lock.json,*.prisma,pnpm-lock.yaml
|
||||
ignore-words-list = afterall,vertx,notIn,alue
|
||||
ignore-words-list = afterall,vertx,notIn,alue,allTime
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
description: Guidelines for positioning elements with banner awareness
|
||||
globs:
|
||||
- "**/*.tsx"
|
||||
- "**/*.jsx"
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
## Banner Height System
|
||||
|
||||
When positioning elements with `sticky`, `fixed`, or `absolute` that have a global reference point (like `top-0`), **always use `top-banner-offset` instead of `top-0`**.
|
||||
|
||||
### Why?
|
||||
The application can display system banners (payment issues, maintenance notices, etc.) that push content down. Using `top-banner-offset` ensures your positioned elements appear below the banner rather than being covered by it.
|
||||
|
||||
### Usage
|
||||
|
||||
**Bad:**
|
||||
```tsx
|
||||
<div className="fixed top-0 left-0 right-0">...</div>
|
||||
<div className="sticky top-0">...</div>
|
||||
<div className="absolute top-0">...</div>
|
||||
<div className="pt-0">...</div>
|
||||
```
|
||||
|
||||
**Good:**
|
||||
```tsx
|
||||
<div className="fixed top-banner-offset left-0 right-0">...</div>
|
||||
<div className="sticky top-banner-offset">...</div>
|
||||
<div className="absolute top-banner-offset">...</div>
|
||||
<div className="pt-banner-offset">...</div>
|
||||
```
|
||||
|
||||
### Additional Utilities
|
||||
|
||||
- `h-screen-with-banner` / `min-h-screen-with-banner` - Use these for full-height containers that should account for banner space
|
||||
- `pt-banner-offset` - Use for padding-top to offset content below the banner
|
||||
- The system uses CSS variables `--banner-height` and `--banner-offset` defined in `web/src/styles/globals.css`
|
||||
- Banner components dynamically update `--banner-height` using ResizeObserver to track their actual rendered height, ensuring accuracy across all screen sizes
|
||||
|
||||
### When NOT to use `top-banner-offset`
|
||||
|
||||
- Elements positioned relative to their parent container (not globally)
|
||||
- Elements that should intentionally appear at the very top of the viewport
|
||||
- Elements inside modals/dialogs that are already z-indexed above banners
|
||||
@@ -2,7 +2,7 @@
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:20-bookworm
|
||||
|
||||
# Install golang-migrate for database migrations
|
||||
RUN curl -L https://github.com/golang-migrate/migrate/releases/download/v4.18.3/migrate.linux-amd64.tar.gz | tar xvz && \
|
||||
RUN curl -L https://github.com/golang-migrate/migrate/releases/download/v4.19.0/migrate.linux-amd64.tar.gz | tar xvz && \
|
||||
chmod +x migrate && \
|
||||
mv migrate /usr/local/bin/migrate
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ REDIS_AUTH="bitnami"
|
||||
REDIS_CLUSTER_ENABLED="true"
|
||||
REDIS_CLUSTER_NODES="127.0.0.1:6370,127.0.0.1:6371,127.0.0.1:6372,127.0.0.1:6373,127.0.0.1:6374,127.0.0.1:6375"
|
||||
LANGFUSE_INGESTION_QUEUE_SHARD_COUNT=8
|
||||
LANGFUSE_OTEL_INGESTION_QUEUE_SHARD_COUNT=4
|
||||
LANGFUSE_TRACE_UPSERT_QUEUE_SHARD_COUNT=4
|
||||
|
||||
# openssl rand -hex 32 used only here
|
||||
@@ -84,7 +85,3 @@ ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
|
||||
# speeds up local development by not executing init scripts on server startup
|
||||
NEXT_PUBLIC_LANGFUSE_RUN_NEXT_INIT="false"
|
||||
|
||||
# Use the following settings to enforce running the new AMTs during the tests
|
||||
LANGFUSE_EXPERIMENT_INSERT_INTO_AGGREGATING_MERGE_TREES="true"
|
||||
LANGFUSE_EXPERIMENT_INSERT_INTO_TRACES_TABLE="false"
|
||||
LANGFUSE_EXPERIMENT_RETURN_NEW_RESULT="true"
|
||||
|
||||
+17
-1
@@ -33,6 +33,7 @@ SALT="salt"
|
||||
# Email
|
||||
EMAIL_FROM_ADDRESS="" # Defines the email address to use as the from address.
|
||||
SMTP_CONNECTION_URL="" # Defines the connection url for smtp server.
|
||||
CLOUD_CRM_EMAIL="" # Optional BCC address for usage threshold emails (e.g., for CRM integration like HubSpot)
|
||||
|
||||
# S3 Batch Exports
|
||||
LANGFUSE_S3_BATCH_EXPORT_ENABLED=true
|
||||
@@ -87,4 +88,19 @@ NEXT_PUBLIC_LANGFUSE_RUN_NEXT_INIT="false"
|
||||
# Slack credentials for development
|
||||
SLACK_CLIENT_ID=your_slack_client_id
|
||||
SLACK_CLIENT_SECRET=your_slack_client_secret
|
||||
SLACK_STATE_SECRET=your_slack_state_secret
|
||||
SLACK_STATE_SECRET=your_slack_state_secret
|
||||
|
||||
# Langfuse AI instance for tracing, prompts
|
||||
LANGFUSE_AI_FEATURES_PUBLIC_KEY="pk-lf-1234567890"
|
||||
LANGFUSE_AI_FEATURES_SECRET_KEY="sk-lf-1234567890"
|
||||
LANGFUSE_AI_FEATURES_HOST="http://localhost:3000"
|
||||
LANGFUSE_AI_FEATURES_PROJECT_ID=7a88fb47-b4e2-43b8-a06c-a5ce950dc53a
|
||||
|
||||
# Langfuse AI Bedrock credentials
|
||||
AWS_ACCESS_KEY_ID="A123456789"
|
||||
AWS_SECRET_ACCESS_KEY="SAK123456789"
|
||||
LANGFUSE_AWS_BEDROCK_REGION="eu-west-1"
|
||||
LANGFUSE_AWS_BEDROCK_MODEL="eu.anthropic.claude-3-haiku-20240307-v1:0"
|
||||
|
||||
# Events table migration
|
||||
LANGFUSE_ENABLE_EVENTS_TABLE_OBSERVATIONS=true
|
||||
|
||||
@@ -270,6 +270,14 @@ LANGFUSE_ENABLE_BLOB_STORAGE_FILE_LOG=true
|
||||
# Rate limiting
|
||||
# LANGFUSE_RATE_LIMITS_ENABLED=
|
||||
|
||||
# Free tier usage thresholds (Cloud deployments only)
|
||||
# Enable the queue consumer that monitors free tier usage (default: true, but requires cloud region)
|
||||
# QUEUE_CONSUMER_FREE_TIER_USAGE_THRESHOLD_QUEUE_IS_ENABLED=true
|
||||
# Enable enforcement: send emails and block orgs that exceed free tier limits (default: false)
|
||||
# LANGFUSE_FREE_TIER_USAGE_THRESHOLD_ENFORCEMENT_ENABLED=false
|
||||
# Optional BCC address for usage threshold emails (e.g., for CRM integration like HubSpot)
|
||||
# CLOUD_CRM_EMAIL=
|
||||
|
||||
# Stripe
|
||||
# STRIPE_SECRET_KEY=
|
||||
# STRIPE_WEBHOOK_SIGNING_SECRET=
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe your question
|
||||
description: Please describe the question you have as clear and concise as possible.
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: hosting
|
||||
attributes:
|
||||
label: Are you using Langfuse Cloud or self-host Langfuse?
|
||||
options:
|
||||
- Langfuse Cloud
|
||||
- Self-hosted Langfuse
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: I checked if there is already an [issue](https://github.com/langfuse/langfuse/issues) or [discussion](https://github.com/orgs/langfuse/discussions) for my question and asked the [Langfuse AI](https://langfuse.com/docs/ask-ai) for help.
|
||||
validations:
|
||||
required: true
|
||||
@@ -6,7 +6,7 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
service:
|
||||
description: 'Service to be deployed'
|
||||
description: "Service to be deployed"
|
||||
type: choice
|
||||
options:
|
||||
- all
|
||||
@@ -15,7 +15,7 @@ on:
|
||||
- worker
|
||||
required: true
|
||||
environment:
|
||||
description: 'Environment to deploy to'
|
||||
description: "Environment to deploy to"
|
||||
type: choice
|
||||
options:
|
||||
- staging
|
||||
|
||||
@@ -148,7 +148,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install golang-migrate for Clickhouse migrations
|
||||
run: |
|
||||
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.18.3/migrate.linux-amd64.tar.gz | tar xvz
|
||||
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.19.0/migrate.linux-amd64.tar.gz | tar xvz
|
||||
sudo mv migrate /usr/bin/migrate
|
||||
which migrate
|
||||
- uses: pnpm/action-setup@v3
|
||||
@@ -225,7 +225,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install golang-migrate for Clickhouse migrations
|
||||
run: |
|
||||
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.18.3/migrate.linux-amd64.tar.gz | tar xvz
|
||||
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.19.0/migrate.linux-amd64.tar.gz | tar xvz
|
||||
sudo mv migrate /usr/bin/migrate
|
||||
which migrate
|
||||
- uses: pnpm/action-setup@v3
|
||||
@@ -266,6 +266,20 @@ jobs:
|
||||
run: |
|
||||
pnpm run db:migrate
|
||||
pnpm --filter=shared ch:up
|
||||
- name: Download and extract ClickHouse client for dev-tables setup
|
||||
if: matrix.deploy-mode == ''
|
||||
run: |
|
||||
CH_VERSION="24.9.3.128"
|
||||
ARCH="amd64"
|
||||
wget "https://packages.clickhouse.com/deb/pool/main/c/clickhouse/clickhouse-common-static_${CH_VERSION}_${ARCH}.deb" -O clickhouse-client.deb
|
||||
|
||||
# The binary is usually located in ./usr/bin/clickhouse in the archive
|
||||
dpkg-deb -x clickhouse-client.deb ch_client_dir
|
||||
sudo cp ch_client_dir/usr/bin/clickhouse /usr/local/bin/
|
||||
- name: Setup Dev Tables
|
||||
if: matrix.deploy-mode == ''
|
||||
run: |
|
||||
pnpm --filter=shared ch:dev-tables
|
||||
- name: Build
|
||||
run: pnpm run build
|
||||
env:
|
||||
@@ -320,7 +334,7 @@ jobs:
|
||||
pnpm install
|
||||
- name: Install golang-migrate for Clickhouse migrations
|
||||
run: |
|
||||
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.18.3/migrate.linux-amd64.tar.gz | tar xvz
|
||||
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.19.0/migrate.linux-amd64.tar.gz | tar xvz
|
||||
sudo mv migrate /usr/bin/migrate
|
||||
which migrate
|
||||
- name: Load default env
|
||||
@@ -349,7 +363,80 @@ jobs:
|
||||
- name: Build
|
||||
run: pnpm --filter=worker... run build
|
||||
- name: run tests
|
||||
run: pnpm --filter=worker run test
|
||||
run: pnpm --filter=worker run test:exclude-llm-connections
|
||||
|
||||
test-worker-llm-connections:
|
||||
timeout-minutes: 20
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- pre-job
|
||||
if: needs.pre-job.outputs.should_skip != 'true'
|
||||
name: test-worker-llm-connections (node24, pg15)
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 9.5.0
|
||||
- name: Login to Docker Hub
|
||||
if: github.repository == 'langfuse/langfuse' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME_READ }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN_READ }}
|
||||
- name: Use Node.js 24
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: "pnpm"
|
||||
cache-dependency-path: "pnpm-lock.yaml"
|
||||
- name: install dependencies
|
||||
run: |
|
||||
pnpm install
|
||||
- name: Install golang-migrate for Clickhouse migrations
|
||||
run: |
|
||||
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.19.0/migrate.linux-amd64.tar.gz | tar xvz
|
||||
sudo mv migrate /usr/bin/migrate
|
||||
which migrate
|
||||
- name: Load default env
|
||||
run: |
|
||||
cp .env.dev.example .env
|
||||
cp .env.dev.example web/.env
|
||||
cp .env.dev.example worker/.env
|
||||
- name: Run + migrate
|
||||
run: |
|
||||
docker compose -f docker-compose.dev.yml up -d
|
||||
sleep 5 # Wait for PostgreSQL to accept connections
|
||||
docker compose ps
|
||||
env:
|
||||
POSTGRES_VERSION: 15
|
||||
- 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
|
||||
- name: Seed DB
|
||||
run: |
|
||||
pnpm run db:migrate
|
||||
pnpm run db:seed
|
||||
pnpm run --filter=shared ch:up
|
||||
- name: Build
|
||||
run: pnpm --filter=worker... run build
|
||||
- name: run llm connection tests
|
||||
run: pnpm --filter=worker run test:llm-connections-only
|
||||
env:
|
||||
LANGFUSE_LLM_CONNECTION_OPENAI_KEY: ${{ secrets.LANGFUSE_LLM_CONNECTION_OPENAI_KEY }}
|
||||
LANGFUSE_LLM_CONNECTION_ANTHROPIC_KEY: ${{ secrets.LANGFUSE_LLM_CONNECTION_ANTHROPIC_KEY }}
|
||||
LANGFUSE_LLM_CONNECTION_AZURE_KEY: ${{ secrets.LANGFUSE_LLM_CONNECTION_AZURE_KEY }}
|
||||
LANGFUSE_LLM_CONNECTION_AZURE_BASE_URL: ${{ secrets.LANGFUSE_LLM_CONNECTION_AZURE_BASE_URL }}
|
||||
LANGFUSE_LLM_CONNECTION_AZURE_MODEL: ${{ secrets.LANGFUSE_LLM_CONNECTION_AZURE_MODEL }}
|
||||
LANGFUSE_LLM_CONNECTION_BEDROCK_ACCESS_KEY_ID: ${{ secrets.LANGFUSE_LLM_CONNECTION_BEDROCK_ACCESS_KEY_ID }}
|
||||
LANGFUSE_LLM_CONNECTION_BEDROCK_SECRET_ACCESS_KEY: ${{ secrets.LANGFUSE_LLM_CONNECTION_BEDROCK_SECRET_ACCESS_KEY }}
|
||||
LANGFUSE_LLM_CONNECTION_BEDROCK_REGION: ${{ secrets.LANGFUSE_LLM_CONNECTION_BEDROCK_REGION }}
|
||||
LANGFUSE_LLM_CONNECTION_VERTEXAI_KEY: ${{ secrets.LANGFUSE_LLM_CONNECTION_VERTEXAI_KEY }}
|
||||
LANGFUSE_LLM_CONNECTION_GOOGLEAISTUDIO_KEY: ${{ secrets.LANGFUSE_LLM_CONNECTION_GOOGLEAISTUDIO_KEY }}
|
||||
|
||||
e2e-tests:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -381,7 +468,7 @@ jobs:
|
||||
cp .env.dev.example web/.env
|
||||
- name: Install golang-migrate for Clickhouse migrations
|
||||
run: |
|
||||
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.18.3/migrate.linux-amd64.tar.gz | tar xvz
|
||||
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.19.0/migrate.linux-amd64.tar.gz | tar xvz
|
||||
sudo mv migrate /usr/bin/migrate
|
||||
which migrate
|
||||
- name: Run + migrate
|
||||
@@ -432,13 +519,12 @@ jobs:
|
||||
node-version: 24
|
||||
cache: "pnpm"
|
||||
cache-dependency-path: "pnpm-lock.yaml"
|
||||
|
||||
- name: install dependencies
|
||||
run: |
|
||||
pnpm install
|
||||
- name: Install golang-migrate for Clickhouse migrations
|
||||
run: |
|
||||
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.18.3/migrate.linux-amd64.tar.gz | tar xvz
|
||||
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.19.0/migrate.linux-amd64.tar.gz | tar xvz
|
||||
sudo mv migrate /usr/bin/migrate
|
||||
which migrate
|
||||
- name: Load default env
|
||||
@@ -486,6 +572,7 @@ jobs:
|
||||
prettier-check,
|
||||
tests-web-sync,
|
||||
tests-worker,
|
||||
test-worker-llm-connections,
|
||||
e2e-tests,
|
||||
test-docker-build,
|
||||
e2e-server-tests,
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
name: SDK API Spec Generation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'fern/**'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: sdk-api-spec-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
generate-sdk-api-specs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "24"
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Generate SDK specs
|
||||
env:
|
||||
FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
|
||||
run: npx fern-api generate --api server --force
|
||||
|
||||
- name: Update Python SDK
|
||||
env:
|
||||
GH_ACCESS_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
|
||||
run: |
|
||||
# Clone langfuse-python repository
|
||||
git clone https://github.com/langfuse/langfuse-python.git ../langfuse-python
|
||||
cd ../langfuse-python
|
||||
|
||||
git config user.name "langfuse-bot"
|
||||
git config user.email "langfuse-bot@langfuse.com"
|
||||
echo "$GH_ACCESS_TOKEN" | gh auth login --with-token
|
||||
gh auth setup-git
|
||||
|
||||
# Delete existing API contents
|
||||
rm -rf langfuse/api/*
|
||||
|
||||
# Copy generated Python SDK files
|
||||
cp -r ../langfuse/generated/python/* langfuse/api/
|
||||
|
||||
# Remove unnecessary integration tests created by Fern
|
||||
# (could not find the right option to prevent this in generation step)
|
||||
rm -rf langfuse/api/tests
|
||||
|
||||
# Install poetry and format
|
||||
pip install poetry
|
||||
poetry install --all-extras
|
||||
poetry run ruff format langfuse/api
|
||||
|
||||
# Check for changes and show diff for debugging
|
||||
if git diff --quiet; then
|
||||
echo "No changes detected in Python SDK"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Close existing api-spec-bot PRs
|
||||
gh pr list --author langfuse-bot --state open --json number --jq '.[].number' | xargs -I {} gh pr close {}
|
||||
|
||||
# Get the GitHub username of the original commit author
|
||||
cd ../langfuse
|
||||
ORIGINAL_AUTHOR=$(gh api repos/langfuse/langfuse/commits/${GITHUB_SHA} --jq '.author.login')
|
||||
cd ../langfuse-python
|
||||
|
||||
# Create new branch and push changes
|
||||
BRANCH_NAME="api-spec-bot-${GITHUB_SHA::7}"
|
||||
git checkout -b "$BRANCH_NAME"
|
||||
git add .
|
||||
git commit -m "feat(api): update API spec from langfuse/langfuse ${GITHUB_SHA::7}"
|
||||
git push origin "$BRANCH_NAME"
|
||||
|
||||
# Create PR with original author as reviewer
|
||||
gh pr create --title "feat(api): update API spec from langfuse/langfuse ${GITHUB_SHA::7}" --body "" --reviewer "$ORIGINAL_AUTHOR"
|
||||
|
||||
- name: Update TypeScript SDK
|
||||
env:
|
||||
GH_ACCESS_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
|
||||
run: |
|
||||
# Clone langfuse-js repository
|
||||
git clone https://github.com/langfuse/langfuse-js.git ../langfuse-js
|
||||
cd ../langfuse-js
|
||||
|
||||
# Configure git
|
||||
git config user.name "langfuse-bot"
|
||||
git config user.email "langfuse-bot@langfuse.com"
|
||||
echo "$GH_ACCESS_TOKEN" | gh auth login --with-token
|
||||
gh auth setup-git
|
||||
|
||||
# Delete existing API contents
|
||||
rm -rf packages/core/src/api/*
|
||||
|
||||
# Copy generated TypeScript SDK files
|
||||
cp -r ../langfuse/generated/typescript/* packages/core/src/api/
|
||||
|
||||
# Patch compilation error inducing output
|
||||
# Without this patch, the JS SDK will not build due to
|
||||
# "Error: packages/core build: src/api/core/auth/index.ts(1,10): error TS1205: Re-exporting a type when 'isolatedModules' is enabled requires using 'export type'."
|
||||
if [ -f "packages/core/src/api/core/auth/index.ts" ]; then
|
||||
if grep -q '^export { AuthProvider } from "./AuthProvider.js";$' packages/core/src/api/core/auth/index.ts; then
|
||||
sed -i '1s/^export { AuthProvider }/export { type AuthProvider }/' packages/core/src/api/core/auth/index.ts
|
||||
fi
|
||||
fi
|
||||
|
||||
# Install dependencies and format
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
pnpm format
|
||||
|
||||
# Check for changes and show diff for debugging
|
||||
if git diff --quiet; then
|
||||
echo "No changes detected in TypeScript SDK"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Close existing api-spec-bot PRs
|
||||
gh pr list --author langfuse-bot --state open --json number --jq '.[].number' | xargs -I {} gh pr close {}
|
||||
|
||||
# Get the GitHub username of the original commit author
|
||||
cd ../langfuse
|
||||
ORIGINAL_AUTHOR=$(gh api repos/langfuse/langfuse/commits/${GITHUB_SHA} --jq '.author.login')
|
||||
cd ../langfuse-js
|
||||
|
||||
# Create new branch and push changes
|
||||
BRANCH_NAME="api-spec-bot-${GITHUB_SHA::7}"
|
||||
git checkout -b "$BRANCH_NAME"
|
||||
git add .
|
||||
git commit -m "feat(api): update API spec from langfuse/langfuse ${GITHUB_SHA::7}" --no-verify
|
||||
git push origin "$BRANCH_NAME"
|
||||
|
||||
# Create PR with original author as reviewer
|
||||
gh pr create --title "feat(api): update API spec from langfuse/langfuse ${GITHUB_SHA::7}" --body "" --reviewer "$ORIGINAL_AUTHOR"
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
name: "Validate PR Title"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "**"
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- synchronize
|
||||
- reopened
|
||||
|
||||
jobs:
|
||||
validate-pr-title:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
statuses: write
|
||||
pull-requests: read
|
||||
steps:
|
||||
- name: Validate PR title follows conventional commits
|
||||
uses: amannn/action-semantic-pull-request@v6
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
# Configure the types of commits allowed
|
||||
types: |
|
||||
feat
|
||||
fix
|
||||
docs
|
||||
style
|
||||
refactor
|
||||
perf
|
||||
test
|
||||
build
|
||||
ci
|
||||
chore
|
||||
revert
|
||||
security
|
||||
# Allow scopes (optional)
|
||||
requireScope: false
|
||||
# Allow any scope (not just the ones listed above)
|
||||
validateSingleCommit: false
|
||||
ignoreLabels: |
|
||||
bot
|
||||
ignore-semantic-pull-request
|
||||
+10
-1
@@ -35,6 +35,10 @@ A good first step is to search for open [issues](https://github.com/langfuse/lan
|
||||
|
||||
## Project Overview
|
||||
|
||||
We recommend checking out DeepWiki to familiarize yourself with the project:
|
||||
|
||||
[](https://deepwiki.com/langfuse/langfuse)
|
||||
|
||||
### Technologies we use
|
||||
|
||||
- Application (this repository)
|
||||
@@ -111,12 +115,16 @@ Requirements
|
||||
- Node.js 24 as specified in the [.nvmrc](.nvmrc)
|
||||
- Pnpm v.9.5.0
|
||||
- Docker to run the database locally
|
||||
- Clickhouse client
|
||||
|
||||
**Note:** You can also simply run Langfuse in a **GitHub Codespace** via the provided devcontainer. To do this, click on the green "Code" button in the top right corner of the repository and select "Open with Codespaces".
|
||||
|
||||
**Steps**
|
||||
|
||||
1. Install [golang-migrate](https://github.com/golang-migrate/migrate/tree/master/cmd/migrate#migrate-cli) as CLI
|
||||
1. Install development dependencies:
|
||||
- [golang-migrate](https://github.com/golang-migrate/migrate/tree/master/cmd/migrate#migrate-cli) as CLI
|
||||
- [clickhouse binary](https://clickhouse.com/docs/install) on macOS with brew: `brew install --cask clickhouse`
|
||||
|
||||
2. Fork the repository and clone it locally
|
||||
|
||||
```bash
|
||||
@@ -416,3 +424,4 @@ npx fern-api generate --api organizations # for the organizations API
|
||||
Langfuse is MIT licensed, except for `ee/` folder. See [LICENSE](LICENSE) and [docs](https://langfuse.com/docs/open-source) for more details.
|
||||
|
||||
When contributing to the Langfuse codebase, you need to agree to the [Contributor License Agreement](https://cla-assistant.io/langfuse/langfuse). You only need to do this once and the CLA bot will remind you if you haven't signed it yet.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||
<img width="4856" height="1000" alt="github-banner" src="https://github.com/user-attachments/assets/6f435ef3-1194-4e26-87af-aa13826bbb5f" />
|
||||
|
||||
<div align="center">
|
||||
<div>
|
||||
@@ -60,6 +60,8 @@
|
||||
<img alt="Issues closed" src="https://img.shields.io/github/issues-search?query=repo%3Alangfuse%2Flangfuse%20is%3Aclosed&label=issues%20closed&labelColor=%20%237d89b0&color=%20%235d6b98"></a>
|
||||
<a href="https://github.com/langfuse/langfuse/discussions/" target="_blank">
|
||||
<img alt="Discussion posts" src="https://img.shields.io/github/discussions/langfuse/langfuse?labelColor=%20%239b8afb&color=%20%237a5af8"></a>
|
||||
<a href="https://deepwiki.com/langfuse/langfuse" target="_blank">
|
||||
<img alt="Ask DeepWiki" src="https://deepwiki.com/badge.svg"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -76,7 +78,7 @@ Langfuse is an **open source LLM engineering** platform. It helps teams collabor
|
||||
|
||||
## ✨ Core Features
|
||||
|
||||

|
||||
<img width="4856" height="1944" alt="Langfuse Overview" src="https://github.com/user-attachments/assets/5dac68ef-d546-49fb-b06f-cfafc19282e3" />
|
||||
|
||||
- [LLM Application Observability](https://langfuse.com/docs/tracing): Instrument your app and start ingesting traces to Langfuse, thereby tracking LLM calls and other relevant logic in your app such as retrieval, embedding, or agent actions. Inspect and debug complex logs and user sessions. Try the interactive [demo](https://langfuse.com/docs/demo) to see this in action.
|
||||
|
||||
@@ -92,7 +94,7 @@ Langfuse is an **open source LLM engineering** platform. It helps teams collabor
|
||||
|
||||
## 📦 Deploy Langfuse
|
||||
|
||||

|
||||
<img width="4856" height="1322" alt="Langfuse Deployment Options" src="https://github.com/user-attachments/assets/98f020c7-7a20-4264-a201-65c41a52a5d5" />
|
||||
|
||||
### Langfuse Cloud
|
||||
|
||||
@@ -127,7 +129,7 @@ See [self-hosting documentation](https://langfuse.com/self-hosting) to learn mor
|
||||
|
||||
## 🔌 Integrations
|
||||
|
||||

|
||||
<img width="4856" height="1322" alt="github-integrations" src="https://github.com/user-attachments/assets/e41ea0fb-742d-41ce-bf94-1d4fb95750cd" />
|
||||
|
||||
### Main Integrations:
|
||||
|
||||
@@ -215,7 +217,7 @@ main()
|
||||
|
||||
See your language model calls and other application logic in Langfuse.
|
||||
|
||||

|
||||
<img width="1787" height="674" alt="Example trace in Langfuse" src="https://github.com/user-attachments/assets/f796eb78-dfb5-4570-b236-bdb4b67d4d55" />
|
||||
|
||||
_[Public example trace in Langfuse](https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/traces/2cec01e3-3dc2-472f-afcf-3b968cf0c1f4?timestamp=2025-02-10T14%3A27%3A30.275Z&observation=cb5ff844-07ef-41e6-b8e2-6c64344bc13b)_
|
||||
|
||||
@@ -268,52 +270,105 @@ This repository is MIT licensed, except for the `ee` folders. See [LICENSE](LICE
|
||||
|
||||
Top open-source Python projects that use Langfuse, ranked by stars ([Source](https://github.com/langfuse/langfuse-docs/blob/main/components-mdx/dependents)):
|
||||
|
||||
| Repository | Stars |
|
||||
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----: |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/127165244?s=40&v=4" width="20" height="20" alt=""> [langgenius](https://github.com/langgenius) / [dify](https://github.com/langgenius/dify) | 54865 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/158137808?s=40&v=4" width="20" height="20" alt=""> [open-webui](https://github.com/open-webui) / [open-webui](https://github.com/open-webui/open-webui) | 51531 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/131470832?s=40&v=4" width="20" height="20" alt=""> [lobehub](https://github.com/lobehub) / [lobe-chat](https://github.com/lobehub/lobe-chat) | 49003 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/85702467?s=40&v=4" width="20" height="20" alt=""> [langflow-ai](https://github.com/langflow-ai) / [langflow](https://github.com/langflow-ai/langflow) | 39093 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/130722866?s=40&v=4" width="20" height="20" alt=""> [run-llama](https://github.com/run-llama) / [llama_index](https://github.com/run-llama/llama_index) | 37368 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/139558948?s=40&v=4" width="20" height="20" alt=""> [chatchat-space](https://github.com/chatchat-space) / [Langchain-Chatchat](https://github.com/chatchat-space/Langchain-Chatchat) | 32486 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/128289781?s=40&v=4" width="20" height="20" alt=""> [FlowiseAI](https://github.com/FlowiseAI) / [Flowise](https://github.com/FlowiseAI/Flowise) | 32448 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/31035808?s=40&v=4" width="20" height="20" alt=""> [mindsdb](https://github.com/mindsdb) / [mindsdb](https://github.com/mindsdb/mindsdb) | 26931 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/119600397?s=40&v=4" width="20" height="20" alt=""> [twentyhq](https://github.com/twentyhq) / [twenty](https://github.com/twentyhq/twenty) | 24195 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/60330232?s=40&v=4" width="20" height="20" alt=""> [PostHog](https://github.com/PostHog) / [posthog](https://github.com/PostHog/posthog) | 22618 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/121462774?s=40&v=4" width="20" height="20" alt=""> [BerriAI](https://github.com/BerriAI) / [litellm](https://github.com/BerriAI/litellm) | 15151 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/179202840?s=40&v=4" width="20" height="20" alt=""> [mediar-ai](https://github.com/mediar-ai) / [screenpipe](https://github.com/mediar-ai/screenpipe) | 11037 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/105877416?s=40&v=4" width="20" height="20" alt=""> [formbricks](https://github.com/formbricks) / [formbricks](https://github.com/formbricks/formbricks) | 9386 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/76263028?s=40&v=4" width="20" height="20" alt=""> [anthropics](https://github.com/anthropics) / [courses](https://github.com/anthropics/courses) | 8385 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/78410652?s=40&v=4" width="20" height="20" alt=""> [GreyDGL](https://github.com/GreyDGL) / [PentestGPT](https://github.com/GreyDGL/PentestGPT) | 7374 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/152537519?s=40&v=4" width="20" height="20" alt=""> [superagent-ai](https://github.com/superagent-ai) / [superagent](https://github.com/superagent-ai/superagent) | 5391 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/137907881?s=40&v=4" width="20" height="20" alt=""> [promptfoo](https://github.com/promptfoo) / [promptfoo](https://github.com/promptfoo/promptfoo) | 4976 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/157326433?s=40&v=4" width="20" height="20" alt=""> [onlook-dev](https://github.com/onlook-dev) / [onlook](https://github.com/onlook-dev/onlook) | 4141 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/7250217?s=40&v=4" width="20" height="20" alt=""> [Canner](https://github.com/Canner) / [WrenAI](https://github.com/Canner/WrenAI) | 2526 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/11855343?s=40&v=4" width="20" height="20" alt=""> [pingcap](https://github.com/pingcap) / [autoflow](https://github.com/pingcap/autoflow) | 2061 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/85268109?s=40&v=4" width="20" height="20" alt=""> [MLSysOps](https://github.com/MLSysOps) / [MLE-agent](https://github.com/MLSysOps/MLE-agent) | 1161 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/158137808?s=40&v=4" width="20" height="20" alt=""> [open-webui](https://github.com/open-webui) / [pipelines](https://github.com/open-webui/pipelines) | 1100 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/18422723?s=40&v=4" width="20" height="20" alt=""> [alishobeiri](https://github.com/alishobeiri) / [thread](https://github.com/alishobeiri/thread) | 1074 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/125468716?s=40&v=4" width="20" height="20" alt=""> [topoteretes](https://github.com/topoteretes) / [cognee](https://github.com/topoteretes/cognee) | 971 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/188657705?s=40&v=4" width="20" height="20" alt=""> [bRAGAI](https://github.com/bRAGAI) / [bRAG-langchain](https://github.com/bRAGAI/bRAG-langchain) | 823 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/169500408?s=40&v=4" width="20" height="20" alt=""> [opslane](https://github.com/opslane) / [opslane](https://github.com/opslane/opslane) | 677 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/151867818?s=40&v=4" width="20" height="20" alt=""> [dynamiq-ai](https://github.com/dynamiq-ai) / [dynamiq](https://github.com/dynamiq-ai/dynamiq) | 639 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/48585267?s=40&v=4" width="20" height="20" alt=""> [theopenconversationkit](https://github.com/theopenconversationkit) / [tock](https://github.com/theopenconversationkit/tock) | 514 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/20493493?s=40&v=4" width="20" height="20" alt=""> [andysingal](https://github.com/andysingal) / [llm-course](https://github.com/andysingal/llm-course) | 394 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/132396805?s=40&v=4" width="20" height="20" alt=""> [phospho-app](https://github.com/phospho-app) / [phospho](https://github.com/phospho-app/phospho) | 384 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/178644984?s=40&v=4" width="20" height="20" alt=""> [sentient-engineering](https://github.com/sentient-engineering) / [agent-q](https://github.com/sentient-engineering/agent-q) | 370 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/168552753?s=40&v=4" width="20" height="20" alt=""> [sql-agi](https://github.com/sql-agi) / [DB-GPT](https://github.com/sql-agi/DB-GPT) | 324 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/60330232?s=40&v=4" width="20" height="20" alt=""> [PostHog](https://github.com/PostHog) / [posthog-foss](https://github.com/PostHog/posthog-foss) | 305 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/154247157?s=40&v=4" width="20" height="20" alt=""> [vespperhq](https://github.com/vespperhq) / [vespper](https://github.com/vespperhq/vespper) | 304 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/185116535?s=40&v=4" width="20" height="20" alt=""> [block](https://github.com/block) / [goose](https://github.com/block/goose) | 295 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/609489?s=40&v=4" width="20" height="20" alt=""> [aorwall](https://github.com/aorwall) / [moatless-tools](https://github.com/aorwall/moatless-tools) | 291 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/2357342?s=40&v=4" width="20" height="20" alt=""> [dmayboroda](https://github.com/dmayboroda) / [minima](https://github.com/dmayboroda/minima) | 221 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/66303003?s=40&v=4" width="20" height="20" alt=""> [RobotecAI](https://github.com/RobotecAI) / [rai](https://github.com/RobotecAI/rai) | 172 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/148684274?s=40&v=4" width="20" height="20" alt=""> [i-am-alice](https://github.com/i-am-alice) / [3rd-devs](https://github.com/i-am-alice/3rd-devs) | 148 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/171735272?s=40&v=4" width="20" height="20" alt=""> [8090-inc](https://github.com/8090-inc) / [xrx-sample-apps](https://github.com/8090-inc/xrx-sample-apps) | 138 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/104478511?s=40&v=4" width="20" height="20" alt=""> [babelcloud](https://github.com/babelcloud) / [LLM-RGB](https://github.com/babelcloud/LLM-RGB) | 135 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/15125613?s=40&v=4" width="20" height="20" alt=""> [souzatharsis](https://github.com/souzatharsis) / [tamingLLMs](https://github.com/souzatharsis/tamingLLMs) | 129 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/169401942?s=40&v=4" width="20" height="20" alt=""> [LibreChat-AI](https://github.com/LibreChat-AI) / [librechat.ai](https://github.com/LibreChat-AI/librechat.ai) | 128 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/51827949?s=40&v=4" width="20" height="20" alt=""> [deepset-ai](https://github.com/deepset-ai) / [haystack-core-integrations](https://github.com/deepset-ai/haystack-core-integrations) | 126 |
|
||||
| Repository | Stars |
|
||||
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -----: |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/85702467?s=40&v=4" width="20" height="20" alt=""> [langflow-ai](https://github.com/langflow-ai) / [langflow](https://github.com/langflow-ai/langflow) | 116251 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/158137808?s=40&v=4" width="20" height="20" alt=""> [open-webui](https://github.com/open-webui) / [open-webui](https://github.com/open-webui/open-webui) | 109642 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/23818?s=40&v=4" width="20" height="20" alt=""> [abi](https://github.com/abi) / [screenshot-to-code](https://github.com/abi/screenshot-to-code) | 70877 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/131470832?s=40&v=4" width="20" height="20" alt=""> [lobehub](https://github.com/lobehub) / [lobe-chat](https://github.com/lobehub/lobe-chat) | 65454 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/69962740?s=40&v=4" width="20" height="20" alt=""> [infiniflow](https://github.com/infiniflow) / [ragflow](https://github.com/infiniflow/ragflow) | 64118 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/135057108?s=40&v=4" width="20" height="20" alt=""> [firecrawl](https://github.com/firecrawl) / [firecrawl](https://github.com/firecrawl/firecrawl) | 56713 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/130722866?s=40&v=4" width="20" height="20" alt=""> [run-llama](https://github.com/run-llama) / [llama_index](https://github.com/run-llama/llama_index) | 44203 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/128289781?s=40&v=4" width="20" height="20" alt=""> [FlowiseAI](https://github.com/FlowiseAI) / [Flowise](https://github.com/FlowiseAI/Flowise) | 43547 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/159330290?s=40&v=4" width="20" height="20" alt=""> [QuivrHQ](https://github.com/QuivrHQ) / [quivr](https://github.com/QuivrHQ/quivr) | 38415 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/6154722?s=40&v=4" width="20" height="20" alt=""> [microsoft](https://github.com/microsoft) / [ai-agents-for-beginners](https://github.com/microsoft/ai-agents-for-beginners) | 38012 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/139558948?s=40&v=4" width="20" height="20" alt=""> [chatchat-space](https://github.com/chatchat-space) / [Langchain-Chatchat](https://github.com/chatchat-space/Langchain-Chatchat) | 36071 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/31035808?s=40&v=4" width="20" height="20" alt=""> [mindsdb](https://github.com/mindsdb) / [mindsdb](https://github.com/mindsdb/mindsdb) | 35669 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/121462774?s=40&v=4" width="20" height="20" alt=""> [BerriAI](https://github.com/BerriAI) / [litellm](https://github.com/BerriAI/litellm) | 28726 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/157326433?s=40&v=4" width="20" height="20" alt=""> [onlook-dev](https://github.com/onlook-dev) / [onlook](https://github.com/onlook-dev/onlook) | 22447 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/487568?s=40&v=4" width="20" height="20" alt=""> [NixOS](https://github.com/NixOS) / [nixpkgs](https://github.com/NixOS/nixpkgs) | 21748 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/170767358?s=40&v=4" width="20" height="20" alt=""> [kortix-ai](https://github.com/kortix-ai) / [suna](https://github.com/kortix-ai/suna) | 17976 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/76263028?s=40&v=4" width="20" height="20" alt=""> [anthropics](https://github.com/anthropics) / [courses](https://github.com/anthropics/courses) | 17057 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/149120496?s=40&v=4" width="20" height="20" alt=""> [mastra-ai](https://github.com/mastra-ai) / [mastra](https://github.com/mastra-ai/mastra) | 16484 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/134601687?s=40&v=4" width="20" height="20" alt=""> [langfuse](https://github.com/langfuse) / [langfuse](https://github.com/langfuse/langfuse) | 16054 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/7250217?s=40&v=4" width="20" height="20" alt=""> [Canner](https://github.com/Canner) / [WrenAI](https://github.com/Canner/WrenAI) | 11868 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/137907881?s=40&v=4" width="20" height="20" alt=""> [promptfoo](https://github.com/promptfoo) / [promptfoo](https://github.com/promptfoo/promptfoo) | 8350 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/193350244?s=40&v=4" width="20" height="20" alt=""> [The-Pocket](https://github.com/The-Pocket) / [PocketFlow](https://github.com/The-Pocket/PocketFlow) | 8313 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/139012218?s=40&v=4" width="20" height="20" alt=""> [OpenPipe](https://github.com/OpenPipe) / [ART](https://github.com/OpenPipe/ART) | 7093 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/125468716?s=40&v=4" width="20" height="20" alt=""> [topoteretes](https://github.com/topoteretes) / [cognee](https://github.com/topoteretes/cognee) | 7011 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/3299148?s=40&v=4" width="20" height="20" alt=""> [awslabs](https://github.com/awslabs) / [agent-squad](https://github.com/awslabs/agent-squad) | 6785 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/162546372?s=40&v=4" width="20" height="20" alt=""> [BasedHardware](https://github.com/BasedHardware) / [omi](https://github.com/BasedHardware/omi) | 6231 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/118301880?s=40&v=4" width="20" height="20" alt=""> [hatchet-dev](https://github.com/hatchet-dev) / [hatchet](https://github.com/hatchet-dev/hatchet) | 6019 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/88676955?s=40&v=4" width="20" height="20" alt=""> [zenml-io](https://github.com/zenml-io) / [zenml](https://github.com/zenml-io/zenml) | 4873 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/132635806?s=40&v=4" width="20" height="20" alt=""> [refly-ai](https://github.com/refly-ai) / [refly](https://github.com/refly-ai/refly) | 4654 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/47287758?s=40&v=4" width="20" height="20" alt=""> [coleam00](https://github.com/coleam00) / [ottomator-agents](https://github.com/coleam00/ottomator-agents) | 4165 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/8251002?s=40&v=4" width="20" height="20" alt=""> [JoshuaC215](https://github.com/JoshuaC215) / [agent-service-toolkit](https://github.com/JoshuaC215/agent-service-toolkit) | 3557 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/185852128?s=40&v=4" width="20" height="20" alt=""> [colanode](https://github.com/colanode) / [colanode](https://github.com/colanode/colanode) | 3517 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/201282378?s=40&v=4" width="20" height="20" alt=""> [VoltAgent](https://github.com/VoltAgent) / [voltagent](https://github.com/VoltAgent/voltagent) | 3210 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/188657705?s=40&v=4" width="20" height="20" alt=""> [bragai](https://github.com/bragai) / [bRAG-langchain](https://github.com/bragai/bRAG-langchain) | 3010 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/11855343?s=40&v=4" width="20" height="20" alt=""> [pingcap](https://github.com/pingcap) / [autoflow](https://github.com/pingcap/autoflow) | 2651 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/135396723?s=40&v=4" width="20" height="20" alt=""> [sourcebot-dev](https://github.com/sourcebot-dev) / [sourcebot](https://github.com/sourcebot-dev/sourcebot) | 2570 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/158137808?s=40&v=4" width="20" height="20" alt=""> [open-webui](https://github.com/open-webui) / [pipelines](https://github.com/open-webui/pipelines) | 2055 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/179994263?s=40&v=4" width="20" height="20" alt=""> [YFGaia](https://github.com/YFGaia) / [dify-plus](https://github.com/YFGaia/dify-plus) | 1734 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/46323662?s=40&v=4" width="20" height="20" alt=""> [TheSpaghettiDetective](https://github.com/TheSpaghettiDetective) / [obico-server](https://github.com/TheSpaghettiDetective/obico-server) | 1687 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/85268109?s=40&v=4" width="20" height="20" alt=""> [MLSysOps](https://github.com/MLSysOps) / [MLE-agent](https://github.com/MLSysOps/MLE-agent) | 1387 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/144196744?s=40&v=4" width="20" height="20" alt=""> [TIGER-AI-Lab](https://github.com/TIGER-AI-Lab) / [TheoremExplainAgent](https://github.com/TIGER-AI-Lab/TheoremExplainAgent) | 1385 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/2314423?s=40&v=4" width="20" height="20" alt=""> [trailofbits](https://github.com/trailofbits) / [buttercup](https://github.com/trailofbits/buttercup) | 1223 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/62564480?s=40&v=4" width="20" height="20" alt=""> [wassim249](https://github.com/wassim249) / [fastapi-langgraph-agent-production-ready-template](https://github.com/wassim249/fastapi-langgraph-agent-production-ready-template) | 1200 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/18422723?s=40&v=4" width="20" height="20" alt=""> [alishobeiri](https://github.com/alishobeiri) / [thread](https://github.com/alishobeiri/thread) | 1098 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/2357342?s=40&v=4" width="20" height="20" alt=""> [dmayboroda](https://github.com/dmayboroda) / [minima](https://github.com/dmayboroda/minima) | 1010 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/65890619?s=40&v=4" width="20" height="20" alt=""> [zstar1003](https://github.com/zstar1003) / [ragflow-plus](https://github.com/zstar1003/ragflow-plus) | 993 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/121352441?s=40&v=4" width="20" height="20" alt=""> [openops-cloud](https://github.com/openops-cloud) / [openops](https://github.com/openops-cloud/openops) | 939 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/151867818?s=40&v=4" width="20" height="20" alt=""> [dynamiq-ai](https://github.com/dynamiq-ai) / [dynamiq](https://github.com/dynamiq-ai/dynamiq) | 927 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/74420131?s=40&v=4" width="20" height="20" alt=""> [xataio](https://github.com/xataio) / [agent](https://github.com/xataio/agent) | 857 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/123981229?s=40&v=4" width="20" height="20" alt=""> [plastic-labs](https://github.com/plastic-labs) / [tutor-gpt](https://github.com/plastic-labs/tutor-gpt) | 845 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/176438967?s=40&v=4" width="20" height="20" alt=""> [trendy-design](https://github.com/trendy-design) / [llmchat](https://github.com/trendy-design/llmchat) | 829 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/2045570?s=40&v=4" width="20" height="20" alt=""> [hotovo](https://github.com/hotovo) / [aider-desk](https://github.com/hotovo/aider-desk) | 781 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/169500408?s=40&v=4" width="20" height="20" alt=""> [opslane](https://github.com/opslane) / [opslane](https://github.com/opslane/opslane) | 719 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/142796018?s=40&v=4" width="20" height="20" alt=""> [wrtnlabs](https://github.com/wrtnlabs) / [autoview](https://github.com/wrtnlabs/autoview) | 688 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/20493493?s=40&v=4" width="20" height="20" alt=""> [andysingal](https://github.com/andysingal) / [llm-course](https://github.com/andysingal/llm-course) | 643 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/48585267?s=40&v=4" width="20" height="20" alt=""> [theopenconversationkit](https://github.com/theopenconversationkit) / [tock](https://github.com/theopenconversationkit/tock) | 587 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/178644984?s=40&v=4" width="20" height="20" alt=""> [sentient-engineering](https://github.com/sentient-engineering) / [agent-q](https://github.com/sentient-engineering/agent-q) | 487 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/58037050?s=40&v=4" width="20" height="20" alt=""> [NicholasGoh](https://github.com/NicholasGoh) / [fastapi-mcp-langgraph-template](https://github.com/NicholasGoh/fastapi-mcp-langgraph-template) | 481 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/148684274?s=40&v=4" width="20" height="20" alt=""> [i-am-alice](https://github.com/i-am-alice) / [3rd-devs](https://github.com/i-am-alice/3rd-devs) | 472 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/163431636?s=40&v=4" width="20" height="20" alt=""> [AIDotNet](https://github.com/AIDotNet) / [koala-ai](https://github.com/AIDotNet/koala-ai) | 470 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/132396805?s=40&v=4" width="20" height="20" alt=""> [phospho-app](https://github.com/phospho-app) / [text-analytics-legacy](https://github.com/phospho-app/text-analytics-legacy) | 439 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/170831637?s=40&v=4" width="20" height="20" alt=""> [inferablehq](https://github.com/inferablehq) / [inferable](https://github.com/inferablehq/inferable) | 403 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/171800766?s=40&v=4" width="20" height="20" alt=""> [duoyang666](https://github.com/duoyang666) / [ai_novel](https://github.com/duoyang666/ai_novel) | 397 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/209155962?s=40&v=4" width="20" height="20" alt=""> [strands-agents](https://github.com/strands-agents) / [samples](https://github.com/strands-agents/samples) | 385 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/16997807?s=40&v=4" width="20" height="20" alt=""> [FranciscoMoretti](https://github.com/FranciscoMoretti) / [sparka](https://github.com/FranciscoMoretti/sparka) | 380 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/66303003?s=40&v=4" width="20" height="20" alt=""> [RobotecAI](https://github.com/RobotecAI) / [rai](https://github.com/RobotecAI/rai) | 373 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/137044109?s=40&v=4" width="20" height="20" alt=""> [ElectricCodeGuy](https://github.com/ElectricCodeGuy) / [SupabaseAuthWithSSR](https://github.com/ElectricCodeGuy/SupabaseAuthWithSSR) | 370 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/169401942?s=40&v=4" width="20" height="20" alt=""> [LibreChat-AI](https://github.com/LibreChat-AI) / [librechat.ai](https://github.com/LibreChat-AI/librechat.ai) | 339 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/15125613?s=40&v=4" width="20" height="20" alt=""> [souzatharsis](https://github.com/souzatharsis) / [tamingLLMs](https://github.com/souzatharsis/tamingLLMs) | 323 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/8931462?s=40&v=4" width="20" height="20" alt=""> [aws-samples](https://github.com/aws-samples) / [aws-ai-ml-workshop-kr](https://github.com/aws-samples/aws-ai-ml-workshop-kr) | 295 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/25676773?s=40&v=4" width="20" height="20" alt=""> [weizxfree](https://github.com/weizxfree) / [KnowFlow](https://github.com/weizxfree/KnowFlow) | 285 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/88676955?s=40&v=4" width="20" height="20" alt=""> [zenml-io](https://github.com/zenml-io) / [zenml-projects](https://github.com/zenml-io/zenml-projects) | 276 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/136071305?s=40&v=4" width="20" height="20" alt=""> [wxai-space](https://github.com/wxai-space) / [LightAgent](https://github.com/wxai-space/LightAgent) | 275 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/90278288?s=40&v=4" width="20" height="20" alt=""> [Ozamatash](https://github.com/Ozamatash) / [deep-research-mcp](https://github.com/Ozamatash/deep-research-mcp) | 269 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/168552753?s=40&v=4" width="20" height="20" alt=""> [sql-agi](https://github.com/sql-agi) / [DB-GPT](https://github.com/sql-agi/DB-GPT) | 241 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/828269?s=40&v=4" width="20" height="20" alt=""> [guyernest](https://github.com/guyernest) / [advanced-rag](https://github.com/guyernest/advanced-rag) | 238 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/175069511?s=40&v=4" width="20" height="20" alt=""> [bklieger-groq](https://github.com/bklieger-groq) / [mathtutor-on-groq](https://github.com/bklieger-groq/mathtutor-on-groq) | 233 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/123981229?s=40&v=4" width="20" height="20" alt=""> [plastic-labs](https://github.com/plastic-labs) / [honcho](https://github.com/plastic-labs/honcho) | 224 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/118967077?s=40&v=4" width="20" height="20" alt=""> [OVINC-CN](https://github.com/OVINC-CN) / [OpenWebUI](https://github.com/OVINC-CN/OpenWebUI) | 202 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/31960995?s=40&v=4" width="20" height="20" alt=""> [zhutoutoutousan](https://github.com/zhutoutoutousan) / [worldquant-miner](https://github.com/zhutoutoutousan/worldquant-miner) | 202 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/29215115?s=40&v=4" width="20" height="20" alt=""> [iceener](https://github.com/iceener) / [ai](https://github.com/iceener/ai) | 186 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/187584218?s=40&v=4" width="20" height="20" alt=""> [giselles-ai](https://github.com/giselles-ai) / [giselle](https://github.com/giselles-ai/giselle) | 181 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/174666116?s=40&v=4" width="20" height="20" alt=""> [ai-shifu](https://github.com/ai-shifu) / [ai-shifu](https://github.com/ai-shifu/ai-shifu) | 181 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/8931462?s=40&v=4" width="20" height="20" alt=""> [aws-samples](https://github.com/aws-samples) / [sample-serverless-mcp-servers](https://github.com/aws-samples/sample-serverless-mcp-servers) | 175 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/202488052?s=40&v=4" width="20" height="20" alt=""> [celerforge](https://github.com/celerforge) / [freenote](https://github.com/celerforge/freenote) | 171 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/104478511?s=40&v=4" width="20" height="20" alt=""> [babelcloud](https://github.com/babelcloud) / [LLM-RGB](https://github.com/babelcloud/LLM-RGB) | 164 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/171735272?s=40&v=4" width="20" height="20" alt=""> [8090-inc](https://github.com/8090-inc) / [xrx-sample-apps](https://github.com/8090-inc/xrx-sample-apps) | 163 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/51827949?s=40&v=4" width="20" height="20" alt=""> [deepset-ai](https://github.com/deepset-ai) / [haystack-core-integrations](https://github.com/deepset-ai/haystack-core-integrations) | 163 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/1009716?s=40&v=4" width="20" height="20" alt=""> [codecentric](https://github.com/codecentric) / [c4-genai-suite](https://github.com/codecentric/c4-genai-suite) | 152 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/196509932?s=40&v=4" width="20" height="20" alt=""> [XSpoonAi](https://github.com/XSpoonAi) / [spoon-core](https://github.com/XSpoonAi/spoon-core) | 150 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/139558948?s=40&v=4" width="20" height="20" alt=""> [chatchat-space](https://github.com/chatchat-space) / [LangGraph-Chatchat](https://github.com/chatchat-space/LangGraph-Chatchat) | 144 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/134601687?s=40&v=4" width="20" height="20" alt=""> [langfuse](https://github.com/langfuse) / [langfuse-docs](https://github.com/langfuse/langfuse-docs) | 139 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/44976328?s=40&v=4" width="20" height="20" alt=""> [piyushgarg-dev](https://github.com/piyushgarg-dev) / [genai-cohort](https://github.com/piyushgarg-dev/genai-cohort) | 135 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/105285801?s=40&v=4" width="20" height="20" alt=""> [i-dot-ai](https://github.com/i-dot-ai) / [redbox](https://github.com/i-dot-ai/redbox) | 132 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/90423581?s=40&v=4" width="20" height="20" alt=""> [bmd1905](https://github.com/bmd1905) / [ChatOpsLLM](https://github.com/bmd1905/ChatOpsLLM) | 127 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/202074444?s=40&v=4" width="20" height="20" alt=""> [Fintech-Dreamer](https://github.com/Fintech-Dreamer) / [FinSynth](https://github.com/Fintech-Dreamer/FinSynth) | 121 |
|
||||
| <img class="avatar mr-2" src="https://avatars.githubusercontent.com/u/1566555?s=40&v=4" width="20" height="20" alt=""> [kenshiro-o](https://github.com/kenshiro-o) / [nagato-ai](https://github.com/kenshiro-o/nagato-ai) | 119 |
|
||||
|
||||
## 🔒 Security & Privacy
|
||||
|
||||
|
||||
@@ -13,3 +13,25 @@
|
||||
### Postgres
|
||||
|
||||
- Most `schema.prisma` changes should produce a change in `packages/shared/prisma/migrations`.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
- Environment variables should be imported from the `env.mjs/ts` file of the respective package and not from `process.env.*` to ensure validation and typing.
|
||||
|
||||
## Redis Invocations
|
||||
|
||||
- Highlight usage of `redis.call` invocations. Those may have suboptimal redis cluster routing and will raise errors. Instead, use the native call patterns.
|
||||
Example: `await redis?.call("SET", key, "1", "NX", "EX", TTLSeconds);` should use `await redis?.set(key, "1", "EX", TTLSeconds, "NX");` instead.
|
||||
|
||||
## Langfuse Cloud
|
||||
|
||||
- When attempting to confirm if the current environment is Langfuse Cloud in the frontend, use the `useLangfuseCloudRegion` hook and never environment variables directly.
|
||||
|
||||
## Banner Height System
|
||||
|
||||
- Use `top-banner-offset` instead of `top-0` for any elements that are positioned `sticky`, `fixed`, or `absolute` with a global reference point (e.g., `top-0`). This ensures proper spacing when system banners (payment, maintenance, etc.) are displayed.
|
||||
- The banner height is managed through CSS variables (`--banner-height` and `--banner-offset`) defined in `web/src/styles/globals.css`.
|
||||
- Banner components (like PaymentBanner) dynamically update `--banner-height` using ResizeObserver to track their actual height, ensuring accurate positioning even when banners resize (e.g., on mobile wrapping).
|
||||
- Available Tailwind utilities:
|
||||
- `top-banner-offset` / `pt-banner-offset` - For sticky/fixed/absolute positioning and padding
|
||||
- `h-screen-with-banner` / `min-h-screen-with-banner` - For full-height containers accounting for banners
|
||||
|
||||
+15
-13
@@ -17,11 +17,11 @@ services:
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment: &langfuse-web-env
|
||||
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres
|
||||
NEXTAUTH_SECRET: mysecret
|
||||
SALT: mysalt
|
||||
ENCRYPTION_KEY: "0000000000000000000000000000000000000000000000000000000000000000" # generate via `openssl rand -hex 32`
|
||||
NEXTAUTH_URL: http://localhost:3000
|
||||
DATABASE_URL: ${DATABASE_URL:-postgresql://postgres:postgres@postgres:5432/postgres}
|
||||
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-mysecret}
|
||||
SALT: ${SALT:-mysalt}
|
||||
ENCRYPTION_KEY: ${ENCRYPTION_KEY:-0000000000000000000000000000000000000000000000000000000000000000} # generate via `openssl rand -hex 32`
|
||||
NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:3000}
|
||||
TELEMETRY_ENABLED: ${TELEMETRY_ENABLED:-true}
|
||||
LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES: ${LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES:-false}
|
||||
LANGFUSE_INIT_ORG_ID: ${LANGFUSE_INIT_ORG_ID:-}
|
||||
@@ -82,8 +82,8 @@ services:
|
||||
user: "101:101"
|
||||
environment:
|
||||
CLICKHOUSE_DB: default
|
||||
CLICKHOUSE_USER: clickhouse
|
||||
CLICKHOUSE_PASSWORD: clickhouse
|
||||
CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse}
|
||||
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-clickhouse}
|
||||
volumes:
|
||||
- langfuse_clickhouse_data:/var/lib/clickhouse
|
||||
- langfuse_clickhouse_logs:/var/log/clickhouse-server
|
||||
@@ -103,8 +103,8 @@ services:
|
||||
# create the 'langfuse' bucket before starting the service
|
||||
command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
|
||||
environment:
|
||||
MINIO_ROOT_USER: minio
|
||||
MINIO_ROOT_PASSWORD: miniosecret
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minio}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-miniosecret}
|
||||
ports:
|
||||
- "9090:9000"
|
||||
- "9091:9001"
|
||||
@@ -131,7 +131,7 @@ services:
|
||||
retries: 10
|
||||
|
||||
postgres:
|
||||
image: docker.io/postgres:${POSTGRES_VERSION:-latest}
|
||||
image: docker.io/postgres:${POSTGRES_VERSION:-17}
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
@@ -139,9 +139,11 @@ services:
|
||||
timeout: 3s
|
||||
retries: 10
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: ${POSTGRES_USER:-postgres}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-postgres}
|
||||
TZ: UTC
|
||||
PGTZ: UTC
|
||||
ports:
|
||||
- 5432:5432
|
||||
volumes:
|
||||
|
||||
@@ -4,8 +4,8 @@ services:
|
||||
user: "101:101"
|
||||
environment:
|
||||
CLICKHOUSE_DB: default
|
||||
CLICKHOUSE_USER: clickhouse
|
||||
CLICKHOUSE_PASSWORD: clickhouse
|
||||
CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse}
|
||||
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-clickhouse}
|
||||
volumes:
|
||||
- langfuse_clickhouse_data:/var/lib/clickhouse
|
||||
- langfuse_clickhouse_logs:/var/log/clickhouse-server
|
||||
@@ -32,7 +32,7 @@ services:
|
||||
- 6379:6379
|
||||
|
||||
postgres:
|
||||
image: docker.io/postgres:${POSTGRES_VERSION:-latest}
|
||||
image: docker.io/postgres:${POSTGRES_VERSION:-17}
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
@@ -41,9 +41,11 @@ services:
|
||||
retries: 10
|
||||
command: ["postgres", "-c", "log_statement=all"]
|
||||
environment:
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
- POSTGRES_DB=postgres
|
||||
- POSTGRES_USER=${POSTGRES_USER:-postgres}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres}
|
||||
- POSTGRES_DB=${POSTGRES_DB:-postgres}
|
||||
- TZ=UTC
|
||||
- PGTZ=UTC
|
||||
ports:
|
||||
- 5432:5432
|
||||
volumes:
|
||||
|
||||
@@ -4,8 +4,8 @@ services:
|
||||
user: "101:101"
|
||||
environment:
|
||||
CLICKHOUSE_DB: default
|
||||
CLICKHOUSE_USER: clickhouse
|
||||
CLICKHOUSE_PASSWORD: clickhouse
|
||||
CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse}
|
||||
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-clickhouse}
|
||||
volumes:
|
||||
- langfuse_clickhouse_data:/var/lib/clickhouse
|
||||
- langfuse_clickhouse_logs:/var/log/clickhouse-server
|
||||
@@ -21,8 +21,8 @@ services:
|
||||
# create the 'langfuse' bucket before starting the service
|
||||
command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
|
||||
environment:
|
||||
MINIO_ACCESS_KEY: minio
|
||||
MINIO_SECRET_KEY: miniosecret
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minio}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-miniosecret}
|
||||
ports:
|
||||
- 127.0.0.1:9090:9000
|
||||
- 127.0.0.1:9091:9001
|
||||
@@ -36,7 +36,7 @@ services:
|
||||
start_period: 1s
|
||||
|
||||
postgres:
|
||||
image: docker.io/postgres:${POSTGRES_VERSION:-latest}
|
||||
image: docker.io/postgres:${POSTGRES_VERSION:-17}
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
@@ -45,9 +45,11 @@ services:
|
||||
retries: 10
|
||||
command: ["postgres", "-c", "log_statement=all"]
|
||||
environment:
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
- POSTGRES_DB=postgres
|
||||
- POSTGRES_USER=${POSTGRES_USER:-postgres}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres}
|
||||
- POSTGRES_DB=${POSTGRES_DB:-postgres}
|
||||
- TZ=UTC
|
||||
- PGTZ=UTC
|
||||
ports:
|
||||
- 127.0.0.1:5432:5432
|
||||
volumes:
|
||||
@@ -55,7 +57,7 @@ services:
|
||||
|
||||
# Redis Cluster. Requires a Unix host to work for network_mode: host. I.e. tests will fail on windows and mac with this setup.
|
||||
redis-node-0:
|
||||
image: docker.io/bitnami/redis-cluster:8.0
|
||||
image: docker.io/bitnamilegacy/redis-cluster:8.0
|
||||
network_mode: host
|
||||
volumes:
|
||||
- redis-cluster_data-0:/bitnami/redis/data
|
||||
@@ -65,7 +67,7 @@ services:
|
||||
REDIS_NODES: 127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375
|
||||
|
||||
redis-node-1:
|
||||
image: docker.io/bitnami/redis-cluster:8.0
|
||||
image: docker.io/bitnamilegacy/redis-cluster:8.0
|
||||
network_mode: host
|
||||
volumes:
|
||||
- redis-cluster_data-1:/bitnami/redis/data
|
||||
@@ -75,7 +77,7 @@ services:
|
||||
REDIS_NODES: 127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375
|
||||
|
||||
redis-node-2:
|
||||
image: docker.io/bitnami/redis-cluster:8.0
|
||||
image: docker.io/bitnamilegacy/redis-cluster:8.0
|
||||
network_mode: host
|
||||
volumes:
|
||||
- redis-cluster_data-2:/bitnami/redis/data
|
||||
@@ -85,7 +87,7 @@ services:
|
||||
REDIS_NODES: 127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375
|
||||
|
||||
redis-node-3:
|
||||
image: docker.io/bitnami/redis-cluster:8.0
|
||||
image: docker.io/bitnamilegacy/redis-cluster:8.0
|
||||
network_mode: host
|
||||
volumes:
|
||||
- redis-cluster_data-3:/bitnami/redis/data
|
||||
@@ -95,7 +97,7 @@ services:
|
||||
REDIS_NODES: 127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375
|
||||
|
||||
redis-node-4:
|
||||
image: docker.io/bitnami/redis-cluster:8.0
|
||||
image: docker.io/bitnamilegacy/redis-cluster:8.0
|
||||
network_mode: host
|
||||
volumes:
|
||||
- redis-cluster_data-4:/bitnami/redis/data
|
||||
@@ -105,7 +107,7 @@ services:
|
||||
REDIS_NODES: 127.0.0.1:6370 127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375
|
||||
|
||||
redis-node-5:
|
||||
image: docker.io/bitnami/redis-cluster:8.0
|
||||
image: docker.io/bitnamilegacy/redis-cluster:8.0
|
||||
network_mode: host
|
||||
volumes:
|
||||
- redis-cluster_data-5:/bitnami/redis/data
|
||||
|
||||
+13
-9
@@ -1,11 +1,13 @@
|
||||
services:
|
||||
clickhouse:
|
||||
image: docker.io/clickhouse/clickhouse-server:24.3
|
||||
# Upgrade from 24.3 to check behaviour on new events table.
|
||||
# Unit tests still verify 24.3 behaviour using -azure and -redis-cluster configs.
|
||||
image: docker.io/clickhouse/clickhouse-server:25.8
|
||||
user: "101:101"
|
||||
environment:
|
||||
CLICKHOUSE_DB: default
|
||||
CLICKHOUSE_USER: clickhouse
|
||||
CLICKHOUSE_PASSWORD: clickhouse
|
||||
CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse}
|
||||
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-clickhouse}
|
||||
volumes:
|
||||
- langfuse_clickhouse_data:/var/lib/clickhouse
|
||||
- langfuse_clickhouse_logs:/var/log/clickhouse-server
|
||||
@@ -21,8 +23,8 @@ services:
|
||||
# create the 'langfuse' bucket before starting the service
|
||||
command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
|
||||
environment:
|
||||
MINIO_ACCESS_KEY: minio
|
||||
MINIO_SECRET_KEY: miniosecret
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minio}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-miniosecret}
|
||||
ports:
|
||||
- 127.0.0.1:9090:9000
|
||||
- 127.0.0.1:9091:9001
|
||||
@@ -44,7 +46,7 @@ services:
|
||||
- 127.0.0.1:6379:6379
|
||||
|
||||
postgres:
|
||||
image: docker.io/postgres:${POSTGRES_VERSION:-latest}
|
||||
image: docker.io/postgres:${POSTGRES_VERSION:-17}
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
@@ -53,9 +55,11 @@ services:
|
||||
retries: 10
|
||||
command: ["postgres", "-c", "log_statement=all"]
|
||||
environment:
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
- POSTGRES_DB=postgres
|
||||
- POSTGRES_USER=${POSTGRES_USER:-postgres}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres}
|
||||
- POSTGRES_DB=${POSTGRES_DB:-postgres}
|
||||
- TZ=UTC
|
||||
- PGTZ=UTC
|
||||
ports:
|
||||
- 127.0.0.1:5432:5432
|
||||
volumes:
|
||||
|
||||
+15
-13
@@ -19,10 +19,10 @@ services:
|
||||
ports:
|
||||
- 127.0.0.1:3030:3030
|
||||
environment: &langfuse-worker-env
|
||||
NEXTAUTH_URL: http://localhost:3000
|
||||
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres # CHANGEME
|
||||
SALT: "mysalt" # CHANGEME
|
||||
ENCRYPTION_KEY: "0000000000000000000000000000000000000000000000000000000000000000" # CHANGEME: generate via `openssl rand -hex 32`
|
||||
NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:3000}
|
||||
DATABASE_URL: ${DATABASE_URL:-postgresql://postgres:postgres@postgres:5432/postgres} # CHANGEME
|
||||
SALT: ${SALT:-mysalt} # CHANGEME
|
||||
ENCRYPTION_KEY: ${ENCRYPTION_KEY:-0000000000000000000000000000000000000000000000000000000000000000} # CHANGEME: generate via `openssl rand -hex 32`
|
||||
TELEMETRY_ENABLED: ${TELEMETRY_ENABLED:-true}
|
||||
LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES: ${LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES:-true}
|
||||
CLICKHOUSE_MIGRATION_URL: ${CLICKHOUSE_MIGRATION_URL:-clickhouse://clickhouse:9000}
|
||||
@@ -74,7 +74,7 @@ services:
|
||||
- 3000:3000
|
||||
environment:
|
||||
<<: *langfuse-worker-env
|
||||
NEXTAUTH_SECRET: mysecret # CHANGEME
|
||||
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-mysecret} # CHANGEME
|
||||
LANGFUSE_INIT_ORG_ID: ${LANGFUSE_INIT_ORG_ID:-}
|
||||
LANGFUSE_INIT_ORG_NAME: ${LANGFUSE_INIT_ORG_NAME:-}
|
||||
LANGFUSE_INIT_PROJECT_ID: ${LANGFUSE_INIT_PROJECT_ID:-}
|
||||
@@ -91,8 +91,8 @@ services:
|
||||
user: "101:101"
|
||||
environment:
|
||||
CLICKHOUSE_DB: default
|
||||
CLICKHOUSE_USER: clickhouse
|
||||
CLICKHOUSE_PASSWORD: clickhouse # CHANGEME
|
||||
CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse}
|
||||
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-clickhouse} # CHANGEME
|
||||
volumes:
|
||||
- langfuse_clickhouse_data:/var/lib/clickhouse
|
||||
- langfuse_clickhouse_logs:/var/log/clickhouse-server
|
||||
@@ -113,8 +113,8 @@ services:
|
||||
# create the 'langfuse' bucket before starting the service
|
||||
command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
|
||||
environment:
|
||||
MINIO_ROOT_USER: minio
|
||||
MINIO_ROOT_PASSWORD: miniosecret # CHANGEME
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minio}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-miniosecret} # CHANGEME
|
||||
ports:
|
||||
- 9090:9000
|
||||
- 127.0.0.1:9091:9001
|
||||
@@ -142,7 +142,7 @@ services:
|
||||
retries: 10
|
||||
|
||||
postgres:
|
||||
image: docker.io/postgres:${POSTGRES_VERSION:-latest}
|
||||
image: docker.io/postgres:${POSTGRES_VERSION:-17}
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
@@ -150,9 +150,11 @@ services:
|
||||
timeout: 3s
|
||||
retries: 10
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres # CHANGEME
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: ${POSTGRES_USER:-postgres}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} # CHANGEME
|
||||
POSTGRES_DB: ${POSTGRES_DB:-postgres}
|
||||
TZ: UTC
|
||||
PGTZ: UTC
|
||||
ports:
|
||||
- 127.0.0.1:5432:5432
|
||||
volumes:
|
||||
|
||||
+3
-1
@@ -27,14 +27,16 @@
|
||||
"@langfuse/shared": "workspace:*",
|
||||
"@opentelemetry/api": ">=1.0.0 <1.10.0",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"next": "15.5.2",
|
||||
"next": "15.5.4",
|
||||
"next-auth": "^4.24.11",
|
||||
"nodemailer": "^7.0.9",
|
||||
"zod": "^3.25.62"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@repo/eslint-config": "workspace:*",
|
||||
"@repo/typescript-config": "workspace:*",
|
||||
"@types/node": "^24.3.0",
|
||||
"@types/nodemailer": "^7.0.2",
|
||||
"@typescript-eslint/parser": "^7.12.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json
|
||||
imports:
|
||||
commons: ./commons.yml
|
||||
|
||||
service:
|
||||
auth: true
|
||||
base-path: /api/public/integrations/blob-storage
|
||||
endpoints:
|
||||
getBlobStorageIntegrations:
|
||||
docs: Get all blob storage integrations for the organization (requires organization-scoped API key)
|
||||
method: GET
|
||||
path: ""
|
||||
response: BlobStorageIntegrationsResponse
|
||||
|
||||
upsertBlobStorageIntegration:
|
||||
docs: Create or update a blob storage integration for a specific project (requires organization-scoped API key). The configuration is validated by performing a test upload to the bucket.
|
||||
method: PUT
|
||||
path: ""
|
||||
request: CreateBlobStorageIntegrationRequest
|
||||
response: BlobStorageIntegrationResponse
|
||||
|
||||
deleteBlobStorageIntegration:
|
||||
docs: Delete a blob storage integration by ID (requires organization-scoped API key)
|
||||
method: DELETE
|
||||
path: "/{id}"
|
||||
path-parameters:
|
||||
id: string
|
||||
response: BlobStorageIntegrationDeletionResponse
|
||||
|
||||
types:
|
||||
BlobStorageIntegrationType:
|
||||
enum:
|
||||
- S3
|
||||
- S3_COMPATIBLE
|
||||
- AZURE_BLOB_STORAGE
|
||||
|
||||
BlobStorageIntegrationFileType:
|
||||
enum:
|
||||
- JSON
|
||||
- CSV
|
||||
- JSONL
|
||||
|
||||
BlobStorageExportMode:
|
||||
enum:
|
||||
- FULL_HISTORY
|
||||
- FROM_TODAY
|
||||
- FROM_CUSTOM_DATE
|
||||
|
||||
BlobStorageExportFrequency:
|
||||
enum:
|
||||
- hourly
|
||||
- daily
|
||||
- weekly
|
||||
|
||||
CreateBlobStorageIntegrationRequest:
|
||||
properties:
|
||||
projectId:
|
||||
type: string
|
||||
docs: ID of the project in which to configure the blob storage integration
|
||||
type: BlobStorageIntegrationType
|
||||
bucketName:
|
||||
type: string
|
||||
docs: Name of the storage bucket
|
||||
endpoint:
|
||||
type: optional<string>
|
||||
docs: Custom endpoint URL (required for S3_COMPATIBLE type)
|
||||
region:
|
||||
type: string
|
||||
docs: Storage region
|
||||
accessKeyId:
|
||||
type: optional<string>
|
||||
docs: Access key ID for authentication
|
||||
secretAccessKey:
|
||||
type: optional<string>
|
||||
docs: Secret access key for authentication (will be encrypted when stored)
|
||||
prefix:
|
||||
type: optional<string>
|
||||
docs: Path prefix for exported files (must end with forward slash if provided)
|
||||
exportFrequency: BlobStorageExportFrequency
|
||||
enabled:
|
||||
type: boolean
|
||||
docs: Whether the integration is active
|
||||
forcePathStyle:
|
||||
type: boolean
|
||||
docs: Use path-style URLs for S3 requests
|
||||
fileType: BlobStorageIntegrationFileType
|
||||
exportMode: BlobStorageExportMode
|
||||
exportStartDate:
|
||||
type: optional<datetime>
|
||||
docs: Custom start date for exports (required when exportMode is FROM_CUSTOM_DATE)
|
||||
|
||||
BlobStorageIntegrationResponse:
|
||||
properties:
|
||||
id: string
|
||||
projectId: string
|
||||
type: BlobStorageIntegrationType
|
||||
bucketName: string
|
||||
endpoint: optional<string>
|
||||
region: string
|
||||
accessKeyId: optional<string>
|
||||
prefix: string
|
||||
exportFrequency: BlobStorageExportFrequency
|
||||
enabled: boolean
|
||||
forcePathStyle: boolean
|
||||
fileType: BlobStorageIntegrationFileType
|
||||
exportMode: BlobStorageExportMode
|
||||
exportStartDate: optional<datetime>
|
||||
nextSyncAt: optional<datetime>
|
||||
lastSyncAt: optional<datetime>
|
||||
createdAt: datetime
|
||||
updatedAt: datetime
|
||||
|
||||
BlobStorageIntegrationsResponse:
|
||||
properties:
|
||||
data: list<BlobStorageIntegrationResponse>
|
||||
|
||||
BlobStorageIntegrationDeletionResponse:
|
||||
properties:
|
||||
message: string
|
||||
@@ -258,7 +258,7 @@ types:
|
||||
docs: Reference a score config on a score. When set, config and score name must be equal and value must comply to optionally defined numerical range
|
||||
queueId:
|
||||
type: optional<string>
|
||||
docs: Reference an annotation queue on a score. Populated if the score was initially created in an annotation queue.
|
||||
docs: The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue.
|
||||
environment:
|
||||
type: optional<string>
|
||||
docs: The environment from which this score originated. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'.
|
||||
@@ -319,7 +319,7 @@ types:
|
||||
docs: Reference a score config on a score. When set, config and score name must be equal and value must comply to optionally defined numerical range
|
||||
queueId:
|
||||
type: optional<string>
|
||||
docs: Reference an annotation queue on a score. Populated if the score was initially created in an annotation queue.
|
||||
docs: The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue.
|
||||
environment:
|
||||
type: optional<string>
|
||||
docs: The environment from which this score originated. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'.
|
||||
|
||||
@@ -8,8 +8,9 @@ service:
|
||||
endpoints:
|
||||
batch:
|
||||
docs: |
|
||||
Batched ingestion for Langfuse Tracing.
|
||||
If you want to use tracing via the API, such as to build your own Langfuse client implementation, this is the only API route you need to implement.
|
||||
**Legacy endpoint for batch ingestion for Langfuse Observability.**
|
||||
|
||||
-> Please use the OpenTelemetry endpoint (`/api/public/otel`). Learn more: https://langfuse.com/integrations/native/opentelemetry
|
||||
|
||||
Within each batch, there can be multiple events.
|
||||
Each event has a type, an id, a timestamp, metadata and a body.
|
||||
@@ -276,6 +277,9 @@ types:
|
||||
datasetRunId: optional<string>
|
||||
name: string
|
||||
environment: optional<string>
|
||||
queueId:
|
||||
type: optional<string>
|
||||
docs: The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue.
|
||||
value:
|
||||
type: commons.CreateScoreValue
|
||||
docs: The value of the score. Must be passed as string for categorical scores, and numeric for boolean and numeric scores. Boolean score values must equal either 1 or 0 (true or false)
|
||||
|
||||
@@ -66,6 +66,9 @@ types:
|
||||
PromptMeta:
|
||||
properties:
|
||||
name: string
|
||||
type:
|
||||
type: PromptType
|
||||
docs: Indicates whether the prompt is a text or chat prompt.
|
||||
versions: list<integer>
|
||||
labels: list<string>
|
||||
tags: list<string>
|
||||
@@ -114,6 +117,11 @@ types:
|
||||
chat: ChatPrompt
|
||||
text: TextPrompt
|
||||
|
||||
PromptType:
|
||||
enum:
|
||||
- chat
|
||||
- text
|
||||
|
||||
BasePrompt:
|
||||
properties:
|
||||
name: string
|
||||
|
||||
@@ -35,6 +35,18 @@ service:
|
||||
type: string
|
||||
docs: The unique langfuse identifier of a score config
|
||||
response: commons.ScoreConfig
|
||||
|
||||
update:
|
||||
docs: Update a score config
|
||||
method: PATCH
|
||||
path: /score-configs/{configId}
|
||||
path-parameters:
|
||||
configId:
|
||||
type: string
|
||||
docs: The unique langfuse identifier of a score config
|
||||
request: UpdateScoreConfigRequest
|
||||
response: commons.ScoreConfig
|
||||
|
||||
types:
|
||||
ScoreConfigs:
|
||||
properties:
|
||||
@@ -56,3 +68,23 @@ types:
|
||||
description:
|
||||
type: optional<string>
|
||||
docs: Description is shown across the Langfuse UI and can be used to e.g. explain the config categories in detail, why a numeric range was set, or provide additional context on config name or usage
|
||||
UpdateScoreConfigRequest:
|
||||
properties:
|
||||
isArchived:
|
||||
type: optional<boolean>
|
||||
docs: The status of the score config showing if it is archived or not
|
||||
name:
|
||||
type: optional<string>
|
||||
docs: The name of the score config
|
||||
categories:
|
||||
type: optional<list<commons.ConfigCategory>>
|
||||
docs: Configure custom categories for categorical scores. Pass a list of objects with `label` and `value` properties. Categories are autogenerated for boolean configs and cannot be passed
|
||||
minValue:
|
||||
type: optional<double>
|
||||
docs: Configure a minimum value for numerical scores. If not set, the minimum value defaults to -∞
|
||||
maxValue:
|
||||
type: optional<double>
|
||||
docs: Configure a maximum value for numerical scores. If not set, the maximum value defaults to +∞
|
||||
description:
|
||||
type: optional<string>
|
||||
docs: Description is shown across the Langfuse UI and can be used to e.g. explain the config categories in detail, why a numeric range was set, or provide additional context on config name or usage
|
||||
|
||||
@@ -50,6 +50,9 @@ service:
|
||||
configId:
|
||||
type: optional<string>
|
||||
docs: Retrieve only scores with a specific configId.
|
||||
sessionId:
|
||||
type: optional<string>
|
||||
docs: Retrieve only scores with a specific sessionId.
|
||||
queueId:
|
||||
type: optional<string>
|
||||
docs: Retrieve only scores with a specific annotation queueId.
|
||||
|
||||
@@ -37,6 +37,9 @@ types:
|
||||
environment:
|
||||
type: optional<string>
|
||||
docs: The environment of the score. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'.
|
||||
queueId:
|
||||
type: optional<string>
|
||||
docs: The annotation queue referenced by the score. Indicates if score was initially created while processing annotation queue.
|
||||
dataType:
|
||||
type: optional<commons.ScoreDataType>
|
||||
docs: The data type of the score. When passing a configId this field is inferred. Otherwise, this field must be passed or will default to numeric.
|
||||
|
||||
@@ -66,6 +66,32 @@ service:
|
||||
fields:
|
||||
type: optional<string>
|
||||
docs: "Comma-separated list of fields to include in the response. Available field groups: 'core' (always included), 'io' (input, output, metadata), 'scores', 'observations', 'metrics'. If not specified, all fields are returned. Example: 'core,scores,metrics'. Note: Excluded 'observations' or 'scores' fields return empty arrays; excluded 'metrics' returns -1 for 'totalCost' and 'latency'."
|
||||
filter:
|
||||
type: optional<string>
|
||||
docs: |
|
||||
JSON string containing an array of filter conditions. When provided, this takes precedence over legacy filter parameters (userId, name, sessionId, tags, version, release, environment, fromTimestamp, toTimestamp).
|
||||
Each filter condition has the following structure:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"type": string, // Required. One of: "datetime", "string", "number", "stringOptions", "categoryOptions", "arrayOptions", "stringObject", "numberObject", "boolean", "null"
|
||||
"column": string, // Required. Column to filter on
|
||||
"operator": string, // Required. Operator based on type:
|
||||
// - datetime: ">", "<", ">=", "<="
|
||||
// - string: "=", "contains", "does not contain", "starts with", "ends with"
|
||||
// - stringOptions: "any of", "none of"
|
||||
// - categoryOptions: "any of", "none of"
|
||||
// - arrayOptions: "any of", "none of", "all of"
|
||||
// - number: "=", ">", "<", ">=", "<="
|
||||
// - stringObject: "=", "contains", "does not contain", "starts with", "ends with"
|
||||
// - numberObject: "=", ">", "<", ">=", "<="
|
||||
// - boolean: "=", "<>"
|
||||
// - null: "is null", "is not null"
|
||||
"value": any, // Required (except for null type). Value to compare against. Type depends on filter type
|
||||
"key": string // Required only for stringObject, numberObject, and categoryOptions types when filtering on nested fields like metadata
|
||||
}
|
||||
]
|
||||
```
|
||||
response: Traces
|
||||
deleteMultiple:
|
||||
docs: Delete multiple traces
|
||||
|
||||
@@ -31,7 +31,7 @@ groups:
|
||||
# client-class-name: LangfuseClient
|
||||
|
||||
- name: fernapi/fern-typescript-node-sdk
|
||||
version: 2.6.1
|
||||
version: 2.12.3
|
||||
output:
|
||||
location: local-file-system
|
||||
path: ../../../generated/typescript
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"organization": "langfuse",
|
||||
"version": "0.56.0"
|
||||
}
|
||||
"version": "0.77.5"
|
||||
}
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "langfuse",
|
||||
"version": "3.106.2",
|
||||
"version": "3.119.0",
|
||||
"author": "engineering@langfuse.com",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
@@ -36,11 +36,11 @@
|
||||
"devDependencies": {
|
||||
"@release-it/bumper": "^7.0.5",
|
||||
"braces": "3.0.3",
|
||||
"dotenv-cli": "^7.4.2",
|
||||
"husky": "^9.0.11",
|
||||
"dotenv-cli": "^7.4.4",
|
||||
"husky": "^9.1.7",
|
||||
"prettier": "^3.6.2",
|
||||
"release-it": "^19.0.3",
|
||||
"turbo": "^2.5.6"
|
||||
"release-it": "^19.0.4",
|
||||
"turbo": "^2.5.8"
|
||||
},
|
||||
"release-it": {
|
||||
"git": {
|
||||
|
||||
@@ -2,10 +2,15 @@ const { resolve } = require("node:path");
|
||||
|
||||
const project = resolve(process.cwd(), "tsconfig.json");
|
||||
|
||||
// Handle eslint-config-turbo's default export
|
||||
const turboConfig = require("eslint-config-turbo");
|
||||
const turboConfigToUse = turboConfig.default || turboConfig;
|
||||
|
||||
/** @type {import("eslint").Linter.Config} */
|
||||
module.exports = {
|
||||
extends: ["eslint:recommended", "prettier", "eslint-config-turbo"],
|
||||
plugins: ["only-warn"],
|
||||
// extends: ["eslint:recommended", "prettier"require().default],
|
||||
extends: ["eslint:recommended", "prettier"],
|
||||
plugins: ["only-warn", "turbo"],
|
||||
globals: {
|
||||
React: true,
|
||||
JSX: true,
|
||||
@@ -30,6 +35,7 @@ module.exports = {
|
||||
rules: {
|
||||
"no-redeclare": "off",
|
||||
"import/order": "off",
|
||||
...(turboConfigToUse.rules || {}),
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
@@ -51,5 +57,6 @@ module.exports = {
|
||||
],
|
||||
},
|
||||
},
|
||||
...(turboConfigToUse.overrides || []),
|
||||
],
|
||||
};
|
||||
|
||||
@@ -2,6 +2,9 @@ const { resolve } = require("node:path");
|
||||
|
||||
const project = resolve(process.cwd(), "tsconfig.json");
|
||||
|
||||
const turboConfig = require("eslint-config-turbo");
|
||||
const turboConfigToUse = turboConfig.default || turboConfig;
|
||||
|
||||
/*
|
||||
* This is a custom ESLint configuration for use with
|
||||
* Next.js apps.
|
||||
@@ -16,7 +19,6 @@ module.exports = {
|
||||
extends: [
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/strict-type-checked",
|
||||
"eslint-config-turbo",
|
||||
],
|
||||
rules: {
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
@@ -47,6 +49,7 @@ module.exports = {
|
||||
ignorePatterns: ["node_modules/", "dist/"],
|
||||
// add rules configurations here
|
||||
rules: {
|
||||
...(turboConfigToUse.rules || {}),
|
||||
"@typescript-eslint/consistent-type-imports": [
|
||||
"warn",
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"@vercel/style-guide": "^6.0.0",
|
||||
"eslint-config-next": "^14.2.15",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-config-turbo": "^2.5.6",
|
||||
"eslint-config-turbo": "^2.5.8",
|
||||
"eslint-plugin-only-warn": "^1.1.0",
|
||||
"typescript": "^5.7.2"
|
||||
}
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
-- Recreate materialized views for project_environments table
|
||||
CREATE MATERIALIZED VIEW project_environments_traces_mv ON CLUSTER default TO project_environments AS
|
||||
SELECT
|
||||
project_id,
|
||||
groupUniqArray(environment) AS environments
|
||||
FROM traces
|
||||
GROUP BY project_id;
|
||||
|
||||
CREATE MATERIALIZED VIEW project_environments_observations_mv ON CLUSTER default TO project_environments AS
|
||||
SELECT
|
||||
project_id,
|
||||
groupUniqArray(environment) AS environments
|
||||
FROM observations
|
||||
GROUP BY project_id;
|
||||
|
||||
CREATE MATERIALIZED VIEW project_environments_scores_mv ON CLUSTER default TO project_environments AS
|
||||
SELECT
|
||||
project_id,
|
||||
groupUniqArray(environment) AS environments
|
||||
FROM scores
|
||||
GROUP BY project_id;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
-- Drop materialized views feeding project_environments table
|
||||
DROP VIEW IF EXISTS project_environments_traces_mv ON CLUSTER default;
|
||||
DROP VIEW IF EXISTS project_environments_observations_mv ON CLUSTER default;
|
||||
DROP VIEW IF EXISTS project_environments_scores_mv ON CLUSTER default;
|
||||
@@ -0,0 +1,114 @@
|
||||
-- Recreate materialized views derived from traces_null table
|
||||
CREATE MATERIALIZED VIEW IF NOT EXISTS traces_all_amt_mv ON CLUSTER default TO traces_all_amt AS
|
||||
SELECT
|
||||
-- Identifiers
|
||||
tn.project_id as project_id,
|
||||
tn.id as id,
|
||||
min(tn.start_time) as timestamp, -- Backward compatibility: redundant with start_time
|
||||
min(tn.start_time) as start_time,
|
||||
max(coalesce(tn.end_time, tn.start_time)) as end_time,
|
||||
anyLast(tn.name) as name,
|
||||
|
||||
-- Metadata properties
|
||||
maxMap(tn.metadata) as metadata,
|
||||
anyLast(tn.user_id) as user_id,
|
||||
anyLast(tn.session_id) as session_id,
|
||||
anyLast(tn.environment) as environment,
|
||||
groupUniqArrayArray(tn.tags) as tags,
|
||||
anyLast(tn.version) as version,
|
||||
anyLast(tn.release) as release,
|
||||
|
||||
-- UI properties
|
||||
argMaxState(tn.bookmarked, if(tn.bookmarked is not null, tn.event_ts, toDateTime64(0, 3))) as bookmarked,
|
||||
argMaxState(tn.public, if(tn.public is not null, tn.event_ts, toDateTime64(0, 3))) as public,
|
||||
|
||||
-- Aggregations -- DO NOT USE
|
||||
groupUniqArrayArray(tn.observation_ids) as observation_ids,
|
||||
groupUniqArrayArray(tn.score_ids) as score_ids,
|
||||
sumMap(tn.cost_details) as cost_details,
|
||||
sumMap(tn.usage_details) as usage_details,
|
||||
|
||||
-- Input/Output
|
||||
argMaxState(tn.input, if(tn.input <> '', tn.event_ts, toDateTime64(0, 3))) as input,
|
||||
argMaxState(tn.output, if(tn.output <> '', tn.event_ts, toDateTime64(0, 3))) as output,
|
||||
|
||||
min(tn.created_at) as created_at,
|
||||
max(tn.updated_at) as updated_at
|
||||
FROM traces_null tn
|
||||
GROUP BY project_id, id;
|
||||
|
||||
CREATE MATERIALIZED VIEW IF NOT EXISTS traces_7d_amt_mv ON CLUSTER default TO traces_7d_amt AS
|
||||
SELECT
|
||||
-- Identifiers
|
||||
tn.project_id as project_id,
|
||||
tn.id as id,
|
||||
min(tn.start_time) as timestamp, -- Backward compatibility: redundant with start_time
|
||||
min(tn.start_time) as start_time,
|
||||
max(coalesce(tn.end_time, tn.start_time)) as end_time,
|
||||
anyLast(tn.name) as name,
|
||||
|
||||
-- Metadata properties
|
||||
maxMap(tn.metadata) as metadata,
|
||||
anyLast(tn.user_id) as user_id,
|
||||
anyLast(tn.session_id) as session_id,
|
||||
anyLast(tn.environment) as environment,
|
||||
groupUniqArrayArray(tn.tags) as tags,
|
||||
anyLast(tn.version) as version,
|
||||
anyLast(tn.release) as release,
|
||||
|
||||
-- UI properties
|
||||
argMaxState(tn.bookmarked, if(tn.bookmarked is not null, tn.event_ts, toDateTime64(0, 3))) as bookmarked,
|
||||
argMaxState(tn.public, if(tn.public is not null, tn.event_ts, toDateTime64(0, 3))) as public,
|
||||
|
||||
-- Aggregations -- DO NOT USE
|
||||
groupUniqArrayArray(tn.observation_ids) as observation_ids,
|
||||
groupUniqArrayArray(tn.score_ids) as score_ids,
|
||||
sumMap(tn.cost_details) as cost_details,
|
||||
sumMap(tn.usage_details) as usage_details,
|
||||
|
||||
-- Input/Output
|
||||
argMaxState(tn.input, if(tn.input <> '', tn.event_ts, toDateTime64(0, 3))) as input,
|
||||
argMaxState(tn.output, if(tn.output <> '', tn.event_ts, toDateTime64(0, 3))) as output,
|
||||
|
||||
min(tn.created_at) as created_at,
|
||||
max(tn.updated_at) as updated_at
|
||||
FROM traces_null tn
|
||||
GROUP BY project_id, id;
|
||||
|
||||
CREATE MATERIALIZED VIEW IF NOT EXISTS traces_30d_amt_mv ON CLUSTER default TO traces_30d_amt AS
|
||||
SELECT
|
||||
-- Identifiers
|
||||
tn.project_id as project_id,
|
||||
tn.id as id,
|
||||
min(tn.start_time) as timestamp, -- Backward compatibility: redundant with start_time
|
||||
min(tn.start_time) as start_time,
|
||||
max(coalesce(tn.end_time, tn.start_time)) as end_time,
|
||||
anyLast(tn.name) as name,
|
||||
|
||||
-- Metadata properties
|
||||
maxMap(tn.metadata) as metadata,
|
||||
anyLast(tn.user_id) as user_id,
|
||||
anyLast(tn.session_id) as session_id,
|
||||
anyLast(tn.environment) as environment,
|
||||
groupUniqArrayArray(tn.tags) as tags,
|
||||
anyLast(tn.version) as version,
|
||||
anyLast(tn.release) as release,
|
||||
|
||||
-- UI properties
|
||||
argMaxState(tn.bookmarked, if(tn.bookmarked is not null, tn.event_ts, toDateTime64(0, 3))) as bookmarked,
|
||||
argMaxState(tn.public, if(tn.public is not null, tn.event_ts, toDateTime64(0, 3))) as public,
|
||||
|
||||
-- Aggregations -- DO NOT USE
|
||||
groupUniqArrayArray(tn.observation_ids) as observation_ids,
|
||||
groupUniqArrayArray(tn.score_ids) as score_ids,
|
||||
sumMap(tn.cost_details) as cost_details,
|
||||
sumMap(tn.usage_details) as usage_details,
|
||||
|
||||
-- Input/Output
|
||||
argMaxState(tn.input, if(tn.input <> '', tn.event_ts, toDateTime64(0, 3))) as input,
|
||||
argMaxState(tn.output, if(tn.output <> '', tn.event_ts, toDateTime64(0, 3))) as output,
|
||||
|
||||
min(tn.created_at) as created_at,
|
||||
max(tn.updated_at) as updated_at
|
||||
FROM traces_null tn
|
||||
GROUP BY project_id, id;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Drop materialized views derived from traces_null table
|
||||
DROP VIEW IF EXISTS traces_all_amt_mv ON CLUSTER default;
|
||||
DROP VIEW IF EXISTS traces_7d_amt_mv ON CLUSTER default;
|
||||
DROP VIEW IF EXISTS traces_30d_amt_mv ON CLUSTER default;
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
-- Recreate traces_null and trace amt tables
|
||||
CREATE TABLE traces_null ON CLUSTER default
|
||||
(
|
||||
-- Identifiers
|
||||
`project_id` String,
|
||||
`id` String,
|
||||
`start_time` DateTime64(3),
|
||||
`end_time` Nullable(DateTime64(3)),
|
||||
`name` Nullable(String),
|
||||
|
||||
-- Metadata properties
|
||||
`metadata` Map(LowCardinality(String), String),
|
||||
`user_id` Nullable(String),
|
||||
`session_id` Nullable(String),
|
||||
`environment` String,
|
||||
`tags` Array(String),
|
||||
`version` Nullable(String),
|
||||
`release` Nullable(String),
|
||||
|
||||
-- UI properties - We make them nullable to prevent absent values being interpreted as overwrites.
|
||||
`bookmarked` Nullable(Bool),
|
||||
`public` Nullable(Bool),
|
||||
|
||||
-- Aggregations -- DO NOT USE
|
||||
`observation_ids` Array(String),
|
||||
`score_ids` Array(String),
|
||||
`cost_details` Map(String, Decimal64(12)),
|
||||
`usage_details` Map(String, UInt64),
|
||||
-- TODO: Do we want to aggregate/collect `levels` seen within the trace?
|
||||
|
||||
-- Input/Output
|
||||
`input` String,
|
||||
`output` String,
|
||||
|
||||
`created_at` DateTime64(3),
|
||||
`updated_at` DateTime64(3),
|
||||
`event_ts` DateTime64(3)
|
||||
) Engine = Null();
|
||||
|
||||
CREATE TABLE traces_all_amt ON CLUSTER default
|
||||
(
|
||||
-- Identifiers
|
||||
`project_id` String,
|
||||
`id` String,
|
||||
`timestamp` SimpleAggregateFunction(min, DateTime64(3)), -- Backward compatibility: redundant with start_time
|
||||
`start_time` SimpleAggregateFunction(min, DateTime64(3)),
|
||||
`end_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
|
||||
`name` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
|
||||
-- Metadata properties
|
||||
`metadata` SimpleAggregateFunction(maxMap, Map(String, String)),
|
||||
`user_id` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`session_id` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`environment` SimpleAggregateFunction(anyLast, String),
|
||||
`tags` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`version` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`release` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
|
||||
-- UI properties
|
||||
`bookmarked` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
|
||||
`public` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
|
||||
|
||||
-- Aggregations -- DO NOT USE
|
||||
`observation_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`score_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`cost_details` SimpleAggregateFunction(sumMap, Map(String, Decimal(38, 12))),
|
||||
`usage_details` SimpleAggregateFunction(sumMap, Map(String, UInt64)),
|
||||
|
||||
-- Input/Output -> prefer correctness via argMax
|
||||
`input` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
|
||||
`output` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
|
||||
|
||||
`created_at` SimpleAggregateFunction(min, DateTime64(3)),
|
||||
`updated_at` SimpleAggregateFunction(max, DateTime64(3)),
|
||||
|
||||
-- Indexes
|
||||
INDEX idx_trace_id id TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_user_id user_id TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_session_id session_id TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_name name TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_version version TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_release release TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_tags tags TYPE bloom_filter(0.001) GRANULARITY 1
|
||||
) Engine = AggregatingMergeTree()
|
||||
ORDER BY (project_id, id);
|
||||
|
||||
CREATE TABLE traces_7d_amt ON CLUSTER default
|
||||
(
|
||||
-- Identifiers
|
||||
`project_id` String,
|
||||
`id` String,
|
||||
`timestamp` SimpleAggregateFunction(min, DateTime64(3)), -- Backward compatibility: redundant with start_time
|
||||
`start_time` SimpleAggregateFunction(min, DateTime64(3)),
|
||||
`end_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
|
||||
`name` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
|
||||
-- Metadata properties
|
||||
`metadata` SimpleAggregateFunction(maxMap, Map(String, String)),
|
||||
`user_id` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`session_id` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`environment` SimpleAggregateFunction(anyLast, String),
|
||||
`tags` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`version` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`release` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
|
||||
-- UI properties
|
||||
`bookmarked` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
|
||||
`public` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
|
||||
|
||||
-- Aggregations -- DO NOT USE
|
||||
`observation_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`score_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`cost_details` SimpleAggregateFunction(sumMap, Map(String, Decimal(38, 12))),
|
||||
`usage_details` SimpleAggregateFunction(sumMap, Map(String, UInt64)),
|
||||
|
||||
-- Input/Output -> prefer correctness via argMax
|
||||
`input` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
|
||||
`output` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
|
||||
|
||||
`created_at` SimpleAggregateFunction(min, DateTime64(3)),
|
||||
`updated_at` SimpleAggregateFunction(max, DateTime64(3)),
|
||||
|
||||
-- Indexes
|
||||
INDEX idx_user_id user_id TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_session_id session_id TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_name name TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_version version TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_release release TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_tags tags TYPE bloom_filter(0.001) GRANULARITY 1
|
||||
) Engine = AggregatingMergeTree()
|
||||
ORDER BY (project_id, id)
|
||||
TTL toDate(start_time) + INTERVAL 7 DAY;
|
||||
|
||||
CREATE TABLE traces_30d_amt ON CLUSTER default
|
||||
(
|
||||
-- Identifiers
|
||||
`project_id` String,
|
||||
`id` String,
|
||||
`timestamp` SimpleAggregateFunction(min, DateTime64(3)), -- Backward compatibility: redundant with start_time
|
||||
`start_time` SimpleAggregateFunction(min, DateTime64(3)),
|
||||
`end_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
|
||||
`name` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
|
||||
-- Metadata properties
|
||||
`metadata` SimpleAggregateFunction(maxMap, Map(String, String)),
|
||||
`user_id` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`session_id` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`environment` SimpleAggregateFunction(anyLast, String),
|
||||
`tags` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`version` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`release` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
|
||||
-- UI properties
|
||||
`bookmarked` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
|
||||
`public` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
|
||||
|
||||
-- Aggregations -- DO NOT USE
|
||||
`observation_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`score_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`cost_details` SimpleAggregateFunction(sumMap, Map(String, Decimal(38, 12))),
|
||||
`usage_details` SimpleAggregateFunction(sumMap, Map(String, UInt64)),
|
||||
|
||||
-- Input/Output -> prefer correctness via argMax
|
||||
`input` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
|
||||
`output` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
|
||||
|
||||
`created_at` SimpleAggregateFunction(min, DateTime64(3)),
|
||||
`updated_at` SimpleAggregateFunction(max, DateTime64(3)),
|
||||
|
||||
-- Indexes
|
||||
INDEX idx_user_id user_id TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_session_id session_id TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_name name TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_version version TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_release release TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_tags tags TYPE bloom_filter(0.001) GRANULARITY 1
|
||||
) Engine = AggregatingMergeTree()
|
||||
ORDER BY (project_id, id)
|
||||
TTL toDate(start_time) + INTERVAL 30 DAY;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
-- Drop traces_null and trace amt tables
|
||||
DROP TABLE IF EXISTS traces_null ON CLUSTER default;
|
||||
DROP TABLE IF EXISTS traces_all_amt ON CLUSTER default;
|
||||
DROP TABLE IF EXISTS traces_7d_amt ON CLUSTER default;
|
||||
DROP TABLE IF EXISTS traces_30d_amt ON CLUSTER default;
|
||||
+1
@@ -0,0 +1 @@
|
||||
ALTER TABLE scores ON CLUSTER default DROP COLUMN IF EXISTS execution_trace_id;
|
||||
+1
@@ -0,0 +1 @@
|
||||
ALTER TABLE scores ON CLUSTER default ADD COLUMN IF NOT EXISTS execution_trace_id Nullable(String);
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
-- Recreate materialized views for project_environments table
|
||||
CREATE MATERIALIZED VIEW project_environments_traces_mv TO project_environments AS
|
||||
SELECT
|
||||
project_id,
|
||||
groupUniqArray(environment) AS environments
|
||||
FROM traces
|
||||
GROUP BY project_id;
|
||||
|
||||
CREATE MATERIALIZED VIEW project_environments_observations_mv TO project_environments AS
|
||||
SELECT
|
||||
project_id,
|
||||
groupUniqArray(environment) AS environments
|
||||
FROM observations
|
||||
GROUP BY project_id;
|
||||
|
||||
CREATE MATERIALIZED VIEW project_environments_scores_mv TO project_environments AS
|
||||
SELECT
|
||||
project_id,
|
||||
groupUniqArray(environment) AS environments
|
||||
FROM scores
|
||||
GROUP BY project_id;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
-- Drop materialized views feeding project_environments table
|
||||
DROP VIEW IF EXISTS project_environments_traces_mv;
|
||||
DROP VIEW IF EXISTS project_environments_observations_mv;
|
||||
DROP VIEW IF EXISTS project_environments_scores_mv;
|
||||
@@ -0,0 +1,114 @@
|
||||
-- Recreate materialized views derived from traces_null table
|
||||
CREATE MATERIALIZED VIEW IF NOT EXISTS traces_all_amt_mv TO traces_all_amt AS
|
||||
SELECT
|
||||
-- Identifiers
|
||||
tn.project_id as project_id,
|
||||
tn.id as id,
|
||||
min(tn.start_time) as timestamp, -- Backward compatibility: redundant with start_time
|
||||
min(tn.start_time) as start_time,
|
||||
max(coalesce(tn.end_time, tn.start_time)) as end_time,
|
||||
anyLast(tn.name) as name,
|
||||
|
||||
-- Metadata properties
|
||||
maxMap(tn.metadata) as metadata,
|
||||
anyLast(tn.user_id) as user_id,
|
||||
anyLast(tn.session_id) as session_id,
|
||||
anyLast(tn.environment) as environment,
|
||||
groupUniqArrayArray(tn.tags) as tags,
|
||||
anyLast(tn.version) as version,
|
||||
anyLast(tn.release) as release,
|
||||
|
||||
-- UI properties
|
||||
argMaxState(tn.bookmarked, if(tn.bookmarked is not null, tn.event_ts, toDateTime64(0, 3))) as bookmarked,
|
||||
argMaxState(tn.public, if(tn.public is not null, tn.event_ts, toDateTime64(0, 3))) as public,
|
||||
|
||||
-- Aggregations -- DO NOT USE
|
||||
groupUniqArrayArray(tn.observation_ids) as observation_ids,
|
||||
groupUniqArrayArray(tn.score_ids) as score_ids,
|
||||
sumMap(tn.cost_details) as cost_details,
|
||||
sumMap(tn.usage_details) as usage_details,
|
||||
|
||||
-- Input/Output
|
||||
argMaxState(tn.input, if(tn.input <> '', tn.event_ts, toDateTime64(0, 3))) as input,
|
||||
argMaxState(tn.output, if(tn.output <> '', tn.event_ts, toDateTime64(0, 3))) as output,
|
||||
|
||||
min(tn.created_at) as created_at,
|
||||
max(tn.updated_at) as updated_at
|
||||
FROM traces_null tn
|
||||
GROUP BY project_id, id;
|
||||
|
||||
CREATE MATERIALIZED VIEW IF NOT EXISTS traces_7d_amt_mv TO traces_7d_amt AS
|
||||
SELECT
|
||||
-- Identifiers
|
||||
tn.project_id as project_id,
|
||||
tn.id as id,
|
||||
min(tn.start_time) as timestamp, -- Backward compatibility: redundant with start_time
|
||||
min(tn.start_time) as start_time,
|
||||
max(coalesce(tn.end_time, tn.start_time)) as end_time,
|
||||
anyLast(tn.name) as name,
|
||||
|
||||
-- Metadata properties
|
||||
maxMap(tn.metadata) as metadata,
|
||||
anyLast(tn.user_id) as user_id,
|
||||
anyLast(tn.session_id) as session_id,
|
||||
anyLast(tn.environment) as environment,
|
||||
groupUniqArrayArray(tn.tags) as tags,
|
||||
anyLast(tn.version) as version,
|
||||
anyLast(tn.release) as release,
|
||||
|
||||
-- UI properties
|
||||
argMaxState(tn.bookmarked, if(tn.bookmarked is not null, tn.event_ts, toDateTime64(0, 3))) as bookmarked,
|
||||
argMaxState(tn.public, if(tn.public is not null, tn.event_ts, toDateTime64(0, 3))) as public,
|
||||
|
||||
-- Aggregations -- DO NOT USE
|
||||
groupUniqArrayArray(tn.observation_ids) as observation_ids,
|
||||
groupUniqArrayArray(tn.score_ids) as score_ids,
|
||||
sumMap(tn.cost_details) as cost_details,
|
||||
sumMap(tn.usage_details) as usage_details,
|
||||
|
||||
-- Input/Output
|
||||
argMaxState(tn.input, if(tn.input <> '', tn.event_ts, toDateTime64(0, 3))) as input,
|
||||
argMaxState(tn.output, if(tn.output <> '', tn.event_ts, toDateTime64(0, 3))) as output,
|
||||
|
||||
min(tn.created_at) as created_at,
|
||||
max(tn.updated_at) as updated_at
|
||||
FROM traces_null tn
|
||||
GROUP BY project_id, id;
|
||||
|
||||
CREATE MATERIALIZED VIEW IF NOT EXISTS traces_30d_amt_mv TO traces_30d_amt AS
|
||||
SELECT
|
||||
-- Identifiers
|
||||
tn.project_id as project_id,
|
||||
tn.id as id,
|
||||
min(tn.start_time) as timestamp, -- Backward compatibility: redundant with start_time
|
||||
min(tn.start_time) as start_time,
|
||||
max(coalesce(tn.end_time, tn.start_time)) as end_time,
|
||||
anyLast(tn.name) as name,
|
||||
|
||||
-- Metadata properties
|
||||
maxMap(tn.metadata) as metadata,
|
||||
anyLast(tn.user_id) as user_id,
|
||||
anyLast(tn.session_id) as session_id,
|
||||
anyLast(tn.environment) as environment,
|
||||
groupUniqArrayArray(tn.tags) as tags,
|
||||
anyLast(tn.version) as version,
|
||||
anyLast(tn.release) as release,
|
||||
|
||||
-- UI properties
|
||||
argMaxState(tn.bookmarked, if(tn.bookmarked is not null, tn.event_ts, toDateTime64(0, 3))) as bookmarked,
|
||||
argMaxState(tn.public, if(tn.public is not null, tn.event_ts, toDateTime64(0, 3))) as public,
|
||||
|
||||
-- Aggregations -- DO NOT USE
|
||||
groupUniqArrayArray(tn.observation_ids) as observation_ids,
|
||||
groupUniqArrayArray(tn.score_ids) as score_ids,
|
||||
sumMap(tn.cost_details) as cost_details,
|
||||
sumMap(tn.usage_details) as usage_details,
|
||||
|
||||
-- Input/Output
|
||||
argMaxState(tn.input, if(tn.input <> '', tn.event_ts, toDateTime64(0, 3))) as input,
|
||||
argMaxState(tn.output, if(tn.output <> '', tn.event_ts, toDateTime64(0, 3))) as output,
|
||||
|
||||
min(tn.created_at) as created_at,
|
||||
max(tn.updated_at) as updated_at
|
||||
FROM traces_null tn
|
||||
GROUP BY project_id, id;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Drop materialized views derived from traces_null table
|
||||
DROP VIEW IF EXISTS traces_all_amt_mv;
|
||||
DROP VIEW IF EXISTS traces_7d_amt_mv;
|
||||
DROP VIEW IF EXISTS traces_30d_amt_mv;
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
-- Recreate traces_null and trace amt tables
|
||||
CREATE TABLE traces_null
|
||||
(
|
||||
-- Identifiers
|
||||
`project_id` String,
|
||||
`id` String,
|
||||
`start_time` DateTime64(3),
|
||||
`end_time` Nullable(DateTime64(3)),
|
||||
`name` Nullable(String),
|
||||
|
||||
-- Metadata properties
|
||||
`metadata` Map(LowCardinality(String), String),
|
||||
`user_id` Nullable(String),
|
||||
`session_id` Nullable(String),
|
||||
`environment` String,
|
||||
`tags` Array(String),
|
||||
`version` Nullable(String),
|
||||
`release` Nullable(String),
|
||||
|
||||
-- UI properties - We make them nullable to prevent absent values being interpreted as overwrites.
|
||||
`bookmarked` Nullable(Bool),
|
||||
`public` Nullable(Bool),
|
||||
|
||||
-- Aggregations -- DO NOT USE
|
||||
`observation_ids` Array(String),
|
||||
`score_ids` Array(String),
|
||||
`cost_details` Map(String, Decimal64(12)),
|
||||
`usage_details` Map(String, UInt64),
|
||||
-- TODO: Do we want to aggregate/collect `levels` seen within the trace?
|
||||
|
||||
-- Input/Output
|
||||
`input` String,
|
||||
`output` String,
|
||||
|
||||
`created_at` DateTime64(3),
|
||||
`updated_at` DateTime64(3),
|
||||
`event_ts` DateTime64(3)
|
||||
) Engine = Null();
|
||||
|
||||
CREATE TABLE traces_all_amt
|
||||
(
|
||||
-- Identifiers
|
||||
`project_id` String,
|
||||
`id` String,
|
||||
`timestamp` SimpleAggregateFunction(min, DateTime64(3)), -- Backward compatibility: redundant with start_time
|
||||
`start_time` SimpleAggregateFunction(min, DateTime64(3)),
|
||||
`end_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
|
||||
`name` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
|
||||
-- Metadata properties
|
||||
`metadata` SimpleAggregateFunction(maxMap, Map(String, String)),
|
||||
`user_id` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`session_id` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`environment` SimpleAggregateFunction(anyLast, String),
|
||||
`tags` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`version` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`release` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
|
||||
-- UI properties
|
||||
`bookmarked` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
|
||||
`public` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
|
||||
|
||||
-- Aggregations -- DO NOT USE
|
||||
`observation_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`score_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`cost_details` SimpleAggregateFunction(sumMap, Map(String, Decimal(38, 12))),
|
||||
`usage_details` SimpleAggregateFunction(sumMap, Map(String, UInt64)),
|
||||
|
||||
-- Input/Output -> prefer correctness via argMax
|
||||
`input` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
|
||||
`output` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
|
||||
|
||||
`created_at` SimpleAggregateFunction(min, DateTime64(3)),
|
||||
`updated_at` SimpleAggregateFunction(max, DateTime64(3)),
|
||||
|
||||
-- Indexes
|
||||
INDEX idx_trace_id id TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_user_id user_id TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_session_id session_id TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_name name TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_version version TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_release release TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_tags tags TYPE bloom_filter(0.001) GRANULARITY 1
|
||||
) Engine = AggregatingMergeTree()
|
||||
ORDER BY (project_id, id);
|
||||
|
||||
CREATE TABLE traces_7d_amt
|
||||
(
|
||||
-- Identifiers
|
||||
`project_id` String,
|
||||
`id` String,
|
||||
`timestamp` SimpleAggregateFunction(min, DateTime64(3)), -- Backward compatibility: redundant with start_time
|
||||
`start_time` SimpleAggregateFunction(min, DateTime64(3)),
|
||||
`end_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
|
||||
`name` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
|
||||
-- Metadata properties
|
||||
`metadata` SimpleAggregateFunction(maxMap, Map(String, String)),
|
||||
`user_id` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`session_id` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`environment` SimpleAggregateFunction(anyLast, String),
|
||||
`tags` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`version` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`release` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
|
||||
-- UI properties
|
||||
`bookmarked` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
|
||||
`public` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
|
||||
|
||||
-- Aggregations -- DO NOT USE
|
||||
`observation_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`score_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`cost_details` SimpleAggregateFunction(sumMap, Map(String, Decimal(38, 12))),
|
||||
`usage_details` SimpleAggregateFunction(sumMap, Map(String, UInt64)),
|
||||
|
||||
-- Input/Output -> prefer correctness via argMax
|
||||
`input` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
|
||||
`output` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
|
||||
|
||||
`created_at` SimpleAggregateFunction(min, DateTime64(3)),
|
||||
`updated_at` SimpleAggregateFunction(max, DateTime64(3)),
|
||||
|
||||
-- Indexes
|
||||
INDEX idx_user_id user_id TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_session_id session_id TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_name name TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_version version TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_release release TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_tags tags TYPE bloom_filter(0.001) GRANULARITY 1
|
||||
) Engine = AggregatingMergeTree()
|
||||
ORDER BY (project_id, id)
|
||||
TTL toDate(start_time) + INTERVAL 7 DAY;
|
||||
|
||||
CREATE TABLE traces_30d_amt
|
||||
(
|
||||
-- Identifiers
|
||||
`project_id` String,
|
||||
`id` String,
|
||||
`timestamp` SimpleAggregateFunction(min, DateTime64(3)), -- Backward compatibility: redundant with start_time
|
||||
`start_time` SimpleAggregateFunction(min, DateTime64(3)),
|
||||
`end_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
|
||||
`name` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
|
||||
-- Metadata properties
|
||||
`metadata` SimpleAggregateFunction(maxMap, Map(String, String)),
|
||||
`user_id` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`session_id` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`environment` SimpleAggregateFunction(anyLast, String),
|
||||
`tags` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`version` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
`release` SimpleAggregateFunction(anyLast, Nullable(String)),
|
||||
|
||||
-- UI properties
|
||||
`bookmarked` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
|
||||
`public` AggregateFunction(argMax, Nullable(Bool), DateTime64(3)),
|
||||
|
||||
-- Aggregations -- DO NOT USE
|
||||
`observation_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`score_ids` SimpleAggregateFunction(groupUniqArrayArray, Array(String)),
|
||||
`cost_details` SimpleAggregateFunction(sumMap, Map(String, Decimal(38, 12))),
|
||||
`usage_details` SimpleAggregateFunction(sumMap, Map(String, UInt64)),
|
||||
|
||||
-- Input/Output -> prefer correctness via argMax
|
||||
`input` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
|
||||
`output` AggregateFunction(argMax, String, DateTime64(3)) CODEC (ZSTD(3)),
|
||||
|
||||
`created_at` SimpleAggregateFunction(min, DateTime64(3)),
|
||||
`updated_at` SimpleAggregateFunction(max, DateTime64(3)),
|
||||
|
||||
-- Indexes
|
||||
INDEX idx_user_id user_id TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_session_id session_id TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_name name TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_version version TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_release release TYPE bloom_filter(0.001) GRANULARITY 1,
|
||||
INDEX idx_tags tags TYPE bloom_filter(0.001) GRANULARITY 1
|
||||
) Engine = AggregatingMergeTree()
|
||||
ORDER BY (project_id, id)
|
||||
TTL toDate(start_time) + INTERVAL 30 DAY;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
-- Drop traces_null and trace amt tables
|
||||
DROP TABLE IF EXISTS traces_null;
|
||||
DROP TABLE IF EXISTS traces_all_amt;
|
||||
DROP TABLE IF EXISTS traces_7d_amt;
|
||||
DROP TABLE IF EXISTS traces_30d_amt;
|
||||
+1
@@ -0,0 +1 @@
|
||||
ALTER TABLE scores DROP COLUMN IF EXISTS execution_trace_id;
|
||||
+1
@@ -0,0 +1 @@
|
||||
ALTER TABLE scores ADD COLUMN IF NOT EXISTS execution_trace_id Nullable(String);
|
||||
@@ -0,0 +1,310 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Development-only ClickHouse table creation script
|
||||
# This script is for creating experimental/development tables that are not yet
|
||||
# ready to be part of the official migration system.
|
||||
#
|
||||
# Usage:
|
||||
# pnpm run ch:dev-tables (from packages/shared/)
|
||||
#
|
||||
# This script is automatically run as part of:
|
||||
# - pnpm run dx
|
||||
# - pnpm run dx-f
|
||||
# - pnpm run ch:reset
|
||||
|
||||
# Load environment variables
|
||||
[ -f ../../.env ] && source ../../.env
|
||||
|
||||
# Check if CLICKHOUSE_MIGRATION_URL is configured
|
||||
if [ -z "${CLICKHOUSE_MIGRATION_URL}" ]; then
|
||||
echo "Error: CLICKHOUSE_MIGRATION_URL is not configured."
|
||||
echo "Please set CLICKHOUSE_MIGRATION_URL in your environment variables."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if CLICKHOUSE_USER is set
|
||||
if [ -z "${CLICKHOUSE_USER}" ]; then
|
||||
echo "Error: CLICKHOUSE_USER is not set."
|
||||
echo "Please set CLICKHOUSE_USER in your environment variables."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if CLICKHOUSE_PASSWORD is set
|
||||
if [ -z "${CLICKHOUSE_PASSWORD}" ]; then
|
||||
echo "Error: CLICKHOUSE_PASSWORD is not set."
|
||||
echo "Please set CLICKHOUSE_PASSWORD in your environment variables."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure CLICKHOUSE_DB is set
|
||||
if [ -z "${CLICKHOUSE_DB}" ]; then
|
||||
export CLICKHOUSE_DB="default"
|
||||
fi
|
||||
|
||||
# Parse the CLICKHOUSE_MIGRATION_URL to extract host and port
|
||||
# Expected format: clickhouse://localhost:9000
|
||||
if [[ $CLICKHOUSE_MIGRATION_URL =~ ^clickhouse://([^:]+):([0-9]+)$ ]]; then
|
||||
CLICKHOUSE_HOST="${BASH_REMATCH[1]}"
|
||||
CLICKHOUSE_PORT="${BASH_REMATCH[2]}"
|
||||
elif [[ $CLICKHOUSE_MIGRATION_URL =~ ^clickhouse://([^:]+)$ ]]; then
|
||||
CLICKHOUSE_HOST="${BASH_REMATCH[1]}"
|
||||
CLICKHOUSE_PORT="9000" # Default native protocol port
|
||||
else
|
||||
echo "Error: Could not parse CLICKHOUSE_MIGRATION_URL: ${CLICKHOUSE_MIGRATION_URL}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v clickhouse &> /dev/null
|
||||
then
|
||||
echo "Error: clickhouse binary could not be found. Please install ClickHouse client tools."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Creating development tables in ClickHouse..."
|
||||
|
||||
# Execute the CREATE TABLE statements
|
||||
# Add your development tables here using CREATE TABLE IF NOT EXISTS
|
||||
|
||||
clickhouse client \
|
||||
--host="${CLICKHOUSE_HOST}" \
|
||||
--port="${CLICKHOUSE_PORT}" \
|
||||
--user="${CLICKHOUSE_USER}" \
|
||||
--password="${CLICKHOUSE_PASSWORD}" \
|
||||
--database="${CLICKHOUSE_DB}" \
|
||||
--multiquery <<EOF
|
||||
|
||||
-- Create observations_batch_staging table for batch processing
|
||||
-- This table uses 3-minute partitions to efficiently process observations in batches
|
||||
-- and merge them with traces data into the events table.
|
||||
-- See LFE-7122 for implementation details.
|
||||
CREATE TABLE IF NOT EXISTS observations_batch_staging
|
||||
(
|
||||
id String,
|
||||
trace_id String,
|
||||
project_id String,
|
||||
type LowCardinality(String),
|
||||
parent_observation_id Nullable(String),
|
||||
start_time DateTime64(3),
|
||||
end_time Nullable(DateTime64(3)),
|
||||
name String,
|
||||
metadata Map(LowCardinality(String), String),
|
||||
level LowCardinality(String),
|
||||
status_message Nullable(String),
|
||||
version Nullable(String),
|
||||
input Nullable(String) CODEC(ZSTD(3)),
|
||||
output Nullable(String) CODEC(ZSTD(3)),
|
||||
provided_model_name Nullable(String),
|
||||
internal_model_id Nullable(String),
|
||||
model_parameters Nullable(String),
|
||||
provided_usage_details Map(LowCardinality(String), UInt64),
|
||||
usage_details Map(LowCardinality(String), UInt64),
|
||||
provided_cost_details Map(LowCardinality(String), Decimal64(12)),
|
||||
cost_details Map(LowCardinality(String), Decimal64(12)),
|
||||
total_cost Nullable(Decimal64(12)),
|
||||
completion_start_time Nullable(DateTime64(3)),
|
||||
prompt_id Nullable(String),
|
||||
prompt_name Nullable(String),
|
||||
prompt_version Nullable(UInt16),
|
||||
created_at DateTime64(3) DEFAULT now(),
|
||||
updated_at DateTime64(3) DEFAULT now(),
|
||||
event_ts DateTime64(3),
|
||||
is_deleted UInt8,
|
||||
s3_first_seen_timestamp DateTime64(3),
|
||||
environment LowCardinality(String) DEFAULT 'default',
|
||||
) ENGINE = ReplacingMergeTree(event_ts, is_deleted)
|
||||
PARTITION BY toStartOfInterval(s3_first_seen_timestamp, INTERVAL 3 MINUTE)
|
||||
ORDER BY (
|
||||
project_id,
|
||||
toDate(s3_first_seen_timestamp),
|
||||
trace_id,
|
||||
id
|
||||
);
|
||||
|
||||
-- Create new events table for development setups.
|
||||
-- We expect this to be fully immutable and eventually replace observations.
|
||||
-- See LFE-5394 for ongoing discussion.
|
||||
-- Remove IF NOT EXISTS when moving this to prod migrations.
|
||||
CREATE TABLE IF NOT EXISTS events
|
||||
(
|
||||
org_id String, -- TODO: Unsure about this one
|
||||
project_id String,
|
||||
trace_id String,
|
||||
span_id String,
|
||||
parent_span_id Nullable(String),
|
||||
|
||||
start_time DateTime64(6),
|
||||
end_time Nullable(DateTime64(6)),
|
||||
|
||||
-- Core properties
|
||||
name String,
|
||||
type LowCardinality(String),
|
||||
environment LowCardinality(String) DEFAULT 'default',
|
||||
version Nullable(String),
|
||||
|
||||
user_id String,
|
||||
session_id String,
|
||||
|
||||
level LowCardinality(String),
|
||||
status_message String, -- Threat '' and null the same for search
|
||||
completion_start_time Nullable(DateTime64(6)),
|
||||
|
||||
-- Prompt
|
||||
prompt_id Nullable(String),
|
||||
prompt_name Nullable(String),
|
||||
prompt_version Nullable(String),
|
||||
|
||||
-- Model
|
||||
model_id Nullable(String),
|
||||
provided_model_name Nullable(String),
|
||||
model_parameters Nullable(String),
|
||||
|
||||
-- Usage
|
||||
provided_usage_details Map(LowCardinality(String), UInt64),
|
||||
usage_details Map(LowCardinality(String), UInt64),
|
||||
provided_cost_details Map(LowCardinality(String), Decimal(18, 12)),
|
||||
cost_details Map(LowCardinality(String), Decimal(18, 12)),
|
||||
total_cost Decimal(18,12), -- 0 if not provided
|
||||
|
||||
-- I/O
|
||||
input String CODEC(ZSTD(3)),
|
||||
output String CODEC(ZSTD(3)),
|
||||
|
||||
-- TODO Metadata: Decide for approach
|
||||
-- -- Approach 1: Use plain JSON type with default config
|
||||
metadata JSON(max_dynamic_paths=1024, max_dynamic_types=32),
|
||||
-- -- Approach 2: Uses ideas from https://www.uber.com/en-DE/blog/logging/
|
||||
-- -- but uses Dynamic type to make this a single list
|
||||
metadata_names Array(String),
|
||||
metadata_values Array(Dynamic(max_types=32)),
|
||||
-- -- Approach 3: 1:1 copy of https://www.uber.com/en-DE/blog/logging/
|
||||
-- -- May require further high-level types and lots of thought during
|
||||
-- -- write and query-time.
|
||||
metadata_string_names Array(String),
|
||||
metadata_string_values Array(String),
|
||||
metadata_number_names Array(String),
|
||||
metadata_number_values Array(Float64),
|
||||
metadata_bool_names Array(String),
|
||||
metadata_bool_values Array(UInt8),
|
||||
|
||||
-- Source metadata (Instrumentation)
|
||||
source LowCardinality(String),
|
||||
service_name Nullable(String),
|
||||
service_version Nullable(String),
|
||||
scope_name Nullable(String),
|
||||
scope_version Nullable(String),
|
||||
telemetry_sdk_language Nullable(String),
|
||||
telemetry_sdk_name Nullable(String),
|
||||
telemetry_sdk_version Nullable(String),
|
||||
|
||||
-- Generic props
|
||||
blob_storage_file_path String,
|
||||
event_raw String,
|
||||
event_bytes UInt64,
|
||||
created_at DateTime64(6) DEFAULT now(),
|
||||
updated_at DateTime64(6) DEFAULT now(),
|
||||
event_ts DateTime64(6),
|
||||
is_deleted UInt8,
|
||||
|
||||
-- Indexes
|
||||
INDEX idx_span_id span_id TYPE bloom_filter(0.01) GRANULARITY 1,
|
||||
INDEX idx_trace_id trace_id TYPE bloom_filter(0.01) GRANULARITY 1,
|
||||
INDEX idx_type type TYPE set(50) GRANULARITY 1,
|
||||
INDEX idx_created_at created_at TYPE minmax GRANULARITY 1,
|
||||
INDEX idx_updated_at updated_at TYPE minmax GRANULARITY 1,
|
||||
|
||||
-- Full Text Search Indexes (We should try different index sizes, e.g. 2048, 4096, or 8192)
|
||||
INDEX idx_fts_input_1 input TYPE ngrambf_v1(1, 1024, 1, 0) GRANULARITY 1,
|
||||
INDEX idx_fts_input_2 input TYPE ngrambf_v1(2, 1024, 1, 0) GRANULARITY 1,
|
||||
INDEX idx_fts_input_4 input TYPE ngrambf_v1(4, 1024, 1, 0) GRANULARITY 1,
|
||||
INDEX idx_fts_input_8 input TYPE ngrambf_v1(8, 1024, 1, 0) GRANULARITY 1,
|
||||
|
||||
INDEX idx_fts_output_1 output TYPE ngrambf_v1(1, 1024, 1, 0) GRANULARITY 1,
|
||||
INDEX idx_fts_output_2 output TYPE ngrambf_v1(2, 1024, 1, 0) GRANULARITY 1,
|
||||
INDEX idx_fts_output_4 output TYPE ngrambf_v1(4, 1024, 1, 0) GRANULARITY 1,
|
||||
INDEX idx_fts_output_8 output TYPE ngrambf_v1(8, 1024, 1, 0) GRANULARITY 1,
|
||||
)
|
||||
ENGINE = ReplacingMergeTree(event_ts, is_deleted)
|
||||
-- ENGINE = (Replicated)ReplacingMergeTree(event_ts, is_deleted)
|
||||
PARTITION BY toYYYYMM(start_time)
|
||||
ORDER BY (project_id, toUnixTimestamp(start_time), trace_id, span_id);
|
||||
|
||||
EOF
|
||||
|
||||
echo "Populating development tables with sample data..."
|
||||
|
||||
clickhouse client \
|
||||
--host="${CLICKHOUSE_HOST}" \
|
||||
--port="${CLICKHOUSE_PORT}" \
|
||||
--user="${CLICKHOUSE_USER}" \
|
||||
--password="${CLICKHOUSE_PASSWORD}" \
|
||||
--database="${CLICKHOUSE_DB}" \
|
||||
--multiquery <<EOF
|
||||
TRUNCATE events;
|
||||
INSERT INTO events (org_id, project_id, trace_id, span_id, parent_span_id, start_time, end_time, name, type,
|
||||
environment, version, user_id, session_id, level, status_message, completion_start_time, prompt_id,
|
||||
prompt_name, prompt_version, model_id, provided_model_name, model_parameters,
|
||||
provided_usage_details, usage_details, provided_cost_details, cost_details, total_cost, input,
|
||||
output, metadata, metadata_names, metadata_values, metadata_string_names, metadata_string_values,
|
||||
metadata_number_names, metadata_number_values, metadata_bool_names, metadata_bool_values, source,
|
||||
service_name, service_version, scope_name, scope_version, telemetry_sdk_language,
|
||||
telemetry_sdk_name, telemetry_sdk_version, blob_storage_file_path, event_raw, event_bytes,
|
||||
created_at, updated_at, event_ts, is_deleted)
|
||||
SELECT concat('o', project_id) AS org_id,
|
||||
project_id,
|
||||
trace_id,
|
||||
id AS span_id,
|
||||
parent_observation_id AS parent_span_id,
|
||||
start_time,
|
||||
end_time,
|
||||
name,
|
||||
type,
|
||||
environment,
|
||||
version,
|
||||
concat('u_', floor(randUniform(1, 100))) AS user_id,
|
||||
concat('s_', floor(randUniform(1, 100))) AS session_id,
|
||||
level,
|
||||
ifNull(status_message, '') AS status_message,
|
||||
completion_start_time,
|
||||
prompt_id,
|
||||
prompt_name,
|
||||
CAST(prompt_version, 'Nullable(String)'),
|
||||
internal_model_id AS model_id,
|
||||
provided_model_name,
|
||||
model_parameters,
|
||||
provided_usage_details,
|
||||
usage_details,
|
||||
provided_cost_details,
|
||||
cost_details,
|
||||
ifNull(total_cost, 0) AS total_cost,
|
||||
ifNull(input, '') AS input,
|
||||
ifNull(output, '') AS output,
|
||||
CAST(metadata, 'JSON'),
|
||||
mapKeys(metadata) AS \`metadata.names\`,
|
||||
mapValues(metadata) AS \`metadata.values\`,
|
||||
mapKeys(metadata) AS metadata_string_names,
|
||||
mapValues(metadata) AS metadata_string_values,
|
||||
[] AS metadata_number_names,
|
||||
[] AS metadata_number_values,
|
||||
[] AS metadata_bool_names,
|
||||
[] AS metadata_bool_values,
|
||||
multiIf(mapContains(metadata, 'resourceAttributes'), 'otel', 'ingestion-api') AS source,
|
||||
NULL AS service_name,
|
||||
NULL AS service_version,
|
||||
NULL AS scope_name,
|
||||
NULL AS scope_version,
|
||||
NULL AS telemetry_sdk_language,
|
||||
NULL AS telemetry_sdk_name,
|
||||
NULL AS telemetry_sdk_version,
|
||||
'' AS blob_storage_file_path,
|
||||
'' AS event_raw,
|
||||
0 AS event_bytes,
|
||||
created_at,
|
||||
updated_at,
|
||||
event_ts,
|
||||
is_deleted
|
||||
FROM observations
|
||||
WHERE (is_deleted = 0);
|
||||
EOF
|
||||
|
||||
echo "Development tables created successfully (or already exist)."
|
||||
echo ""
|
||||
@@ -47,7 +47,8 @@
|
||||
"ch:up": "bash clickhouse/scripts/up.sh",
|
||||
"ch:down": "bash clickhouse/scripts/down.sh",
|
||||
"ch:drop": "bash clickhouse/scripts/drop.sh",
|
||||
"ch:reset": "pnpm run ch:down && pnpm run ch:up && pnpm run ch:seed",
|
||||
"ch:dev-tables": "bash clickhouse/scripts/dev-tables.sh",
|
||||
"ch:reset": "pnpm run ch:down && pnpm run ch:up && pnpm run ch:seed && pnpm run ch:dev-tables",
|
||||
"ch:seed": "dotenv -e ../../.env -- ts-node -r tsconfig-paths/register -r dotenv/config --compiler-options '{\"module\":\"CommonJS\"}' scripts/seeder/seed-clickhouse.ts",
|
||||
"load:setup": "dotenv -e ../../.env -- tsx scripts/seeder/load-seed-clickhouse.ts"
|
||||
},
|
||||
@@ -63,7 +64,7 @@
|
||||
"@azure/storage-blob": "^12.26.0",
|
||||
"@clickhouse/client": "^1.12.1",
|
||||
"@google-cloud/storage": "^7.17.0",
|
||||
"@langchain/anthropic": "^0.3.22",
|
||||
"@langchain/anthropic": "^0.3.29",
|
||||
"@langchain/aws": "^0.1.11",
|
||||
"@langchain/core": "^0.3.58",
|
||||
"@langchain/google-genai": "^0.2.12",
|
||||
@@ -73,12 +74,13 @@
|
||||
"@prisma/client": "^6.10.1",
|
||||
"@react-email/components": "^0.5.1",
|
||||
"@react-email/render": "^1.2.1",
|
||||
"@slack/oauth": "^3.0.3",
|
||||
"@slack/web-api": "^7.9.3",
|
||||
"@slack/oauth": "^3.0.4",
|
||||
"@slack/web-api": "^7.10.0",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bullmq": "^5.34.10",
|
||||
"dd-trace": "^5.36.0",
|
||||
"date-fns": "^3.3.1",
|
||||
"dd-trace": "^5.65.0",
|
||||
"decimal.js": "^10.4.3",
|
||||
"exponential-backoff": "^3.1.2",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
@@ -86,12 +88,12 @@
|
||||
"ipaddr.js": "^2.2.0",
|
||||
"jsonpath-plus": "10.3.0",
|
||||
"kysely": "^0.27.4",
|
||||
"langchain": "^0.3.28",
|
||||
"langfuse-langchain": "3.38.4",
|
||||
"langchain": "^0.3.30",
|
||||
"langfuse-langchain": "3.38.6",
|
||||
"lodash": "^4.17.21",
|
||||
"lossless-json": "^4.1.1",
|
||||
"next-auth": "^4.24.11",
|
||||
"nodemailer": "^6.9.15",
|
||||
"nodemailer": "^7.0.9",
|
||||
"prisma-extension-kysely": "^2.1.0",
|
||||
"uuid": "^9.0.1",
|
||||
"winston": "^3.15.0",
|
||||
@@ -103,9 +105,9 @@
|
||||
"@repo/typescript-config": "workspace:*",
|
||||
"@types/lodash": "^4.17.10",
|
||||
"@types/node": "^24.3.0",
|
||||
"@types/nodemailer": "^6.4.16",
|
||||
"@types/nodemailer": "^7.0.2",
|
||||
"@types/pg": "^8.11.10",
|
||||
"@types/react": "19.1.12",
|
||||
"@types/react": "19.2.2",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@typescript-eslint/parser": "^7.12.0",
|
||||
"eslint": "^8.57.0",
|
||||
@@ -113,7 +115,6 @@
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"kysely-codegen": "^0.16.8",
|
||||
"nodemon": "^3.1.7",
|
||||
"prettier": "^3.6.2",
|
||||
"prisma": "^6.10.1",
|
||||
"prisma-erd-generator": "^1.11.2",
|
||||
@@ -123,7 +124,7 @@
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "~19.1.12",
|
||||
"react": "~19.1.1"
|
||||
"@types/react": "~19.2.2",
|
||||
"react": "~19.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ export const JobExecutionStatus = {
|
||||
ERROR: "ERROR",
|
||||
PENDING: "PENDING",
|
||||
CANCELLED: "CANCELLED",
|
||||
DELAYED: "DELAYED",
|
||||
} as const;
|
||||
export type JobExecutionStatus =
|
||||
(typeof JobExecutionStatus)[keyof typeof JobExecutionStatus];
|
||||
@@ -332,6 +333,15 @@ export type BlobStorageIntegration = {
|
||||
created_at: Generated<Timestamp>;
|
||||
updated_at: Generated<Timestamp>;
|
||||
};
|
||||
export type CloudSpendAlert = {
|
||||
id: string;
|
||||
org_id: string;
|
||||
title: string;
|
||||
threshold: string;
|
||||
triggered_at: Timestamp | null;
|
||||
created_at: Generated<Timestamp>;
|
||||
updated_at: Generated<Timestamp>;
|
||||
};
|
||||
export type Comment = {
|
||||
id: string;
|
||||
project_id: string;
|
||||
@@ -478,6 +488,7 @@ export type JobExecution = {
|
||||
job_input_observation_id: string | null;
|
||||
job_input_dataset_item_id: string | null;
|
||||
job_output_score_id: string | null;
|
||||
execution_trace_id: string | null;
|
||||
};
|
||||
export type LegacyPrismaObservation = {
|
||||
id: string;
|
||||
@@ -641,6 +652,11 @@ export type Organization = {
|
||||
updated_at: Generated<Timestamp>;
|
||||
cloud_config: unknown | null;
|
||||
metadata: unknown | null;
|
||||
cloud_billing_cycle_anchor: Generated<Timestamp | null>;
|
||||
cloud_billing_cycle_updated_at: Timestamp | null;
|
||||
cloud_current_cycle_usage: number | null;
|
||||
cloud_free_tier_usage_threshold_state: string | null;
|
||||
ai_features_enabled: Generated<boolean>;
|
||||
};
|
||||
export type OrganizationMembership = {
|
||||
id: string;
|
||||
@@ -846,6 +862,7 @@ export type DB = {
|
||||
batch_exports: BatchExport;
|
||||
billing_meter_backups: BillingMeterBackup;
|
||||
blob_storage_integrations: BlobStorageIntegration;
|
||||
cloud_spend_alerts: CloudSpendAlert;
|
||||
comments: Comment;
|
||||
cron_jobs: CronJobs;
|
||||
dashboard_widgets: DashboardWidget;
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "organizations" ADD COLUMN "ai_features_enabled" BOOLEAN NOT NULL DEFAULT false;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS "job_executions_project_id_job_output_score_id_idx" ON "job_executions"("project_id", "job_output_score_id");
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "organizations" ADD COLUMN "cloud_billing_cycle_anchor" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
|
||||
ADD COLUMN "cloud_billing_cycle_updated_at" TIMESTAMP(3),
|
||||
ADD COLUMN "cloud_current_cycle_usage" INTEGER,
|
||||
ADD COLUMN "cloud_free_tier_usage_threshold_state" TEXT;
|
||||
|
||||
-- Backfill cloud_billing_cycle_anchor for existing organizations:
|
||||
-- Step 1: Set to NULL for orgs WITH active subscriptions (will be backfilled from Stripe by worker job)
|
||||
UPDATE "organizations"
|
||||
SET "cloud_billing_cycle_anchor" = NULL;
|
||||
|
||||
-- Step 2: Set to created_at for orgs WITHOUT active subscriptions (free tier orgs)
|
||||
UPDATE "organizations"
|
||||
SET "cloud_billing_cycle_anchor" = "created_at"
|
||||
WHERE
|
||||
"cloud_config" IS NULL
|
||||
OR NOT (
|
||||
"cloud_config"::jsonb -> 'stripe' ? 'activeSubscriptionId'
|
||||
AND "cloud_config"::jsonb -> 'stripe' ->> 'activeSubscriptionId' IS NOT NULL
|
||||
AND "cloud_config"::jsonb -> 'stripe' ->> 'activeSubscriptionId' != ''
|
||||
);
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
INSERT INTO background_migrations (id, name, script, args)
|
||||
VALUES ('0199b890-1093-7d1f-b662-be3c03527e93', '20250102_backfill_billing_cycle_anchors', 'backfillBillingCycleAnchors', '{}');
|
||||
@@ -0,0 +1,15 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "cloud_spend_alerts" (
|
||||
"id" TEXT NOT NULL,
|
||||
"org_id" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"threshold" DECIMAL(65,30) NOT NULL,
|
||||
"triggered_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "cloud_spend_alerts_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "cloud_spend_alerts" ADD CONSTRAINT "cloud_spend_alerts_org_id_fkey" FOREIGN KEY ("org_id") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY "cloud_spend_alerts_org_id_idx" ON "cloud_spend_alerts"("org_id");
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DELETE FROM "llm_api_keys" WHERE adapter = 'atla'
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "job_executions" ADD COLUMN "execution_trace_id" TEXT;
|
||||
|
||||
-- AlterEnum
|
||||
ALTER TYPE "JobExecutionStatus" ADD VALUE 'DELAYED';
|
||||
@@ -104,17 +104,23 @@ model VerificationToken {
|
||||
}
|
||||
|
||||
model Organization {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||
cloudConfig Json? @map("cloud_config") // Langfuse Cloud, for zod schema see @/src/features/organizations/utils/cloudConfigSchema
|
||||
metadata Json?
|
||||
organizationMemberships OrganizationMembership[]
|
||||
projects Project[]
|
||||
MembershipInvitation MembershipInvitation[]
|
||||
ApiKey ApiKey[]
|
||||
surveys Survey[]
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||
cloudConfig Json? @map("cloud_config") // Langfuse Cloud, for zod schema see @/src/features/organizations/utils/cloudConfigSchema
|
||||
metadata Json?
|
||||
cloudBillingCycleAnchor DateTime? @default(now()) @map("cloud_billing_cycle_anchor")
|
||||
cloudBillingCycleUpdatedAt DateTime? @map("cloud_billing_cycle_updated_at")
|
||||
cloudCurrentCycleUsage Int? @map("cloud_current_cycle_usage")
|
||||
cloudFreeTierUsageThresholdState String? @map("cloud_free_tier_usage_threshold_state")
|
||||
aiFeaturesEnabled Boolean @default(false) @map("ai_features_enabled")
|
||||
organizationMemberships OrganizationMembership[]
|
||||
projects Project[]
|
||||
MembershipInvitation MembershipInvitation[]
|
||||
ApiKey ApiKey[]
|
||||
surveys Survey[]
|
||||
cloudSpendAlerts CloudSpendAlert[]
|
||||
|
||||
@@map("organizations")
|
||||
}
|
||||
@@ -853,6 +859,8 @@ model EvalTemplate {
|
||||
@@map("eval_templates")
|
||||
}
|
||||
|
||||
// We currently assume in the evalRouter that _all_ job_executions are for EVAL job_configs.
|
||||
// If we ever extend this, we need to adjust the filter condition there. ref.: fetchJobExecutionsByStatus.
|
||||
enum JobType {
|
||||
EVAL
|
||||
}
|
||||
@@ -893,6 +901,7 @@ enum JobExecutionStatus {
|
||||
ERROR
|
||||
PENDING
|
||||
CANCELLED
|
||||
DELAYED
|
||||
}
|
||||
|
||||
model JobExecution {
|
||||
@@ -924,9 +933,12 @@ model JobExecution {
|
||||
|
||||
jobOutputScoreId String? @map("job_output_score_id")
|
||||
|
||||
executionTraceId String? @map("execution_trace_id")
|
||||
|
||||
@@index([projectId, jobConfigurationId, jobInputTraceId])
|
||||
@@index([projectId, status])
|
||||
@@index([projectId, id])
|
||||
@@index([projectId, jobOutputScoreId])
|
||||
@@map("job_executions")
|
||||
}
|
||||
|
||||
@@ -1425,3 +1437,20 @@ enum SurveyName {
|
||||
|
||||
@@map("SurveyName")
|
||||
}
|
||||
|
||||
model CloudSpendAlert {
|
||||
id String @id @default(cuid())
|
||||
orgId String @map("org_id")
|
||||
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||||
|
||||
title String // e.g., "Production Alert"
|
||||
threshold Decimal @map("threshold") // USD amount
|
||||
|
||||
triggeredAt DateTime? @map("triggered_at") // Last trigger timestamp
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||
|
||||
@@index([orgId])
|
||||
@@map("cloud_spend_alerts")
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
JobExecutionStatus,
|
||||
PrismaClient,
|
||||
type Project,
|
||||
ScoreConfigCategoryDomain,
|
||||
ScoreDataType,
|
||||
} from "../../src/index";
|
||||
import { getDisplaySecretKey, hashSecretKey, logger } from "../../src/server";
|
||||
@@ -29,11 +30,6 @@ import {
|
||||
generateEvalTraceId,
|
||||
} from "./utils/seed-helpers";
|
||||
|
||||
type ConfigCategory = {
|
||||
label: string;
|
||||
value: number;
|
||||
};
|
||||
|
||||
const options = {
|
||||
environment: { type: "string" },
|
||||
} as const;
|
||||
@@ -110,6 +106,9 @@ async function main() {
|
||||
},
|
||||
});
|
||||
|
||||
// Realistic support chat scenario
|
||||
await createSupportChatSession(project1);
|
||||
|
||||
await prisma.organizationMembership.upsert({
|
||||
where: {
|
||||
orgId_userId: {
|
||||
@@ -533,23 +532,13 @@ export async function createDatasets(
|
||||
const datasetItem = await prisma.datasetItem.upsert({
|
||||
where: {
|
||||
id_projectId: {
|
||||
id: generateDatasetItemId(
|
||||
datasetName,
|
||||
index,
|
||||
projectId,
|
||||
SEED_DATASETS.indexOf(data) || 0,
|
||||
),
|
||||
id: generateDatasetItemId(datasetName, index, projectId),
|
||||
projectId,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
projectId,
|
||||
id: generateDatasetItemId(
|
||||
datasetName,
|
||||
index,
|
||||
projectId,
|
||||
SEED_DATASETS.indexOf(data) || 0,
|
||||
),
|
||||
id: generateDatasetItemId(datasetName, index, projectId),
|
||||
datasetId: dataset.id,
|
||||
sourceTraceId: sourceTraceId ?? null,
|
||||
sourceObservationId: null,
|
||||
@@ -733,6 +722,7 @@ async function generatePrompts(project: Project) {
|
||||
createdBy: version.createdBy,
|
||||
prompt: version.prompt,
|
||||
name: version.name,
|
||||
type: version.type ?? "text",
|
||||
config: version.config,
|
||||
version: version.version,
|
||||
labels: version.labels,
|
||||
@@ -754,7 +744,7 @@ async function generateConfigsForProject(projects: Project[]) {
|
||||
name: string;
|
||||
id: string;
|
||||
dataType: ScoreDataType;
|
||||
categories: ConfigCategory[] | null;
|
||||
categories: ScoreConfigCategoryDomain[] | null;
|
||||
}[]
|
||||
> = new Map();
|
||||
|
||||
@@ -781,12 +771,30 @@ async function createTraceSessions(project1: Project, project2: Project) {
|
||||
}
|
||||
}
|
||||
|
||||
async function createSupportChatSession(project: Project) {
|
||||
const sessionId = "support-chat-session";
|
||||
await prisma.traceSession.upsert({
|
||||
where: {
|
||||
id_projectId: {
|
||||
id: sessionId,
|
||||
projectId: project.id,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
id: sessionId,
|
||||
projectId: project.id,
|
||||
environment: "default",
|
||||
},
|
||||
update: {},
|
||||
});
|
||||
}
|
||||
|
||||
async function generateConfigs(project: Project) {
|
||||
const configNameAndId: {
|
||||
name: string;
|
||||
id: string;
|
||||
dataType: ScoreDataType;
|
||||
categories: ConfigCategory[] | null;
|
||||
categories: ScoreConfigCategoryDomain[] | null;
|
||||
}[] = [];
|
||||
|
||||
const configs = [
|
||||
@@ -863,7 +871,7 @@ async function generateQueuesForProject(
|
||||
name: string;
|
||||
id: string;
|
||||
dataType: ScoreDataType;
|
||||
categories: ConfigCategory[] | null;
|
||||
categories: ScoreConfigCategoryDomain[] | null;
|
||||
}[]
|
||||
>,
|
||||
) {
|
||||
@@ -887,7 +895,7 @@ async function generateQueues(
|
||||
name: string;
|
||||
id: string;
|
||||
dataType: ScoreDataType;
|
||||
categories: ConfigCategory[] | null;
|
||||
categories: ScoreConfigCategoryDomain[] | null;
|
||||
}[],
|
||||
) {
|
||||
const queue = {
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
TraceRecordInsertType,
|
||||
DatasetRunItemRecordInsertType,
|
||||
createDatasetRunItem,
|
||||
createDatasetRunScore,
|
||||
} from "../../../src/server";
|
||||
|
||||
/**
|
||||
@@ -106,7 +107,6 @@ export class DataGenerator {
|
||||
input.datasetName,
|
||||
input.itemIndex,
|
||||
projectId,
|
||||
input.runNumber || 0,
|
||||
),
|
||||
dataset_item_input: input.item.input,
|
||||
dataset_item_expected_output: input.item.expectedOutput,
|
||||
@@ -216,6 +216,59 @@ export class DataGenerator {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates scores for dataset experiment scores with variable values.
|
||||
* Use for: Dataset experiments requiring detailed score tracking.
|
||||
*/
|
||||
generateDatasetScore(
|
||||
trace: TraceRecordInsertType,
|
||||
input: DatasetItemInput,
|
||||
projectId: string,
|
||||
scoreNames: string[],
|
||||
): ScoreRecordInsertType {
|
||||
const scoreId = `score-dataset-${input.datasetName}-${input.itemIndex}-${input.runNumber}-${projectId.slice(-8)}`;
|
||||
|
||||
return createTraceScore({
|
||||
id: scoreId,
|
||||
trace_id: trace.id,
|
||||
project_id: projectId,
|
||||
name: this.randomElement(scoreNames),
|
||||
value: Math.random() * 100,
|
||||
string_value: undefined,
|
||||
data_type: "NUMERIC",
|
||||
source: "API",
|
||||
environment: "langfuse-prompt-experiments",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates scores for dataset experiment traces with variable values.
|
||||
* Use for: Dataset experiments requiring detailed score tracking.
|
||||
*/
|
||||
generateDatasetRunScore(
|
||||
runId: string,
|
||||
input: {
|
||||
datasetName: string;
|
||||
runNumber: number;
|
||||
},
|
||||
projectId: string,
|
||||
scoreNames: string[],
|
||||
): ScoreRecordInsertType {
|
||||
const scoreId = `score-dataset-run-${input.datasetName}-${input.runNumber}-${projectId.slice(-8)}`;
|
||||
|
||||
return createDatasetRunScore({
|
||||
id: scoreId,
|
||||
project_id: projectId,
|
||||
dataset_run_id: runId,
|
||||
name: this.randomElement(scoreNames),
|
||||
value: Math.random() * 100,
|
||||
string_value: undefined,
|
||||
data_type: "NUMERIC",
|
||||
source: "API",
|
||||
environment: "langfuse-prompt-experiments",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates large-scale synthetic traces for performance testing.
|
||||
* Use for: Load testing, dashboard demos, realistic usage simulation.
|
||||
@@ -899,6 +952,310 @@ export class DataGenerator {
|
||||
: JSON.stringify(this.fileContent.chatMlJson);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates realistic support chat session data with conversational flow.
|
||||
* Use for: Demonstrating session-based conversations with tool calls and scoring.
|
||||
*/
|
||||
generateSupportChatSessionData(projectId: string): {
|
||||
traces: TraceRecordInsertType[];
|
||||
observations: ObservationRecordInsertType[];
|
||||
scores: ScoreRecordInsertType[];
|
||||
} {
|
||||
// Domain: Art museum membership support (non-tech)
|
||||
const dialogues = [
|
||||
{
|
||||
user: "Hi—my family membership at the City Art Museum was charged twice this month.",
|
||||
assistant:
|
||||
"Sorry about that. I can help. Could you share your membership ID and the last 4 digits of the card?",
|
||||
},
|
||||
{
|
||||
user: "Membership ID is CAM-48291, card ending 4417.",
|
||||
assistant: "Thanks. I'll check your billing records now.",
|
||||
tool: {
|
||||
name: "billing.lookup",
|
||||
input: { membershipId: "CAM-48291", last4: "4417" },
|
||||
output: {
|
||||
duplicateCharge: true,
|
||||
amounts: [95.0, 95.0],
|
||||
date: "2025-08-28",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
user: "Okay.",
|
||||
assistant:
|
||||
"I found a duplicate charge on 2025-08-28 for $95. I can issue a refund to the original payment method. Shall I proceed?",
|
||||
},
|
||||
{
|
||||
user: "Yes, please issue the refund.",
|
||||
assistant: "Processing your refund now. One moment.",
|
||||
tool: {
|
||||
name: "billing.refund",
|
||||
input: { membershipId: "CAM-48291", amount: 95.0 },
|
||||
output: { status: "success", refundId: "RFND-20931" },
|
||||
},
|
||||
},
|
||||
{
|
||||
user: "Thank you!",
|
||||
assistant:
|
||||
"Refund RFND-20931 has been issued. You'll see it on your statement within 3–5 business days.",
|
||||
},
|
||||
{
|
||||
user: "No, that's all. Appreciate the quick help!",
|
||||
assistant: "Happy to help. Enjoy your next visit to the museum!",
|
||||
},
|
||||
// a couple more lightweight turns for scrolling realism
|
||||
{
|
||||
user: "Oh, and do members get early access to exhibitions?",
|
||||
assistant:
|
||||
"Yes—members get a 48-hour early booking window and a preview evening invite.",
|
||||
},
|
||||
{
|
||||
user: "Perfect.",
|
||||
assistant: "You're all set. Have a great day!",
|
||||
},
|
||||
];
|
||||
|
||||
const now = Date.now();
|
||||
const traces: TraceRecordInsertType[] = dialogues.map((d, index) => ({
|
||||
id: `support-chat-${index}-${projectId.slice(-8)}`,
|
||||
timestamp: now + index * 1000,
|
||||
name: "SupportChatSession",
|
||||
user_id: null,
|
||||
metadata: { scenario: "support-chat" },
|
||||
release: null,
|
||||
version: null,
|
||||
project_id: projectId,
|
||||
environment: "default",
|
||||
public: false,
|
||||
bookmarked: false,
|
||||
tags: ["support", "chat", "session"],
|
||||
input: JSON.stringify(
|
||||
d.tool
|
||||
? {
|
||||
messages: [
|
||||
{ role: "user", content: d.user },
|
||||
{ role: "assistant", content: d.assistant },
|
||||
{
|
||||
role: "tool",
|
||||
name: d.tool.name,
|
||||
content: d.tool.output,
|
||||
},
|
||||
],
|
||||
}
|
||||
: { messages: [{ role: "user", content: d.user }] },
|
||||
),
|
||||
output: JSON.stringify({ role: "assistant", content: d.assistant }),
|
||||
session_id: "support-chat-session",
|
||||
created_at: now + index * 1000,
|
||||
updated_at: now + index * 1000 + 500,
|
||||
event_ts: now + index * 1000,
|
||||
is_deleted: 0,
|
||||
}));
|
||||
|
||||
// Create one GENERATION observation per trace
|
||||
const observations: ObservationRecordInsertType[] = dialogues
|
||||
.map((d, index) => {
|
||||
const start = now + index * 1000 + 50;
|
||||
const end = start + 400 + Math.floor(Math.random() * 400);
|
||||
const inputTokens = 80 + Math.floor(Math.random() * 60);
|
||||
const outputTokens = 60 + Math.floor(Math.random() * 60);
|
||||
const totalTokens = inputTokens + outputTokens;
|
||||
|
||||
const baseGen: ObservationRecordInsertType = {
|
||||
id: `support-chat-${index}-${projectId.slice(-8)}-gen`,
|
||||
trace_id: `support-chat-${index}-${projectId.slice(-8)}`,
|
||||
project_id: projectId,
|
||||
type: "GENERATION",
|
||||
parent_observation_id: null,
|
||||
environment: "default",
|
||||
start_time: start,
|
||||
end_time: end,
|
||||
name: "llm-generation",
|
||||
metadata: {},
|
||||
level: "DEFAULT",
|
||||
status_message: null,
|
||||
version: null,
|
||||
input: JSON.stringify({
|
||||
messages: [
|
||||
{ role: "user", content: d.user },
|
||||
d.tool
|
||||
? {
|
||||
role: "tool",
|
||||
name: d.tool.name,
|
||||
content: d.tool.output,
|
||||
}
|
||||
: undefined,
|
||||
].filter(Boolean),
|
||||
}),
|
||||
output: JSON.stringify({ role: "assistant", content: d.assistant }),
|
||||
provided_model_name: "gpt-4o",
|
||||
internal_model_id: null,
|
||||
model_parameters: JSON.stringify({ temperature: 0.2 }),
|
||||
provided_usage_details: {
|
||||
input: inputTokens,
|
||||
output: outputTokens,
|
||||
total: totalTokens,
|
||||
},
|
||||
usage_details: {
|
||||
input: inputTokens,
|
||||
output: outputTokens,
|
||||
total: totalTokens,
|
||||
},
|
||||
provided_cost_details: {
|
||||
input: Math.round(inputTokens * 2) / 1_000_000,
|
||||
output: Math.round(outputTokens * 3) / 1_000_000,
|
||||
total: Math.round(totalTokens * 5) / 1_000_000,
|
||||
},
|
||||
cost_details: {
|
||||
input: Math.round(inputTokens * 2) / 1_000_000,
|
||||
output: Math.round(outputTokens * 3) / 1_000_000,
|
||||
total: Math.round(totalTokens * 5) / 1_000_000,
|
||||
},
|
||||
total_cost: Math.round(totalTokens * 5) / 1_000_000,
|
||||
completion_start_time: start + 120,
|
||||
prompt_id: null,
|
||||
prompt_name: null,
|
||||
prompt_version: null,
|
||||
created_at: start,
|
||||
updated_at: end,
|
||||
event_ts: start,
|
||||
is_deleted: 0,
|
||||
};
|
||||
|
||||
if (!d.tool) return [baseGen];
|
||||
|
||||
const toolObs: ObservationRecordInsertType = {
|
||||
id: `support-chat-${index}-${projectId.slice(-8)}-tool`,
|
||||
trace_id: `support-chat-${index}-${projectId.slice(-8)}`,
|
||||
project_id: projectId,
|
||||
type: "TOOL",
|
||||
parent_observation_id: null,
|
||||
environment: "default",
|
||||
start_time: start - 40,
|
||||
end_time: start - 5,
|
||||
name: d.tool.name,
|
||||
metadata: {},
|
||||
level: "DEFAULT",
|
||||
status_message: null,
|
||||
version: null,
|
||||
input: JSON.stringify(d.tool.input),
|
||||
output: JSON.stringify(d.tool.output),
|
||||
provided_model_name: null,
|
||||
internal_model_id: null,
|
||||
model_parameters: null,
|
||||
provided_usage_details: {},
|
||||
usage_details: {},
|
||||
provided_cost_details: {},
|
||||
cost_details: {},
|
||||
total_cost: null,
|
||||
completion_start_time: null,
|
||||
prompt_id: null,
|
||||
prompt_name: null,
|
||||
prompt_version: null,
|
||||
created_at: start - 40,
|
||||
updated_at: start - 5,
|
||||
event_ts: start - 40,
|
||||
is_deleted: 0,
|
||||
};
|
||||
|
||||
return [toolObs, baseGen];
|
||||
})
|
||||
.flat();
|
||||
|
||||
// Create a couple of scores per trace
|
||||
const scores: ScoreRecordInsertType[] = dialogues
|
||||
.map((_, index) => {
|
||||
const baseTs = now + index * 1000 + 600;
|
||||
const helpfulness: ScoreRecordInsertType = {
|
||||
id: `support-chat-${index}-${projectId.slice(-8)}-score-helpfulness`,
|
||||
project_id: projectId,
|
||||
trace_id: `support-chat-${index}-${projectId.slice(-8)}`,
|
||||
session_id: null,
|
||||
dataset_run_id: null,
|
||||
observation_id: null,
|
||||
environment: "default",
|
||||
name: "helpfulness",
|
||||
value: 70 + Math.random() * 25,
|
||||
source: "API",
|
||||
comment: "Heuristic helpfulness score",
|
||||
metadata: {},
|
||||
author_user_id: null,
|
||||
config_id: null,
|
||||
data_type: "NUMERIC",
|
||||
string_value: null,
|
||||
queue_id: null,
|
||||
created_at: baseTs,
|
||||
updated_at: baseTs,
|
||||
timestamp: baseTs,
|
||||
event_ts: baseTs,
|
||||
is_deleted: 0,
|
||||
};
|
||||
|
||||
const safeVal = Math.random() > 0.1 ? 1 : 0;
|
||||
const safety: ScoreRecordInsertType = {
|
||||
id: `support-chat-${index}-${projectId.slice(-8)}-score-safety`,
|
||||
project_id: projectId,
|
||||
trace_id: `support-chat-${index}-${projectId.slice(-8)}`,
|
||||
session_id: null,
|
||||
dataset_run_id: null,
|
||||
observation_id: null,
|
||||
environment: "default",
|
||||
name: "safe",
|
||||
value: safeVal,
|
||||
source: "API",
|
||||
comment: "Content safety",
|
||||
metadata: {},
|
||||
author_user_id: null,
|
||||
config_id: null,
|
||||
data_type: "BOOLEAN",
|
||||
string_value: safeVal === 1 ? "true" : "false",
|
||||
queue_id: null,
|
||||
created_at: baseTs + 10,
|
||||
updated_at: baseTs + 10,
|
||||
timestamp: baseTs + 10,
|
||||
event_ts: baseTs + 10,
|
||||
is_deleted: 0,
|
||||
};
|
||||
|
||||
// Optional: resolution score on last turn
|
||||
const isFinal = index === dialogues.length - 1;
|
||||
const resolved: ScoreRecordInsertType | null = isFinal
|
||||
? {
|
||||
id: `support-chat-${index}-${projectId.slice(-8)}-score-resolved`,
|
||||
project_id: projectId,
|
||||
trace_id: `support-chat-${index}-${projectId.slice(-8)}`,
|
||||
session_id: null,
|
||||
dataset_run_id: null,
|
||||
observation_id: null,
|
||||
environment: "default",
|
||||
name: "resolved",
|
||||
value: 1,
|
||||
source: "API",
|
||||
comment: "Conversation resolved",
|
||||
metadata: {},
|
||||
author_user_id: null,
|
||||
config_id: null,
|
||||
data_type: "BOOLEAN",
|
||||
string_value: "true",
|
||||
queue_id: null,
|
||||
created_at: baseTs + 20,
|
||||
updated_at: baseTs + 20,
|
||||
timestamp: baseTs + 20,
|
||||
event_ts: baseTs + 20,
|
||||
is_deleted: 0,
|
||||
}
|
||||
: null;
|
||||
|
||||
return [helpfulness, safety, resolved].filter(
|
||||
Boolean,
|
||||
) as ScoreRecordInsertType[];
|
||||
})
|
||||
.flat();
|
||||
|
||||
return { traces, observations, scores };
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates exactly one score per evaluation trace with prefixed IDs.
|
||||
* Use for: Evaluation traces that need score validation, evaluator testing.
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Framework Traces
|
||||
|
||||
This folder contains real traces produced through framework instrumentation.
|
||||
Most of them stem from here: https://langfuse.com/integrations/frameworks/agno-agents and so on.
|
||||
|
||||
## How to add further traces
|
||||
1. Generate a trace
|
||||
2. Download the trace from the UI using the download button. This **excludes** the `input`/`output`/`metadata` fields of the observations
|
||||
3. In the trace, click `Log View (Beta)` and switch to `JSON` format. Click the copy all button and save this to a file in this folder.
|
||||
4. Run the script `npx ts-node merge-observations.ts trace-file.json observations.json trace-merged.json`
|
||||
5. Leave the `merged` file in this folder. Name it with the date of the trace as displayed in the UI.
|
||||
6. ???
|
||||
7. Profit
|
||||
|
||||
## How to use the trace in the UI
|
||||
|
||||
All trace ids are like `framework-frameworkName-traceId`, so search for framework in the trace table.
|
||||
We don't rewrite the time, so you have to filter for All Time most likely.
|
||||
Also, you can filter for `source: "framework-trace"` on the trace to find them.
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"trace": {
|
||||
"id": "1b72c51fabed12ae7df83bfd4a09f545",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"name": "autogen-chat-trace",
|
||||
"timestamp": "2025-06-06T11:31:33.965Z",
|
||||
"environment": "default",
|
||||
"tags": [
|
||||
"autogen",
|
||||
"dev"
|
||||
],
|
||||
"bookmarked": false,
|
||||
"release": null,
|
||||
"version": "1.0.0",
|
||||
"userId": "user_123",
|
||||
"sessionId": "session_abc",
|
||||
"public": true,
|
||||
"input": "\"Say 'Hello World!'\"",
|
||||
"output": "\"Hello World!\"",
|
||||
"metadata": "{\"email\":\"user@langfuse.com\",\"resourceAttributes\":{\"telemetry.sdk.language\":\"python\",\"telemetry.sdk.name\":\"opentelemetry\",\"telemetry.sdk.version\":\"1.33.1\",\"service.name\":\"unknown_service\"},\"scope\":{\"name\":\"langfuse-sdk\",\"version\":\"3.0.0\",\"attributes\":{\"public_key\":\"pk-lf-5855d85e-3943-497e-bd10-f50ad414bcba\"}}}",
|
||||
"createdAt": "2025-06-06T11:31:35.000Z",
|
||||
"updatedAt": "2025-06-06T11:31:35.337Z",
|
||||
"scores": [],
|
||||
"latency": 0.645
|
||||
},
|
||||
"observations": [
|
||||
{
|
||||
"id": "95f4f51edeb603d9",
|
||||
"traceId": "1b72c51fabed12ae7df83bfd4a09f545",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "SPAN",
|
||||
"environment": "default",
|
||||
"parentObservationId": null,
|
||||
"startTime": "2025-06-06T11:31:33.965Z",
|
||||
"endTime": "2025-06-06T11:31:34.610Z",
|
||||
"name": "autogen-chat-trace",
|
||||
"metadata": {
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.33.1",
|
||||
"service.name": "unknown_service"
|
||||
},
|
||||
"scope": {
|
||||
"name": "langfuse-sdk",
|
||||
"version": "3.0.0",
|
||||
"attributes": {
|
||||
"public_key": "pk-lf-5855d85e-3943-497e-bd10-f50ad414bcba"
|
||||
}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": "1.0.0",
|
||||
"modelParameters": null,
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-06-06T11:31:35.295Z",
|
||||
"updatedAt": "2025-06-06T11:31:35.337Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 645,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": null,
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"id": "83517f9062f5ada4",
|
||||
"traceId": "1b72c51fabed12ae7df83bfd4a09f545",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "GENERATION",
|
||||
"environment": "default",
|
||||
"parentObservationId": "95f4f51edeb603d9",
|
||||
"startTime": "2025-06-06T11:31:33.978Z",
|
||||
"endTime": "2025-06-06T11:31:34.606Z",
|
||||
"name": "chat gpt-4o",
|
||||
"metadata": {
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.33.1",
|
||||
"service.name": "unknown_service"
|
||||
},
|
||||
"scope": {
|
||||
"name": "langfuse-sdk",
|
||||
"version": "3.0.0",
|
||||
"attributes": {
|
||||
"public_key": "pk-lf-5855d85e-3943-497e-bd10-f50ad414bcba"
|
||||
}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": {
|
||||
"seed": "",
|
||||
"frequency_penalty": 0,
|
||||
"max_tokens": -1,
|
||||
"presence_penalty": 0,
|
||||
"stop_sequences": "{\"arrayValue\":{}}",
|
||||
"temperature": 1,
|
||||
"top_p": 1,
|
||||
"service_tier": "auto",
|
||||
"user": "",
|
||||
"is_stream": false
|
||||
},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-06-06T11:31:35.090Z",
|
||||
"updatedAt": "2025-06-06T11:31:35.153Z",
|
||||
"usageDetails": {
|
||||
"input": 41,
|
||||
"output": 3,
|
||||
"total": 44
|
||||
},
|
||||
"costDetails": {
|
||||
"total": 0.000025
|
||||
},
|
||||
"providedCostDetails": {
|
||||
"total": 0.000025
|
||||
},
|
||||
"model": "gpt-4o",
|
||||
"internalModelId": "b9854a5c92dc496b997d99d20",
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 628,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0.000025,
|
||||
"inputUsage": 41,
|
||||
"outputUsage": 3,
|
||||
"totalUsage": 44,
|
||||
"input": "system: You are a helpful AI assistant. Solve tasks using your tools. Reply with TERMINATE when the task has been completed.\nuser: Say 'Hello World!'",
|
||||
"output": "Hello World!"
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,203 @@
|
||||
import { readFileSync, readdirSync } from "fs";
|
||||
import path from "path";
|
||||
import {
|
||||
createTrace,
|
||||
createObservation,
|
||||
logger,
|
||||
type TraceRecordInsertType,
|
||||
type ObservationRecordInsertType,
|
||||
type ScoreRecordInsertType,
|
||||
} from "../../../../src/server";
|
||||
|
||||
/**
|
||||
* Loads framework traces from JSON files and converts them to ClickHouse insert types.
|
||||
* Expected JSON structure:
|
||||
* {
|
||||
* trace: { trace data without observations },
|
||||
* observations: [{ individual observation with id and input/output/metadata and all the other stuff}]
|
||||
* }
|
||||
*/
|
||||
export class FrameworkTraceLoader {
|
||||
private frameworkTracesDir: string;
|
||||
|
||||
constructor() {
|
||||
this.frameworkTracesDir = __dirname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and adapt all framework traces for a project.
|
||||
*/
|
||||
loadTracesForProject(projectId: string): {
|
||||
traces: TraceRecordInsertType[];
|
||||
observations: ObservationRecordInsertType[];
|
||||
scores: ScoreRecordInsertType[];
|
||||
} {
|
||||
const traces: TraceRecordInsertType[] = [];
|
||||
const observations: ObservationRecordInsertType[] = [];
|
||||
const scores: ScoreRecordInsertType[] = [];
|
||||
|
||||
const files = readdirSync(this.frameworkTracesDir).filter(
|
||||
(file) => file.endsWith(".json") && file !== "package.json",
|
||||
);
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(this.frameworkTracesDir, file);
|
||||
const content = readFileSync(filePath, "utf-8");
|
||||
const data = JSON.parse(content);
|
||||
|
||||
const frameworkName = file.replace(".json", "");
|
||||
const adapted = this.adaptTrace(data, projectId, frameworkName);
|
||||
|
||||
traces.push(adapted.trace);
|
||||
observations.push(...adapted.observations);
|
||||
scores.push(...adapted.scores);
|
||||
|
||||
logger.info(
|
||||
`Loaded 1 trace with ${adapted.observations.length} observations from ${frameworkName}`,
|
||||
);
|
||||
}
|
||||
|
||||
return { traces, observations, scores };
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapt a single framework trace to a specific project.
|
||||
*/
|
||||
private adaptTrace(
|
||||
data: any,
|
||||
projectId: string,
|
||||
frameworkName: string,
|
||||
): {
|
||||
trace: TraceRecordInsertType;
|
||||
observations: ObservationRecordInsertType[];
|
||||
scores: ScoreRecordInsertType[];
|
||||
} {
|
||||
const rawTrace = data.trace;
|
||||
const rawObservations = data.observations || [];
|
||||
|
||||
const originalTraceId = rawTrace.id;
|
||||
const newTraceId = originalTraceId;
|
||||
// we just change the name for now.
|
||||
// const newTraceId = `framework-${frameworkName}-${originalTraceId}-${projectId.slice(-8)}`;
|
||||
const newName = `framework-${frameworkName}-${rawTrace.name}`;
|
||||
|
||||
// Parse trace metadata if it's a string
|
||||
let metadata = rawTrace.metadata;
|
||||
if (typeof metadata === "string") {
|
||||
metadata = JSON.parse(metadata);
|
||||
}
|
||||
|
||||
// Create trace
|
||||
const trace = createTrace({
|
||||
id: newTraceId,
|
||||
project_id: projectId,
|
||||
name: newName,
|
||||
timestamp: new Date(rawTrace.timestamp).getTime(),
|
||||
input: rawTrace.input,
|
||||
output: rawTrace.output,
|
||||
user_id: rawTrace.userId,
|
||||
session_id: rawTrace.sessionId,
|
||||
environment: rawTrace.environment,
|
||||
metadata: {
|
||||
...metadata,
|
||||
// metadata to filter for those kinda traces
|
||||
source: "framework-trace",
|
||||
framework: frameworkName,
|
||||
},
|
||||
release: rawTrace.release,
|
||||
version: rawTrace.version,
|
||||
public: rawTrace.public,
|
||||
bookmarked: rawTrace.bookmarked,
|
||||
tags: rawTrace.tags,
|
||||
});
|
||||
|
||||
// Map observation IDs (old -> new)
|
||||
const obsIdMap = new Map<string, string>();
|
||||
for (const obs of rawObservations) {
|
||||
const newObsId = `framework-${frameworkName}-${obs.id}-${projectId.slice(-8)}`;
|
||||
obsIdMap.set(obs.id, newObsId);
|
||||
}
|
||||
|
||||
// Create observations
|
||||
const observations: ObservationRecordInsertType[] = [];
|
||||
for (const obs of rawObservations) {
|
||||
const newObsId = obsIdMap.get(obs.id)!;
|
||||
|
||||
// Map parent observation ID
|
||||
const parentObservationId = obs.parentObservationId
|
||||
? obsIdMap.get(obs.parentObservationId) || null
|
||||
: null;
|
||||
|
||||
// Convert input/output to strings, preserving null
|
||||
const input =
|
||||
obs.input === undefined || obs.input === null
|
||||
? obs.input
|
||||
: JSON.stringify(obs.input);
|
||||
const output =
|
||||
obs.output === undefined || obs.output === null
|
||||
? obs.output
|
||||
: JSON.stringify(obs.output);
|
||||
|
||||
// Parse metadata if it's a string
|
||||
let obsMetadata = obs.metadata;
|
||||
if (typeof obsMetadata === "string") {
|
||||
obsMetadata = JSON.parse(obsMetadata);
|
||||
}
|
||||
|
||||
// Use usage/cost details from JSON if present, otherwise reconstruct from flat fields
|
||||
const usageDetails =
|
||||
obs.usageDetails && Object.keys(obs.usageDetails).length > 0
|
||||
? obs.usageDetails
|
||||
: obs.totalUsage > 0
|
||||
? {
|
||||
input: obs.inputUsage,
|
||||
output: obs.outputUsage,
|
||||
total: obs.totalUsage,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const costDetails =
|
||||
obs.costDetails && Object.keys(obs.costDetails).length > 0
|
||||
? obs.costDetails
|
||||
: obs.totalCost > 0
|
||||
? {
|
||||
input: (obs.totalCost * obs.inputUsage) / obs.totalUsage,
|
||||
output: (obs.totalCost * obs.outputUsage) / obs.totalUsage,
|
||||
total: obs.totalCost,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
observations.push(
|
||||
createObservation({
|
||||
id: newObsId,
|
||||
trace_id: newTraceId,
|
||||
project_id: projectId,
|
||||
type: obs.type,
|
||||
parent_observation_id: parentObservationId,
|
||||
start_time: new Date(obs.startTime).getTime(),
|
||||
end_time: obs.endTime ? new Date(obs.endTime).getTime() : undefined,
|
||||
name: obs.name,
|
||||
metadata: obsMetadata,
|
||||
level: obs.level,
|
||||
status_message: obs.statusMessage,
|
||||
input,
|
||||
output,
|
||||
provided_model_name: obs.model,
|
||||
model_parameters: obs.modelParameters
|
||||
? JSON.stringify(obs.modelParameters)
|
||||
: undefined,
|
||||
prompt_name: obs.promptName,
|
||||
prompt_version: obs.promptVersion,
|
||||
usage_details: usageDetails,
|
||||
provided_usage_details: obs.providedUsageDetails,
|
||||
cost_details: costDetails,
|
||||
provided_cost_details: obs.providedCostDetails,
|
||||
total_cost: obs.totalCost,
|
||||
environment: rawTrace.environment,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return { trace, observations, scores: [] };
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,863 @@
|
||||
{
|
||||
"trace": {
|
||||
"id": "dff173a675b759ce1b70e522b27d6846",
|
||||
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
|
||||
"name": "agent.f9f77d1a-437f-4ba6-890e-b592a50cbd21",
|
||||
"timestamp": "2025-08-26T19:40:59.145Z",
|
||||
"environment": "default",
|
||||
"tags": [],
|
||||
"bookmarked": false,
|
||||
"release": null,
|
||||
"version": "0.3.0",
|
||||
"userId": null,
|
||||
"sessionId": null,
|
||||
"public": true,
|
||||
"input": null,
|
||||
"output": null,
|
||||
"metadata": "{\"attributes\":{\"gen_ai.request.model\":\"gpt-4o\",\"gen_ai.system\":\"openai\",\"gen_ai.agent.id\":\"f9f77d1a-437f-4ba6-890e-b592a50cbd21\",\"gen_ai.operation.name\":\"create_agent\"},\"resourceAttributes\":{\"os.arch\":\"aarch64\",\"os.type\":\"Mac OS X\",\"os.version\":\"15.4.1\",\"service.instance.time\":\"2025-08-26T19:40:59.130398Z\",\"service.name\":\"ai.koog\",\"service.version\":\"0.3.0\"},\"scope\":{\"name\":\"ai.koog\",\"version\":\"0.3.0\",\"attributes\":{}}}",
|
||||
"createdAt": "2025-08-26T19:40:59.145Z",
|
||||
"updatedAt": "2025-08-26T19:41:05.434Z",
|
||||
"scores": [],
|
||||
"latency": 5.843
|
||||
},
|
||||
"observations": [
|
||||
{
|
||||
"id": "1b7200380e3316e7",
|
||||
"traceId": "dff173a675b759ce1b70e522b27d6846",
|
||||
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
|
||||
"type": "AGENT",
|
||||
"environment": "default",
|
||||
"parentObservationId": null,
|
||||
"startTime": "2025-08-26T19:40:59.145Z",
|
||||
"endTime": "2025-08-26T19:41:04.988Z",
|
||||
"name": "agent.f9f77d1a-437f-4ba6-890e-b592a50cbd21",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"gen_ai.request.model": "gpt-4o",
|
||||
"gen_ai.system": "openai",
|
||||
"gen_ai.agent.id": "f9f77d1a-437f-4ba6-890e-b592a50cbd21",
|
||||
"gen_ai.operation.name": "create_agent"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"os.arch": "aarch64",
|
||||
"os.type": "Mac OS X",
|
||||
"os.version": "15.4.1",
|
||||
"service.instance.time": "2025-08-26T19:40:59.130398Z",
|
||||
"service.name": "ai.koog",
|
||||
"service.version": "0.3.0"
|
||||
},
|
||||
"scope": {
|
||||
"name": "ai.koog",
|
||||
"version": "0.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": "0.3.0",
|
||||
"modelParameters": {},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-08-26T19:41:05.401Z",
|
||||
"updatedAt": "2025-08-26T19:41:05.420Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": "gpt-4o",
|
||||
"internalModelId": "b9854a5c92dc496b997d99d20",
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 5843,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": null,
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"id": "bb189bc3771ff83d",
|
||||
"traceId": "dff173a675b759ce1b70e522b27d6846",
|
||||
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
|
||||
"type": "AGENT",
|
||||
"environment": "default",
|
||||
"parentObservationId": "1b7200380e3316e7",
|
||||
"startTime": "2025-08-26T19:40:59.150Z",
|
||||
"endTime": "2025-08-26T19:41:04.988Z",
|
||||
"name": "run.6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"koog.agent.strategy.name": "single_run_sequential",
|
||||
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
|
||||
"gen_ai.system": "openai",
|
||||
"gen_ai.agent.id": "f9f77d1a-437f-4ba6-890e-b592a50cbd21",
|
||||
"gen_ai.operation.name": "invoke_agent"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"os.arch": "aarch64",
|
||||
"os.type": "Mac OS X",
|
||||
"os.version": "15.4.1",
|
||||
"service.instance.time": "2025-08-26T19:40:59.130398Z",
|
||||
"service.name": "ai.koog",
|
||||
"service.version": "0.3.0"
|
||||
},
|
||||
"scope": {
|
||||
"name": "ai.koog",
|
||||
"version": "0.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": "0.3.0",
|
||||
"modelParameters": {},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-08-26T19:41:05.330Z",
|
||||
"updatedAt": "2025-08-26T19:41:05.354Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 5838,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": null,
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"id": "7578374e12a7faa5",
|
||||
"traceId": "dff173a675b759ce1b70e522b27d6846",
|
||||
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
|
||||
"type": "SPAN",
|
||||
"environment": "default",
|
||||
"parentObservationId": "bb189bc3771ff83d",
|
||||
"startTime": "2025-08-26T19:41:04.984Z",
|
||||
"endTime": "2025-08-26T19:41:04.985Z",
|
||||
"name": "node.__finish__",
|
||||
"metadata": {
|
||||
"langgraph_node": "__finish__",
|
||||
"langgraph_step": 4,
|
||||
"attributes": {
|
||||
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
|
||||
"koog.node.name": "__finish__",
|
||||
"langfuse.observation.metadata.langgraph_node": "__finish__",
|
||||
"langfuse.observation.metadata.langgraph_step": "4"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"os.arch": "aarch64",
|
||||
"os.type": "Mac OS X",
|
||||
"os.version": "15.4.1",
|
||||
"service.instance.time": "2025-08-26T19:40:59.130398Z",
|
||||
"service.name": "ai.koog",
|
||||
"service.version": "0.3.0"
|
||||
},
|
||||
"scope": {
|
||||
"name": "ai.koog",
|
||||
"version": "0.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": "0.3.0",
|
||||
"modelParameters": null,
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-08-26T19:41:05.313Z",
|
||||
"updatedAt": "2025-08-26T19:41:05.340Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 1,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": null,
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"id": "e44e72cf2d221781",
|
||||
"traceId": "dff173a675b759ce1b70e522b27d6846",
|
||||
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
|
||||
"type": "GENERATION",
|
||||
"environment": "default",
|
||||
"parentObservationId": "6021df46275ad2d8",
|
||||
"startTime": "2025-08-26T19:41:03.041Z",
|
||||
"endTime": "2025-08-26T19:41:04.983Z",
|
||||
"name": "llm.chat",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"gen_ai.prompt.4.content": [
|
||||
{
|
||||
"function": {
|
||||
"name": "ByeTool",
|
||||
"arguments": {
|
||||
"name": "Bob"
|
||||
}
|
||||
},
|
||||
"id": "call_dOeEHJDI0liHrCdFWTpkTsuO",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"gen_ai.prompt.5.role": "tool",
|
||||
"gen_ai.completion.0.content": "Hello my darling Bob! And, bye my dear Bob!",
|
||||
"gen_ai.request.temperature": "1",
|
||||
"gen_ai.prompt.3.role": "tool",
|
||||
"gen_ai.prompt.0.role": "system",
|
||||
"gen_ai.prompt.5.content": "Bye my dear Bob!",
|
||||
"gen_ai.completion.0.role": "assistant",
|
||||
"gen_ai.prompt.4.role": "tool",
|
||||
"gen_ai.request.model": "gpt-4o",
|
||||
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
|
||||
"gen_ai.prompt.0.content": "You are a nice and polite assistant.",
|
||||
"gen_ai.prompt.1.role": "user",
|
||||
"gen_ai.response.finish_reasons": [
|
||||
"stop"
|
||||
],
|
||||
"gen_ai.prompt.1.content": "Greet and say goodbye to the user in the same message. Use provided tools to generate a proper greeting. The user name is Bob.",
|
||||
"gen_ai.system": "openai",
|
||||
"gen_ai.prompt.2.content": [
|
||||
{
|
||||
"function": {
|
||||
"name": "HelloTool",
|
||||
"arguments": {
|
||||
"name": "Bob"
|
||||
}
|
||||
},
|
||||
"id": "call_n2HaWg9KFP0BZDqXv6G4b3nJ",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"gen_ai.operation.name": "chat",
|
||||
"gen_ai.prompt.2.role": "tool",
|
||||
"gen_ai.prompt.3.content": "Hello my darling Bob!"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"os.arch": "aarch64",
|
||||
"os.type": "Mac OS X",
|
||||
"os.version": "15.4.1",
|
||||
"service.instance.time": "2025-08-26T19:40:59.130398Z",
|
||||
"service.name": "ai.koog",
|
||||
"service.version": "0.3.0"
|
||||
},
|
||||
"scope": {
|
||||
"name": "ai.koog",
|
||||
"version": "0.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": "0.3.0",
|
||||
"modelParameters": {
|
||||
"temperature": 1
|
||||
},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-08-26T19:41:05.314Z",
|
||||
"updatedAt": "2025-08-26T19:41:05.333Z",
|
||||
"usageDetails": {
|
||||
"input": 168,
|
||||
"output": 19,
|
||||
"total": 187
|
||||
},
|
||||
"costDetails": {
|
||||
"input": 0.00042,
|
||||
"output": 0.00019,
|
||||
"total": 0.00061
|
||||
},
|
||||
"providedCostDetails": {},
|
||||
"model": "gpt-4o",
|
||||
"internalModelId": "b9854a5c92dc496b997d99d20",
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 1942,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": 0.00042,
|
||||
"outputCost": 0.00019,
|
||||
"totalCost": 0.00061,
|
||||
"inputUsage": 168,
|
||||
"outputUsage": 19,
|
||||
"totalUsage": 187,
|
||||
"input": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a nice and polite assistant."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Greet and say goodbye to the user in the same message. Use provided tools to generate a proper greeting. The user name is Bob."
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"function": {
|
||||
"name": "HelloTool",
|
||||
"arguments": {
|
||||
"name": "Bob"
|
||||
}
|
||||
},
|
||||
"id": "call_n2HaWg9KFP0BZDqXv6G4b3nJ",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"role": "tool"
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"content": "Hello my darling Bob!"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"function": {
|
||||
"name": "ByeTool",
|
||||
"arguments": {
|
||||
"name": "Bob"
|
||||
}
|
||||
},
|
||||
"id": "call_dOeEHJDI0liHrCdFWTpkTsuO",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"role": "tool"
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"content": "Bye my dear Bob!"
|
||||
}
|
||||
],
|
||||
"output": [
|
||||
{
|
||||
"content": "Hello my darling Bob! And, bye my dear Bob!",
|
||||
"role": "assistant"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "6021df46275ad2d8",
|
||||
"traceId": "dff173a675b759ce1b70e522b27d6846",
|
||||
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
|
||||
"type": "SPAN",
|
||||
"environment": "default",
|
||||
"parentObservationId": "bb189bc3771ff83d",
|
||||
"startTime": "2025-08-26T19:41:03.038Z",
|
||||
"endTime": "2025-08-26T19:41:04.984Z",
|
||||
"name": "node.nodeSendToolResult",
|
||||
"metadata": {
|
||||
"langgraph_node": "nodeSendToolResult",
|
||||
"langgraph_step": 3,
|
||||
"attributes": {
|
||||
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
|
||||
"koog.node.name": "nodeSendToolResult",
|
||||
"langfuse.observation.metadata.langgraph_node": "nodeSendToolResult",
|
||||
"langfuse.observation.metadata.langgraph_step": "3"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"os.arch": "aarch64",
|
||||
"os.type": "Mac OS X",
|
||||
"os.version": "15.4.1",
|
||||
"service.instance.time": "2025-08-26T19:40:59.130398Z",
|
||||
"service.name": "ai.koog",
|
||||
"service.version": "0.3.0"
|
||||
},
|
||||
"scope": {
|
||||
"name": "ai.koog",
|
||||
"version": "0.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": "0.3.0",
|
||||
"modelParameters": null,
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-08-26T19:41:05.305Z",
|
||||
"updatedAt": "2025-08-26T19:41:05.324Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 1946,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": null,
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"id": "33188493784a060d",
|
||||
"traceId": "dff173a675b759ce1b70e522b27d6846",
|
||||
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
|
||||
"type": "SPAN",
|
||||
"environment": "default",
|
||||
"parentObservationId": "5555afb6655e4d44",
|
||||
"startTime": "2025-08-26T19:41:02.023Z",
|
||||
"endTime": "2025-08-26T19:41:03.037Z",
|
||||
"name": "tool.ByeTool",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"input.value": {
|
||||
"name": "Bob"
|
||||
},
|
||||
"output.value": "Bye my dear Bob!",
|
||||
"gen_ai.tool.description": "A tool that says bye to the user",
|
||||
"gen_ai.tool.call.id": "call_dOeEHJDI0liHrCdFWTpkTsuO",
|
||||
"gen_ai.tool.name": "ByeTool"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"os.arch": "aarch64",
|
||||
"os.type": "Mac OS X",
|
||||
"os.version": "15.4.1",
|
||||
"service.instance.time": "2025-08-26T19:40:59.130398Z",
|
||||
"service.name": "ai.koog",
|
||||
"service.version": "0.3.0"
|
||||
},
|
||||
"scope": {
|
||||
"name": "ai.koog",
|
||||
"version": "0.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": "0.3.0",
|
||||
"modelParameters": null,
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-08-26T19:41:03.511Z",
|
||||
"updatedAt": "2025-08-26T19:41:03.531Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 1014,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": {
|
||||
"name": "Bob"
|
||||
},
|
||||
"output": "Bye my dear Bob!"
|
||||
},
|
||||
{
|
||||
"id": "66f269083a2fff81",
|
||||
"traceId": "dff173a675b759ce1b70e522b27d6846",
|
||||
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
|
||||
"type": "SPAN",
|
||||
"environment": "default",
|
||||
"parentObservationId": "5555afb6655e4d44",
|
||||
"startTime": "2025-08-26T19:41:02.023Z",
|
||||
"endTime": "2025-08-26T19:41:03.034Z",
|
||||
"name": "tool.HelloTool",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"input.value": {
|
||||
"name": "Bob"
|
||||
},
|
||||
"output.value": "Hello my darling Bob!",
|
||||
"gen_ai.tool.description": "A tool greets the user",
|
||||
"gen_ai.tool.call.id": "call_n2HaWg9KFP0BZDqXv6G4b3nJ",
|
||||
"gen_ai.tool.name": "HelloTool"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"os.arch": "aarch64",
|
||||
"os.type": "Mac OS X",
|
||||
"os.version": "15.4.1",
|
||||
"service.instance.time": "2025-08-26T19:40:59.130398Z",
|
||||
"service.name": "ai.koog",
|
||||
"service.version": "0.3.0"
|
||||
},
|
||||
"scope": {
|
||||
"name": "ai.koog",
|
||||
"version": "0.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": "0.3.0",
|
||||
"modelParameters": null,
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-08-26T19:41:03.431Z",
|
||||
"updatedAt": "2025-08-26T19:41:03.479Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 1011,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": {
|
||||
"name": "Bob"
|
||||
},
|
||||
"output": "Hello my darling Bob!"
|
||||
},
|
||||
{
|
||||
"id": "5555afb6655e4d44",
|
||||
"traceId": "dff173a675b759ce1b70e522b27d6846",
|
||||
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
|
||||
"type": "SPAN",
|
||||
"environment": "default",
|
||||
"parentObservationId": "bb189bc3771ff83d",
|
||||
"startTime": "2025-08-26T19:41:02.017Z",
|
||||
"endTime": "2025-08-26T19:41:03.038Z",
|
||||
"name": "node.nodeExecuteTool",
|
||||
"metadata": {
|
||||
"langgraph_node": "nodeExecuteTool",
|
||||
"langgraph_step": 2,
|
||||
"attributes": {
|
||||
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
|
||||
"koog.node.name": "nodeExecuteTool",
|
||||
"langfuse.observation.metadata.langgraph_node": "nodeExecuteTool",
|
||||
"langfuse.observation.metadata.langgraph_step": "2"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"os.arch": "aarch64",
|
||||
"os.type": "Mac OS X",
|
||||
"os.version": "15.4.1",
|
||||
"service.instance.time": "2025-08-26T19:40:59.130398Z",
|
||||
"service.name": "ai.koog",
|
||||
"service.version": "0.3.0"
|
||||
},
|
||||
"scope": {
|
||||
"name": "ai.koog",
|
||||
"version": "0.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": "0.3.0",
|
||||
"modelParameters": null,
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-08-26T19:41:03.432Z",
|
||||
"updatedAt": "2025-08-26T19:41:03.456Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 1021,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": null,
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"id": "6a0770019fe7c2b4",
|
||||
"traceId": "dff173a675b759ce1b70e522b27d6846",
|
||||
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
|
||||
"type": "SPAN",
|
||||
"environment": "default",
|
||||
"parentObservationId": "bb189bc3771ff83d",
|
||||
"startTime": "2025-08-26T19:40:59.171Z",
|
||||
"endTime": "2025-08-26T19:41:02.017Z",
|
||||
"name": "node.nodeCallLLM",
|
||||
"metadata": {
|
||||
"langgraph_node": "nodeCallLLM",
|
||||
"langgraph_step": 1,
|
||||
"attributes": {
|
||||
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
|
||||
"koog.node.name": "nodeCallLLM",
|
||||
"langfuse.observation.metadata.langgraph_node": "nodeCallLLM",
|
||||
"langfuse.observation.metadata.langgraph_step": "1"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"os.arch": "aarch64",
|
||||
"os.type": "Mac OS X",
|
||||
"os.version": "15.4.1",
|
||||
"service.instance.time": "2025-08-26T19:40:59.130398Z",
|
||||
"service.name": "ai.koog",
|
||||
"service.version": "0.3.0"
|
||||
},
|
||||
"scope": {
|
||||
"name": "ai.koog",
|
||||
"version": "0.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": "0.3.0",
|
||||
"modelParameters": null,
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-08-26T19:41:02.422Z",
|
||||
"updatedAt": "2025-08-26T19:41:02.440Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 2846,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": null,
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"id": "042f9350c3729147",
|
||||
"traceId": "dff173a675b759ce1b70e522b27d6846",
|
||||
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
|
||||
"type": "GENERATION",
|
||||
"environment": "default",
|
||||
"parentObservationId": "6a0770019fe7c2b4",
|
||||
"startTime": "2025-08-26T19:40:59.176Z",
|
||||
"endTime": "2025-08-26T19:41:02.016Z",
|
||||
"name": "llm.chat",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"gen_ai.completion.0.content": [
|
||||
{
|
||||
"function": {
|
||||
"name": "HelloTool",
|
||||
"arguments": {
|
||||
"name": "Bob"
|
||||
}
|
||||
},
|
||||
"id": "call_n2HaWg9KFP0BZDqXv6G4b3nJ",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"gen_ai.request.temperature": "1",
|
||||
"gen_ai.prompt.0.role": "system",
|
||||
"gen_ai.completion.0.role": "assistant",
|
||||
"gen_ai.request.model": "gpt-4o",
|
||||
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
|
||||
"gen_ai.completion.0.finish_reason": "tool_calls",
|
||||
"gen_ai.prompt.0.content": "You are a nice and polite assistant.",
|
||||
"gen_ai.prompt.1.role": "user",
|
||||
"gen_ai.response.finish_reasons": [
|
||||
"tool_calls"
|
||||
],
|
||||
"gen_ai.prompt.1.content": "Greet and say goodbye to the user in the same message. Use provided tools to generate a proper greeting. The user name is Bob.",
|
||||
"gen_ai.completion.1.finish_reason": "tool_calls",
|
||||
"gen_ai.system": "openai",
|
||||
"gen_ai.completion.1.role": "assistant",
|
||||
"gen_ai.completion.1.content": [
|
||||
{
|
||||
"function": {
|
||||
"name": "ByeTool",
|
||||
"arguments": {
|
||||
"name": "Bob"
|
||||
}
|
||||
},
|
||||
"id": "call_dOeEHJDI0liHrCdFWTpkTsuO",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"gen_ai.operation.name": "chat"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"os.arch": "aarch64",
|
||||
"os.type": "Mac OS X",
|
||||
"os.version": "15.4.1",
|
||||
"service.instance.time": "2025-08-26T19:40:59.130398Z",
|
||||
"service.name": "ai.koog",
|
||||
"service.version": "0.3.0"
|
||||
},
|
||||
"scope": {
|
||||
"name": "ai.koog",
|
||||
"version": "0.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": "0.3.0",
|
||||
"modelParameters": {
|
||||
"temperature": 1
|
||||
},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-08-26T19:41:02.328Z",
|
||||
"updatedAt": "2025-08-26T19:41:02.356Z",
|
||||
"usageDetails": {
|
||||
"input": 47,
|
||||
"output": 106,
|
||||
"total": 153
|
||||
},
|
||||
"costDetails": {
|
||||
"input": 0.0001175,
|
||||
"output": 0.00106,
|
||||
"total": 0.0011775
|
||||
},
|
||||
"providedCostDetails": {},
|
||||
"model": "gpt-4o",
|
||||
"internalModelId": "b9854a5c92dc496b997d99d20",
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 2840,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": 0.0001175,
|
||||
"outputCost": 0.00106,
|
||||
"totalCost": 0.0011775,
|
||||
"inputUsage": 47,
|
||||
"outputUsage": 106,
|
||||
"totalUsage": 153,
|
||||
"input": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a nice and polite assistant."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Greet and say goodbye to the user in the same message. Use provided tools to generate a proper greeting. The user name is Bob."
|
||||
}
|
||||
],
|
||||
"output": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"function": {
|
||||
"name": "HelloTool",
|
||||
"arguments": {
|
||||
"name": "Bob"
|
||||
}
|
||||
},
|
||||
"id": "call_n2HaWg9KFP0BZDqXv6G4b3nJ",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"role": "assistant",
|
||||
"finish_reason": "tool_calls"
|
||||
},
|
||||
{
|
||||
"finish_reason": "tool_calls",
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"function": {
|
||||
"name": "ByeTool",
|
||||
"arguments": {
|
||||
"name": "Bob"
|
||||
}
|
||||
},
|
||||
"id": "call_dOeEHJDI0liHrCdFWTpkTsuO",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "890e9114a4449257",
|
||||
"traceId": "dff173a675b759ce1b70e522b27d6846",
|
||||
"projectId": "cmcmdwcag00c2ad077xp1qnyc",
|
||||
"type": "SPAN",
|
||||
"environment": "default",
|
||||
"parentObservationId": "bb189bc3771ff83d",
|
||||
"startTime": "2025-08-26T19:40:59.153Z",
|
||||
"endTime": "2025-08-26T19:40:59.153Z",
|
||||
"name": "node.__start__",
|
||||
"metadata": {
|
||||
"langgraph_node": "__start__",
|
||||
"langgraph_step": 0,
|
||||
"attributes": {
|
||||
"gen_ai.conversation.id": "6f8d2449-33ed-4c7c-82e7-5410e34ee4e6",
|
||||
"koog.node.name": "__start__",
|
||||
"langfuse.observation.metadata.langgraph_node": "__start__",
|
||||
"langfuse.observation.metadata.langgraph_step": "0"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"os.arch": "aarch64",
|
||||
"os.type": "Mac OS X",
|
||||
"os.version": "15.4.1",
|
||||
"service.instance.time": "2025-08-26T19:40:59.130398Z",
|
||||
"service.name": "ai.koog",
|
||||
"service.version": "0.3.0"
|
||||
},
|
||||
"scope": {
|
||||
"name": "ai.koog",
|
||||
"version": "0.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": "0.3.0",
|
||||
"modelParameters": null,
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-08-26T19:40:59.897Z",
|
||||
"updatedAt": "2025-08-26T19:40:59.915Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 0,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": null,
|
||||
"output": null
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"trace": {
|
||||
"id": "12ea412956f99347b0503c1144acd0ec",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"name": "llama-index-trace",
|
||||
"timestamp": "2025-06-05T15:45:52.971Z",
|
||||
"environment": "default",
|
||||
"tags": [
|
||||
"llama-index",
|
||||
"rag"
|
||||
],
|
||||
"bookmarked": false,
|
||||
"release": null,
|
||||
"version": "1.0.0",
|
||||
"userId": "user_123",
|
||||
"sessionId": "session_abc",
|
||||
"public": true,
|
||||
"input": "\"What is Langfuse?\"",
|
||||
"output": "\"Langfuse is a tool designed to help developers monitor and debug applications that utilize large language models (LLMs). It provides features for tracking and analyzing the performance of LLMs, enabling developers to gain insights into how these models are functioning within their applications. Langfuse can be particularly useful for identifying issues, optimizing performance, and ensuring that the integration of language models into applications is smooth and effective.\"",
|
||||
"metadata": "{\"email\":\"user@langfuse.com\",\"resourceAttributes\":{\"telemetry.sdk.language\":\"python\",\"telemetry.sdk.name\":\"opentelemetry\",\"telemetry.sdk.version\":\"1.33.1\",\"service.name\":\"unknown_service\"},\"scope\":{\"name\":\"langfuse-sdk\",\"version\":\"3.0.0\",\"attributes\":{\"public_key\":\"pk-lf-5855d85e-3943-497e-bd10-f50ad414bcba\"}}}",
|
||||
"createdAt": "2025-06-05T15:45:56.000Z",
|
||||
"updatedAt": "2025-06-05T15:45:56.029Z",
|
||||
"scores": [],
|
||||
"latency": 2.779
|
||||
},
|
||||
"observations": [
|
||||
{
|
||||
"id": "3344aabdd4e66110",
|
||||
"traceId": "12ea412956f99347b0503c1144acd0ec",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "SPAN",
|
||||
"environment": "default",
|
||||
"parentObservationId": null,
|
||||
"startTime": "2025-06-05T15:45:52.971Z",
|
||||
"endTime": "2025-06-05T15:45:55.750Z",
|
||||
"name": "llama-index-trace",
|
||||
"metadata": {
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.33.1",
|
||||
"service.name": "unknown_service"
|
||||
},
|
||||
"scope": {
|
||||
"name": "langfuse-sdk",
|
||||
"version": "3.0.0",
|
||||
"attributes": {
|
||||
"public_key": "pk-lf-5855d85e-3943-497e-bd10-f50ad414bcba"
|
||||
}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": "1.0.0",
|
||||
"modelParameters": null,
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-06-05T15:45:55.996Z",
|
||||
"updatedAt": "2025-06-05T15:45:56.027Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 2779,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": null,
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"id": "7ca8b22f6628d0c7",
|
||||
"traceId": "12ea412956f99347b0503c1144acd0ec",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "GENERATION",
|
||||
"environment": "default",
|
||||
"parentObservationId": "3344aabdd4e66110",
|
||||
"startTime": "2025-06-05T15:45:52.971Z",
|
||||
"endTime": "2025-06-05T15:45:55.747Z",
|
||||
"name": "OpenAI.complete",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"llm.model_name": "gpt-4o",
|
||||
"llm.invocation_parameters": {
|
||||
"context_window": 128000,
|
||||
"num_output": -1,
|
||||
"is_chat_model": true,
|
||||
"is_function_calling_model": true,
|
||||
"model_name": "gpt-4o",
|
||||
"system_role": "system"
|
||||
},
|
||||
"llm.provider": "openai",
|
||||
"llm.system": "openai",
|
||||
"input.value": {
|
||||
"args": [
|
||||
"What is Langfuse?"
|
||||
]
|
||||
},
|
||||
"input.mime_type": "application/json",
|
||||
"llm.prompts": [
|
||||
"What is Langfuse?"
|
||||
],
|
||||
"output.value": "Langfuse is a tool designed to help developers monitor and debug applications that utilize large language models (LLMs). It provides features for tracking and analyzing the performance of LLMs, enabling developers to gain insights into how these models are functioning within their applications. Langfuse can be particularly useful for identifying issues, optimizing performance, and ensuring that the integration of language models into applications is smooth and effective.",
|
||||
"llm.token_count.prompt": "13",
|
||||
"llm.token_count.prompt_details.cache_read": "0",
|
||||
"llm.token_count.prompt_details.audio": "0",
|
||||
"llm.token_count.completion": "81",
|
||||
"llm.token_count.completion_details.reasoning": "0",
|
||||
"llm.token_count.completion_details.audio": "0",
|
||||
"llm.token_count.total": "94",
|
||||
"openinference.span.kind": "LLM"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.33.1",
|
||||
"service.name": "unknown_service"
|
||||
},
|
||||
"scope": {
|
||||
"name": "openinference.instrumentation.llama_index",
|
||||
"version": "4.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": {
|
||||
"context_window": 128000,
|
||||
"num_output": -1,
|
||||
"is_chat_model": true,
|
||||
"is_function_calling_model": true,
|
||||
"model_name": "gpt-4o",
|
||||
"system_role": "system"
|
||||
},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-06-05T15:45:55.866Z",
|
||||
"updatedAt": "2025-06-05T15:45:56.021Z",
|
||||
"usageDetails": {
|
||||
"input": 13,
|
||||
"prompt_details.cache_read": 0,
|
||||
"prompt_details.audio": 0,
|
||||
"output": 81,
|
||||
"completion_details.reasoning": 0,
|
||||
"completion_details.audio": 0,
|
||||
"total": 94
|
||||
},
|
||||
"costDetails": {
|
||||
"input": 0.0000325,
|
||||
"output": 0.00081,
|
||||
"total": 0.000842499999
|
||||
},
|
||||
"providedCostDetails": {},
|
||||
"model": "gpt-4o",
|
||||
"internalModelId": "b9854a5c92dc496b997d99d20",
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 2776,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": 0.0000325,
|
||||
"outputCost": 0.00081,
|
||||
"totalCost": 0.000842499999,
|
||||
"inputUsage": 13,
|
||||
"outputUsage": 81,
|
||||
"totalUsage": 94,
|
||||
"input": {
|
||||
"args": [
|
||||
"What is Langfuse?"
|
||||
]
|
||||
},
|
||||
"output": "Langfuse is a tool designed to help developers monitor and debug applications that utilize large language models (LLMs). It provides features for tracking and analyzing the performance of LLMs, enabling developers to gain insights into how these models are functioning within their applications. Langfuse can be particularly useful for identifying issues, optimizing performance, and ensuring that the integration of language models into applications is smooth and effective."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { readFileSync, writeFileSync } from "fs";
|
||||
|
||||
/**
|
||||
* Merges detailed observation data (input/output/metadata) into the base trace file.
|
||||
* how to use? SEE README.md
|
||||
*
|
||||
* Usage: ts-node merge-observations.ts <base-file> <detailed-obs-file> <output-file>
|
||||
*
|
||||
* Example:
|
||||
* ts-node merge-observations.ts pydantic-base.json pydantic-details.json pydantic-ai.json
|
||||
*
|
||||
*/
|
||||
|
||||
const [baseFile, detailedFile, outputFile] = process.argv.slice(2);
|
||||
|
||||
if (!baseFile || !detailedFile || !outputFile) {
|
||||
console.error(
|
||||
"Usage: ts-node merge-observations.ts <base-file> <detailed-obs-file> <output-file>",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const baseData = JSON.parse(readFileSync(baseFile, "utf-8"));
|
||||
const detailedObs = JSON.parse(readFileSync(detailedFile, "utf-8"));
|
||||
|
||||
// Build ID to detailed observation map
|
||||
const obsMap = new Map<string, any>();
|
||||
for (const obs of Object.values(detailedObs)) {
|
||||
obsMap.set((obs as any).id, obs);
|
||||
}
|
||||
|
||||
// Merge input/output/metadata into observations array
|
||||
const mergedObservations = baseData.observations.map((obs: any) => {
|
||||
const detailed = obsMap.get(obs.id);
|
||||
if (detailed) {
|
||||
return {
|
||||
...obs,
|
||||
input: detailed.input,
|
||||
output: detailed.output,
|
||||
metadata: detailed.metadata,
|
||||
};
|
||||
}
|
||||
return obs;
|
||||
});
|
||||
|
||||
// demo project
|
||||
const PROJECT_ID = "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a";
|
||||
|
||||
// Remove observations from trace, create final structure
|
||||
const output = {
|
||||
trace: {
|
||||
...baseData.trace,
|
||||
projectId: PROJECT_ID,
|
||||
},
|
||||
observations: mergedObservations.map((obs: any) => ({
|
||||
...obs,
|
||||
projectId: PROJECT_ID,
|
||||
})),
|
||||
};
|
||||
|
||||
// Remove observations array from trace if it exists
|
||||
delete (output.trace as any).observations;
|
||||
|
||||
writeFileSync(outputFile, JSON.stringify(output, null, 2));
|
||||
|
||||
console.log(`Merged ${mergedObservations.length} observations`);
|
||||
console.log(`Wrote to ${outputFile}`);
|
||||
+522
@@ -0,0 +1,522 @@
|
||||
{
|
||||
"trace": {
|
||||
"id": "e3a407423619277a2dcef3c13dc89982",
|
||||
"projectId": "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
|
||||
"name": "invoke_agent 99981f6a-abd1-4d09-9fba-00e2ab172193",
|
||||
"timestamp": "2025-10-14T16:44:29.952Z",
|
||||
"environment": "default",
|
||||
"tags": [],
|
||||
"bookmarked": false,
|
||||
"release": null,
|
||||
"version": null,
|
||||
"userId": null,
|
||||
"sessionId": null,
|
||||
"public": false,
|
||||
"input": "\"[{\\\"role\\\": \\\"user\\\", \\\"parts\\\": [{\\\"type\\\": \\\"text\\\", \\\"content\\\": \\\"What's the weather like in Portland?\\\"}]}]\"",
|
||||
"output": "\"[{\\\"role\\\": \\\"assistant\\\", \\\"parts\\\": [{\\\"type\\\": \\\"tool_call\\\", \\\"id\\\": \\\"[\\\\\\\"run_9guMCbt68iSVgtsx6WdKMA18\\\\\\\", \\\\\\\"call_Sz1QP8T7fuJkIECGDLFWOorq\\\\\\\"]\\\", \\\"name\\\": \\\"get_weather\\\", \\\"arguments\\\": {\\\"location\\\": \\\"Portland\\\"}}]}, {\\\"role\\\": \\\"tool\\\", \\\"parts\\\": [{\\\"type\\\": \\\"tool_call_response\\\", \\\"id\\\": \\\"[\\\\\\\"run_9guMCbt68iSVgtsx6WdKMA18\\\\\\\", \\\\\\\"call_Sz1QP8T7fuJkIECGDLFWOorq\\\\\\\"]\\\", \\\"response\\\": \\\"\\\\\\\"The weather in Portland is stormy with a high of 19\\\\\\\\u00b0C.\\\\\\\"\\\"}]}, {\\\"role\\\": \\\"assistant\\\", \\\"parts\\\": [{\\\"type\\\": \\\"text\\\", \\\"content\\\": \\\"The weather in Portland is currently stormy with a high temperature of 19\\\\u00b0C.\\\"}]}]\"",
|
||||
"metadata": "{\"attributes\":{\"gen_ai.operation.name\":\"invoke_agent\",\"gen_ai.request.choice.count\":\"1\",\"gen_ai.provider.name\":\"microsoft.agent_framework\",\"gen_ai.request.model\":\"unknown\",\"gen_ai.tool.definitions\":\"[{\\\"type\\\": \\\"function\\\", \\\"function\\\": {\\\"name\\\": \\\"get_weather\\\", \\\"description\\\": \\\"Get the weather for a given location.\\\", \\\"parameters\\\": {\\\"properties\\\": {\\\"location\\\": {\\\"description\\\": \\\"The location to get the weather for.\\\", \\\"title\\\": \\\"Location\\\", \\\"type\\\": \\\"string\\\"}}, \\\"required\\\": [\\\"location\\\"], \\\"title\\\": \\\"get_weather_input\\\", \\\"type\\\": \\\"object\\\"}}}]\",\"gen_ai.agent.id\":\"99981f6a-abd1-4d09-9fba-00e2ab172193\",\"gen_ai.agent.name\":\"99981f6a-abd1-4d09-9fba-00e2ab172193\",\"gen_ai.response.id\":\"run_9guMCbt68iSVgtsx6WdKMA18\",\"gen_ai.usage.input_tokens\":\"631\",\"gen_ai.usage.output_tokens\":\"35\"},\"resourceAttributes\":{\"telemetry.sdk.language\":\"python\",\"telemetry.sdk.name\":\"opentelemetry\",\"telemetry.sdk.version\":\"1.37.0\",\"service.name\":\"agent_framework\"},\"scope\":{\"name\":\"agent_framework\",\"version\":\"1.0.0b251007\",\"attributes\":{}}}",
|
||||
"createdAt": "2025-10-14T16:44:37.000Z",
|
||||
"updatedAt": "2025-10-14T16:44:46.795Z",
|
||||
"scores": [
|
||||
{
|
||||
"id": "1b8254bc-9840-4fe8-ab2b-51ccedbb68d1",
|
||||
"timestamp": "2025-10-14T16:45:06.802Z",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"environment": "default",
|
||||
"name": "toxicity",
|
||||
"source": "EVAL",
|
||||
"authorUserId": null,
|
||||
"comment": "The generation provides a straightforward weather report without any harmful or disrespectful language, thus it does not exhibit any toxicity.",
|
||||
"metadata": "{}",
|
||||
"configId": null,
|
||||
"createdAt": "2025-10-14T16:45:07.000Z",
|
||||
"updatedAt": "2025-10-14T16:45:06.860Z",
|
||||
"queueId": null,
|
||||
"traceId": "e3a407423619277a2dcef3c13dc89982",
|
||||
"observationId": null,
|
||||
"sessionId": null,
|
||||
"datasetRunId": null,
|
||||
"value": 0,
|
||||
"stringValue": null,
|
||||
"dataType": "NUMERIC"
|
||||
},
|
||||
{
|
||||
"id": "8e4079c8-bc62-4868-8a8c-450050308a91",
|
||||
"timestamp": "2025-10-14T16:45:05.004Z",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"environment": "default",
|
||||
"name": "hallucination",
|
||||
"source": "EVAL",
|
||||
"authorUserId": null,
|
||||
"comment": "The generation provides a specific current weather report for Portland ('stormy with a high of 19°C') without real-time access or evidence that this is accurate at the time of generation. Unless it is actually confirmed by a current weather API, it is likely to be invented or outdated. Since weather information is highly time-dependent and unverifiable here, the response can be considered a complete hallucination.",
|
||||
"metadata": "{}",
|
||||
"configId": null,
|
||||
"createdAt": "2025-10-14T16:45:06.000Z",
|
||||
"updatedAt": "2025-10-14T16:45:05.199Z",
|
||||
"queueId": null,
|
||||
"traceId": "e3a407423619277a2dcef3c13dc89982",
|
||||
"observationId": null,
|
||||
"sessionId": null,
|
||||
"datasetRunId": null,
|
||||
"value": 1,
|
||||
"stringValue": null,
|
||||
"dataType": "NUMERIC"
|
||||
}
|
||||
],
|
||||
"latency": 12.447
|
||||
},
|
||||
"observations": [
|
||||
{
|
||||
"id": "026d1c61bcaa1743",
|
||||
"traceId": "e3a407423619277a2dcef3c13dc89982",
|
||||
"projectId": "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
|
||||
"type": "AGENT",
|
||||
"environment": "default",
|
||||
"parentObservationId": null,
|
||||
"startTime": "2025-10-14T16:44:29.952Z",
|
||||
"endTime": "2025-10-14T16:44:42.399Z",
|
||||
"name": "invoke_agent 99981f6a-abd1-4d09-9fba-00e2ab172193",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"gen_ai.operation.name": "invoke_agent",
|
||||
"gen_ai.request.choice.count": "1",
|
||||
"gen_ai.provider.name": "microsoft.agent_framework",
|
||||
"gen_ai.request.model": "unknown",
|
||||
"gen_ai.tool.definitions": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get the weather for a given location.",
|
||||
"parameters": {
|
||||
"properties": {
|
||||
"location": {
|
||||
"description": "The location to get the weather for.",
|
||||
"title": "Location",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"location"
|
||||
],
|
||||
"title": "get_weather_input",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"gen_ai.agent.id": "99981f6a-abd1-4d09-9fba-00e2ab172193",
|
||||
"gen_ai.agent.name": "99981f6a-abd1-4d09-9fba-00e2ab172193",
|
||||
"gen_ai.response.id": "run_9guMCbt68iSVgtsx6WdKMA18",
|
||||
"gen_ai.usage.input_tokens": "631",
|
||||
"gen_ai.usage.output_tokens": "35"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.37.0",
|
||||
"service.name": "agent_framework"
|
||||
},
|
||||
"scope": {
|
||||
"name": "agent_framework",
|
||||
"version": "1.0.0b251007",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": {
|
||||
"choice.count": 1
|
||||
},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-10-14T16:44:46.725Z",
|
||||
"updatedAt": "2025-10-14T16:44:46.725Z",
|
||||
"usageDetails": {
|
||||
"input": 631,
|
||||
"output": 35,
|
||||
"total": 666
|
||||
},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": "unknown",
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 12447,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 631,
|
||||
"outputUsage": 35,
|
||||
"totalUsage": 666,
|
||||
"input": [
|
||||
{
|
||||
"role": "user",
|
||||
"parts": [
|
||||
{
|
||||
"type": "text",
|
||||
"content": "What's the weather like in Portland?"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"output": [
|
||||
{
|
||||
"role": "assistant",
|
||||
"parts": [
|
||||
{
|
||||
"type": "tool_call",
|
||||
"id": [
|
||||
"run_9guMCbt68iSVgtsx6WdKMA18",
|
||||
"call_Sz1QP8T7fuJkIECGDLFWOorq"
|
||||
],
|
||||
"name": "get_weather",
|
||||
"arguments": {
|
||||
"location": "Portland"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"parts": [
|
||||
{
|
||||
"type": "tool_call_response",
|
||||
"id": [
|
||||
"run_9guMCbt68iSVgtsx6WdKMA18",
|
||||
"call_Sz1QP8T7fuJkIECGDLFWOorq"
|
||||
],
|
||||
"response": "The weather in Portland is stormy with a high of 19°C."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"parts": [
|
||||
{
|
||||
"type": "text",
|
||||
"content": "The weather in Portland is currently stormy with a high temperature of 19°C."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "3c2d6f2f26930c50",
|
||||
"traceId": "e3a407423619277a2dcef3c13dc89982",
|
||||
"projectId": "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
|
||||
"type": "GENERATION",
|
||||
"environment": "default",
|
||||
"parentObservationId": "026d1c61bcaa1743",
|
||||
"startTime": "2025-10-14T16:44:35.939Z",
|
||||
"endTime": "2025-10-14T16:44:42.398Z",
|
||||
"name": "chat gpt-4o",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"gen_ai.operation.name": "chat",
|
||||
"gen_ai.request.choice.count": "1",
|
||||
"gen_ai.provider.name": "openai",
|
||||
"gen_ai.request.model": "gpt-4o",
|
||||
"server.address": "Unknown",
|
||||
"gen_ai.conversation.id": "thread_G78qCSLpzn0YQwFwrvK5OivI",
|
||||
"gen_ai.tool.definitions": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get the weather for a given location.",
|
||||
"parameters": {
|
||||
"properties": {
|
||||
"location": {
|
||||
"description": "The location to get the weather for.",
|
||||
"title": "Location",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"location"
|
||||
],
|
||||
"title": "get_weather_input",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"gen_ai.response.id": "run_9guMCbt68iSVgtsx6WdKMA18",
|
||||
"gen_ai.usage.input_tokens": "631",
|
||||
"gen_ai.usage.output_tokens": "35",
|
||||
"gen_ai.client.operation.duration": "6.458223418999864"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.37.0",
|
||||
"service.name": "agent_framework"
|
||||
},
|
||||
"scope": {
|
||||
"name": "agent_framework",
|
||||
"version": "1.0.0b251007",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": {
|
||||
"choice.count": 1
|
||||
},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-10-14T16:44:46.725Z",
|
||||
"updatedAt": "2025-10-14T16:44:46.725Z",
|
||||
"usageDetails": {
|
||||
"input": 631,
|
||||
"output": 35,
|
||||
"total": 666
|
||||
},
|
||||
"costDetails": {
|
||||
"input": 0.0015775,
|
||||
"output": 0.00035,
|
||||
"total": 0.0019275
|
||||
},
|
||||
"providedCostDetails": {},
|
||||
"model": "gpt-4o",
|
||||
"internalModelId": "b9854a5c92dc496b997d99d20",
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 6459,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": 0.0015775,
|
||||
"outputCost": 0.00035,
|
||||
"totalCost": 0.0019275,
|
||||
"inputUsage": 631,
|
||||
"outputUsage": 35,
|
||||
"totalUsage": 666,
|
||||
"input": [
|
||||
{
|
||||
"role": "assistant",
|
||||
"parts": [
|
||||
{
|
||||
"type": "tool_call",
|
||||
"id": [
|
||||
"run_9guMCbt68iSVgtsx6WdKMA18",
|
||||
"call_Sz1QP8T7fuJkIECGDLFWOorq"
|
||||
],
|
||||
"name": "get_weather",
|
||||
"arguments": {
|
||||
"location": "Portland"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"parts": [
|
||||
{
|
||||
"type": "tool_call_response",
|
||||
"id": [
|
||||
"run_9guMCbt68iSVgtsx6WdKMA18",
|
||||
"call_Sz1QP8T7fuJkIECGDLFWOorq"
|
||||
],
|
||||
"response": "The weather in Portland is stormy with a high of 19°C."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"output": [
|
||||
{
|
||||
"role": "assistant",
|
||||
"parts": [
|
||||
{
|
||||
"type": "text",
|
||||
"content": "The weather in Portland is currently stormy with a high temperature of 19°C."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "c6b8d4db5f0eac82",
|
||||
"traceId": "e3a407423619277a2dcef3c13dc89982",
|
||||
"projectId": "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
|
||||
"type": "GENERATION",
|
||||
"environment": "default",
|
||||
"parentObservationId": "026d1c61bcaa1743",
|
||||
"startTime": "2025-10-14T16:44:29.953Z",
|
||||
"endTime": "2025-10-14T16:44:35.937Z",
|
||||
"name": "chat gpt-4o",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"gen_ai.operation.name": "chat",
|
||||
"gen_ai.request.choice.count": "1",
|
||||
"gen_ai.provider.name": "openai",
|
||||
"gen_ai.request.model": "gpt-4o",
|
||||
"server.address": "Unknown",
|
||||
"gen_ai.tool.definitions": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get the weather for a given location.",
|
||||
"parameters": {
|
||||
"properties": {
|
||||
"location": {
|
||||
"description": "The location to get the weather for.",
|
||||
"title": "Location",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"location"
|
||||
],
|
||||
"title": "get_weather_input",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"gen_ai.response.id": "run_9guMCbt68iSVgtsx6WdKMA18",
|
||||
"gen_ai.client.operation.duration": "5.983186655000054"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.37.0",
|
||||
"service.name": "agent_framework"
|
||||
},
|
||||
"scope": {
|
||||
"name": "agent_framework",
|
||||
"version": "1.0.0b251007",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": {
|
||||
"choice.count": 1
|
||||
},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-10-14T16:44:36.520Z",
|
||||
"updatedAt": "2025-10-14T16:44:36.520Z",
|
||||
"usageDetails": {
|
||||
"input": 25,
|
||||
"output": 70,
|
||||
"total": 95
|
||||
},
|
||||
"costDetails": {
|
||||
"input": 0.0000625,
|
||||
"output": 0.0007,
|
||||
"total": 0.000762499999
|
||||
},
|
||||
"providedCostDetails": {},
|
||||
"model": "gpt-4o",
|
||||
"internalModelId": "b9854a5c92dc496b997d99d20",
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 5984,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": 0.0000625,
|
||||
"outputCost": 0.0007,
|
||||
"totalCost": 0.000762499999,
|
||||
"inputUsage": 25,
|
||||
"outputUsage": 70,
|
||||
"totalUsage": 95,
|
||||
"input": [
|
||||
{
|
||||
"role": "user",
|
||||
"parts": [
|
||||
{
|
||||
"type": "text",
|
||||
"content": "What's the weather like in Portland?"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"output": [
|
||||
{
|
||||
"role": "assistant",
|
||||
"parts": [
|
||||
{
|
||||
"type": "tool_call",
|
||||
"id": [
|
||||
"run_9guMCbt68iSVgtsx6WdKMA18",
|
||||
"call_Sz1QP8T7fuJkIECGDLFWOorq"
|
||||
],
|
||||
"name": "get_weather",
|
||||
"arguments": {
|
||||
"location": "Portland"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7f845be8c0da22f3",
|
||||
"traceId": "e3a407423619277a2dcef3c13dc89982",
|
||||
"projectId": "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
|
||||
"type": "TOOL",
|
||||
"environment": "default",
|
||||
"parentObservationId": "026d1c61bcaa1743",
|
||||
"startTime": "2025-10-14T16:44:35.938Z",
|
||||
"endTime": "2025-10-14T16:44:35.938Z",
|
||||
"name": "execute_tool get_weather",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"gen_ai.operation.name": "execute_tool",
|
||||
"gen_ai.tool.name": "get_weather",
|
||||
"gen_ai.tool.call.id": [
|
||||
"run_9guMCbt68iSVgtsx6WdKMA18",
|
||||
"call_Sz1QP8T7fuJkIECGDLFWOorq"
|
||||
],
|
||||
"gen_ai.tool.type": "function",
|
||||
"gen_ai.tool.description": "Get the weather for a given location.",
|
||||
"agent_framework.function.invocation.duration": "0.000022586999989471224"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.37.0",
|
||||
"service.name": "agent_framework"
|
||||
},
|
||||
"scope": {
|
||||
"name": "agent_framework",
|
||||
"version": "1.0.0b251007",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": {},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-10-14T16:44:36.520Z",
|
||||
"updatedAt": "2025-10-14T16:44:36.520Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 0,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": {
|
||||
"location": "Portland"
|
||||
},
|
||||
"output": "The weather in Portland is stormy with a high of 19°C."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,931 @@
|
||||
{
|
||||
"trace": {
|
||||
"id": "fee618f96dc31e0ca38b2f7b26eb8b29",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"name": "Agent workflow",
|
||||
"timestamp": "2025-09-30T09:03:59.962Z",
|
||||
"environment": "default",
|
||||
"tags": [],
|
||||
"bookmarked": false,
|
||||
"release": null,
|
||||
"version": null,
|
||||
"userId": null,
|
||||
"sessionId": null,
|
||||
"public": true,
|
||||
"input": null,
|
||||
"output": null,
|
||||
"metadata": "{\"attributes\":{\"openinference.span.kind\":\"AGENT\"},\"resourceAttributes\":{\"telemetry.sdk.language\":\"python\",\"telemetry.sdk.name\":\"opentelemetry\",\"telemetry.sdk.version\":\"1.37.0\",\"service.name\":\"unknown_service\"},\"scope\":{\"name\":\"openinference.instrumentation.openai_agents\",\"version\":\"1.3.0\",\"attributes\":{}}}",
|
||||
"createdAt": "2025-09-30T09:04:03.000Z",
|
||||
"updatedAt": "2025-09-30T09:04:07.465Z",
|
||||
"scores": [
|
||||
{
|
||||
"id": "0721bd4f-a35c-4d29-a2c6-38879e1d758e",
|
||||
"timestamp": "2025-09-30T09:04:30.125Z",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"environment": "default",
|
||||
"name": "toxicity",
|
||||
"source": "EVAL",
|
||||
"authorUserId": null,
|
||||
"comment": "The generation contains aggressive and dismissive language that undermines trust in professionals and promotes a negative view of the public's intelligence, which is harmful and disrespectful.",
|
||||
"metadata": "{}",
|
||||
"configId": null,
|
||||
"createdAt": "2025-09-30T09:04:31.000Z",
|
||||
"updatedAt": "2025-09-30T09:04:30.197Z",
|
||||
"queueId": null,
|
||||
"traceId": "fee618f96dc31e0ca38b2f7b26eb8b29",
|
||||
"observationId": null,
|
||||
"sessionId": null,
|
||||
"datasetRunId": null,
|
||||
"value": 0.8,
|
||||
"stringValue": null,
|
||||
"dataType": "NUMERIC"
|
||||
},
|
||||
{
|
||||
"id": "f60dc1c4-17af-4512-ada9-f2440f437ce1",
|
||||
"timestamp": "2025-09-30T09:04:28.687Z",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"environment": "default",
|
||||
"name": "hallucination",
|
||||
"source": "EVAL",
|
||||
"authorUserId": null,
|
||||
"comment": "There is no specific generation provided to evaluate for hallucination. Therefore, it is impossible to assess or score the degree of hallucination in this case.",
|
||||
"metadata": "{}",
|
||||
"configId": null,
|
||||
"createdAt": "2025-09-30T09:04:29.000Z",
|
||||
"updatedAt": "2025-09-30T09:04:28.773Z",
|
||||
"queueId": null,
|
||||
"traceId": "fee618f96dc31e0ca38b2f7b26eb8b29",
|
||||
"observationId": null,
|
||||
"sessionId": null,
|
||||
"datasetRunId": null,
|
||||
"value": 0,
|
||||
"stringValue": null,
|
||||
"dataType": "NUMERIC"
|
||||
}
|
||||
],
|
||||
"latency": 2.19
|
||||
},
|
||||
"observations": [
|
||||
{
|
||||
"id": "e5a76f27f51ad40e",
|
||||
"traceId": "fee618f96dc31e0ca38b2f7b26eb8b29",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "AGENT",
|
||||
"environment": "default",
|
||||
"parentObservationId": null,
|
||||
"startTime": "2025-09-30T09:03:59.962Z",
|
||||
"endTime": "2025-09-30T09:04:02.152Z",
|
||||
"name": "Agent workflow",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"openinference.span.kind": "AGENT"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.37.0",
|
||||
"service.name": "unknown_service"
|
||||
},
|
||||
"scope": {
|
||||
"name": "openinference.instrumentation.openai_agents",
|
||||
"version": "1.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": {},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-09-30T09:04:07.382Z",
|
||||
"updatedAt": "2025-09-30T09:04:07.382Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 2190,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": null,
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"id": "d950b668796240b5",
|
||||
"traceId": "fee618f96dc31e0ca38b2f7b26eb8b29",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "AGENT",
|
||||
"environment": "default",
|
||||
"parentObservationId": "e5a76f27f51ad40e",
|
||||
"startTime": "2025-09-30T09:03:59.962Z",
|
||||
"endTime": "2025-09-30T09:04:02.152Z",
|
||||
"name": "Hello world",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"llm.system": "openai",
|
||||
"graph.node.id": "Hello world",
|
||||
"openinference.span.kind": "AGENT"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.37.0",
|
||||
"service.name": "unknown_service"
|
||||
},
|
||||
"scope": {
|
||||
"name": "openinference.instrumentation.openai_agents",
|
||||
"version": "1.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": {},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-09-30T09:04:07.382Z",
|
||||
"updatedAt": "2025-09-30T09:04:07.382Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 2190,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": null,
|
||||
"output": null
|
||||
},
|
||||
{
|
||||
"id": "90d94774e8e3724d",
|
||||
"traceId": "fee618f96dc31e0ca38b2f7b26eb8b29",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "GENERATION",
|
||||
"environment": "default",
|
||||
"parentObservationId": "d950b668796240b5",
|
||||
"startTime": "2025-09-30T09:04:01.001Z",
|
||||
"endTime": "2025-09-30T09:04:02.151Z",
|
||||
"name": "response",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"llm.system": "openai",
|
||||
"output.mime_type": "application/json",
|
||||
"output.value": {
|
||||
"id": "resp_0f00ca5b7e22bb4c0068db9d0110d8819da676bd1f2dd03a40",
|
||||
"created_at": 1759223041,
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": "You are a helpful agent.",
|
||||
"metadata": {},
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"object": "response",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_0f00ca5b7e22bb4c0068db9d019a78819d9fe1e4d3b6c96b68",
|
||||
"content": [
|
||||
{
|
||||
"annotations": [],
|
||||
"text": "The weather in Tokyo is currently sunny. If you need more details like temperature or forecast for the upcoming days, just let me know!",
|
||||
"type": "output_text",
|
||||
"logprobs": []
|
||||
}
|
||||
],
|
||||
"role": "assistant",
|
||||
"status": "completed",
|
||||
"type": "message"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"temperature": 1,
|
||||
"tool_choice": "auto",
|
||||
"tools": [
|
||||
{
|
||||
"name": "get_weather",
|
||||
"parameters": {
|
||||
"properties": {
|
||||
"city": {
|
||||
"title": "City",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["city"],
|
||||
"title": "get_weather_args",
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
},
|
||||
"strict": true,
|
||||
"type": "function",
|
||||
"description": null
|
||||
}
|
||||
],
|
||||
"top_p": 1,
|
||||
"background": false,
|
||||
"conversation": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"previous_response_id": null,
|
||||
"prompt": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"generate_summary": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"status": "completed",
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"top_logprobs": 0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 85,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 29,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 114
|
||||
},
|
||||
"user": null,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"store": true
|
||||
},
|
||||
"llm.tools.0.tool.json_schema": {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": null,
|
||||
"parameters": {
|
||||
"properties": {
|
||||
"city": {
|
||||
"title": "City",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["city"],
|
||||
"title": "get_weather_args",
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
},
|
||||
"strict": true
|
||||
}
|
||||
},
|
||||
"llm.token_count.completion": "29",
|
||||
"llm.token_count.prompt": "85",
|
||||
"llm.token_count.total": "114",
|
||||
"llm.token_count.prompt_details.cache_read": "0",
|
||||
"llm.token_count.completion_details.reasoning": "0",
|
||||
"llm.output_messages.0.message.role": "assistant",
|
||||
"llm.output_messages.0.message.contents.0.message_content.type": "text",
|
||||
"llm.output_messages.0.message.contents.0.message_content.text": "The weather in Tokyo is currently sunny. If you need more details like temperature or forecast for the upcoming days, just let me know!",
|
||||
"llm.input_messages.0.message.role": "system",
|
||||
"llm.input_messages.0.message.content": "You are a helpful agent.",
|
||||
"llm.model_name": "gpt-4.1-2025-04-14",
|
||||
"llm.invocation_parameters": {
|
||||
"id": "resp_0f00ca5b7e22bb4c0068db9d0110d8819da676bd1f2dd03a40",
|
||||
"created_at": 1759223041,
|
||||
"instructions": "You are a helpful agent.",
|
||||
"metadata": {},
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"parallel_tool_calls": true,
|
||||
"temperature": 1,
|
||||
"tool_choice": "auto",
|
||||
"top_p": 1,
|
||||
"background": false,
|
||||
"reasoning": {},
|
||||
"service_tier": "default",
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"top_logprobs": 0,
|
||||
"truncation": "disabled",
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"store": true
|
||||
},
|
||||
"input.mime_type": "application/json",
|
||||
"input.value": [
|
||||
{
|
||||
"content": "What's the weather in Tokyo?",
|
||||
"role": "user"
|
||||
},
|
||||
{
|
||||
"arguments": {
|
||||
"city": "Tokyo"
|
||||
},
|
||||
"call_id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
|
||||
"name": "get_weather",
|
||||
"type": "function_call",
|
||||
"id": "fc_0f00ca5b7e22bb4c0068db9d00d258819d987f4b9b63189e76",
|
||||
"status": "completed"
|
||||
},
|
||||
{
|
||||
"call_id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
|
||||
"output": "The weather in Tokyo is sunny.",
|
||||
"type": "function_call_output"
|
||||
}
|
||||
],
|
||||
"llm.input_messages.1.message.role": "user",
|
||||
"llm.input_messages.1.message.content": "What's the weather in Tokyo?",
|
||||
"llm.input_messages.2.message.role": "assistant",
|
||||
"llm.input_messages.2.message.tool_calls.0.tool_call.id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
|
||||
"llm.input_messages.2.message.tool_calls.0.tool_call.function.name": "get_weather",
|
||||
"llm.input_messages.2.message.tool_calls.0.tool_call.function.arguments": {
|
||||
"city": "Tokyo"
|
||||
},
|
||||
"llm.input_messages.3.message.role": "tool",
|
||||
"llm.input_messages.3.message.tool_call_id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
|
||||
"llm.input_messages.3.message.content": "The weather in Tokyo is sunny.",
|
||||
"openinference.span.kind": "LLM"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.37.0",
|
||||
"service.name": "unknown_service"
|
||||
},
|
||||
"scope": {
|
||||
"name": "openinference.instrumentation.openai_agents",
|
||||
"version": "1.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": {
|
||||
"id": "resp_0f00ca5b7e22bb4c0068db9d0110d8819da676bd1f2dd03a40",
|
||||
"created_at": 1759223041,
|
||||
"instructions": "You are a helpful agent.",
|
||||
"metadata": "{}",
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"parallel_tool_calls": "true",
|
||||
"temperature": 1,
|
||||
"tool_choice": "auto",
|
||||
"top_p": 1,
|
||||
"background": "false",
|
||||
"reasoning": "{}",
|
||||
"service_tier": "default",
|
||||
"text": "{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"}",
|
||||
"top_logprobs": 0,
|
||||
"truncation": "disabled",
|
||||
"billing": "{\"payer\":\"developer\"}",
|
||||
"store": "true"
|
||||
},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-09-30T09:04:07.382Z",
|
||||
"updatedAt": "2025-09-30T09:04:07.382Z",
|
||||
"usageDetails": {
|
||||
"output": 29,
|
||||
"input": 85,
|
||||
"total": 114,
|
||||
"prompt_details.cache_read": 0,
|
||||
"completion_details.reasoning": 0
|
||||
},
|
||||
"costDetails": {
|
||||
"output": 0.000232,
|
||||
"input": 0.00017,
|
||||
"total": 0.000402
|
||||
},
|
||||
"providedCostDetails": {},
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"internalModelId": "cm7qahw732891bpmzy45r3x70",
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 1150,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": 0.00017,
|
||||
"outputCost": 0.000232,
|
||||
"totalCost": 0.000402,
|
||||
"inputUsage": 85,
|
||||
"outputUsage": 29,
|
||||
"totalUsage": 114,
|
||||
"input": [
|
||||
{
|
||||
"content": "What's the weather in Tokyo?",
|
||||
"role": "user"
|
||||
},
|
||||
{
|
||||
"arguments": {
|
||||
"city": "Tokyo"
|
||||
},
|
||||
"call_id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
|
||||
"name": "get_weather",
|
||||
"type": "function_call",
|
||||
"id": "fc_0f00ca5b7e22bb4c0068db9d00d258819d987f4b9b63189e76",
|
||||
"status": "completed"
|
||||
},
|
||||
{
|
||||
"call_id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
|
||||
"output": "The weather in Tokyo is sunny.",
|
||||
"type": "function_call_output"
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"id": "resp_0f00ca5b7e22bb4c0068db9d0110d8819da676bd1f2dd03a40",
|
||||
"created_at": 1759223041,
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": "You are a helpful agent.",
|
||||
"metadata": {},
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"object": "response",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_0f00ca5b7e22bb4c0068db9d019a78819d9fe1e4d3b6c96b68",
|
||||
"content": [
|
||||
{
|
||||
"annotations": [],
|
||||
"text": "The weather in Tokyo is currently sunny. If you need more details like temperature or forecast for the upcoming days, just let me know!",
|
||||
"type": "output_text",
|
||||
"logprobs": []
|
||||
}
|
||||
],
|
||||
"role": "assistant",
|
||||
"status": "completed",
|
||||
"type": "message"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"temperature": 1,
|
||||
"tool_choice": "auto",
|
||||
"tools": [
|
||||
{
|
||||
"name": "get_weather",
|
||||
"parameters": {
|
||||
"properties": {
|
||||
"city": {
|
||||
"title": "City",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["city"],
|
||||
"title": "get_weather_args",
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
},
|
||||
"strict": true,
|
||||
"type": "function",
|
||||
"description": null
|
||||
}
|
||||
],
|
||||
"top_p": 1,
|
||||
"background": false,
|
||||
"conversation": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"previous_response_id": null,
|
||||
"prompt": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"generate_summary": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"status": "completed",
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"top_logprobs": 0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 85,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 29,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 114
|
||||
},
|
||||
"user": null,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"store": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "98870087af69bf06",
|
||||
"traceId": "fee618f96dc31e0ca38b2f7b26eb8b29",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "GENERATION",
|
||||
"environment": "default",
|
||||
"parentObservationId": "d950b668796240b5",
|
||||
"startTime": "2025-09-30T09:03:59.963Z",
|
||||
"endTime": "2025-09-30T09:04:00.999Z",
|
||||
"name": "response",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"llm.system": "openai",
|
||||
"output.mime_type": "application/json",
|
||||
"output.value": {
|
||||
"id": "resp_0f00ca5b7e22bb4c0068db9d000be4819d824c447c54b65bc1",
|
||||
"created_at": 1759223040,
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": "You are a helpful agent.",
|
||||
"metadata": {},
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"object": "response",
|
||||
"output": [
|
||||
{
|
||||
"arguments": {
|
||||
"city": "Tokyo"
|
||||
},
|
||||
"call_id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
|
||||
"name": "get_weather",
|
||||
"type": "function_call",
|
||||
"id": "fc_0f00ca5b7e22bb4c0068db9d00d258819d987f4b9b63189e76",
|
||||
"status": "completed"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"temperature": 1,
|
||||
"tool_choice": "auto",
|
||||
"tools": [
|
||||
{
|
||||
"name": "get_weather",
|
||||
"parameters": {
|
||||
"properties": {
|
||||
"city": {
|
||||
"title": "City",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["city"],
|
||||
"title": "get_weather_args",
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
},
|
||||
"strict": true,
|
||||
"type": "function",
|
||||
"description": null
|
||||
}
|
||||
],
|
||||
"top_p": 1,
|
||||
"background": false,
|
||||
"conversation": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"previous_response_id": null,
|
||||
"prompt": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"generate_summary": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"status": "completed",
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"top_logprobs": 0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 55,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 15,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 70
|
||||
},
|
||||
"user": null,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"store": true
|
||||
},
|
||||
"llm.tools.0.tool.json_schema": {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": null,
|
||||
"parameters": {
|
||||
"properties": {
|
||||
"city": {
|
||||
"title": "City",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["city"],
|
||||
"title": "get_weather_args",
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
},
|
||||
"strict": true
|
||||
}
|
||||
},
|
||||
"llm.token_count.completion": "15",
|
||||
"llm.token_count.prompt": "55",
|
||||
"llm.token_count.total": "70",
|
||||
"llm.token_count.prompt_details.cache_read": "0",
|
||||
"llm.token_count.completion_details.reasoning": "0",
|
||||
"llm.output_messages.0.message.role": "assistant",
|
||||
"llm.output_messages.0.message.tool_calls.0.tool_call.id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
|
||||
"llm.output_messages.0.message.tool_calls.0.tool_call.function.name": "get_weather",
|
||||
"llm.output_messages.0.message.tool_calls.0.tool_call.function.arguments": {
|
||||
"city": "Tokyo"
|
||||
},
|
||||
"llm.input_messages.0.message.role": "system",
|
||||
"llm.input_messages.0.message.content": "You are a helpful agent.",
|
||||
"llm.model_name": "gpt-4.1-2025-04-14",
|
||||
"llm.invocation_parameters": {
|
||||
"id": "resp_0f00ca5b7e22bb4c0068db9d000be4819d824c447c54b65bc1",
|
||||
"created_at": 1759223040,
|
||||
"instructions": "You are a helpful agent.",
|
||||
"metadata": {},
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"parallel_tool_calls": true,
|
||||
"temperature": 1,
|
||||
"tool_choice": "auto",
|
||||
"top_p": 1,
|
||||
"background": false,
|
||||
"reasoning": {},
|
||||
"service_tier": "default",
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"top_logprobs": 0,
|
||||
"truncation": "disabled",
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"store": true
|
||||
},
|
||||
"input.mime_type": "application/json",
|
||||
"input.value": [
|
||||
{
|
||||
"content": "What's the weather in Tokyo?",
|
||||
"role": "user"
|
||||
}
|
||||
],
|
||||
"llm.input_messages.1.message.role": "user",
|
||||
"llm.input_messages.1.message.content": "What's the weather in Tokyo?",
|
||||
"openinference.span.kind": "LLM"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.37.0",
|
||||
"service.name": "unknown_service"
|
||||
},
|
||||
"scope": {
|
||||
"name": "openinference.instrumentation.openai_agents",
|
||||
"version": "1.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": {
|
||||
"id": "resp_0f00ca5b7e22bb4c0068db9d000be4819d824c447c54b65bc1",
|
||||
"created_at": 1759223040,
|
||||
"instructions": "You are a helpful agent.",
|
||||
"metadata": "{}",
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"parallel_tool_calls": "true",
|
||||
"temperature": 1,
|
||||
"tool_choice": "auto",
|
||||
"top_p": 1,
|
||||
"background": "false",
|
||||
"reasoning": "{}",
|
||||
"service_tier": "default",
|
||||
"text": "{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"}",
|
||||
"top_logprobs": 0,
|
||||
"truncation": "disabled",
|
||||
"billing": "{\"payer\":\"developer\"}",
|
||||
"store": "true"
|
||||
},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-09-30T09:04:02.217Z",
|
||||
"updatedAt": "2025-09-30T09:04:02.217Z",
|
||||
"usageDetails": {
|
||||
"output": 15,
|
||||
"input": 55,
|
||||
"total": 70,
|
||||
"prompt_details.cache_read": 0,
|
||||
"completion_details.reasoning": 0
|
||||
},
|
||||
"costDetails": {
|
||||
"output": 0.00012,
|
||||
"input": 0.00011,
|
||||
"total": 0.00023
|
||||
},
|
||||
"providedCostDetails": {},
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"internalModelId": "cm7qahw732891bpmzy45r3x70",
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 1036,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": 0.00011,
|
||||
"outputCost": 0.00012,
|
||||
"totalCost": 0.00023,
|
||||
"inputUsage": 55,
|
||||
"outputUsage": 15,
|
||||
"totalUsage": 70,
|
||||
"input": [
|
||||
{
|
||||
"content": "What's the weather in Tokyo?",
|
||||
"role": "user"
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"id": "resp_0f00ca5b7e22bb4c0068db9d000be4819d824c447c54b65bc1",
|
||||
"created_at": 1759223040,
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": "You are a helpful agent.",
|
||||
"metadata": {},
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"object": "response",
|
||||
"output": [
|
||||
{
|
||||
"arguments": {
|
||||
"city": "Tokyo"
|
||||
},
|
||||
"call_id": "call_Kud0j0DxWSmLzv9W5m6qVqXn",
|
||||
"name": "get_weather",
|
||||
"type": "function_call",
|
||||
"id": "fc_0f00ca5b7e22bb4c0068db9d00d258819d987f4b9b63189e76",
|
||||
"status": "completed"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"temperature": 1,
|
||||
"tool_choice": "auto",
|
||||
"tools": [
|
||||
{
|
||||
"name": "get_weather",
|
||||
"parameters": {
|
||||
"properties": {
|
||||
"city": {
|
||||
"title": "City",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["city"],
|
||||
"title": "get_weather_args",
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
},
|
||||
"strict": true,
|
||||
"type": "function",
|
||||
"description": null
|
||||
}
|
||||
],
|
||||
"top_p": 1,
|
||||
"background": false,
|
||||
"conversation": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"previous_response_id": null,
|
||||
"prompt": null,
|
||||
"prompt_cache_key": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"generate_summary": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"status": "completed",
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"top_logprobs": 0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 55,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 15,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 70
|
||||
},
|
||||
"user": null,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"store": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "5b99c3e3411ed17b",
|
||||
"traceId": "fee618f96dc31e0ca38b2f7b26eb8b29",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "TOOL",
|
||||
"environment": "default",
|
||||
"parentObservationId": "d950b668796240b5",
|
||||
"startTime": "2025-09-30T09:04:01.000Z",
|
||||
"endTime": "2025-09-30T09:04:01.000Z",
|
||||
"name": "get_weather",
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"llm.system": "openai",
|
||||
"tool.name": "get_weather",
|
||||
"input.value": {
|
||||
"city": "Tokyo"
|
||||
},
|
||||
"input.mime_type": "application/json",
|
||||
"output.value": "The weather in Tokyo is sunny.",
|
||||
"openinference.span.kind": "TOOL"
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.37.0",
|
||||
"service.name": "unknown_service"
|
||||
},
|
||||
"scope": {
|
||||
"name": "openinference.instrumentation.openai_agents",
|
||||
"version": "1.3.0",
|
||||
"attributes": {}
|
||||
}
|
||||
},
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": {},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-09-30T09:04:02.217Z",
|
||||
"updatedAt": "2025-09-30T09:04:02.217Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 0,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": {
|
||||
"city": "Tokyo"
|
||||
},
|
||||
"output": "The weather in Tokyo is sunny."
|
||||
}
|
||||
]
|
||||
}
|
||||
+338
@@ -0,0 +1,338 @@
|
||||
{
|
||||
"trace": {
|
||||
"id": "b3b7b128-5664-4f42-9fab-31999da9e2f1",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"name": "run_math_tutor",
|
||||
"timestamp": "2024-05-29T13:46:10.728Z",
|
||||
"environment": "default",
|
||||
"tags": [],
|
||||
"bookmarked": false,
|
||||
"release": null,
|
||||
"version": null,
|
||||
"userId": null,
|
||||
"sessionId": null,
|
||||
"public": true,
|
||||
"input": "\"{\\\"args\\\":[\\\"I need to solve the equation `3x + 11 = 14`. Can you help me?\\\"],\\\"kwargs\\\":{}}\"",
|
||||
"output": "\"\\\"Sure, first subtract 11 from both sides to get `3x = 3`, then divide both sides by 3 to solve for `x`. The solution is `x = 1`.\\\"\"",
|
||||
"metadata": "{}",
|
||||
"createdAt": "2024-05-29T13:46:11.304Z",
|
||||
"updatedAt": "2024-05-29T13:46:34.171Z",
|
||||
"latency": 9.236
|
||||
},
|
||||
"observations": [
|
||||
{
|
||||
"id": "0d8f5427-c91a-44c8-86c5-e12d806edd57",
|
||||
"traceId": "b3b7b128-5664-4f42-9fab-31999da9e2f1",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "GENERATION",
|
||||
"environment": "default",
|
||||
"parentObservationId": "0d6c73e1-be16-436d-a905-b4108912fa93",
|
||||
"startTime": "2024-05-29T13:46:19.963Z",
|
||||
"endTime": null,
|
||||
"name": "",
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": null,
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2024-05-29T13:46:20.714Z",
|
||||
"updatedAt": "2024-05-29T13:46:20.714Z",
|
||||
"usageDetails": {
|
||||
"input": 49,
|
||||
"output": 40,
|
||||
"total": 89
|
||||
},
|
||||
"costDetails": {
|
||||
"input": 0.00147,
|
||||
"output": 0.0024,
|
||||
"total": 0.00387
|
||||
},
|
||||
"providedCostDetails": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"total": 0
|
||||
},
|
||||
"model": "gpt-4",
|
||||
"internalModelId": "clrntkjgy000f08jx79v9g1xj",
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": null,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": 0.00147,
|
||||
"outputCost": 0.0024,
|
||||
"totalCost": 0.00387,
|
||||
"inputUsage": 49,
|
||||
"outputUsage": 40,
|
||||
"totalUsage": 89,
|
||||
"input": [
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "I am a math tutor that likes to help math students, how can I help?"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I need to solve the equation `3x + 11 = 14`. Can you help me?"
|
||||
}
|
||||
],
|
||||
"output": "Sure, first subtract 11 from both sides to get `3x = 3`, then divide both sides by 3 to solve for `x`. The solution is `x = 1`.",
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"id": "0d6c73e1-be16-436d-a905-b4108912fa93",
|
||||
"traceId": "b3b7b128-5664-4f42-9fab-31999da9e2f1",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "SPAN",
|
||||
"environment": "default",
|
||||
"parentObservationId": null,
|
||||
"startTime": "2024-05-29T13:46:17.971Z",
|
||||
"endTime": "2024-05-29T13:46:19.964Z",
|
||||
"name": "get_response",
|
||||
"metadata": "{}",
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": null,
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2024-05-29T13:46:18.345Z",
|
||||
"updatedAt": "2024-05-29T13:46:20.796Z",
|
||||
"usageDetails": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"total": 0
|
||||
},
|
||||
"costDetails": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"total": 0
|
||||
},
|
||||
"providedCostDetails": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"total": 0
|
||||
},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 1993,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": 0,
|
||||
"outputCost": 0,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": {
|
||||
"args": [
|
||||
"thread_fbFB5caAj0sEpE1c5ICMDs7I",
|
||||
"run_RsJmiMTrgkBzb3FvyCvRoqpd"
|
||||
],
|
||||
"kwargs": {}
|
||||
},
|
||||
"output": [
|
||||
"Sure, first subtract 11 from both sides to get `3x = 3`, then divide both sides by 3 to solve for `x`. The solution is `x = 1`.",
|
||||
{
|
||||
"id": "run_AherTwpS29w0GC8zlKDBhhlZ",
|
||||
"model": "gpt-4",
|
||||
"tools": [],
|
||||
"top_p": 1,
|
||||
"usage": null,
|
||||
"object": "thread.run",
|
||||
"status": "queued",
|
||||
"metadata": {},
|
||||
"failed_at": null,
|
||||
"thread_id": "thread_DhsY59BBhtKWN8YYq44cULrH",
|
||||
"created_at": 1716990330,
|
||||
"expires_at": 1716990930,
|
||||
"last_error": null,
|
||||
"started_at": null,
|
||||
"temperature": 1,
|
||||
"tool_choice": "auto",
|
||||
"assistant_id": "asst_tjgFtRASnPoQaWpks1ezPfSp",
|
||||
"cancelled_at": null,
|
||||
"completed_at": null,
|
||||
"instructions": "You are a personal math tutor. Answer questions briefly, in a sentence or less.",
|
||||
"tool_resources": {},
|
||||
"required_action": null,
|
||||
"response_format": "auto",
|
||||
"max_prompt_tokens": null,
|
||||
"incomplete_details": null,
|
||||
"truncation_strategy": {
|
||||
"type": "auto",
|
||||
"last_messages": null
|
||||
},
|
||||
"max_completion_tokens": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "e584f95b-e8ef-4d43-bcb1-fd296f57f94b",
|
||||
"traceId": "b3b7b128-5664-4f42-9fab-31999da9e2f1",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "SPAN",
|
||||
"environment": "default",
|
||||
"parentObservationId": null,
|
||||
"startTime": "2024-05-29T13:46:11.057Z",
|
||||
"endTime": "2024-05-29T13:46:12.959Z",
|
||||
"name": "run_assistant",
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": null,
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2024-05-29T13:46:11.480Z",
|
||||
"updatedAt": "2024-05-29T13:46:13.161Z",
|
||||
"usageDetails": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"total": 0
|
||||
},
|
||||
"costDetails": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"total": 0
|
||||
},
|
||||
"providedCostDetails": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"total": 0
|
||||
},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 1902,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": 0,
|
||||
"outputCost": 0,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": {
|
||||
"args": [
|
||||
"asst_01wKwjx3rT7CNprPxzE8azMl",
|
||||
"I need to solve the equation `3x + 11 = 14`. Can you help me?"
|
||||
],
|
||||
"kwargs": {}
|
||||
},
|
||||
"output": [
|
||||
{
|
||||
"id": "run_RsJmiMTrgkBzb3FvyCvRoqpd",
|
||||
"model": "gpt-4",
|
||||
"tools": [],
|
||||
"top_p": 1,
|
||||
"usage": null,
|
||||
"object": "thread.run",
|
||||
"status": "queued",
|
||||
"metadata": {},
|
||||
"failed_at": null,
|
||||
"thread_id": "thread_fbFB5caAj0sEpE1c5ICMDs7I",
|
||||
"created_at": 1716990372,
|
||||
"expires_at": 1716990972,
|
||||
"last_error": null,
|
||||
"started_at": null,
|
||||
"temperature": 1,
|
||||
"tool_choice": "auto",
|
||||
"assistant_id": "asst_01wKwjx3rT7CNprPxzE8azMl",
|
||||
"cancelled_at": null,
|
||||
"completed_at": null,
|
||||
"instructions": "You are a personal math tutor. Answer questions briefly, in a sentence or less.",
|
||||
"tool_resources": {},
|
||||
"required_action": null,
|
||||
"response_format": "auto",
|
||||
"max_prompt_tokens": null,
|
||||
"incomplete_details": null,
|
||||
"truncation_strategy": {
|
||||
"type": "auto",
|
||||
"last_messages": null
|
||||
},
|
||||
"max_completion_tokens": null
|
||||
},
|
||||
{
|
||||
"id": "thread_fbFB5caAj0sEpE1c5ICMDs7I",
|
||||
"object": "thread",
|
||||
"metadata": {},
|
||||
"created_at": 1716990371,
|
||||
"tool_resources": {
|
||||
"file_search": null,
|
||||
"code_interpreter": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"id": "be59b862-98dd-4b0e-b92e-c150b9777c2f",
|
||||
"traceId": "b3b7b128-5664-4f42-9fab-31999da9e2f1",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "SPAN",
|
||||
"environment": "default",
|
||||
"parentObservationId": null,
|
||||
"startTime": "2024-05-29T13:46:10.728Z",
|
||||
"endTime": "2024-05-29T13:46:11.056Z",
|
||||
"name": "create_assistant",
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": null,
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2024-05-29T13:46:11.350Z",
|
||||
"updatedAt": "2024-05-29T13:46:11.405Z",
|
||||
"usageDetails": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"total": 0
|
||||
},
|
||||
"costDetails": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"total": 0
|
||||
},
|
||||
"providedCostDetails": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"total": 0
|
||||
},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 328,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": 0,
|
||||
"outputCost": 0,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": {
|
||||
"args": [],
|
||||
"kwargs": {}
|
||||
},
|
||||
"output": {
|
||||
"id": "asst_01wKwjx3rT7CNprPxzE8azMl",
|
||||
"name": "Math Tutor",
|
||||
"model": "gpt-4",
|
||||
"tools": [],
|
||||
"top_p": 1,
|
||||
"object": "assistant",
|
||||
"metadata": {},
|
||||
"created_at": 1716990370,
|
||||
"description": null,
|
||||
"temperature": 1,
|
||||
"instructions": "You are a personal math tutor. Answer questions briefly, in a sentence or less.",
|
||||
"tool_resources": {
|
||||
"file_search": null,
|
||||
"code_interpreter": null
|
||||
},
|
||||
"response_format": "auto"
|
||||
},
|
||||
"metadata": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
{
|
||||
"trace": {
|
||||
"id": "25f4bdeebaab60e6e1bee7e8469554bc",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"name": "pydantic-ai-qa-trace",
|
||||
"timestamp": "2025-06-06T14:40:28.562Z",
|
||||
"environment": "default",
|
||||
"tags": ["dev", "pydantic-ai"],
|
||||
"bookmarked": false,
|
||||
"release": null,
|
||||
"version": "1.0.0",
|
||||
"userId": "user_123",
|
||||
"sessionId": "session_abc",
|
||||
"public": true,
|
||||
"input": "\"What is Langfuse?\"",
|
||||
"output": "\"Langfuse is a tool designed for observability and monitoring of applications that utilize large language models (LLMs). It provides features to trace, log, and visualize requests, helping developers understand the behavior of their LLM-powered applications more effectively. This can be particularly useful for debugging, performance optimization, and ensuring reliable operation of systems that depend on LLMs. Langfuse is designed to integrate easily with existing infrastructure, supporting both self-hosted implementations and cloud-based solutions.\"",
|
||||
"metadata": "{\"email\":\"user@langfuse.com\",\"resourceAttributes\":{\"telemetry.sdk.language\":\"python\",\"telemetry.sdk.name\":\"opentelemetry\",\"telemetry.sdk.version\":\"1.33.1\",\"service.name\":\"unknown_service\"},\"scope\":{\"name\":\"langfuse-sdk\",\"version\":\"3.0.0\",\"attributes\":{\"public_key\":\"pk-lf-5855d85e-3943-497e-bd10-f50ad414bcba\"}}}",
|
||||
"createdAt": "2025-06-06T14:40:34.000Z",
|
||||
"updatedAt": "2025-06-06T14:40:33.635Z",
|
||||
"latency": 4.795
|
||||
},
|
||||
"observations": [
|
||||
{
|
||||
"id": "538fed87d11686d3",
|
||||
"traceId": "25f4bdeebaab60e6e1bee7e8469554bc",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "SPAN",
|
||||
"environment": "default",
|
||||
"parentObservationId": null,
|
||||
"startTime": "2025-06-06T14:40:28.562Z",
|
||||
"endTime": "2025-06-06T14:40:33.357Z",
|
||||
"name": "pydantic-ai-qa-trace",
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": "1.0.0",
|
||||
"modelParameters": null,
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-06-06T14:40:33.595Z",
|
||||
"updatedAt": "2025-06-06T14:40:33.630Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 4795,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": null,
|
||||
"output": null,
|
||||
"metadata": {
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.33.1",
|
||||
"service.name": "unknown_service"
|
||||
},
|
||||
"scope": {
|
||||
"name": "langfuse-sdk",
|
||||
"version": "3.0.0",
|
||||
"attributes": {
|
||||
"public_key": "pk-lf-5855d85e-3943-497e-bd10-f50ad414bcba"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "0a4fac2553f22f2e",
|
||||
"traceId": "25f4bdeebaab60e6e1bee7e8469554bc",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "SPAN",
|
||||
"environment": "default",
|
||||
"parentObservationId": "538fed87d11686d3",
|
||||
"startTime": "2025-06-06T14:40:28.562Z",
|
||||
"endTime": "2025-06-06T14:40:33.357Z",
|
||||
"name": "qa_agent run",
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": null,
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-06-06T14:40:33.596Z",
|
||||
"updatedAt": "2025-06-06T14:40:33.641Z",
|
||||
"usageDetails": {},
|
||||
"costDetails": {},
|
||||
"providedCostDetails": {},
|
||||
"model": null,
|
||||
"internalModelId": null,
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 4795,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": null,
|
||||
"outputCost": null,
|
||||
"totalCost": 0,
|
||||
"inputUsage": 0,
|
||||
"outputUsage": 0,
|
||||
"totalUsage": 0,
|
||||
"input": null,
|
||||
"output": [
|
||||
{
|
||||
"content": "You are a helpful assistant that answers questions clearly and concisely.",
|
||||
"role": "system",
|
||||
"gen_ai.message.index": 0,
|
||||
"event.name": "gen_ai.system.message"
|
||||
},
|
||||
{
|
||||
"content": "What is Langfuse?",
|
||||
"role": "user",
|
||||
"gen_ai.message.index": 0,
|
||||
"event.name": "gen_ai.user.message"
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Langfuse is a tool designed for observability and monitoring of applications that utilize large language models (LLMs). It provides features to trace, log, and visualize requests, helping developers understand the behavior of their LLM-powered applications more effectively. This can be particularly useful for debugging, performance optimization, and ensuring reliable operation of systems that depend on LLMs. Langfuse is designed to integrate easily with existing infrastructure, supporting both self-hosted implementations and cloud-based solutions.",
|
||||
"gen_ai.message.index": 1,
|
||||
"event.name": "gen_ai.assistant.message"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"model_name": "gpt-4o",
|
||||
"agent_name": "qa_agent",
|
||||
"logfire.msg": "qa_agent run",
|
||||
"final_result": "Langfuse is a tool designed for observability and monitoring of applications that utilize large language models (LLMs). It provides features to trace, log, and visualize requests, helping developers understand the behavior of their LLM-powered applications more effectively. This can be particularly useful for debugging, performance optimization, and ensuring reliable operation of systems that depend on LLMs. Langfuse is designed to integrate easily with existing infrastructure, supporting both self-hosted implementations and cloud-based solutions.",
|
||||
"gen_ai.usage.input_tokens": "31",
|
||||
"gen_ai.usage.output_tokens": "96",
|
||||
"all_messages_events": [
|
||||
{
|
||||
"content": "You are a helpful assistant that answers questions clearly and concisely.",
|
||||
"role": "system",
|
||||
"gen_ai.message.index": 0,
|
||||
"event.name": "gen_ai.system.message"
|
||||
},
|
||||
{
|
||||
"content": "What is Langfuse?",
|
||||
"role": "user",
|
||||
"gen_ai.message.index": 0,
|
||||
"event.name": "gen_ai.user.message"
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Langfuse is a tool designed for observability and monitoring of applications that utilize large language models (LLMs). It provides features to trace, log, and visualize requests, helping developers understand the behavior of their LLM-powered applications more effectively. This can be particularly useful for debugging, performance optimization, and ensuring reliable operation of systems that depend on LLMs. Langfuse is designed to integrate easily with existing infrastructure, supporting both self-hosted implementations and cloud-based solutions.",
|
||||
"gen_ai.message.index": 1,
|
||||
"event.name": "gen_ai.assistant.message"
|
||||
}
|
||||
],
|
||||
"logfire.json_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"all_messages_events": {
|
||||
"type": "array"
|
||||
},
|
||||
"final_result": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.33.1",
|
||||
"service.name": "unknown_service"
|
||||
},
|
||||
"scope": {
|
||||
"name": "pydantic-ai",
|
||||
"version": "0.2.15",
|
||||
"attributes": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "5cb389a3bdd21a6e",
|
||||
"traceId": "25f4bdeebaab60e6e1bee7e8469554bc",
|
||||
"projectId": "cloramnkj0002jz088vzn1ja4",
|
||||
"type": "GENERATION",
|
||||
"environment": "default",
|
||||
"parentObservationId": "0a4fac2553f22f2e",
|
||||
"startTime": "2025-06-06T14:40:28.562Z",
|
||||
"endTime": "2025-06-06T14:40:33.356Z",
|
||||
"name": "chat gpt-4o",
|
||||
"level": "DEFAULT",
|
||||
"statusMessage": null,
|
||||
"version": null,
|
||||
"modelParameters": {},
|
||||
"completionStartTime": null,
|
||||
"promptId": null,
|
||||
"createdAt": "2025-06-06T14:40:33.440Z",
|
||||
"updatedAt": "2025-06-06T14:40:33.478Z",
|
||||
"usageDetails": {
|
||||
"input": 31,
|
||||
"output": 96,
|
||||
"total": 127
|
||||
},
|
||||
"costDetails": {
|
||||
"input": 0.0000775,
|
||||
"output": 0.00096,
|
||||
"total": 0.0010375
|
||||
},
|
||||
"providedCostDetails": {},
|
||||
"model": "gpt-4o",
|
||||
"internalModelId": "b9854a5c92dc496b997d99d20",
|
||||
"promptName": null,
|
||||
"promptVersion": null,
|
||||
"latency": 4794,
|
||||
"timeToFirstToken": null,
|
||||
"inputCost": 0.0000775,
|
||||
"outputCost": 0.00096,
|
||||
"totalCost": 0.0010375,
|
||||
"inputUsage": 31,
|
||||
"outputUsage": 96,
|
||||
"totalUsage": 127,
|
||||
"input": [
|
||||
{
|
||||
"content": "You are a helpful assistant that answers questions clearly and concisely.",
|
||||
"role": "system",
|
||||
"gen_ai.system": "openai",
|
||||
"gen_ai.message.index": 0,
|
||||
"event.name": "gen_ai.system.message"
|
||||
},
|
||||
{
|
||||
"content": "What is Langfuse?",
|
||||
"role": "user",
|
||||
"gen_ai.system": "openai",
|
||||
"gen_ai.message.index": 0,
|
||||
"event.name": "gen_ai.user.message"
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "Langfuse is a tool designed for observability and monitoring of applications that utilize large language models (LLMs). It provides features to trace, log, and visualize requests, helping developers understand the behavior of their LLM-powered applications more effectively. This can be particularly useful for debugging, performance optimization, and ensuring reliable operation of systems that depend on LLMs. Langfuse is designed to integrate easily with existing infrastructure, supporting both self-hosted implementations and cloud-based solutions."
|
||||
},
|
||||
"gen_ai.system": "openai",
|
||||
"event.name": "gen_ai.choice"
|
||||
},
|
||||
"metadata": {
|
||||
"attributes": {
|
||||
"gen_ai.operation.name": "chat",
|
||||
"gen_ai.system": "openai",
|
||||
"gen_ai.request.model": "gpt-4o",
|
||||
"server.address": "api.openai.com",
|
||||
"model_request_parameters": {
|
||||
"function_tools": [],
|
||||
"allow_text_output": true,
|
||||
"output_tools": []
|
||||
},
|
||||
"gen_ai.usage.input_tokens": "31",
|
||||
"gen_ai.usage.output_tokens": "96",
|
||||
"gen_ai.response.model": "gpt-4o-2024-08-06",
|
||||
"events": [
|
||||
{
|
||||
"content": "You are a helpful assistant that answers questions clearly and concisely.",
|
||||
"role": "system",
|
||||
"gen_ai.system": "openai",
|
||||
"gen_ai.message.index": 0,
|
||||
"event.name": "gen_ai.system.message"
|
||||
},
|
||||
{
|
||||
"content": "What is Langfuse?",
|
||||
"role": "user",
|
||||
"gen_ai.system": "openai",
|
||||
"gen_ai.message.index": 0,
|
||||
"event.name": "gen_ai.user.message"
|
||||
},
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "Langfuse is a tool designed for observability and monitoring of applications that utilize large language models (LLMs). It provides features to trace, log, and visualize requests, helping developers understand the behavior of their LLM-powered applications more effectively. This can be particularly useful for debugging, performance optimization, and ensuring reliable operation of systems that depend on LLMs. Langfuse is designed to integrate easily with existing infrastructure, supporting both self-hosted implementations and cloud-based solutions."
|
||||
},
|
||||
"gen_ai.system": "openai",
|
||||
"event.name": "gen_ai.choice"
|
||||
}
|
||||
],
|
||||
"logfire.json_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"events": {
|
||||
"type": "array"
|
||||
},
|
||||
"model_request_parameters": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourceAttributes": {
|
||||
"telemetry.sdk.language": "python",
|
||||
"telemetry.sdk.name": "opentelemetry",
|
||||
"telemetry.sdk.version": "1.33.1",
|
||||
"service.name": "unknown_service"
|
||||
},
|
||||
"scope": {
|
||||
"name": "pydantic-ai",
|
||||
"version": "0.2.15",
|
||||
"attributes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -400,6 +400,177 @@ export const SEED_CHAT_ML_PROMPTS = [
|
||||
];
|
||||
|
||||
export const SEED_PROMPT_VERSIONS = [
|
||||
{
|
||||
createdBy: "user-1",
|
||||
type: "chat",
|
||||
prompt: [
|
||||
{
|
||||
role: "system",
|
||||
content: `## Role
|
||||
You are a Langfuse filter generator. Your sole function is to parse user queries about AI traces and output the corresponding filter array in JSON format. Map natural language to appropriate column names, operators, and values.
|
||||
|
||||
## Available columns and their types:
|
||||
- bookmarked (boolean): Starred/bookmarked traces
|
||||
- id (string): Trace ID
|
||||
- name (stringOptions): Trace name
|
||||
- environment (stringOptions): Environment (dev, prod, etc.)
|
||||
- timestamp (datetime): When the trace occurred
|
||||
- userId (string): User identifier
|
||||
- sessionId (string): Session identifier
|
||||
- metadata (stringObject): Custom metadata
|
||||
- version (string): Version identifier
|
||||
- release (string): Release identifier
|
||||
- level (stringOptions): Log level - DEBUG, DEFAULT, WARNING, ERROR
|
||||
- tags (arrayOptions): Custom tags
|
||||
- inputTokens (number): Input token count
|
||||
- outputTokens (number): Output token count
|
||||
- totalTokens (number): Total token count
|
||||
- tokens (number): Alias for total tokens
|
||||
- errorCount (number): Count of error-level observations
|
||||
- warningCount (number): Count of warning-level observations
|
||||
- defaultCount (number): Count of default-level observations
|
||||
- debugCount (number): Count of debug-level observations
|
||||
- scores_avg (numberObject): Numeric evaluation scores
|
||||
- score_categories (categoryOptions): Categorical evaluation scores
|
||||
- latency (number): Latency in seconds
|
||||
- inputCost (number): Input cost in USD
|
||||
- outputCost (number): Output cost in USD
|
||||
- totalCost (number): Total cost in USD
|
||||
|
||||
## Type-Operator Compatibility Rules
|
||||
|
||||
**string type** - Use for text matching operations:
|
||||
- Operators: "=", "contains", "does not contain", "starts with", "ends with"
|
||||
- Value: Always a single string
|
||||
- Example: {"type": "string", "column": "userId", "operator": "contains", "value": "john"}
|
||||
|
||||
**stringOptions type** - Use ONLY for selecting from predefined options:
|
||||
- Operators: "any of", "none of" ONLY
|
||||
- Value: Always an array of strings
|
||||
- Use when filtering columns like name, environment, level with multiple possible values
|
||||
- Example: {"type": "stringOptions", "column": "environment", "operator": "any of", "value": ["dev", "staging"]}
|
||||
|
||||
**arrayOptions type** - Use for tag filtering:
|
||||
- Operators: "any of", "none of", "all of"
|
||||
- Value: Always an array of strings
|
||||
- Example: {"type": "arrayOptions", "column": "tags", "operator": "any of", "value": ["important", "bug"]}
|
||||
|
||||
**number type** - Use for numeric comparisons:
|
||||
- Operators: "=", ">", "<", ">=", "<="
|
||||
- Value: Always a number
|
||||
- Example: {"type": "number", "column": "latency", "operator": ">", "value": 2.5}
|
||||
|
||||
**datetime type** - Use for time-based filtering:
|
||||
- Operators: ">", "<", ">=", "<="
|
||||
- Value: Always a date string
|
||||
- Example: {"type": "datetime", "column": "timestamp", "operator": ">", "value": "2024-01-01T00:00:00Z"}
|
||||
|
||||
**boolean type** - Use for true/false values:
|
||||
- Operators: "=", "<>"
|
||||
- Value: Always true or false
|
||||
- Example: {"type": "boolean", "column": "bookmarked", "operator": "=", "value": true}
|
||||
|
||||
**stringObject type** - Use for metadata key-value searches:
|
||||
- Operators: "=", "contains", "does not contain", "starts with", "ends with"
|
||||
- Value: Always a string
|
||||
- Requires "key" field for the metadata key
|
||||
- Example: {"type": "stringObject", "column": "metadata", "key": "userId", "operator": "=", "value": "123"}
|
||||
|
||||
**numberObject type** - Use for numeric score searches:
|
||||
- Operators: "=", ">", "<", ">=", "<="
|
||||
- Value: Always a number
|
||||
- Requires "key" field for the score name
|
||||
- Example: {"type": "numberObject", "column": "scores_avg", "key": "quality", "operator": ">", "value": 0.8}
|
||||
|
||||
## Output Format
|
||||
|
||||
Please respond ONLY with valid JSON. Do not include any explanation or extra text.
|
||||
The response should look like this without the leading EOF and trailing EOF.
|
||||
|
||||
EOF
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"type": "stringOptions|number|string|categoryOptions",
|
||||
"value": "value or array",
|
||||
"column": "exact column name",
|
||||
"operator": "= | > | < | any of"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
## Intent Parsing Guidelines
|
||||
|
||||
**Temporal expressions:**
|
||||
- "today", "yesterday", "last week" → timestamp filters
|
||||
- "after 2pm", "before noon", "since Monday" → timestamp with appropriate operators
|
||||
- Relative times: "last 24 hours", "past 3 days" → calculate from current time
|
||||
|
||||
**Performance queries:**
|
||||
- "slow", "high latency", "taking too long" → latency > threshold
|
||||
- "expensive", "costly", "high cost" → totalCost > threshold
|
||||
- "many tokens", "token heavy" → totalTokens > threshold
|
||||
- "cheap", "fast", "quick" → use < operators
|
||||
|
||||
**Error/Quality queries:**
|
||||
- "errors", "failed", "broken" → level = ERROR or errorCount > 0
|
||||
- "warnings" → level = WARNING or warningCount > 0
|
||||
- "successful", "working" → level = DEFAULT or errorCount = 0
|
||||
|
||||
**User/Session queries:**
|
||||
- "user john", "by user", "user ID" → userId filters
|
||||
- "session abc", "in session" → sessionId filters
|
||||
|
||||
**Environment queries:**
|
||||
- "prod", "production" → environment = production
|
||||
- "dev", "development", "staging" → environment matching
|
||||
- "live", "deployed" → typically production environment
|
||||
|
||||
**Metadata/Tags:**
|
||||
- "tagged with", "has tag" → tags contains
|
||||
- "metadata contains", "custom field" → metadata object queries
|
||||
|
||||
**Comparison operators:**
|
||||
- "more than", "over", "above", "greater" → >
|
||||
- "less than", "under", "below", "fewer" → <
|
||||
- "at least", "minimum" → >=
|
||||
- "at most", "maximum" → <=
|
||||
- "exactly", "equal to" → =
|
||||
- "not", "except", "excluding" → not_equals or not_contains
|
||||
|
||||
**Text search operations:**
|
||||
- "contains", "includes", "has" → use "string" type with "contains" operator
|
||||
- "equals", "is exactly" → use "string" type with "=" operator
|
||||
- "starts with", "begins with" → use "string" type with "starts with" operator
|
||||
|
||||
**Multi-option selections:**
|
||||
- "environment is dev or staging" → use "stringOptions" type with "any of" operator
|
||||
- "name is one of X, Y, Z" → use "stringOptions" type with "any of" operator
|
||||
- "level is ERROR or WARNING" → use "stringOptions" type with "any of" operator
|
||||
|
||||
## Current DateTime
|
||||
|
||||
This is the current datetime: {{currentDatetime}}
|
||||
Use it as a reference for datetime based queries when needed.
|
||||
|
||||
## Examples
|
||||
|
||||
Input: "I want to see all dev traces"
|
||||
Output: {"filters":[{"type":"stringOptions","value":["development","dev"],"column":"environment","operator":"any of"}]}
|
||||
|
||||
Input: "Show traces with version starting with v2"
|
||||
Output: {"filters":[{"type":"string","value":"v2","column":"version","operator":"starts with"}]}`,
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: "{{userPrompt}}",
|
||||
},
|
||||
],
|
||||
name: "get-filter-conditions-from-query",
|
||||
version: 1,
|
||||
labels: ["production", "latest"],
|
||||
},
|
||||
{
|
||||
createdBy: "user-1",
|
||||
prompt: "Prompt 4 version 1 content with {{variable}}",
|
||||
|
||||
@@ -11,9 +11,8 @@ export const generateDatasetItemId = (
|
||||
datasetName: string,
|
||||
itemIndex: number,
|
||||
projectId: string,
|
||||
runNumber: number,
|
||||
) => {
|
||||
return `dataset-item-${datasetName}-${itemIndex}-${projectId.slice(-8)}-${runNumber}`;
|
||||
return `dataset-item-${datasetName}-${itemIndex}-${projectId.slice(-8)}-0`;
|
||||
};
|
||||
|
||||
export const generateDatasetRunTraceId = (
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
import { FileContent, SeederOptions } from "./types";
|
||||
import { DataGenerator } from "./data-generators";
|
||||
import { ClickHouseQueryBuilder } from "./clickhouse-builder";
|
||||
import { FrameworkTraceLoader } from "./framework-traces/framework-trace-loader";
|
||||
import { EVAL_TRACE_COUNT, SEED_DATASETS } from "./postgres-seed-constants";
|
||||
import {
|
||||
clickhouseClient,
|
||||
DatasetRunItemRecordInsertType,
|
||||
logger,
|
||||
ObservationRecordInsertType,
|
||||
ScoreRecordInsertType,
|
||||
TraceRecordInsertType,
|
||||
} from "../../../src/server";
|
||||
import path from "path";
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
const DATASET_SCORE_NAMES = ["score-1", "score-2", "score-3"];
|
||||
const DATASET_RUN_SCORE_NAMES = [
|
||||
"dataset-run-score-1",
|
||||
"dataset-run-score-2",
|
||||
"dataset-run-score-3",
|
||||
];
|
||||
|
||||
/**
|
||||
* Orchestrates seeding operations across ClickHouse and PostgreSQL.
|
||||
*
|
||||
@@ -98,6 +107,7 @@ export class SeederOrchestrator {
|
||||
const traces: TraceRecordInsertType[] = [];
|
||||
const observations: ObservationRecordInsertType[] = [];
|
||||
const datasetRunItems: DatasetRunItemRecordInsertType[] = [];
|
||||
const scores: ScoreRecordInsertType[] = [];
|
||||
|
||||
for (const seedDataset of SEED_DATASETS) {
|
||||
for (const [itemIndex, datasetItem] of seedDataset.items.entries()) {
|
||||
@@ -136,16 +146,43 @@ export class SeederOrchestrator {
|
||||
projectId,
|
||||
);
|
||||
|
||||
// Generate score data
|
||||
const score = this.dataGenerator.generateDatasetScore(
|
||||
trace,
|
||||
{
|
||||
datasetName: seedDataset.name,
|
||||
itemIndex,
|
||||
item: datasetItem,
|
||||
runNumber,
|
||||
},
|
||||
projectId,
|
||||
DATASET_SCORE_NAMES,
|
||||
);
|
||||
|
||||
traces.push(trace);
|
||||
observations.push(observation);
|
||||
datasetRunItems.push(datasetRunItem);
|
||||
scores.push(score);
|
||||
}
|
||||
|
||||
// create dataset run level scores
|
||||
const datasetRunScore = this.dataGenerator.generateDatasetRunScore(
|
||||
`${seedDataset.name}-${projectId.slice(-8)}`,
|
||||
{
|
||||
datasetName: seedDataset.name,
|
||||
runNumber,
|
||||
},
|
||||
projectId,
|
||||
DATASET_RUN_SCORE_NAMES,
|
||||
);
|
||||
scores.push(datasetRunScore);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.queryBuilder.executeTracesInsert(traces);
|
||||
await this.queryBuilder.executeObservationsInsert(observations);
|
||||
await this.queryBuilder.executeDatasetRunItemsInsert(datasetRunItems);
|
||||
await this.queryBuilder.executeScoresInsert(scores);
|
||||
} catch (error) {
|
||||
logger.error(`✗ Insert failed:`, error);
|
||||
throw error;
|
||||
@@ -285,6 +322,12 @@ export class SeederOrchestrator {
|
||||
// Create synthetic data
|
||||
await this.createSyntheticData(projectIds, opts);
|
||||
|
||||
// Create traces for a realistic chat session
|
||||
await this.createSupportChatSessionTraces(projectIds);
|
||||
|
||||
// create traces from real examples for each framework source
|
||||
await this.createFrameworkTraces(projectIds);
|
||||
|
||||
// Log completion statistics (commented out to reduce terminal noise)
|
||||
await this.logStatistics();
|
||||
|
||||
@@ -342,4 +385,57 @@ export class SeederOrchestrator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async createSupportChatSessionTraces(projectIds: string[]): Promise<void> {
|
||||
logger.info(
|
||||
`Creating support chat session data for ${projectIds.length} projects.`,
|
||||
);
|
||||
|
||||
for (const projectId of projectIds) {
|
||||
logger.info(`Processing support chat session for project ${projectId}`);
|
||||
|
||||
// Generate data using the data generator
|
||||
const { traces, observations, scores } =
|
||||
this.dataGenerator.generateSupportChatSessionData(projectId);
|
||||
|
||||
try {
|
||||
await this.queryBuilder.executeTracesInsert(traces);
|
||||
await this.queryBuilder.executeObservationsInsert(observations);
|
||||
await this.queryBuilder.executeScoresInsert(scores);
|
||||
} catch (error) {
|
||||
logger.error(`✗ Support chat session insert failed:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create traces from real examples for each framework source
|
||||
// useful for testing rendering
|
||||
async createFrameworkTraces(projectIds: string[]): Promise<void> {
|
||||
logger.info(`Creating framework traces for ${projectIds.length} projects.`);
|
||||
|
||||
const loader = new FrameworkTraceLoader();
|
||||
|
||||
for (const projectId of projectIds) {
|
||||
logger.info(`Processing framework traces for project ${projectId}`);
|
||||
|
||||
const { traces, observations, scores } =
|
||||
loader.loadTracesForProject(projectId);
|
||||
|
||||
try {
|
||||
if (traces.length > 0) {
|
||||
await this.queryBuilder.executeTracesInsert(traces);
|
||||
}
|
||||
if (observations.length > 0) {
|
||||
await this.queryBuilder.executeObservationsInsert(observations);
|
||||
}
|
||||
if (scores.length > 0) {
|
||||
await this.queryBuilder.executeScoresInsert(scores);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`✗ Framework traces insert failed:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,4 +25,14 @@ export const DatasetRunItemSchema = z.object({
|
||||
datasetItemMetadata: MetadataDomain,
|
||||
});
|
||||
|
||||
export type DatasetRunItemDomain = z.infer<typeof DatasetRunItemSchema>;
|
||||
// Conditional type for dataset run item domain with optional IO
|
||||
export type DatasetRunItemDomain<WithIO extends boolean = true> =
|
||||
WithIO extends true
|
||||
? z.infer<typeof DatasetRunItemSchema>
|
||||
: Omit<
|
||||
z.infer<typeof DatasetRunItemSchema>,
|
||||
| "datasetRunMetadata"
|
||||
| "datasetItemInput"
|
||||
| "datasetItemExpectedOutput"
|
||||
| "datasetItemMetadata"
|
||||
>;
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import { z } from "zod/v4";
|
||||
import { isPresent } from "../utils/typeChecks";
|
||||
|
||||
// Category type, used for categorical and boolean configs
|
||||
export const ScoreConfigCategory = z.object({
|
||||
label: z.string().min(1),
|
||||
value: z.number(),
|
||||
});
|
||||
|
||||
// Numeric config fields
|
||||
export const NumericConfigFields = z.object({
|
||||
maxValue: z.number().nullish(),
|
||||
minValue: z.number().nullish(),
|
||||
dataType: z.literal("NUMERIC"),
|
||||
categories: z.undefined().nullish(),
|
||||
});
|
||||
|
||||
// Boolean config fields
|
||||
export const BooleanConfigFields = z.object({
|
||||
dataType: z.literal("BOOLEAN"),
|
||||
maxValue: z.number().nullish(),
|
||||
minValue: z.number().nullish(),
|
||||
categories: z
|
||||
.array(ScoreConfigCategory)
|
||||
.length(2, "Boolean data type must have exactly 2 categories.")
|
||||
.refine((categories) => {
|
||||
const expectedCategories = [
|
||||
{ label: "True", value: 1 },
|
||||
{ label: "False", value: 0 },
|
||||
];
|
||||
return categories.every(
|
||||
(category, index) =>
|
||||
category.label === expectedCategories[index].label &&
|
||||
category.value === expectedCategories[index].value,
|
||||
);
|
||||
}),
|
||||
});
|
||||
|
||||
// Category config fields and types
|
||||
export const validateCategories = (
|
||||
categories: z.infer<typeof ScoreConfigCategory>[],
|
||||
ctx: z.RefinementCtx,
|
||||
) => {
|
||||
const uniqueNames = new Set<string>();
|
||||
const uniqueValues = new Set<number>();
|
||||
|
||||
for (const category of categories) {
|
||||
if (uniqueNames.has(category.label)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Duplicate category label: ${category.label}, category labels must be unique`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
uniqueNames.add(category.label);
|
||||
|
||||
if (uniqueValues.has(category.value)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Duplicate category value: ${category.value}, category values must be unique`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
uniqueValues.add(category.value);
|
||||
}
|
||||
};
|
||||
|
||||
export const CategoricalConfigFields = z.object({
|
||||
maxValue: z.undefined().nullish(),
|
||||
minValue: z.undefined().nullish(),
|
||||
dataType: z.literal("CATEGORICAL"),
|
||||
categories: z.array(ScoreConfigCategory).superRefine(validateCategories),
|
||||
});
|
||||
|
||||
const ScoreConfigBase = z.object({
|
||||
id: z.string(),
|
||||
name: z.string().min(1).max(35),
|
||||
isArchived: z.boolean(),
|
||||
description: z.string().nullish(),
|
||||
createdAt: z.coerce.date(),
|
||||
updatedAt: z.coerce.date(),
|
||||
projectId: z.string(),
|
||||
});
|
||||
|
||||
export const validateNumericRangeFields = (
|
||||
data: Pick<ScoreConfigDomain, "maxValue" | "minValue" | "dataType">,
|
||||
ctx: z.RefinementCtx,
|
||||
): void | Promise<void> => {
|
||||
if (data.dataType === "NUMERIC") {
|
||||
if (
|
||||
isPresent(data.maxValue) &&
|
||||
isPresent(data.minValue) &&
|
||||
data.maxValue <= data.minValue
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Maximum value must be greater than Minimum value",
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const ScoreConfigSchema = z
|
||||
.discriminatedUnion("dataType", [
|
||||
z.object({
|
||||
...ScoreConfigBase.shape,
|
||||
...NumericConfigFields.shape,
|
||||
}),
|
||||
z.object({
|
||||
...ScoreConfigBase.shape,
|
||||
...CategoricalConfigFields.shape,
|
||||
}),
|
||||
z.object({
|
||||
...ScoreConfigBase.shape,
|
||||
...BooleanConfigFields.shape,
|
||||
}),
|
||||
])
|
||||
.superRefine(validateNumericRangeFields);
|
||||
|
||||
export type ScoreConfigDomain = z.infer<typeof ScoreConfigSchema>;
|
||||
export type ScoreConfigCategoryDomain = z.infer<typeof ScoreConfigCategory>;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ScoreDataType } from "@prisma/client";
|
||||
import z from "zod/v4";
|
||||
import { MetadataDomain } from "./traces";
|
||||
import { ScoreDataType } from "@prisma/client";
|
||||
|
||||
export const ScoreSource = {
|
||||
ANNOTATION: "ANNOTATION",
|
||||
@@ -29,6 +29,7 @@ export const ScoreSchema = z.object({
|
||||
configId: z.string().nullable(),
|
||||
stringValue: z.string().nullable(),
|
||||
queueId: z.string().nullable(),
|
||||
executionTraceId: z.string().nullable(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
dataType: z.enum(ScoreDataType),
|
||||
|
||||
+19
-35
@@ -38,6 +38,7 @@ const EnvSchema = z.object({
|
||||
LANGFUSE_CACHE_PROMPT_ENABLED: z.enum(["true", "false"]).default("true"),
|
||||
LANGFUSE_CACHE_PROMPT_TTL_SECONDS: z.coerce.number().default(300), // 5 minutes
|
||||
CLICKHOUSE_URL: z.string().url(),
|
||||
CLICKHOUSE_READ_ONLY_URL: z.string().url().optional(),
|
||||
CLICKHOUSE_CLUSTER_NAME: z.string().default("default"),
|
||||
CLICKHOUSE_DB: z.string().default("default"),
|
||||
CLICKHOUSE_USER: z.string(),
|
||||
@@ -50,6 +51,10 @@ const EnvSchema = z.object({
|
||||
.nonnegative()
|
||||
.default(15_000),
|
||||
LANGFUSE_INGESTION_QUEUE_SHARD_COUNT: z.coerce.number().positive().default(1),
|
||||
LANGFUSE_OTEL_INGESTION_QUEUE_SHARD_COUNT: z.coerce
|
||||
.number()
|
||||
.positive()
|
||||
.default(1),
|
||||
LANGFUSE_TRACE_UPSERT_QUEUE_SHARD_COUNT: z.coerce
|
||||
.number()
|
||||
.positive()
|
||||
@@ -126,41 +131,6 @@ const EnvSchema = z.object({
|
||||
LANGFUSE_CLICKHOUSE_DELETION_TIMEOUT_MS: z.coerce.number().default(600_000), // 10 minutes
|
||||
LANGFUSE_CLICKHOUSE_QUERY_MAX_ATTEMPTS: z.coerce.number().default(3), // Maximum attempts for socket hang up errors
|
||||
LANGFUSE_SKIP_S3_LIST_FOR_OBSERVATIONS_PROJECT_IDS: z.string().optional(),
|
||||
LANGFUSE_EXPERIMENT_COMPARE_READ_FROM_AGGREGATING_MERGE_TREES: z
|
||||
.enum(["true", "false"])
|
||||
.default("false"),
|
||||
LANGFUSE_EXPERIMENT_ADD_QUERY_RESULT_TO_SPAN_PROJECT_IDS: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((s) =>
|
||||
s ? s.split(",").map((s) => s.toLowerCase().trim()) : [],
|
||||
),
|
||||
LANGFUSE_EXPERIMENT_SAMPLING_RATE: z.coerce
|
||||
.number()
|
||||
.min(0)
|
||||
.max(1)
|
||||
.default(0.1),
|
||||
LANGFUSE_EXPERIMENT_WHITELISTED_PROJECT_IDS: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((s) =>
|
||||
s ? s.split(",").map((s) => s.toLowerCase().trim()) : [],
|
||||
),
|
||||
LANGFUSE_EXPERIMENT_RETURN_NEW_RESULT: z
|
||||
.enum(["true", "false"])
|
||||
.default("false"),
|
||||
LANGFUSE_EXPERIMENT_RETURN_NEW_RESULT_SHORT_TERM: z
|
||||
.enum(["true", "false"])
|
||||
.default("false"),
|
||||
LANGFUSE_EXPERIMENT_INSERT_INTO_AGGREGATING_MERGE_TREES: z
|
||||
.enum(["true", "false"])
|
||||
.default("false"),
|
||||
LANGFUSE_EXPERIMENT_WHITELISTED_AMT_TABLES: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((s) =>
|
||||
s ? s.split(",").map((s) => s.toLowerCase().trim()) : [],
|
||||
),
|
||||
LANGFUSE_INGESTION_PROCESSING_SAMPLED_PROJECTS: z
|
||||
.string()
|
||||
.optional()
|
||||
@@ -193,6 +163,12 @@ const EnvSchema = z.object({
|
||||
return new Map<string, number>();
|
||||
}
|
||||
}),
|
||||
LANGFUSE_WEBHOOK_WHITELISTED_IPS: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((s) =>
|
||||
s ? s.split(",").map((s) => s.toLowerCase().trim()) : [],
|
||||
),
|
||||
SLACK_CLIENT_ID: z.string().optional(),
|
||||
SLACK_CLIENT_SECRET: z.string().optional(),
|
||||
SLACK_STATE_SECRET: z.string().optional(),
|
||||
@@ -223,6 +199,14 @@ const EnvSchema = z.object({
|
||||
.int()
|
||||
.positive()
|
||||
.default(120_000), // 2 minutes
|
||||
|
||||
LANGFUSE_AWS_BEDROCK_REGION: z.string().optional(),
|
||||
|
||||
// Langfuse AI Features
|
||||
LANGFUSE_AI_FEATURES_PUBLIC_KEY: z.string().optional(),
|
||||
LANGFUSE_AI_FEATURES_SECRET_KEY: z.string().optional(),
|
||||
LANGFUSE_AI_FEATURES_HOST: z.string().optional(),
|
||||
LANGFUSE_AI_FEATURES_PROJECT_ID: z.string().optional(),
|
||||
});
|
||||
|
||||
export const env: z.infer<typeof EnvSchema> =
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { BaseError } from "./BaseError";
|
||||
|
||||
export class ServiceUnavailableError extends BaseError {
|
||||
constructor(description = "Service Temporarily Unavailable") {
|
||||
super("ServiceUnavailableError", 503, description, true);
|
||||
}
|
||||
}
|
||||
@@ -7,4 +7,4 @@ export { MethodNotAllowedError } from "./MethodNotAllowedError";
|
||||
export { ApiError } from "./ApiError";
|
||||
export { InternalServerError } from "./InternalServerError";
|
||||
export { LangfuseConflictError } from "./ConflictError";
|
||||
export { QUEUE_ERROR_MESSAGES } from "./utils/constants";
|
||||
export { ServiceUnavailableError } from "./ServiceUnavailableError";
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
export const QUEUE_ERROR_MESSAGES = {
|
||||
API_KEY_ERROR: "API key for provider",
|
||||
NO_DEFAULT_MODEL_ERROR: "No default model or custom model found for project",
|
||||
MAPPED_DATA_ERROR:
|
||||
"Please ensure the mapped data exists and consider extending the job delay.",
|
||||
INVALID_JSON_ERROR: "is not valid JSON",
|
||||
TOO_LOW_MAX_TOKENS_ERROR: "Error: Unterminated string in JSON at position",
|
||||
OUTPUT_TOKENS_TOO_LONG_ERROR:
|
||||
"Could not parse response content as the length limit was reached",
|
||||
TIMEOUT_ERROR: "Request timed out",
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./validation";
|
||||
@@ -0,0 +1,49 @@
|
||||
import { z } from "zod/v4";
|
||||
import { ScoreConfig as ScoreConfigDbType } from "@prisma/client";
|
||||
import {
|
||||
ScoreConfigDomain,
|
||||
ScoreConfigSchema,
|
||||
} from "../../domain/score-configs";
|
||||
|
||||
/**
|
||||
* Use this function when pulling a list of score configs from the database before using in the application to ensure type safety.
|
||||
* All score configs are expected to pass the validation. If a score fails validation, it will be logged to Otel.
|
||||
* @param scoreConfigs
|
||||
* @returns list of validated score configs
|
||||
*/
|
||||
export const filterAndValidateDbScoreConfigList = (
|
||||
scoreConfigs: ScoreConfigDbType[],
|
||||
onParseError?: (error: z.ZodError) => void, // eslint-disable-line no-unused-vars
|
||||
): ScoreConfigDomain[] =>
|
||||
scoreConfigs.reduce((acc, ts) => {
|
||||
const result = ScoreConfigSchema.safeParse(ts);
|
||||
if (result.success) {
|
||||
acc.push(result.data);
|
||||
} else {
|
||||
onParseError?.(result.error);
|
||||
}
|
||||
return acc;
|
||||
}, [] as ScoreConfigDomain[]);
|
||||
|
||||
/**
|
||||
* Use this function when pulling a single score config from the database before using in the application to ensure type safety.
|
||||
* The score is expected to pass the validation. If a score fails validation, an error will be thrown.
|
||||
* @param scoreConfig
|
||||
* @returns validated score config
|
||||
* @throws error if score fails validation
|
||||
*/
|
||||
export const validateDbScoreConfig = (
|
||||
scoreConfig: ScoreConfigDbType,
|
||||
): ScoreConfigDomain => ScoreConfigSchema.parse(scoreConfig);
|
||||
|
||||
/**
|
||||
* Use this function when pulling a single score config from the database before using in the application to ensure type safety.
|
||||
* This function will NOT throw an error by default. The score is expected to pass the validation.
|
||||
* @param scoreConfig
|
||||
* @returns score config validation object:
|
||||
* - success: true if the score config passes validation
|
||||
* - data: the validated score config if success is true
|
||||
* - error: the error object if success is false
|
||||
*/
|
||||
export const validateDbScoreConfigSafe = (scoreConfig: ScoreConfigDbType) =>
|
||||
ScoreConfigSchema.safeParse(scoreConfig);
|
||||
@@ -1,2 +1 @@
|
||||
export * from "./interfaces";
|
||||
export * from "./scoreConfigTypes";
|
||||
|
||||
@@ -50,6 +50,7 @@ export const ScoreFoundationSchema = z.object({
|
||||
createdAt: z.coerce.date(),
|
||||
updatedAt: z.coerce.date(),
|
||||
queueId: z.string().nullish(),
|
||||
executionTraceId: z.string().nullish(),
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,7 +8,9 @@ export const GetScoreQueryV2 = GetScoreQuery;
|
||||
export const GetScoreResponseV2 = APIScoreSchemaV2;
|
||||
|
||||
// GET /scores v2
|
||||
export const GetScoresQueryV2 = GetScoresQuery;
|
||||
export const GetScoresQueryV2 = GetScoresQuery.extend({
|
||||
sessionId: z.string().nullish(),
|
||||
});
|
||||
export const GetScoreResponseDataV2 = z.intersection(
|
||||
APIScoreSchemaV2,
|
||||
z.object({
|
||||
|
||||
@@ -2,7 +2,7 @@ import z from "zod/v4";
|
||||
import { applyScoreValidation } from "../../../../utils/scores";
|
||||
import { PostScoreBodyFoundationSchema } from "../shared";
|
||||
import { isPresent } from "../../../../utils/typeChecks";
|
||||
import { Category as ConfigCategory } from "../../scoreConfigTypes";
|
||||
import { ScoreConfigCategory } from "../../../../domain/score-configs";
|
||||
|
||||
export const ScoreBodyWithoutConfig = applyScoreValidation(
|
||||
z.discriminatedUnion("dataType", [
|
||||
@@ -54,7 +54,7 @@ const ScorePropsAgainstConfigNumeric = z
|
||||
const ScorePropsAgainstConfigCategorical = z
|
||||
.object({
|
||||
value: z.string(),
|
||||
categories: z.array(ConfigCategory),
|
||||
categories: z.array(ScoreConfigCategory),
|
||||
dataType: z.literal("CATEGORICAL"),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
|
||||
@@ -17,4 +17,5 @@ export const PostScoreBodyFoundationSchema = z.object({
|
||||
comment: z.string().nullish(),
|
||||
metadata: jsonSchema.nullish(),
|
||||
environment: z.string().default("default"),
|
||||
queueId: z.string().nullish(),
|
||||
});
|
||||
|
||||
@@ -19,10 +19,9 @@ export type NumericAggregate = BaseAggregate & {
|
||||
average: number;
|
||||
};
|
||||
|
||||
export type ScoreAggregate = Record<
|
||||
string,
|
||||
CategoricalAggregate | NumericAggregate
|
||||
>;
|
||||
export type AggregatedScoreData = CategoricalAggregate | NumericAggregate;
|
||||
|
||||
export type ScoreAggregate = Record<string, AggregatedScoreData>;
|
||||
|
||||
export type ScoreSimplified = {
|
||||
id: string;
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { ScoreConfig as ScoreConfigDbType } from "@prisma/client";
|
||||
|
||||
import { isPresent } from "../../utils/typeChecks";
|
||||
import {
|
||||
jsonSchema,
|
||||
paginationMetaResponseZod,
|
||||
publicApiPaginationZod,
|
||||
} from "../../utils/zod";
|
||||
|
||||
/**
|
||||
* Types to use across codebase
|
||||
*/
|
||||
export type ConfigCategory = z.infer<typeof Category>;
|
||||
export type ValidatedScoreConfig = z.infer<typeof ValidatedScoreConfigSchema>;
|
||||
|
||||
const validateCategories = (
|
||||
categories: ConfigCategory[],
|
||||
ctx: z.RefinementCtx,
|
||||
) => {
|
||||
const uniqueNames = new Set<string>();
|
||||
const uniqueValues = new Set<number>();
|
||||
|
||||
for (const category of categories) {
|
||||
if (uniqueNames.has(category.label)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Duplicate category label: ${category.label}, category labels must be unique`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
uniqueNames.add(category.label);
|
||||
|
||||
if (uniqueValues.has(category.value)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Duplicate category value: ${category.value}, category values must be unique`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
uniqueValues.add(category.value);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Objects
|
||||
*/
|
||||
export const Category = z.object({
|
||||
label: z.string().min(1),
|
||||
value: z.number(),
|
||||
});
|
||||
|
||||
const Categories = z.array(Category);
|
||||
|
||||
const NumericScoreConfig = z.object({
|
||||
maxValue: z.number().nullish(),
|
||||
minValue: z.number().nullish(),
|
||||
dataType: z.literal("NUMERIC"),
|
||||
categories: z.undefined().nullish(),
|
||||
});
|
||||
|
||||
const CategoricalScoreConfig = z.object({
|
||||
maxValue: z.undefined().nullish(),
|
||||
minValue: z.undefined().nullish(),
|
||||
dataType: z.literal("CATEGORICAL"),
|
||||
categories: jsonSchema.superRefine((categories, ctx) => {
|
||||
const parseResult = Categories.safeParse(categories);
|
||||
if (!parseResult.success) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message:
|
||||
"Category must be an array of objects with label value pairs, where labels and values are unique.",
|
||||
} as z.core.$ZodIssueCustom);
|
||||
return;
|
||||
}
|
||||
|
||||
validateCategories(categories as ConfigCategory[], ctx);
|
||||
}),
|
||||
});
|
||||
|
||||
const BooleanScoreConfig = z.object({
|
||||
maxValue: z.undefined().nullish(),
|
||||
minValue: z.undefined().nullish(),
|
||||
dataType: z.literal("BOOLEAN"),
|
||||
categories: z
|
||||
.array(Category)
|
||||
.length(2, "Boolean data type must have exactly 2 categories.")
|
||||
.refine((categories) => {
|
||||
const expectedCategories = [
|
||||
{ label: "True", value: 1 },
|
||||
{ label: "False", value: 0 },
|
||||
];
|
||||
return categories.every(
|
||||
(category, index) =>
|
||||
category.label === expectedCategories[index].label &&
|
||||
category.value === expectedCategories[index].value,
|
||||
);
|
||||
}),
|
||||
});
|
||||
|
||||
const ScoreConfigBase = z.object({
|
||||
id: z.string(),
|
||||
name: z.string().min(1).max(35),
|
||||
isArchived: z.boolean(),
|
||||
description: z.string().nullish(),
|
||||
createdAt: z.coerce.date(),
|
||||
updatedAt: z.coerce.date(),
|
||||
projectId: z.string(),
|
||||
});
|
||||
|
||||
const ScoreConfigPostBase = z.object({
|
||||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
});
|
||||
|
||||
const ValidatedScoreConfigSchema = z
|
||||
.union([
|
||||
ScoreConfigBase.merge(NumericScoreConfig),
|
||||
ScoreConfigBase.merge(
|
||||
z.object({
|
||||
maxValue: z.undefined().nullish(),
|
||||
minValue: z.undefined().nullish(),
|
||||
dataType: z.literal("CATEGORICAL"),
|
||||
categories: Categories.superRefine(validateCategories),
|
||||
}),
|
||||
),
|
||||
ScoreConfigBase.merge(BooleanScoreConfig),
|
||||
])
|
||||
.superRefine((data, ctx) => {
|
||||
if (data.dataType === "NUMERIC") {
|
||||
if (
|
||||
isPresent(data.maxValue) &&
|
||||
isPresent(data.minValue) &&
|
||||
data.maxValue <= data.minValue
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Maximum value must be greater than Minimum value",
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Use this function when pulling a list of score configs from the database before using in the application to ensure type safety.
|
||||
* All score configs are expected to pass the validation. If a score fails validation, it will be logged to Otel.
|
||||
* @param scoreConfigs
|
||||
* @returns list of validated score configs
|
||||
*/
|
||||
export const filterAndValidateDbScoreConfigList = (
|
||||
scoreConfigs: ScoreConfigDbType[],
|
||||
onParseError?: (error: z.ZodError) => void, // eslint-disable-line no-unused-vars
|
||||
): ValidatedScoreConfig[] =>
|
||||
scoreConfigs.reduce((acc, ts) => {
|
||||
const result = ValidatedScoreConfigSchema.safeParse(ts);
|
||||
if (result.success) {
|
||||
acc.push(result.data);
|
||||
} else {
|
||||
onParseError?.(result.error);
|
||||
}
|
||||
return acc;
|
||||
}, [] as ValidatedScoreConfig[]);
|
||||
|
||||
/**
|
||||
* Use this function when pulling a single score config from the database before using in the application to ensure type safety.
|
||||
* The score is expected to pass the validation. If a score fails validation, an error will be thrown.
|
||||
* @param scoreConfig
|
||||
* @returns validated score config
|
||||
* @throws error if score fails validation
|
||||
*/
|
||||
export const validateDbScoreConfig = (
|
||||
scoreConfig: ScoreConfigDbType,
|
||||
): ValidatedScoreConfig => ValidatedScoreConfigSchema.parse(scoreConfig);
|
||||
|
||||
/**
|
||||
* Use this function when pulling a single score config from the database before using in the application to ensure type safety.
|
||||
* This function will NOT throw an error by default. The score is expected to pass the validation.
|
||||
* @param scoreConfig
|
||||
* @returns score config validation object:
|
||||
* - success: true if the score config passes validation
|
||||
* - data: the validated score config if success is true
|
||||
* - error: the error object if success is false
|
||||
*/
|
||||
export const validateDbScoreConfigSafe = (scoreConfig: ScoreConfigDbType) =>
|
||||
ValidatedScoreConfigSchema.safeParse(scoreConfig);
|
||||
|
||||
/**
|
||||
* Endpoints
|
||||
*/
|
||||
|
||||
// GET /score-configs/{configId}
|
||||
export const GetScoreConfigQuery = z.object({
|
||||
configId: z.string(),
|
||||
});
|
||||
|
||||
export const GetScoreConfigResponse = ValidatedScoreConfigSchema;
|
||||
|
||||
// POST /score-configs
|
||||
export const PostScoreConfigBody = z
|
||||
.discriminatedUnion("dataType", [
|
||||
ScoreConfigPostBase.merge(CategoricalScoreConfig),
|
||||
ScoreConfigPostBase.merge(NumericScoreConfig),
|
||||
ScoreConfigPostBase.merge(
|
||||
z.object({
|
||||
dataType: z.literal("BOOLEAN"),
|
||||
categories: z.undefined().nullish(),
|
||||
}),
|
||||
),
|
||||
])
|
||||
.superRefine((data, ctx) => {
|
||||
if (data.dataType === "NUMERIC") {
|
||||
if (
|
||||
isPresent(data.maxValue) &&
|
||||
isPresent(data.minValue) &&
|
||||
data.maxValue <= data.minValue
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Maximum value must be greater than Minimum value",
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const PostScoreConfigResponse = ValidatedScoreConfigSchema;
|
||||
|
||||
// GET /score-configs
|
||||
export const GetScoreConfigsQuery = z.object({
|
||||
...publicApiPaginationZod,
|
||||
});
|
||||
|
||||
export const GetScoreConfigsResponse = z.object({
|
||||
data: z.array(ValidatedScoreConfigSchema),
|
||||
meta: paginationMetaResponseZod,
|
||||
});
|
||||
@@ -19,6 +19,7 @@ export * from "./interfaces/rate-limits";
|
||||
export * from "./tableDefinitions/typeHelpers";
|
||||
export * from "./domain/webhooks";
|
||||
export * from "./domain/dataset-run-items";
|
||||
export * from "./domain/score-configs";
|
||||
|
||||
// llm api
|
||||
export * from "./server/llm/types";
|
||||
@@ -37,6 +38,9 @@ export * from "./features/annotation/types";
|
||||
// scores
|
||||
export * from "./features/scores";
|
||||
|
||||
// score configs
|
||||
export * from "./features/scoreConfigs";
|
||||
|
||||
// comments
|
||||
export * from "./features/comments/types";
|
||||
|
||||
|
||||
@@ -13,26 +13,19 @@ export const CloudConfigSchema = z.object({
|
||||
customerId: z.string().optional(),
|
||||
activeSubscriptionId: z.string().optional(),
|
||||
activeProductId: z.string().optional(),
|
||||
activeUsageProductId: z.string().optional(),
|
||||
subscriptionStatus: z.string().optional(), // should be one of ["active","past_due", "unpaid", "canceled", "incomplete", "incomplete_expired", "paused"]; we don't enforce to have a backwards compatibility for this field
|
||||
})
|
||||
.transform((data) => ({
|
||||
...data,
|
||||
isLegacySubscription:
|
||||
data?.activeProductId !== undefined &&
|
||||
data?.activeUsageProductId === undefined,
|
||||
}))
|
||||
.optional(),
|
||||
|
||||
// custom rate limits for an organization
|
||||
rateLimitOverrides: CloudConfigRateLimit.optional(),
|
||||
|
||||
// billing alert configuration
|
||||
usageAlerts: z
|
||||
.object({
|
||||
enabled: z.boolean().default(true),
|
||||
type: z.enum(["STRIPE"]).default("STRIPE"),
|
||||
threshold: z.number().int().positive(),
|
||||
alertId: z.string(), // Alert ID for tracking
|
||||
meterId: z.string(), // Meter ID for usage tracking
|
||||
notifications: z.object({
|
||||
email: z.boolean().default(true),
|
||||
recipients: z.array(z.string().email()).default([]),
|
||||
}),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type CloudConfigSchema = z.infer<typeof CloudConfigSchema>;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user