Compare commits

..

77 Commits

Author SHA1 Message Date
David Corbitt
4e176088e9 Auto-resize project menu width 2023-08-10 22:46:01 -07:00
David Corbitt
3cec1f7786 Merge branch 'main' into logged-calls 2023-08-10 22:19:16 -07:00
arcticfly
81fbaeae44 Style project settings on mobile (#134)
* Style project settings on mobile

* Use auto-resize text area for display name

* Remove unused import
2023-08-10 22:15:45 -07:00
David Corbitt
b3d8f96fa8 Merge branch 'main' into logged-calls 2023-08-10 21:49:31 -07:00
arcticfly
5277afa199 Change logo (#133)
* Change logo

* Add more vertical padding on desktop

* Fix prettier
2023-08-10 21:44:33 -07:00
David Corbitt
54d97ddfa8 Add getUsage function 2023-08-10 19:51:36 -07:00
David Corbitt
1f8e3b820f Rename prompt and completion tokens to input and output tokens 2023-08-10 19:49:18 -07:00
arcticfly
76c34d64e6 Change menu styles (#132)
* Change ProjectMenu placement

* Reduce UserMenu width
2023-08-10 18:48:23 -07:00
Kyle Corbitt
454ac9a0d3 Merge pull request #131 from OpenPipe/better-template-vars
Better scenario variable editing
2023-08-10 12:25:54 -07:00
Kyle Corbitt
5ed7adadf9 Better scenario variable editing
Some users have gotten confused by the scenario variable editing interface. This change makes the interface easier to understand.
2023-08-10 12:08:17 -07:00
Kyle Corbitt
b8e0f392ab Merge pull request #130 from OpenPipe/output-wrapping
Preserve linebreaks in model output
2023-08-10 07:26:55 -07:00
Kyle Corbitt
b2af83341d Preserve linebreaks in model output 2023-08-09 21:58:41 -07:00
Kyle Corbitt
e6d229d5f9 Merge pull request #129 from OpenPipe/persist-proj
persist the currently-selected project
2023-08-09 17:05:17 -07:00
Kyle Corbitt
1a6ae3aef7 Merge pull request #128 from OpenPipe/proj-styling
Sidebar styling
2023-08-09 17:05:02 -07:00
Kyle Corbitt
9051d80775 Sidebar styling
Unify the menu styles between the UserMenu and ProjectMenu
2023-08-09 16:47:09 -07:00
Kyle Corbitt
6c060c6ea0 persist the currently-selected project 2023-08-09 16:45:54 -07:00
Kyle Corbitt
f70e73e338 Merge pull request #126 from OpenPipe/org-to-proj
Rename Organization to Project
2023-08-09 16:04:36 -07:00
Kyle Corbitt
16aa6672fc Rename Organization to Project
We'll probably need a concept of organizations at some point in the future, but in practice the way we're using these in the codebase right now is as a project, so this renames it to that to avoid confusion.
2023-08-09 16:01:13 -07:00
Kyle Corbitt
ac99c8e0f7 Merge pull request #127 from OpenPipe/pause-champs
Pause world championships
2023-08-09 15:59:15 -07:00
Kyle Corbitt
df121db78c Merge pull request #125 from OpenPipe/claude-1.1
Support Claude Instant 1.2
2023-08-09 15:58:36 -07:00
Kyle Corbitt
816b41adad Pause world championships
We aren't actually quite ready to run this yet, so announcing a pause it for now.
2023-08-09 15:55:40 -07:00
Kyle Corbitt
f09dfe18be Support Claude Instant 1.2 2023-08-09 14:43:54 -07:00
Kyle Corbitt
868d7084f0 Merge pull request #123 from OpenPipe/add-openapi
Add Logged Calls and projects
2023-08-09 14:37:17 -07:00
Kyle Corbitt
f6f04e537e project menu updates 2023-08-09 14:26:15 -07:00
David Corbitt
4feb3e5829 Avoid adding extra day to usage statistics 2023-08-09 01:22:04 -07:00
David Corbitt
c62ced867a Increase dashboard seeding number 2023-08-09 01:21:42 -07:00
David Corbitt
7bb414026e Fix typescript error 2023-08-08 18:30:02 -07:00
David Corbitt
1b2b6c1456 Send organizationId in fork mutation 2023-08-08 18:19:59 -07:00
David Corbitt
760bfbbe32 Fix issue with timezones 2023-08-08 18:15:42 -07:00
David Corbitt
3424aa36ba Automatically push personalOrg into list 2023-08-08 17:20:20 -07:00
David Corbitt
ded86cba08 Add dashboard seed command 2023-08-08 16:39:42 -07:00
David Corbitt
65a0f9065f Backfill usage statistics 2023-08-08 15:26:14 -07:00
David Corbitt
2861c64428 Fix prettier 2023-08-08 14:31:08 -07:00
David Corbitt
ca33bb0b08 Add beta to logged calls 2023-08-08 14:27:14 -07:00
David Corbitt
72e680e77c Replace USE_OPENPIPE with OPENPIPE_API_KEY 2023-08-08 13:57:10 -07:00
David Corbitt
5dd7e67396 Make project options full width 2023-08-08 13:50:57 -07:00
David Corbitt
fd286f6874 Fix prettier 2023-08-08 13:47:54 -07:00
David Corbitt
7a4aa5f0aa Use openpipe optionally in app 2023-08-08 13:45:46 -07:00
David Corbitt
cb791e3c73 Replace home page with logged calls page 2023-08-08 13:37:55 -07:00
David Corbitt
a2c322ff43 Add requireAuth to AppShell 2023-08-08 13:20:02 -07:00
David Corbitt
a2ace63f25 Hackily fix seeding 2023-08-08 12:24:14 -07:00
David Corbitt
41d06596cb Fix prettier 2023-08-08 12:09:09 -07:00
David Corbitt
49c68fdbf2 Upsert personalOrg when listing orgs 2023-08-08 12:07:18 -07:00
David Corbitt
6188f55569 Fix dashboard stats 2023-08-08 11:49:08 -07:00
David Corbitt
ea91d692d3 Use crypto-random-string 2023-08-08 11:45:03 -07:00
David Corbitt
ae7acbfdd4 Require user to be able to view organization to get it 2023-08-08 11:40:58 -07:00
David Corbitt
b9396e63cc Change /settings to /project/settings 2023-08-08 11:34:15 -07:00
David Corbitt
753a48f6e9 Use boxSize on ProjectBreadcrumbContents 2023-08-08 11:32:13 -07:00
David Corbitt
bd7c8b43b0 utlilize useHandledAsyncCallback in CopiableCode 2023-08-08 11:27:59 -07:00
David Corbitt
a1249f17c9 Add basic dashboard to homepage 2023-08-08 11:18:35 -07:00
David Corbitt
6f8db40f74 Fix logging 2023-08-08 11:12:04 -07:00
David Corbitt
8c5345a291 Allow user to open project menu on mobile 2023-08-08 10:20:10 -07:00
David Corbitt
f47010a6e7 Fix prettier 2023-08-07 21:45:36 -07:00
David Corbitt
6d32f1c06e Allow admins to delete projects 2023-08-07 21:45:21 -07:00
David Corbitt
8fed9730da Send api token properly 2023-08-07 21:04:38 -07:00
David Corbitt
0f9a83cf45 Assign experiments and datasets to correct org 2023-08-07 19:10:27 -07:00
David Corbitt
9f17d98736 Attempt to log (without api key) 2023-08-07 17:12:09 -07:00
David Corbitt
74029e5478 Close project menu after navigating 2023-08-07 15:21:03 -07:00
David Corbitt
d220cd30e8 Allow user to change projects 2023-08-07 15:18:23 -07:00
David Corbitt
c0f10cd522 Remove comment 2023-08-07 14:12:22 -07:00
David Corbitt
dc497dbd99 Query experiments and datasets by org 2023-08-07 14:10:32 -07:00
David Corbitt
f8f855adf4 Theme default divider 2023-08-07 14:10:01 -07:00
David Corbitt
8f49bace53 Backfill api keys 2023-08-07 13:08:33 -07:00
David Corbitt
c9f59bfb79 Add project to breadcrumb 2023-08-07 11:50:59 -07:00
David Corbitt
57166e96b4 Fix project settings IconLink 2023-08-07 11:21:59 -07:00
David Corbitt
1a838824ae Use PageHeaderContainer for all breadcrumbs 2023-08-07 11:16:54 -07:00
David Corbitt
6b304f8456 Show selected org 2023-08-06 23:23:20 -07:00
David Corbitt
a53d70d8b2 Add basic typescript lib 2023-08-06 17:29:06 -07:00
David Corbitt
109a9ddb1e Add js client lib 2023-08-06 13:50:43 -07:00
David Corbitt
7f8b574c9f Add checkCache and report routes 2023-08-05 20:37:16 -07:00
Kyle Corbitt
9e859c199e two LoggedCall tables 2023-08-05 17:56:51 -07:00
David Corbitt
deabbb094b condense CellOptions 2023-08-05 16:01:09 -07:00
Kyle Corbitt
7637b94ea7 schema changes 2023-08-05 13:49:03 -07:00
Kyle Corbitt
721f1726eb Merge pull request #122 from OpenPipe/app-dir
Move app to app/ subdir
2023-08-05 10:08:31 -07:00
Kyle Corbitt
cfeb4dfa92 run render and CI in app subdir 2023-08-05 10:06:06 -07:00
Kyle Corbitt
21ef67ed4c move app to app/ subdir 2023-08-05 10:00:10 -07:00
Kyle Corbitt
7707d451e0 Merge pull request #121 from OpenPipe/prompt-constructor
Rename constructFn to promptConstructor
2023-08-05 09:32:35 -07:00
282 changed files with 7183 additions and 1566 deletions

View File

@@ -6,6 +6,10 @@ on:
push:
branches: [main]
defaults:
run:
working-directory: app
jobs:
run-checks:
runs-on: ubuntu-latest

View File

@@ -65,7 +65,14 @@ OpenPipe includes a tool to generate new test scenarios based on your existing p
4. Clone this repository: `git clone https://github.com/openpipe/openpipe`
5. Install the dependencies: `cd openpipe && pnpm install`
6. Create a `.env` file (`cp .env.example .env`) and enter your `OPENAI_API_KEY`.
7. Update `DATABASE_URL` if necessary to point to your Postgres instance and run `pnpm prisma db push` to create the database.
7. Update `DATABASE_URL` if necessary to point to your Postgres instance and run `pnpm prisma migrate dev` to create the database.
8. Create a [GitHub OAuth App](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) and update the `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` values. (Note: a PR to make auth optional when running locally would be a great contribution!)
9. Start the app: `pnpm dev`.
10. Navigate to [http://localhost:3000](http://localhost:3000)
## Testing Locally
1. Copy your `.env` file to `.env.test`.
2. Update the `DATABASE_URL` to have a different database name than your development one
3. Run `DATABASE_URL=[your new datatase url] pnpm prisma migrate dev --skip-seed --skip-generate`
4. Run `pnpm test`

View File

@@ -31,3 +31,6 @@ NEXT_PUBLIC_HOST="http://localhost:3000"
# Next Auth Github Provider
GITHUB_CLIENT_ID="your_client_id"
GITHUB_CLIENT_SECRET="your_secret"
OPENPIPE_BASE_URL="http://localhost:3000/api"
OPENPIPE_API_KEY="your_key"

View File

@@ -34,6 +34,7 @@ yarn-error.log*
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
.env
.env*.local
.env.test
# vercel
.vercel

View File

@@ -12,8 +12,10 @@ declare module "nextjs-routes" {
export type Route =
| StaticRoute<"/account/signin">
| DynamicRoute<"/api/[...trpc]", { "trpc": string[] }>
| DynamicRoute<"/api/auth/[...nextauth]", { "nextauth": string[] }>
| StaticRoute<"/api/experiments/og-image">
| StaticRoute<"/api/openapi">
| StaticRoute<"/api/sentry-example-api">
| DynamicRoute<"/api/trpc/[trpc]", { "trpc": string }>
| DynamicRoute<"/data/[id]", { "id": string }>
@@ -21,6 +23,8 @@ declare module "nextjs-routes" {
| DynamicRoute<"/experiments/[id]", { "id": string }>
| StaticRoute<"/experiments">
| StaticRoute<"/">
| StaticRoute<"/logged-calls">
| StaticRoute<"/project/settings">
| StaticRoute<"/sentry-example-page">
| StaticRoute<"/world-champs">
| StaticRoute<"/world-champs/signup">;

View File

@@ -23,6 +23,7 @@ ARG NEXT_PUBLIC_SOCKET_URL
ARG NEXT_PUBLIC_HOST
ARG NEXT_PUBLIC_SENTRY_DSN
ARG SENTRY_AUTH_TOKEN
ARG NEXT_PUBLIC_FF_SHOW_LOGGED_CALLS
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules

7
app/openapitools.json Normal file
View File

@@ -0,0 +1,7 @@
{
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "6.6.0"
}
}

View File

@@ -16,10 +16,10 @@
"postinstall": "prisma generate",
"lint": "next lint",
"start": "next start",
"codegen": "tsx src/codegen/export-openai-types.ts",
"codegen": "tsx src/server/scripts/client-codegen.ts",
"seed": "tsx prisma/seed.ts",
"check": "concurrently 'pnpm lint' 'pnpm tsc' 'pnpm prettier . --check'",
"test": "pnpm vitest --no-threads"
"test": "pnpm vitest"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.5.8",
@@ -50,6 +50,7 @@
"chroma-js": "^2.4.2",
"concurrently": "^8.2.0",
"cors": "^2.8.5",
"crypto-random-string": "^5.0.0",
"dayjs": "^1.11.8",
"dedent": "^1.0.1",
"dotenv": "^16.3.1",
@@ -62,12 +63,16 @@
"json-schema-to-typescript": "^13.0.2",
"json-stringify-pretty-compact": "^4.0.0",
"jsonschema": "^1.4.1",
"kysely": "^0.26.1",
"lodash-es": "^4.17.21",
"lucide-react": "^0.265.0",
"next": "^13.4.2",
"next-auth": "^4.22.1",
"next-query-params": "^4.2.3",
"nextjs-cors": "^2.1.2",
"nextjs-routes": "^2.0.1",
"openai": "4.0.0-beta.7",
"pg": "^8.11.2",
"pluralize": "^8.0.0",
"posthog-js": "^1.75.3",
"posthog-node": "^3.1.1",
@@ -83,10 +88,12 @@
"react-syntax-highlighter": "^15.5.0",
"react-textarea-autosize": "^8.5.0",
"recast": "^0.23.3",
"recharts": "^2.7.2",
"replicate": "^0.12.3",
"socket.io": "^4.7.1",
"socket.io-client": "^4.7.1",
"superjson": "1.12.2",
"trpc-openapi": "^1.2.0",
"tsx": "^3.12.7",
"type-fest": "^4.0.0",
"use-query-params": "^2.2.1",
@@ -106,6 +113,7 @@
"@types/json-schema": "^7.0.12",
"@types/lodash-es": "^4.17.8",
"@types/node": "^18.16.0",
"@types/pg": "^8.10.2",
"@types/pluralize": "^0.0.30",
"@types/prismjs": "^1.26.0",
"@types/react": "^18.2.6",

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,90 @@
-- CreateTable
CREATE TABLE "LoggedCall" (
"id" UUID NOT NULL,
"startTime" TIMESTAMP(3) NOT NULL,
"cacheHit" BOOLEAN NOT NULL,
"modelResponseId" UUID NOT NULL,
"organizationId" UUID NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "LoggedCall_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "LoggedCallModelResponse" (
"id" UUID NOT NULL,
"reqPayload" JSONB NOT NULL,
"respStatus" INTEGER,
"respPayload" JSONB,
"error" TEXT,
"startTime" TIMESTAMP(3) NOT NULL,
"endTime" TIMESTAMP(3) NOT NULL,
"cacheKey" TEXT,
"durationMs" INTEGER,
"inputTokens" INTEGER,
"outputTokens" INTEGER,
"finishReason" TEXT,
"completionId" TEXT,
"totalCost" DECIMAL(18,12),
"originalLoggedCallId" UUID NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "LoggedCallModelResponse_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "LoggedCallTag" (
"id" UUID NOT NULL,
"name" TEXT NOT NULL,
"value" TEXT,
"loggedCallId" UUID NOT NULL,
CONSTRAINT "LoggedCallTag_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ApiKey" (
"id" UUID NOT NULL,
"name" TEXT NOT NULL,
"apiKey" TEXT NOT NULL,
"organizationId" UUID NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ApiKey_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "LoggedCall_startTime_idx" ON "LoggedCall"("startTime");
-- CreateIndex
CREATE UNIQUE INDEX "LoggedCallModelResponse_originalLoggedCallId_key" ON "LoggedCallModelResponse"("originalLoggedCallId");
-- CreateIndex
CREATE INDEX "LoggedCallModelResponse_cacheKey_idx" ON "LoggedCallModelResponse"("cacheKey");
-- CreateIndex
CREATE INDEX "LoggedCallTag_name_idx" ON "LoggedCallTag"("name");
-- CreateIndex
CREATE INDEX "LoggedCallTag_name_value_idx" ON "LoggedCallTag"("name", "value");
-- CreateIndex
CREATE UNIQUE INDEX "ApiKey_apiKey_key" ON "ApiKey"("apiKey");
-- AddForeignKey
ALTER TABLE "LoggedCall" ADD CONSTRAINT "LoggedCall_modelResponseId_fkey" FOREIGN KEY ("modelResponseId") REFERENCES "LoggedCallModelResponse"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LoggedCall" ADD CONSTRAINT "LoggedCall_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LoggedCallModelResponse" ADD CONSTRAINT "LoggedCallModelResponse_originalLoggedCallId_fkey" FOREIGN KEY ("originalLoggedCallId") REFERENCES "LoggedCall"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LoggedCallTag" ADD CONSTRAINT "LoggedCallTag_loggedCallId_fkey" FOREIGN KEY ("loggedCallId") REFERENCES "LoggedCall"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ApiKey" ADD CONSTRAINT "ApiKey_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Organization" ADD COLUMN "name" TEXT NOT NULL DEFAULT 'Project 1';

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "LoggedCall" ALTER COLUMN "modelResponseId" DROP NOT NULL;

View File

@@ -0,0 +1,37 @@
-- Rename Enum
ALTER TYPE "OrganizationUserRole" RENAME TO "ProjectUserRole";
-- Drop and recreate foreign keys
ALTER TABLE "ApiKey" DROP CONSTRAINT "ApiKey_organizationId_fkey";
ALTER TABLE "Dataset" DROP CONSTRAINT "Dataset_organizationId_fkey";
ALTER TABLE "Experiment" DROP CONSTRAINT "Experiment_organizationId_fkey";
ALTER TABLE "LoggedCall" DROP CONSTRAINT "LoggedCall_organizationId_fkey";
ALTER TABLE "OrganizationUser" DROP CONSTRAINT "OrganizationUser_organizationId_fkey";
ALTER TABLE "OrganizationUser" DROP CONSTRAINT "OrganizationUser_userId_fkey";
-- Rename columns
ALTER TABLE "ApiKey" RENAME COLUMN "organizationId" TO "projectId";
ALTER TABLE "Dataset" RENAME COLUMN "organizationId" TO "projectId";
ALTER TABLE "Experiment" RENAME COLUMN "organizationId" TO "projectId";
ALTER TABLE "LoggedCall" RENAME COLUMN "organizationId" TO "projectId";
ALTER TABLE "OrganizationUser" RENAME COLUMN "organizationId" TO "projectId";
ALTER TABLE "Organization" RENAME COLUMN "personalOrgUserId" TO "personalProjectUserId";
-- Rename table
ALTER TABLE "Organization" RENAME TO "Project";
ALTER TABLE "OrganizationUser" RENAME TO "ProjectUser";
-- Recreate foreign keys
ALTER TABLE "Experiment" ADD CONSTRAINT "Experiment_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Dataset" ADD CONSTRAINT "Dataset_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ProjectUser" ADD CONSTRAINT "ProjectUser_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ProjectUser" ADD CONSTRAINT "ProjectUser_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "LoggedCall" ADD CONSTRAINT "LoggedCall_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ApiKey" ADD CONSTRAINT "ApiKey_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- Rename indexes
ALTER TABLE "Project" RENAME CONSTRAINT "Organization_pkey" TO "Project_pkey";
ALTER TABLE "ProjectUser" RENAME CONSTRAINT "OrganizationUser_pkey" TO "ProjectUser_pkey";
ALTER TABLE "Project" RENAME CONSTRAINT "Organization_personalOrgUserId_fkey" TO "Project_personalProjectUserId_fkey";
ALTER INDEX "Organization_personalOrgUserId_key" RENAME TO "Project_personalProjectUserId_key";
ALTER INDEX "OrganizationUser_organizationId_userId_key" RENAME TO "ProjectUser_projectId_userId_key";

View File

@@ -0,0 +1 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

View File

@@ -0,0 +1,15 @@
/*
Warnings:
- You are about to rename the column `completionTokens` to `outputTokens` on the `ModelResponse` table.
- You are about to rename the column `promptTokens` to `inputTokens` on the `ModelResponse` table.
*/
-- Rename completionTokens to outputTokens
ALTER TABLE "ModelResponse"
RENAME COLUMN "completionTokens" TO "outputTokens";
-- Rename promptTokens to inputTokens
ALTER TABLE "ModelResponse"
RENAME COLUMN "promptTokens" TO "inputTokens";

View File

@@ -16,8 +16,8 @@ model Experiment {
sortIndex Int @default(0)
organizationId String @db.Uuid
organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
projectId String @db.Uuid
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -117,8 +117,8 @@ model ModelResponse {
receivedAt DateTime?
output Json?
cost Float?
promptTokens Int?
completionTokens Int?
inputTokens Int?
outputTokens Int?
statusCode Int?
errorMessage String?
retryTime DateTime?
@@ -180,8 +180,8 @@ model Dataset {
name String
datasetEntries DatasetEntry[]
organizationId String @db.Uuid
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
projectId String @db.Uuid
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -200,31 +200,35 @@ model DatasetEntry {
updatedAt DateTime @updatedAt
}
model Organization {
id String @id @default(uuid()) @db.Uuid
personalOrgUserId String? @unique @db.Uuid
PersonalOrgUser User? @relation(fields: [personalOrgUserId], references: [id], onDelete: Cascade)
model Project {
id String @id @default(uuid()) @db.Uuid
name String @default("Project 1")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organizationUsers OrganizationUser[]
experiments Experiment[]
datasets Dataset[]
personalProjectUserId String? @unique @db.Uuid
personalProjectUser User? @relation(fields: [personalProjectUserId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
projectUsers ProjectUser[]
experiments Experiment[]
datasets Dataset[]
loggedCalls LoggedCall[]
apiKeys ApiKey[]
}
enum OrganizationUserRole {
enum ProjectUserRole {
ADMIN
MEMBER
VIEWER
}
model OrganizationUser {
model ProjectUser {
id String @id @default(uuid()) @db.Uuid
role OrganizationUserRole
role ProjectUserRole
organizationId String @db.Uuid
organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
projectId String @db.Uuid
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
userId String @db.Uuid
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@ -232,7 +236,7 @@ model OrganizationUser {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([organizationId, userId])
@@unique([projectId, userId])
}
model WorldChampEntrant {
@@ -249,6 +253,99 @@ model WorldChampEntrant {
@@unique([userId])
}
model LoggedCall {
id String @id @default(uuid()) @db.Uuid
startTime DateTime
// True if this call was served from the cache, false otherwise
cacheHit Boolean
// A LoggedCall is always associated with a LoggedCallModelResponse. If this
// is a cache miss, we create a new LoggedCallModelResponse.
// If it's a cache hit, it's a pre-existing LoggedCallModelResponse.
modelResponseId String? @db.Uuid
modelResponse LoggedCallModelResponse? @relation(fields: [modelResponseId], references: [id], onDelete: Cascade)
// The responses created by this LoggedCall. Will be empty if this LoggedCall was a cache hit.
createdResponses LoggedCallModelResponse[] @relation(name: "ModelResponseOriginalCall")
projectId String @db.Uuid
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
tags LoggedCallTag[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([startTime])
}
model LoggedCallModelResponse {
id String @id @default(uuid()) @db.Uuid
reqPayload Json
// The HTTP status returned by the model provider
respStatus Int?
respPayload Json?
// Should be null if the request was successful, and some string if the request failed.
error String?
startTime DateTime
endTime DateTime
// Note: the function to calculate the cacheKey should include the project
// ID so we don't share cached responses between projects, which could be an
// attack vector. Also, we should only set the cacheKey on the model if the
// request was successful.
cacheKey String?
// Derived fields
durationMs Int?
inputTokens Int?
outputTokens Int?
finishReason String?
completionId String?
totalCost Decimal? @db.Decimal(18, 12)
// The LoggedCall that created this LoggedCallModelResponse
originalLoggedCallId String @unique @db.Uuid
originalLoggedCall LoggedCall @relation(name: "ModelResponseOriginalCall", fields: [originalLoggedCallId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
loggedCalls LoggedCall[]
@@index([cacheKey])
}
model LoggedCallTag {
id String @id @default(uuid()) @db.Uuid
name String
value String?
loggedCallId String @db.Uuid
loggedCall LoggedCall @relation(fields: [loggedCallId], references: [id], onDelete: Cascade)
@@index([name])
@@index([name, value])
}
model ApiKey {
id String @id @default(uuid()) @db.Uuid
name String
apiKey String @unique
projectId String @db.Uuid
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Account {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid
@@ -292,8 +389,8 @@ model User {
accounts Account[]
sessions Session[]
organizationUsers OrganizationUser[]
organizations Organization[]
projectUsers ProjectUser[]
projects Project[]
worldChampEntrant WorldChampEntrant?
createdAt DateTime @default(now())

View File

@@ -5,14 +5,14 @@ import { promptConstructorVersion } from "~/promptConstructor/version";
const defaultId = "11111111-1111-1111-1111-111111111111";
await prisma.organization.deleteMany({
await prisma.project.deleteMany({
where: { id: defaultId },
});
// If there's an existing org, just seed into it
const org =
(await prisma.organization.findFirst({})) ??
(await prisma.organization.create({
// If there's an existing project, just seed into it
const project =
(await prisma.project.findFirst({})) ??
(await prisma.project.create({
data: { id: defaultId },
}));
@@ -26,7 +26,7 @@ await prisma.experiment.create({
data: {
id: defaultId,
label: "Country Capitals Example",
organizationId: org.id,
projectId: project.id,
},
});

View File

@@ -7,14 +7,14 @@ import { promptConstructorVersion } from "~/promptConstructor/version";
const defaultId = "11111111-1111-1111-1111-111111111112";
await prisma.organization.deleteMany({
await prisma.project.deleteMany({
where: { id: defaultId },
});
// If there's an existing org, just seed into it
const org =
(await prisma.organization.findFirst({})) ??
(await prisma.organization.create({
// If there's an existing project, just seed into it
const project =
(await prisma.project.findFirst({})) ??
(await prisma.project.create({
data: { id: defaultId },
}));
@@ -47,7 +47,7 @@ for (const dataset of datasets) {
const oldExperiment = await prisma.experiment.findFirst({
where: {
label: experimentName,
organizationId: org.id,
projectId: project.id,
},
});
if (oldExperiment) {
@@ -60,7 +60,7 @@ for (const dataset of datasets) {
data: {
id: oldExperiment?.id ?? undefined,
label: experimentName,
organizationId: org.id,
projectId: project.id,
},
});

410
app/prisma/seedDashboard.ts Normal file

File diff suppressed because one or more lines are too long

View File

@@ -6,14 +6,14 @@ import { promptConstructorVersion } from "~/promptConstructor/version";
const defaultId = "11111111-1111-1111-1111-111111111112";
await prisma.organization.deleteMany({
await prisma.project.deleteMany({
where: { id: defaultId },
});
// If there's an existing org, just seed into it
const org =
(await prisma.organization.findFirst({})) ??
(await prisma.organization.create({
// If there's an existing project, just seed into it
const project =
(await prisma.project.findFirst({})) ??
(await prisma.project.create({
data: { id: defaultId },
}));
@@ -27,7 +27,7 @@ const experimentName = `Twitter Sentiment Analysis`;
const oldExperiment = await prisma.experiment.findFirst({
where: {
label: experimentName,
organizationId: org.id,
projectId: project.id,
},
});
if (oldExperiment) {
@@ -40,7 +40,7 @@ const experiment = await prisma.experiment.create({
data: {
id: oldExperiment?.id ?? undefined,
label: experimentName,
organizationId: org.id,
projectId: project.id,
},
});

BIN
app/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -9,10 +9,9 @@ Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,550.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M813 5478 c-18 -13 -37 -36 -43 -52 -6 -19 -10 -236 -10 -603 0 -638
-1 -626 65 -657 25 -12 67 -16 179 -16 l146 0 0 -2032 0 -2032 23 -33 c12 -18
35 -37 51 -43 19 -7 539 -10 1528 -10 1663 0 1549 -5 1582 65 14 30 16 235 16
2059 l0 2026 156 0 156 0 39 39 39 39 0 587 c0 651 1 638 -65 669 -30 14 -223
16 -1932 16 l-1898 0 -32 -22z"/>
<path d="M785 5474 l-25 -27 0 -622 0 -622 25 -27 24 -26 171 0 170 0 0 -2050
0 -2051 25 -25 24 -24 1557 2 1556 3 19 24 c19 23 19 70 19 2072 l0 2049 169
0 c165 0 169 1 195 25 l26 24 0 626 0 626 -26 24 -27 25 -1939 0 -1939 0 -24
-26z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 858 B

After

Width:  |  Height:  |  Size: 755 B

28
app/public/logo.svg Normal file
View File

@@ -0,0 +1,28 @@
<svg width="398" height="550" viewBox="0 0 398 550" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M39 125H359V542C359 546.418 355.418 550 351 550H47C42.5817 550 39 546.418 39 542V125Z" fill="black"/>
<path d="M0 8C0 3.58172 3.58172 0 8 0H390C394.418 0 398 3.58172 398 8V127C398 131.418 394.418 135 390 135H7.99999C3.58171 135 0 131.418 0 127V8Z" fill="black"/>
<path d="M50 135H348V535C348 537.209 346.209 539 344 539H54C51.7909 539 50 537.209 50 535V135Z" fill="#FF5733"/>
<path d="M11 14.0001C11 11.791 12.7909 10.0001 15 10.0001H384C386.209 10.0001 388 11.791 388 14.0001V120C388 122.209 386.209 124 384 124H15C12.7909 124 11 122.209 11 120V14.0001Z" fill="#FF5733"/>
<path d="M11 14.0001C11 11.791 12.7909 10.0001 15 10.0001H384C386.209 10.0001 388 11.791 388 14.0001V120C388 122.209 386.209 124 384 124H15C12.7909 124 11 122.209 11 120V14.0001Z" fill="url(#paint0_linear_102_49)"/>
<path d="M50 134H348V535C348 537.209 346.209 539 344 539H54C51.7909 539 50 537.209 50 535V134Z" fill="url(#paint1_linear_102_49)"/>
<path d="M108 142H156V535H108V142Z" fill="white"/>
<path d="M300 135H348V535C348 537.209 346.209 539 344 539H300V135Z" fill="white" fill-opacity="0.25"/>
<path d="M96 142H108V535H96V142Z" fill="white" fill-opacity="0.5"/>
<path d="M84 10.0001H133V120H84V10.0001Z" fill="white"/>
<path d="M339 10.0001H384C386.209 10.0001 388 11.791 388 14.0001V120C388 122.209 386.209 124 384 124H339V10.0001Z" fill="white" fill-opacity="0.25"/>
<path d="M71.9995 10.0001H83.9995V120H71.9995V10.0001Z" fill="white" fill-opacity="0.5"/>
<path d="M108 534.529H156V539.019H108V534.529Z" fill="#AAAAAA"/>
<path opacity="0.5" d="M95.9927 534.529H107.982V539.019H95.9927V534.529Z" fill="#AAAAAA"/>
<path d="M84.0029 119.887H133.007V124.027H84.0029V119.887Z" fill="#AAAAAA"/>
<path opacity="0.5" d="M71.9883 119.887H83.978V124.027H71.9883V119.887Z" fill="#AAAAAA"/>
<defs>
<linearGradient id="paint0_linear_102_49" x1="335" y1="67.0001" x2="137" y2="67.0001" gradientUnits="userSpaceOnUse">
<stop stop-color="#D62600"/>
<stop offset="1" stop-color="#FF5733" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint1_linear_102_49" x1="306.106" y1="336.5" x2="149.597" y2="336.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#D62600"/>
<stop offset="1" stop-color="#FF5733" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
app/public/og.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,40 @@
import { HStack, Icon, IconButton, Tooltip, Text } from "@chakra-ui/react";
import { useState } from "react";
import { MdContentCopy } from "react-icons/md";
import { useHandledAsyncCallback } from "~/utils/hooks";
const CopiableCode = ({ code }: { code: string }) => {
const [copied, setCopied] = useState(false);
const [copyToClipboard] = useHandledAsyncCallback(async () => {
await navigator.clipboard.writeText(code);
setCopied(true);
}, [code]);
return (
<HStack
backgroundColor="blackAlpha.800"
color="white"
borderRadius={4}
padding={3}
w="full"
justifyContent="space-between"
>
<Text fontFamily="inconsolata" fontWeight="bold" letterSpacing={0.5} overflowX="auto">
{code}
</Text>
<Tooltip closeOnClick={false} label={copied ? "Copied!" : "Copy to clipboard"}>
<IconButton
aria-label="Copy"
icon={<Icon as={MdContentCopy} boxSize={5} />}
size="xs"
colorScheme="white"
variant="ghost"
onClick={copyToClipboard}
onMouseLeave={() => setCopied(false)}
/>
</Tooltip>
</HStack>
);
};
export default CopiableCode;

View File

@@ -14,6 +14,7 @@ import {
import { useRouter } from "next/router";
import { useRef } from "react";
import { BsTrash } from "react-icons/bs";
import { useAppStore } from "~/state/store";
import { api } from "~/utils/api";
import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks";
@@ -23,6 +24,8 @@ export const DeleteButton = () => {
const utils = api.useContext();
const router = useRouter();
const closeDrawer = useAppStore((s) => s.closeDrawer);
const { isOpen, onOpen, onClose } = useDisclosure();
const cancelRef = useRef<HTMLButtonElement>(null);
@@ -31,6 +34,8 @@ export const DeleteButton = () => {
await mutation.mutateAsync({ id: experiment.data.id });
await utils.experiments.list.invalidate();
await router.push({ pathname: "/experiments" });
closeDrawer();
onClose();
}, [mutation, experiment.data?.id, router]);

View File

@@ -8,8 +8,8 @@ export default function Favicon() {
<link rel="icon" type="image/png" sizes="16x16" href="/favicons/favicon-16x16.png" />
<link rel="manifest" href="/favicons/site.webmanifest" />
<link rel="shortcut icon" href="/favicons/favicon.ico" />
<link rel="mask-icon" href="/favicons/safari-pinned-tab.svg" color="#5bbad5" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="msapplication-config" content="/favicons/browserconfig.xml" />
<meta name="theme-color" content="#ffffff" />
</Head>
);

View File

@@ -12,6 +12,7 @@ import {
Select,
FormHelperText,
Code,
IconButton,
} from "@chakra-ui/react";
import { type Evaluation, EvalType } from "@prisma/client";
import { useCallback, useState } from "react";
@@ -183,46 +184,37 @@ export default function EditEvaluations() {
<Text flex={1}>
{evaluation.evalType}: &quot;{evaluation.value}&quot;
</Text>
<Button
<IconButton
aria-label="Edit"
variant="unstyled"
color="gray.400"
height="unset"
width="unset"
minW="unset"
color="gray.400"
onClick={() => setEditingId(evaluation.id)}
_hover={{
color: "gray.800",
cursor: "pointer",
}}
>
<Icon as={BsPencil} boxSize={4} />
</Button>
<Button
_hover={{ color: "gray.800", cursor: "pointer" }}
icon={<Icon as={BsPencil} />}
/>
<IconButton
aria-label="Delete"
variant="unstyled"
color="gray.400"
height="unset"
width="unset"
minW="unset"
color="gray.400"
onClick={() => onDelete(evaluation.id)}
_hover={{
color: "gray.800",
cursor: "pointer",
}}
>
<Icon as={BsX} boxSize={6} />
</Button>
_hover={{ color: "gray.800", cursor: "pointer" }}
icon={<Icon as={BsX} boxSize={6} />}
/>
</HStack>
),
)}
{editingId == null && (
<Button
onClick={() => setEditingId("new")}
alignSelf="flex-start"
alignSelf="end"
size="sm"
mt={4}
colorScheme="blue"
>
Add Evaluation
New Evaluation
</Button>
)}
{editingId == "new" && (

View File

@@ -0,0 +1,185 @@
import { Text, Button, HStack, Heading, Icon, IconButton, Stack, VStack } from "@chakra-ui/react";
import { type TemplateVariable } from "@prisma/client";
import { useEffect, useState } from "react";
import { BsPencil, BsX } from "react-icons/bs";
import { api } from "~/utils/api";
import { useExperiment, useHandledAsyncCallback, useScenarioVars } from "~/utils/hooks";
import { maybeReportError } from "~/utils/standardResponses";
import { FloatingLabelInput } from "./FloatingLabelInput";
export const ScenarioVar = ({
variable,
isEditing,
setIsEditing,
}: {
variable: Pick<TemplateVariable, "id" | "label">;
isEditing: boolean;
setIsEditing: (isEditing: boolean) => void;
}) => {
const utils = api.useContext();
const [label, setLabel] = useState(variable.label);
useEffect(() => {
setLabel(variable.label);
}, [variable.label]);
const renameVarMutation = api.scenarioVars.rename.useMutation();
const [onRename] = useHandledAsyncCallback(async () => {
const resp = await renameVarMutation.mutateAsync({ id: variable.id, label });
if (maybeReportError(resp)) return;
setIsEditing(false);
await utils.scenarioVars.list.invalidate();
await utils.scenarios.list.invalidate();
}, [label, variable.id]);
const deleteMutation = api.scenarioVars.delete.useMutation();
const [onDeleteVar] = useHandledAsyncCallback(async () => {
await deleteMutation.mutateAsync({ id: variable.id });
await utils.scenarioVars.list.invalidate();
}, [variable.id]);
if (isEditing) {
return (
<HStack w="full">
<FloatingLabelInput
flex={1}
label="Renamed Variable"
value={label}
onChange={(e) => setLabel(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
onRename();
}
// If the user types a space, replace it with an underscore
if (e.key === " ") {
e.preventDefault();
setLabel((label) => label && `${label}_`);
}
}}
/>
<Button size="sm" onClick={() => setIsEditing(false)}>
Cancel
</Button>
<Button size="sm" colorScheme="blue" onClick={onRename}>
Save
</Button>
</HStack>
);
} else {
return (
<HStack w="full" borderTopWidth={1} borderColor="gray.200">
<Text flex={1}>{variable.label}</Text>
<IconButton
aria-label="Edit"
variant="unstyled"
minW="unset"
color="gray.400"
onClick={() => setIsEditing(true)}
_hover={{ color: "gray.800", cursor: "pointer" }}
icon={<Icon as={BsPencil} />}
/>
<IconButton
aria-label="Delete"
variant="unstyled"
minW="unset"
color="gray.400"
onClick={onDeleteVar}
_hover={{ color: "gray.800", cursor: "pointer" }}
icon={<Icon as={BsX} boxSize={6} />}
/>
</HStack>
);
}
};
export default function EditScenarioVars() {
const experiment = useExperiment();
const vars = useScenarioVars();
const [currentlyEditingId, setCurrentlyEditingId] = useState<string | null>(null);
const [newVariable, setNewVariable] = useState<string>("");
const newVarIsValid = newVariable?.length ?? 0 > 0;
const utils = api.useContext();
const addVarMutation = api.scenarioVars.create.useMutation();
const [onAddVar] = useHandledAsyncCallback(async () => {
if (!experiment.data?.id) return;
if (!newVariable) return;
const resp = await addVarMutation.mutateAsync({
experimentId: experiment.data.id,
label: newVariable,
});
if (maybeReportError(resp)) return;
await utils.scenarioVars.list.invalidate();
setNewVariable("");
}, [addVarMutation, experiment.data?.id, newVarIsValid, newVariable]);
return (
<Stack>
<Heading size="sm">Scenario Variables</Heading>
<VStack spacing={4}>
<Text fontSize="sm">
Scenario variables can be used in your prompt variants as well as evaluations.
</Text>
<VStack spacing={0} w="full">
{vars.data?.map((variable) => (
<ScenarioVar
variable={variable}
key={variable.id}
isEditing={currentlyEditingId === variable.id}
setIsEditing={(isEditing) => {
if (isEditing) {
setCurrentlyEditingId(variable.id);
} else {
setCurrentlyEditingId(null);
}
}}
/>
))}
</VStack>
{currentlyEditingId !== "new" && (
<Button
colorScheme="blue"
size="sm"
onClick={() => setCurrentlyEditingId("new")}
alignSelf="end"
>
New Variable
</Button>
)}
{currentlyEditingId === "new" && (
<HStack w="full">
<FloatingLabelInput
flex={1}
label="New Variable"
value={newVariable}
onChange={(e) => setNewVariable(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
onAddVar();
}
// If the user types a space, replace it with an underscore
if (e.key === " ") {
e.preventDefault();
setNewVariable((v) => v && `${v}_`);
}
}}
/>
<Button size="sm" onClick={() => setCurrentlyEditingId(null)}>
Cancel
</Button>
<Button size="sm" colorScheme="blue" onClick={onAddVar}>
Save
</Button>
</HStack>
)}
</VStack>
</Stack>
);
}

View File

@@ -1,7 +1,7 @@
import { api } from "~/utils/api";
import { type PromptVariant, type Scenario } from "../types";
import { type StackProps, Text, VStack } from "@chakra-ui/react";
import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks";
import { useScenarioVars, useHandledAsyncCallback } from "~/utils/hooks";
import SyntaxHighlighter from "react-syntax-highlighter";
import { docco } from "react-syntax-highlighter/dist/cjs/styles/hljs";
import stringify from "json-stringify-pretty-compact";
@@ -23,10 +23,7 @@ export default function OutputCell({
variant: PromptVariant;
}): ReactElement | null {
const utils = api.useContext();
const experiment = useExperiment();
const vars = api.templateVars.list.useQuery({
experimentId: experiment.data?.id ?? "",
}).data;
const vars = useScenarioVars().data;
const scenarioVariables = scenario.variableValues as Record<string, string>;
const templateHasVariables =
@@ -191,7 +188,7 @@ export default function OutputCell({
return (
<CellWrapper>
<Text>{contentToDisplay}</Text>
<Text whiteSpace="pre-wrap">{contentToDisplay}</Text>
</CellWrapper>
);
}

View File

@@ -19,8 +19,8 @@ export const OutputStats = ({
? modelResponse.receivedAt.getTime() - modelResponse.requestedAt.getTime()
: 0;
const promptTokens = modelResponse.promptTokens;
const completionTokens = modelResponse.completionTokens;
const inputTokens = modelResponse.inputTokens;
const outputTokens = modelResponse.outputTokens;
return (
<HStack
@@ -55,8 +55,8 @@ export const OutputStats = ({
</HStack>
{modelResponse.cost && (
<CostTooltip
promptTokens={promptTokens}
completionTokens={completionTokens}
inputTokens={inputTokens}
outputTokens={outputTokens}
cost={modelResponse.cost}
>
<HStack spacing={0}>

View File

@@ -18,13 +18,13 @@ export const CellOptions = ({
const modalDisclosure = useDisclosure();
return (
<HStack justifyContent="flex-end" w="full">
<HStack justifyContent="flex-end" w="full" spacing={1}>
{cell && (
<>
<Tooltip label="See Prompt">
<IconButton
aria-label="See Prompt"
icon={<Icon as={BsInfoCircle} boxSize={4} />}
icon={<Icon as={BsInfoCircle} boxSize={3.5} />}
onClick={modalDisclosure.onOpen}
size="xs"
colorScheme="gray"

View File

@@ -1,7 +1,7 @@
import { isEqual } from "lodash-es";
import { useEffect, useState, type DragEvent } from "react";
import { api } from "~/utils/api";
import { useExperiment, useExperimentAccess, useHandledAsyncCallback } from "~/utils/hooks";
import { useExperimentAccess, useHandledAsyncCallback, useScenarioVars } from "~/utils/hooks";
import { type Scenario } from "./types";
import {
@@ -41,8 +41,7 @@ export default function ScenarioEditor({
if (savedValues) setValues(savedValues);
}, [savedValues]);
const experiment = useExperiment();
const vars = api.templateVars.list.useQuery({ experimentId: experiment.data?.id ?? "" });
const vars = useScenarioVars();
const variableLabels = vars.data?.map((v) => v.label) ?? [];

View File

@@ -58,7 +58,7 @@ export const ScenarioEditorModal = ({
await utils.scenarios.list.invalidate();
}, [mutation, values]);
const vars = api.templateVars.list.useQuery({ experimentId: experiment.data?.id ?? "" });
const vars = api.scenarioVars.list.useQuery({ experimentId: experiment.data?.id ?? "" });
const variableLabels = vars.data?.map((v) => v.label) ?? [];
return (

View File

@@ -72,7 +72,7 @@ export const ScenariosHeader = () => {
Autogenerate Scenario
</MenuItem>
<MenuItem icon={<BsPencil />} onClick={openDrawer}>
Edit Vars
Add or Remove Variables
</MenuItem>
</MenuList>
</Menu>

View File

@@ -17,8 +17,8 @@ export default function VariantStats(props: { variant: PromptVariant }) {
initialData: {
evalResults: [],
overallCost: 0,
promptTokens: 0,
completionTokens: 0,
inputTokens: 0,
outputTokens: 0,
scenarioCount: 0,
outputCount: 0,
awaitingEvals: false,
@@ -68,8 +68,8 @@ export default function VariantStats(props: { variant: PromptVariant }) {
</HStack>
{data.overallCost && (
<CostTooltip
promptTokens={data.promptTokens}
completionTokens={data.completionTokens}
inputTokens={data.inputTokens}
outputTokens={data.outputTokens}
cost={data.overallCost}
>
<HStack spacing={0} align="center" color="gray.500">

Some files were not shown because too many files have changed in this diff Show More