Compare commits
35 Commits
python-sdk
...
move-api
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
754e273049 | ||
|
|
c4cef35717 | ||
|
|
8552baf632 | ||
|
|
f41e2229ca | ||
|
|
e649f42c9c | ||
|
|
99f305483b | ||
|
|
b28f4cad57 | ||
|
|
df4a3a0950 | ||
|
|
e423ad656a | ||
|
|
7d0d94de3a | ||
|
|
344b257db4 | ||
|
|
28b43b6e6d | ||
|
|
8d373ec9b5 | ||
|
|
537525667d | ||
|
|
519367c553 | ||
|
|
1a338ec863 | ||
|
|
01d0b8f778 | ||
|
|
d99836ec30 | ||
|
|
33751c12d2 | ||
|
|
89815e1f7f | ||
|
|
5fa5109f34 | ||
|
|
b06ab2cbf9 | ||
|
|
35fb554038 | ||
|
|
f238177277 | ||
|
|
723c0f7505 | ||
|
|
ce6936f753 | ||
|
|
2a80cbf74a | ||
|
|
098805ef25 | ||
|
|
ed90bc5a99 | ||
|
|
de9be8c7ce | ||
|
|
3e02bcf9b8 | ||
|
|
cef2ee31fb | ||
|
|
228c547839 | ||
|
|
e1fcc8fb38 | ||
|
|
3a908d51aa |
5
.dockerignore
Normal file
5
.dockerignore
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
**/node_modules/
|
||||||
|
.git
|
||||||
|
**/.venv/
|
||||||
|
**/.env*
|
||||||
|
**/.next/
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
|||||||
.env
|
.env
|
||||||
.venv/
|
.venv/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
node_modules/
|
||||||
|
*.tsbuildinfo
|
||||||
|
|||||||
@@ -32,5 +32,5 @@ NEXT_PUBLIC_HOST="http://localhost:3000"
|
|||||||
GITHUB_CLIENT_ID="your_client_id"
|
GITHUB_CLIENT_ID="your_client_id"
|
||||||
GITHUB_CLIENT_SECRET="your_secret"
|
GITHUB_CLIENT_SECRET="your_secret"
|
||||||
|
|
||||||
OPENPIPE_BASE_URL="http://localhost:3000/api"
|
OPENPIPE_BASE_URL="http://localhost:3000/api/v1"
|
||||||
OPENPIPE_API_KEY="your_key"
|
OPENPIPE_API_KEY="your_key"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const config = {
|
|||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
extends: ["plugin:@typescript-eslint/recommended-requiring-type-checking"],
|
extends: ["plugin:@typescript-eslint/recommended-requiring-type-checking"],
|
||||||
files: ["*.ts", "*.tsx"],
|
files: ["*.mts", "*.ts", "*.tsx"],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
project: path.join(__dirname, "tsconfig.json"),
|
project: path.join(__dirname, "tsconfig.json"),
|
||||||
},
|
},
|
||||||
|
|||||||
3
app/.gitignore
vendored
3
app/.gitignore
vendored
@@ -44,3 +44,6 @@ yarn-error.log*
|
|||||||
|
|
||||||
# Sentry Auth Token
|
# Sentry Auth Token
|
||||||
.sentryclirc
|
.sentryclirc
|
||||||
|
|
||||||
|
# custom openai intialization
|
||||||
|
src/server/utils/openaiCustomConfig.json
|
||||||
|
|||||||
8
app/@types/nextjs-routes.d.ts
vendored
8
app/@types/nextjs-routes.d.ts
vendored
@@ -12,19 +12,19 @@ declare module "nextjs-routes" {
|
|||||||
|
|
||||||
export type Route =
|
export type Route =
|
||||||
| StaticRoute<"/account/signin">
|
| StaticRoute<"/account/signin">
|
||||||
| DynamicRoute<"/api/[...trpc]", { "trpc": string[] }>
|
|
||||||
| DynamicRoute<"/api/auth/[...nextauth]", { "nextauth": string[] }>
|
| DynamicRoute<"/api/auth/[...nextauth]", { "nextauth": string[] }>
|
||||||
| StaticRoute<"/api/experiments/og-image">
|
| StaticRoute<"/api/experiments/og-image">
|
||||||
| StaticRoute<"/api/openapi">
|
|
||||||
| StaticRoute<"/api/sentry-example-api">
|
|
||||||
| DynamicRoute<"/api/trpc/[trpc]", { "trpc": string }>
|
| DynamicRoute<"/api/trpc/[trpc]", { "trpc": string }>
|
||||||
|
| DynamicRoute<"/api/v1/[...trpc]", { "trpc": string[] }>
|
||||||
|
| StaticRoute<"/api/v1/openapi">
|
||||||
|
| StaticRoute<"/dashboard">
|
||||||
| DynamicRoute<"/data/[id]", { "id": string }>
|
| DynamicRoute<"/data/[id]", { "id": string }>
|
||||||
| StaticRoute<"/data">
|
| StaticRoute<"/data">
|
||||||
| DynamicRoute<"/experiments/[id]", { "id": string }>
|
| DynamicRoute<"/experiments/[id]", { "id": string }>
|
||||||
| StaticRoute<"/experiments">
|
| StaticRoute<"/experiments">
|
||||||
| StaticRoute<"/">
|
| StaticRoute<"/">
|
||||||
| StaticRoute<"/logged-calls">
|
|
||||||
| StaticRoute<"/project/settings">
|
| StaticRoute<"/project/settings">
|
||||||
|
| StaticRoute<"/request-logs">
|
||||||
| StaticRoute<"/sentry-example-page">
|
| StaticRoute<"/sentry-example-page">
|
||||||
| StaticRoute<"/world-champs">
|
| StaticRoute<"/world-champs">
|
||||||
| StaticRoute<"/world-champs/signup">;
|
| StaticRoute<"/world-champs/signup">;
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ RUN yarn global add pnpm
|
|||||||
# DEPS
|
# DEPS
|
||||||
FROM base as deps
|
FROM base as deps
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /code
|
||||||
|
|
||||||
COPY prisma ./
|
COPY app/prisma app/package.json ./app/
|
||||||
|
COPY client-libs/typescript/package.json ./client-libs/typescript/
|
||||||
|
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||||
|
|
||||||
COPY package.json pnpm-lock.yaml ./
|
RUN cd app && pnpm install --frozen-lockfile
|
||||||
|
|
||||||
RUN pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
# BUILDER
|
# BUILDER
|
||||||
FROM base as builder
|
FROM base as builder
|
||||||
@@ -25,22 +25,24 @@ ARG NEXT_PUBLIC_SENTRY_DSN
|
|||||||
ARG SENTRY_AUTH_TOKEN
|
ARG SENTRY_AUTH_TOKEN
|
||||||
ARG NEXT_PUBLIC_FF_SHOW_LOGGED_CALLS
|
ARG NEXT_PUBLIC_FF_SHOW_LOGGED_CALLS
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /code
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
COPY --from=deps /code/node_modules ./node_modules
|
||||||
|
COPY --from=deps /code/app/node_modules ./app/node_modules
|
||||||
|
COPY --from=deps /code/client-libs/typescript/node_modules ./client-libs/typescript/node_modules
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN SKIP_ENV_VALIDATION=1 pnpm build
|
RUN cd app && SKIP_ENV_VALIDATION=1 pnpm build
|
||||||
|
|
||||||
# RUNNER
|
# RUNNER
|
||||||
FROM base as runner
|
FROM base as runner
|
||||||
WORKDIR /app
|
WORKDIR /code/app
|
||||||
|
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
|
||||||
COPY --from=builder /app/ ./
|
COPY --from=builder /code/ /code/
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
ENV PORT 3000
|
ENV PORT 3000
|
||||||
|
|
||||||
# Run the "run-prod.sh" script
|
# Run the "run-prod.sh" script
|
||||||
CMD /app/run-prod.sh
|
CMD /code/app/run-prod.sh
|
||||||
@@ -36,6 +36,8 @@ let config = {
|
|||||||
});
|
});
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
transpilePackages: ["openpipe"],
|
||||||
};
|
};
|
||||||
|
|
||||||
config = nextRoutes()(config);
|
config = nextRoutes()(config);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "openpipe",
|
"name": "openpipe-app",
|
||||||
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@@ -16,7 +17,7 @@
|
|||||||
"postinstall": "prisma generate",
|
"postinstall": "prisma generate",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"codegen": "tsx src/server/scripts/client-codegen.ts",
|
"codegen:clients": "tsx src/server/scripts/client-codegen.ts",
|
||||||
"seed": "tsx prisma/seed.ts",
|
"seed": "tsx prisma/seed.ts",
|
||||||
"check": "concurrently 'pnpm lint' 'pnpm tsc' 'pnpm prettier . --check'",
|
"check": "concurrently 'pnpm lint' 'pnpm tsc' 'pnpm prettier . --check'",
|
||||||
"test": "pnpm vitest"
|
"test": "pnpm vitest"
|
||||||
@@ -24,7 +25,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@anthropic-ai/sdk": "^0.5.8",
|
"@anthropic-ai/sdk": "^0.5.8",
|
||||||
"@apidevtools/json-schema-ref-parser": "^10.1.0",
|
"@apidevtools/json-schema-ref-parser": "^10.1.0",
|
||||||
"@babel/preset-typescript": "^7.22.5",
|
|
||||||
"@babel/standalone": "^7.22.9",
|
"@babel/standalone": "^7.22.9",
|
||||||
"@chakra-ui/anatomy": "^2.2.0",
|
"@chakra-ui/anatomy": "^2.2.0",
|
||||||
"@chakra-ui/next-js": "^2.1.4",
|
"@chakra-ui/next-js": "^2.1.4",
|
||||||
@@ -100,7 +100,8 @@
|
|||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"vite-tsconfig-paths": "^4.2.0",
|
"vite-tsconfig-paths": "^4.2.0",
|
||||||
"zod": "^3.21.4",
|
"zod": "^3.21.4",
|
||||||
"zustand": "^4.3.9"
|
"zustand": "^4.3.9",
|
||||||
|
"openpipe": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@openapi-contrib/openapi-schema-to-json-schema": "^4.0.5",
|
"@openapi-contrib/openapi-schema-to-json-schema": "^4.0.5",
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "LoggedCall" ADD COLUMN "model" TEXT;
|
||||||
@@ -112,17 +112,17 @@ model ScenarioVariantCell {
|
|||||||
model ModelResponse {
|
model ModelResponse {
|
||||||
id String @id @default(uuid()) @db.Uuid
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
|
||||||
cacheKey String
|
cacheKey String
|
||||||
requestedAt DateTime?
|
requestedAt DateTime?
|
||||||
receivedAt DateTime?
|
receivedAt DateTime?
|
||||||
respPayload Json?
|
respPayload Json?
|
||||||
cost Float?
|
cost Float?
|
||||||
inputTokens Int?
|
inputTokens Int?
|
||||||
outputTokens Int?
|
outputTokens Int?
|
||||||
statusCode Int?
|
statusCode Int?
|
||||||
errorMessage String?
|
errorMessage String?
|
||||||
retryTime DateTime?
|
retryTime DateTime?
|
||||||
outdated Boolean @default(false)
|
outdated Boolean @default(false)
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
@@ -273,7 +273,8 @@ model LoggedCall {
|
|||||||
projectId String @db.Uuid
|
projectId String @db.Uuid
|
||||||
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
tags LoggedCallTag[]
|
model String?
|
||||||
|
tags LoggedCallTag[]
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
@@ -294,7 +295,7 @@ model LoggedCallModelResponse {
|
|||||||
errorMessage String?
|
errorMessage String?
|
||||||
|
|
||||||
requestedAt DateTime
|
requestedAt DateTime
|
||||||
receivedAt DateTime
|
receivedAt DateTime
|
||||||
|
|
||||||
// Note: the function to calculate the cacheKey should include the project
|
// 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
|
// ID so we don't share cached responses between projects, which could be an
|
||||||
@@ -339,8 +340,8 @@ model ApiKey {
|
|||||||
name String
|
name String
|
||||||
apiKey String @unique
|
apiKey String @unique
|
||||||
|
|
||||||
projectId String @db.Uuid
|
projectId String @db.Uuid
|
||||||
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { prisma } from "~/server/db";
|
|||||||
import dedent from "dedent";
|
import dedent from "dedent";
|
||||||
import { generateNewCell } from "~/server/utils/generateNewCell";
|
import { generateNewCell } from "~/server/utils/generateNewCell";
|
||||||
import { promptConstructorVersion } from "~/promptConstructor/version";
|
import { promptConstructorVersion } from "~/promptConstructor/version";
|
||||||
|
import { env } from "~/env.mjs";
|
||||||
|
|
||||||
const defaultId = "11111111-1111-1111-1111-111111111111";
|
const defaultId = "11111111-1111-1111-1111-111111111111";
|
||||||
|
|
||||||
@@ -16,6 +17,16 @@ const project =
|
|||||||
data: { id: defaultId },
|
data: { id: defaultId },
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
if (env.OPENPIPE_API_KEY) {
|
||||||
|
await prisma.apiKey.create({
|
||||||
|
data: {
|
||||||
|
projectId: project.id,
|
||||||
|
name: "Default API Key",
|
||||||
|
apiKey: env.OPENPIPE_API_KEY,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await prisma.experiment.deleteMany({
|
await prisma.experiment.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
id: defaultId,
|
id: defaultId,
|
||||||
|
|||||||
@@ -33,25 +33,11 @@ export default function AddVariantButton() {
|
|||||||
<Flex w="100%" justifyContent="flex-end">
|
<Flex w="100%" justifyContent="flex-end">
|
||||||
<ActionButton
|
<ActionButton
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
py={5}
|
py={7}
|
||||||
leftIcon={<Icon as={loading ? Spinner : BsPlus} boxSize={6} mr={loading ? 1 : 0} />}
|
leftIcon={<Icon as={loading ? Spinner : BsPlus} boxSize={6} mr={loading ? 1 : 0} />}
|
||||||
>
|
>
|
||||||
<Text display={{ base: "none", md: "flex" }}>Add Variant</Text>
|
<Text display={{ base: "none", md: "flex" }}>Add Variant</Text>
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
{/* <Button
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
fontWeight="normal"
|
|
||||||
bgColor="transparent"
|
|
||||||
_hover={{ bgColor: "gray.100" }}
|
|
||||||
px={cellPadding.x}
|
|
||||||
onClick={onClick}
|
|
||||||
height="unset"
|
|
||||||
minH={headerMinHeight}
|
|
||||||
>
|
|
||||||
<Icon as={loading ? Spinner : BsPlus} boxSize={6} mr={loading ? 1 : 0} />
|
|
||||||
<Text display={{ base: "none", md: "flex" }}>Add Variant</Text>
|
|
||||||
</Button> */}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default function OutputCell({
|
|||||||
|
|
||||||
if (!templateHasVariables) disabledReason = "Add a value to the scenario variables to see output";
|
if (!templateHasVariables) disabledReason = "Add a value to the scenario variables to see output";
|
||||||
|
|
||||||
const [refetchInterval, setRefetchInterval] = useState(0);
|
const [refetchInterval, setRefetchInterval] = useState<number | false>(false);
|
||||||
const { data: cell, isLoading: queryLoading } = api.scenarioVariantCells.get.useQuery(
|
const { data: cell, isLoading: queryLoading } = api.scenarioVariantCells.get.useQuery(
|
||||||
{ scenarioId: scenario.id, variantId: variant.id },
|
{ scenarioId: scenario.id, variantId: variant.id },
|
||||||
{ refetchInterval },
|
{ refetchInterval },
|
||||||
@@ -64,7 +64,8 @@ export default function OutputCell({
|
|||||||
cell.retrievalStatus === "PENDING" ||
|
cell.retrievalStatus === "PENDING" ||
|
||||||
cell.retrievalStatus === "IN_PROGRESS" ||
|
cell.retrievalStatus === "IN_PROGRESS" ||
|
||||||
hardRefetching;
|
hardRefetching;
|
||||||
useEffect(() => setRefetchInterval(awaitingOutput ? 1000 : 0), [awaitingOutput]);
|
|
||||||
|
useEffect(() => setRefetchInterval(awaitingOutput ? 1000 : false), [awaitingOutput]);
|
||||||
|
|
||||||
// TODO: disconnect from socket if we're not streaming anymore
|
// TODO: disconnect from socket if we're not streaming anymore
|
||||||
const streamedMessage = useSocket<OutputSchema>(cell?.id);
|
const streamedMessage = useSocket<OutputSchema>(cell?.id);
|
||||||
@@ -120,8 +121,13 @@ export default function OutputCell({
|
|||||||
? response.receivedAt.getTime()
|
? response.receivedAt.getTime()
|
||||||
: Date.now();
|
: Date.now();
|
||||||
if (response.requestedAt) {
|
if (response.requestedAt) {
|
||||||
numWaitingMessages = Math.floor(
|
numWaitingMessages = Math.min(
|
||||||
(relativeWaitingTime - response.requestedAt.getTime()) / WAITING_MESSAGE_INTERVAL,
|
Math.floor(
|
||||||
|
(relativeWaitingTime - response.requestedAt.getTime()) / WAITING_MESSAGE_INTERVAL,
|
||||||
|
),
|
||||||
|
// Don't try to render more than 15, it'll use too much CPU and
|
||||||
|
// break the page
|
||||||
|
15,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,21 +1,16 @@
|
|||||||
|
import { type StackProps } from "@chakra-ui/react";
|
||||||
|
|
||||||
import { useScenarios } from "~/utils/hooks";
|
import { useScenarios } from "~/utils/hooks";
|
||||||
import Paginator from "../Paginator";
|
import Paginator from "../Paginator";
|
||||||
|
|
||||||
const ScenarioPaginator = () => {
|
const ScenarioPaginator = (props: StackProps) => {
|
||||||
const { data } = useScenarios();
|
const { data } = useScenarios();
|
||||||
|
|
||||||
if (!data) return null;
|
if (!data) return null;
|
||||||
|
|
||||||
const { scenarios, startIndex, lastPage, count } = data;
|
const { count } = data;
|
||||||
|
|
||||||
return (
|
return <Paginator count={count} condense {...props} />;
|
||||||
<Paginator
|
|
||||||
numItemsLoaded={scenarios.length}
|
|
||||||
startIndex={startIndex}
|
|
||||||
lastPage={lastPage}
|
|
||||||
count={count}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ScenarioPaginator;
|
export default ScenarioPaginator;
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ const ScenarioRow = (props: {
|
|||||||
variants: PromptVariant[];
|
variants: PromptVariant[];
|
||||||
canHide: boolean;
|
canHide: boolean;
|
||||||
rowStart: number;
|
rowStart: number;
|
||||||
|
isFirst: boolean;
|
||||||
|
isLast: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
|
||||||
@@ -21,10 +23,14 @@ const ScenarioRow = (props: {
|
|||||||
onMouseEnter={() => setIsHovered(true)}
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
onMouseLeave={() => setIsHovered(false)}
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
sx={isHovered ? highlightStyle : undefined}
|
sx={isHovered ? highlightStyle : undefined}
|
||||||
borderLeftWidth={1}
|
bgColor="white"
|
||||||
{...borders}
|
|
||||||
rowStart={props.rowStart}
|
rowStart={props.rowStart}
|
||||||
colStart={1}
|
colStart={1}
|
||||||
|
borderLeftWidth={1}
|
||||||
|
borderTopWidth={props.isFirst ? 1 : 0}
|
||||||
|
borderTopLeftRadius={props.isFirst ? 8 : 0}
|
||||||
|
borderBottomLeftRadius={props.isLast ? 8 : 0}
|
||||||
|
{...borders}
|
||||||
>
|
>
|
||||||
<ScenarioEditor scenario={props.scenario} hovered={isHovered} canHide={props.canHide} />
|
<ScenarioEditor scenario={props.scenario} hovered={isHovered} canHide={props.canHide} />
|
||||||
</GridItem>
|
</GridItem>
|
||||||
@@ -34,8 +40,12 @@ const ScenarioRow = (props: {
|
|||||||
onMouseEnter={() => setIsHovered(true)}
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
onMouseLeave={() => setIsHovered(false)}
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
sx={isHovered ? highlightStyle : undefined}
|
sx={isHovered ? highlightStyle : undefined}
|
||||||
|
bgColor="white"
|
||||||
rowStart={props.rowStart}
|
rowStart={props.rowStart}
|
||||||
colStart={i + 2}
|
colStart={i + 2}
|
||||||
|
borderTopWidth={props.isFirst ? 1 : 0}
|
||||||
|
borderTopRightRadius={props.isFirst && i === props.variants.length - 1 ? 8 : 0}
|
||||||
|
borderBottomRightRadius={props.isLast && i === props.variants.length - 1 ? 8 : 0}
|
||||||
{...borders}
|
{...borders}
|
||||||
>
|
>
|
||||||
<OutputCell key={variant.id} scenario={props.scenario} variant={variant} />
|
<OutputCell key={variant.id} scenario={props.scenario} variant={variant} />
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export const ScenariosHeader = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack w="100%" pb={cellPadding.y} pt={0} align="center" spacing={0}>
|
<HStack w="100%" py={cellPadding.y} px={cellPadding.x} align="center" spacing={0}>
|
||||||
<Text fontSize={16} fontWeight="bold">
|
<Text fontSize={16} fontWeight="bold">
|
||||||
Scenarios ({scenarios.data?.count})
|
Scenarios ({scenarios.data?.count})
|
||||||
</Text>
|
</Text>
|
||||||
@@ -57,11 +57,16 @@ export const ScenariosHeader = () => {
|
|||||||
<MenuButton
|
<MenuButton
|
||||||
as={IconButton}
|
as={IconButton}
|
||||||
mt={1}
|
mt={1}
|
||||||
|
ml={2}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
aria-label="Edit Scenarios"
|
aria-label="Edit Scenarios"
|
||||||
icon={<Icon as={loading ? Spinner : BsGear} />}
|
icon={<Icon as={loading ? Spinner : BsGear} />}
|
||||||
|
maxW={8}
|
||||||
|
minW={8}
|
||||||
|
minH={8}
|
||||||
|
maxH={8}
|
||||||
/>
|
/>
|
||||||
<MenuList fontSize="md" zIndex="dropdown" mt={-3}>
|
<MenuList fontSize="md" zIndex="dropdown" mt={-1}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<Icon as={BsPlus} boxSize={6} mx="-5px" />}
|
icon={<Icon as={BsPlus} boxSize={6} mx="-5px" />}
|
||||||
onClick={() => onAddScenario(false)}
|
onClick={() => onAddScenario(false)}
|
||||||
|
|||||||
@@ -21,14 +21,18 @@ export default function VariantStats(props: { variant: PromptVariant }) {
|
|||||||
outputTokens: 0,
|
outputTokens: 0,
|
||||||
scenarioCount: 0,
|
scenarioCount: 0,
|
||||||
outputCount: 0,
|
outputCount: 0,
|
||||||
|
awaitingCompletions: false,
|
||||||
awaitingEvals: false,
|
awaitingEvals: false,
|
||||||
},
|
},
|
||||||
refetchInterval,
|
refetchInterval,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Poll every two seconds while we are waiting for LLM retrievals to finish
|
// Poll every five seconds while we are waiting for LLM retrievals to finish
|
||||||
useEffect(() => setRefetchInterval(data.awaitingEvals ? 5000 : 0), [data.awaitingEvals]);
|
useEffect(
|
||||||
|
() => setRefetchInterval(data.awaitingCompletions || data.awaitingEvals ? 5000 : 0),
|
||||||
|
[data.awaitingCompletions, data.awaitingEvals],
|
||||||
|
);
|
||||||
|
|
||||||
const [passColor, neutralColor, failColor] = useToken("colors", [
|
const [passColor, neutralColor, failColor] = useToken("colors", [
|
||||||
"green.500",
|
"green.500",
|
||||||
|
|||||||
@@ -53,20 +53,29 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
|
|||||||
colStart: i + 2,
|
colStart: i + 2,
|
||||||
borderLeftWidth: i === 0 ? 1 : 0,
|
borderLeftWidth: i === 0 ? 1 : 0,
|
||||||
marginLeft: i === 0 ? "-1px" : 0,
|
marginLeft: i === 0 ? "-1px" : 0,
|
||||||
backgroundColor: "gray.100",
|
backgroundColor: "white",
|
||||||
};
|
};
|
||||||
|
const isFirst = i === 0;
|
||||||
|
const isLast = i === variants.data.length - 1;
|
||||||
return (
|
return (
|
||||||
<Fragment key={variant.uiId}>
|
<Fragment key={variant.uiId}>
|
||||||
<VariantHeader
|
<VariantHeader
|
||||||
variant={variant}
|
variant={variant}
|
||||||
canHide={variants.data.length > 1}
|
canHide={variants.data.length > 1}
|
||||||
rowStart={1}
|
rowStart={1}
|
||||||
|
borderTopLeftRadius={isFirst ? 8 : 0}
|
||||||
|
borderTopRightRadius={isLast ? 8 : 0}
|
||||||
{...sharedProps}
|
{...sharedProps}
|
||||||
/>
|
/>
|
||||||
<GridItem rowStart={2} {...sharedProps}>
|
<GridItem rowStart={2} {...sharedProps}>
|
||||||
<VariantEditor variant={variant} />
|
<VariantEditor variant={variant} />
|
||||||
</GridItem>
|
</GridItem>
|
||||||
<GridItem rowStart={3} {...sharedProps}>
|
<GridItem
|
||||||
|
rowStart={3}
|
||||||
|
{...sharedProps}
|
||||||
|
borderBottomLeftRadius={isFirst ? 8 : 0}
|
||||||
|
borderBottomRightRadius={isLast ? 8 : 0}
|
||||||
|
>
|
||||||
<VariantStats variant={variant} />
|
<VariantStats variant={variant} />
|
||||||
</GridItem>
|
</GridItem>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
@@ -77,7 +86,6 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
|
|||||||
colSpan={allCols - 1}
|
colSpan={allCols - 1}
|
||||||
rowStart={variantHeaderRows + 1}
|
rowStart={variantHeaderRows + 1}
|
||||||
colStart={1}
|
colStart={1}
|
||||||
{...borders}
|
|
||||||
borderRightWidth={0}
|
borderRightWidth={0}
|
||||||
>
|
>
|
||||||
<ScenariosHeader />
|
<ScenariosHeader />
|
||||||
@@ -90,6 +98,8 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
|
|||||||
scenario={scenario}
|
scenario={scenario}
|
||||||
variants={variants.data}
|
variants={variants.data}
|
||||||
canHide={visibleScenariosCount > 1}
|
canHide={visibleScenariosCount > 1}
|
||||||
|
isFirst={i === 0}
|
||||||
|
isLast={i === visibleScenariosCount - 1}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<GridItem
|
<GridItem
|
||||||
|
|||||||
@@ -1,77 +1,117 @@
|
|||||||
import { Box, HStack, IconButton } from "@chakra-ui/react";
|
import { HStack, IconButton, Text, Select, type StackProps, Icon } from "@chakra-ui/react";
|
||||||
import {
|
import React, { useCallback } from "react";
|
||||||
BsChevronDoubleLeft,
|
import { FiChevronsLeft, FiChevronsRight, FiChevronLeft, FiChevronRight } from "react-icons/fi";
|
||||||
BsChevronDoubleRight,
|
import { usePageParams } from "~/utils/hooks";
|
||||||
BsChevronLeft,
|
|
||||||
BsChevronRight,
|
const pageSizeOptions = [10, 25, 50, 100];
|
||||||
} from "react-icons/bs";
|
|
||||||
import { usePage } from "~/utils/hooks";
|
|
||||||
|
|
||||||
const Paginator = ({
|
const Paginator = ({
|
||||||
numItemsLoaded,
|
|
||||||
startIndex,
|
|
||||||
lastPage,
|
|
||||||
count,
|
count,
|
||||||
}: {
|
condense,
|
||||||
numItemsLoaded: number;
|
...props
|
||||||
startIndex: number;
|
}: { count: number; condense?: boolean } & StackProps) => {
|
||||||
lastPage: number;
|
const { page, pageSize, setPageParams } = usePageParams();
|
||||||
count: number;
|
|
||||||
}) => {
|
const lastPage = Math.ceil(count / pageSize);
|
||||||
const [page, setPage] = usePage();
|
|
||||||
|
const updatePageSize = useCallback(
|
||||||
|
(newPageSize: number) => {
|
||||||
|
const newPage = Math.floor(((page - 1) * pageSize) / newPageSize) + 1;
|
||||||
|
setPageParams({ page: newPage, pageSize: newPageSize }, "replace");
|
||||||
|
},
|
||||||
|
[page, pageSize, setPageParams],
|
||||||
|
);
|
||||||
|
|
||||||
const nextPage = () => {
|
const nextPage = () => {
|
||||||
if (page < lastPage) {
|
if (page < lastPage) {
|
||||||
setPage(page + 1, "replace");
|
setPageParams({ page: page + 1 }, "replace");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const prevPage = () => {
|
const prevPage = () => {
|
||||||
if (page > 1) {
|
if (page > 1) {
|
||||||
setPage(page - 1, "replace");
|
setPageParams({ page: page - 1 }, "replace");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const goToLastPage = () => setPage(lastPage, "replace");
|
const goToLastPage = () => setPageParams({ page: lastPage }, "replace");
|
||||||
const goToFirstPage = () => setPage(1, "replace");
|
const goToFirstPage = () => setPageParams({ page: 1 }, "replace");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack pt={4}>
|
<HStack
|
||||||
<IconButton
|
pt={4}
|
||||||
variant="ghost"
|
spacing={8}
|
||||||
size="sm"
|
justifyContent={condense ? "flex-start" : "space-between"}
|
||||||
onClick={goToFirstPage}
|
alignItems="center"
|
||||||
isDisabled={page === 1}
|
w="full"
|
||||||
aria-label="Go to first page"
|
{...props}
|
||||||
icon={<BsChevronDoubleLeft />}
|
>
|
||||||
/>
|
{!condense && (
|
||||||
<IconButton
|
<>
|
||||||
variant="ghost"
|
<HStack>
|
||||||
size="sm"
|
<Text>Rows</Text>
|
||||||
onClick={prevPage}
|
<Select
|
||||||
isDisabled={page === 1}
|
value={pageSize}
|
||||||
aria-label="Previous page"
|
onChange={(e) => updatePageSize(parseInt(e.target.value))}
|
||||||
icon={<BsChevronLeft />}
|
w={20}
|
||||||
/>
|
backgroundColor="white"
|
||||||
<Box>
|
>
|
||||||
{startIndex}-{startIndex + numItemsLoaded - 1} / {count}
|
{pageSizeOptions.map((option) => (
|
||||||
</Box>
|
<option key={option} value={option}>
|
||||||
<IconButton
|
{option}
|
||||||
variant="ghost"
|
</option>
|
||||||
size="sm"
|
))}
|
||||||
onClick={nextPage}
|
</Select>
|
||||||
isDisabled={page === lastPage}
|
</HStack>
|
||||||
aria-label="Next page"
|
<Text>
|
||||||
icon={<BsChevronRight />}
|
Page {page} of {lastPage}
|
||||||
/>
|
</Text>
|
||||||
<IconButton
|
</>
|
||||||
variant="ghost"
|
)}
|
||||||
size="sm"
|
|
||||||
onClick={goToLastPage}
|
<HStack>
|
||||||
isDisabled={page === lastPage}
|
<IconButton
|
||||||
aria-label="Go to last page"
|
variant="outline"
|
||||||
icon={<BsChevronDoubleRight />}
|
size="sm"
|
||||||
/>
|
onClick={goToFirstPage}
|
||||||
|
isDisabled={page === 1}
|
||||||
|
aria-label="Go to first page"
|
||||||
|
icon={<Icon as={FiChevronsLeft} boxSize={5} strokeWidth={1.5} />}
|
||||||
|
bgColor="white"
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={prevPage}
|
||||||
|
isDisabled={page === 1}
|
||||||
|
aria-label="Previous page"
|
||||||
|
icon={<Icon as={FiChevronLeft} boxSize={5} strokeWidth={1.5} />}
|
||||||
|
bgColor="white"
|
||||||
|
/>
|
||||||
|
{condense && (
|
||||||
|
<Text>
|
||||||
|
Page {page} of {lastPage}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
<IconButton
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={nextPage}
|
||||||
|
isDisabled={page === lastPage}
|
||||||
|
aria-label="Next page"
|
||||||
|
icon={<Icon as={FiChevronRight} boxSize={5} strokeWidth={1.5} />}
|
||||||
|
bgColor="white"
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={goToLastPage}
|
||||||
|
isDisabled={page === lastPage}
|
||||||
|
aria-label="Go to last page"
|
||||||
|
icon={<Icon as={FiChevronsRight} boxSize={5} strokeWidth={1.5} />}
|
||||||
|
bgColor="white"
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export default function VariantHeader(
|
|||||||
padding={0}
|
padding={0}
|
||||||
sx={{
|
sx={{
|
||||||
position: "sticky",
|
position: "sticky",
|
||||||
top: "0",
|
top: "-2",
|
||||||
// Ensure that the menu always appears above the sticky header of other variants
|
// Ensure that the menu always appears above the sticky header of other variants
|
||||||
zIndex: menuOpen ? "dropdown" : 10,
|
zIndex: menuOpen ? "dropdown" : 10,
|
||||||
}}
|
}}
|
||||||
@@ -84,6 +84,7 @@ export default function VariantHeader(
|
|||||||
>
|
>
|
||||||
<HStack
|
<HStack
|
||||||
spacing={2}
|
spacing={2}
|
||||||
|
py={2}
|
||||||
alignItems="flex-start"
|
alignItems="flex-start"
|
||||||
minH={headerMinHeight}
|
minH={headerMinHeight}
|
||||||
draggable={!isInputHovered}
|
draggable={!isInputHovered}
|
||||||
@@ -102,7 +103,9 @@ export default function VariantHeader(
|
|||||||
setIsDragTarget(false);
|
setIsDragTarget(false);
|
||||||
}}
|
}}
|
||||||
onDrop={onReorder}
|
onDrop={onReorder}
|
||||||
backgroundColor={isDragTarget ? "gray.200" : "gray.100"}
|
backgroundColor={isDragTarget ? "gray.200" : "white"}
|
||||||
|
borderTopLeftRadius={gridItemProps.borderTopLeftRadius}
|
||||||
|
borderTopRightRadius={gridItemProps.borderTopRightRadius}
|
||||||
h="full"
|
h="full"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
|
|||||||
@@ -1,209 +0,0 @@
|
|||||||
import {
|
|
||||||
Box,
|
|
||||||
Card,
|
|
||||||
CardHeader,
|
|
||||||
Heading,
|
|
||||||
Table,
|
|
||||||
Tbody,
|
|
||||||
Td,
|
|
||||||
Th,
|
|
||||||
Thead,
|
|
||||||
Tr,
|
|
||||||
Tooltip,
|
|
||||||
Collapse,
|
|
||||||
HStack,
|
|
||||||
VStack,
|
|
||||||
IconButton,
|
|
||||||
useToast,
|
|
||||||
Icon,
|
|
||||||
Button,
|
|
||||||
ButtonGroup,
|
|
||||||
} from "@chakra-ui/react";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
import relativeTime from "dayjs/plugin/relativeTime";
|
|
||||||
import { ChevronUpIcon, ChevronDownIcon, CopyIcon } from "lucide-react";
|
|
||||||
import { useMemo, useState } from "react";
|
|
||||||
import { type RouterOutputs, api } from "~/utils/api";
|
|
||||||
import SyntaxHighlighter from "react-syntax-highlighter";
|
|
||||||
import { atelierCaveLight } from "react-syntax-highlighter/dist/cjs/styles/hljs";
|
|
||||||
import stringify from "json-stringify-pretty-compact";
|
|
||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
dayjs.extend(relativeTime);
|
|
||||||
|
|
||||||
type LoggedCall = RouterOutputs["dashboard"]["loggedCalls"][0];
|
|
||||||
|
|
||||||
const FormattedJson = ({ json }: { json: any }) => {
|
|
||||||
const jsonString = stringify(json, { maxLength: 40 });
|
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
const copyToClipboard = async (text: string) => {
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(text);
|
|
||||||
toast({
|
|
||||||
title: "Copied to clipboard",
|
|
||||||
status: "success",
|
|
||||||
duration: 2000,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
toast({
|
|
||||||
title: "Failed to copy to clipboard",
|
|
||||||
status: "error",
|
|
||||||
duration: 2000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box position="relative" fontSize="sm" borderRadius="md" overflow="hidden">
|
|
||||||
<SyntaxHighlighter
|
|
||||||
customStyle={{ overflowX: "unset" }}
|
|
||||||
language="json"
|
|
||||||
style={atelierCaveLight}
|
|
||||||
lineProps={{
|
|
||||||
style: { wordBreak: "break-all", whiteSpace: "pre-wrap" },
|
|
||||||
}}
|
|
||||||
wrapLines
|
|
||||||
>
|
|
||||||
{jsonString}
|
|
||||||
</SyntaxHighlighter>
|
|
||||||
<IconButton
|
|
||||||
aria-label="Copy"
|
|
||||||
icon={<CopyIcon />}
|
|
||||||
position="absolute"
|
|
||||||
top={1}
|
|
||||||
right={1}
|
|
||||||
size="xs"
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() => void copyToClipboard(jsonString)}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
function TableRow({
|
|
||||||
loggedCall,
|
|
||||||
isExpanded,
|
|
||||||
onToggle,
|
|
||||||
}: {
|
|
||||||
loggedCall: LoggedCall;
|
|
||||||
isExpanded: boolean;
|
|
||||||
onToggle: () => void;
|
|
||||||
}) {
|
|
||||||
const isError = loggedCall.modelResponse?.statusCode !== 200;
|
|
||||||
const timeAgo = dayjs(loggedCall.requestedAt).fromNow();
|
|
||||||
const fullTime = dayjs(loggedCall.requestedAt).toString();
|
|
||||||
|
|
||||||
const model = useMemo(
|
|
||||||
() => loggedCall.tags.find((tag) => tag.name.startsWith("$model"))?.value,
|
|
||||||
[loggedCall.tags],
|
|
||||||
);
|
|
||||||
|
|
||||||
const durationCell = (
|
|
||||||
<Td isNumeric>
|
|
||||||
{loggedCall.cacheHit
|
|
||||||
? "Cache hit"
|
|
||||||
: ((loggedCall.modelResponse?.durationMs ?? 0) / 1000).toFixed(2) + "s"}
|
|
||||||
</Td>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Tr
|
|
||||||
onClick={onToggle}
|
|
||||||
key={loggedCall.id}
|
|
||||||
_hover={{ bgColor: "gray.100", cursor: "pointer" }}
|
|
||||||
sx={{
|
|
||||||
"> td": { borderBottom: "none" },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Td>
|
|
||||||
<Icon boxSize={6} as={isExpanded ? ChevronUpIcon : ChevronDownIcon} />
|
|
||||||
</Td>
|
|
||||||
<Td>
|
|
||||||
<Tooltip label={fullTime} placement="top">
|
|
||||||
<Box whiteSpace="nowrap" minW="120px">
|
|
||||||
{timeAgo}
|
|
||||||
</Box>
|
|
||||||
</Tooltip>
|
|
||||||
</Td>
|
|
||||||
<Td width="100%">{model}</Td>
|
|
||||||
{durationCell}
|
|
||||||
<Td isNumeric>{loggedCall.modelResponse?.inputTokens}</Td>
|
|
||||||
<Td isNumeric>{loggedCall.modelResponse?.outputTokens}</Td>
|
|
||||||
<Td sx={{ color: isError ? "red.500" : "green.500", fontWeight: "semibold" }} isNumeric>
|
|
||||||
{loggedCall.modelResponse?.statusCode ?? "No response"}
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
<Tr>
|
|
||||||
<Td colSpan={8} p={0}>
|
|
||||||
<Collapse in={isExpanded} unmountOnExit={true}>
|
|
||||||
<VStack p={4} align="stretch">
|
|
||||||
<HStack align="stretch">
|
|
||||||
<VStack flex={1} align="stretch">
|
|
||||||
<Heading size="sm">Input</Heading>
|
|
||||||
<FormattedJson json={loggedCall.modelResponse?.reqPayload} />
|
|
||||||
</VStack>
|
|
||||||
<VStack flex={1} align="stretch">
|
|
||||||
<Heading size="sm">Output</Heading>
|
|
||||||
<FormattedJson json={loggedCall.modelResponse?.respPayload} />
|
|
||||||
</VStack>
|
|
||||||
</HStack>
|
|
||||||
<ButtonGroup alignSelf="flex-end">
|
|
||||||
<Button as={Link} colorScheme="blue" href={{ pathname: "/experiments" }}>
|
|
||||||
Experiments
|
|
||||||
</Button>
|
|
||||||
</ButtonGroup>
|
|
||||||
</VStack>
|
|
||||||
</Collapse>
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function LoggedCallTable() {
|
|
||||||
const [expandedRow, setExpandedRow] = useState<string | null>(null);
|
|
||||||
const loggedCalls = api.dashboard.loggedCalls.useQuery({});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card variant="outline" width="100%" overflow="hidden">
|
|
||||||
<CardHeader>
|
|
||||||
<Heading as="h3" size="sm">
|
|
||||||
Logged Calls
|
|
||||||
</Heading>
|
|
||||||
</CardHeader>
|
|
||||||
<Table>
|
|
||||||
<Thead>
|
|
||||||
<Tr>
|
|
||||||
<Th />
|
|
||||||
<Th>Time</Th>
|
|
||||||
<Th>Model</Th>
|
|
||||||
<Th isNumeric>Duration</Th>
|
|
||||||
<Th isNumeric>Input tokens</Th>
|
|
||||||
<Th isNumeric>Output tokens</Th>
|
|
||||||
<Th isNumeric>Status</Th>
|
|
||||||
</Tr>
|
|
||||||
</Thead>
|
|
||||||
<Tbody>
|
|
||||||
{loggedCalls.data?.map((loggedCall) => {
|
|
||||||
return (
|
|
||||||
<TableRow
|
|
||||||
key={loggedCall.id}
|
|
||||||
loggedCall={loggedCall}
|
|
||||||
isExpanded={loggedCall.id === expandedRow}
|
|
||||||
onToggle={() => {
|
|
||||||
if (loggedCall.id === expandedRow) {
|
|
||||||
setExpandedRow(null);
|
|
||||||
} else {
|
|
||||||
setExpandedRow(loggedCall.id);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Tbody>
|
|
||||||
</Table>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
46
app/src/components/dashboard/LoggedCallsTable.tsx
Normal file
46
app/src/components/dashboard/LoggedCallsTable.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { Card, CardHeader, Heading, Table, Tbody, HStack, Button, Text } from "@chakra-ui/react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useLoggedCalls } from "~/utils/hooks";
|
||||||
|
import { TableHeader, TableRow } from "../requestLogs/TableRow";
|
||||||
|
|
||||||
|
export default function LoggedCallsTable() {
|
||||||
|
const [expandedRow, setExpandedRow] = useState<string | null>(null);
|
||||||
|
const { data: loggedCalls } = useLoggedCalls();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card width="100%" overflow="hidden">
|
||||||
|
<CardHeader>
|
||||||
|
<HStack justifyContent="space-between">
|
||||||
|
<Heading as="h3" size="sm">
|
||||||
|
Request Logs
|
||||||
|
</Heading>
|
||||||
|
<Button as={Link} href="/request-logs" variant="ghost" colorScheme="blue">
|
||||||
|
<Text>View All</Text>
|
||||||
|
</Button>
|
||||||
|
</HStack>
|
||||||
|
</CardHeader>
|
||||||
|
<Table>
|
||||||
|
<TableHeader />
|
||||||
|
<Tbody>
|
||||||
|
{loggedCalls?.calls.map((loggedCall) => {
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
key={loggedCall.id}
|
||||||
|
loggedCall={loggedCall}
|
||||||
|
isExpanded={loggedCall.id === expandedRow}
|
||||||
|
onToggle={() => {
|
||||||
|
if (loggedCall.id === expandedRow) {
|
||||||
|
setExpandedRow(null);
|
||||||
|
} else {
|
||||||
|
setExpandedRow(loggedCall.id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Tbody>
|
||||||
|
</Table>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,21 +1,16 @@
|
|||||||
|
import { type StackProps } from "@chakra-ui/react";
|
||||||
|
|
||||||
import { useDatasetEntries } from "~/utils/hooks";
|
import { useDatasetEntries } from "~/utils/hooks";
|
||||||
import Paginator from "../Paginator";
|
import Paginator from "../Paginator";
|
||||||
|
|
||||||
const DatasetEntriesPaginator = () => {
|
const DatasetEntriesPaginator = (props: StackProps) => {
|
||||||
const { data } = useDatasetEntries();
|
const { data } = useDatasetEntries();
|
||||||
|
|
||||||
if (!data) return null;
|
if (!data) return null;
|
||||||
|
|
||||||
const { entries, startIndex, lastPage, count } = data;
|
const { count } = data;
|
||||||
|
|
||||||
return (
|
return <Paginator count={count} {...props} />;
|
||||||
<Paginator
|
|
||||||
numItemsLoaded={entries.length}
|
|
||||||
startIndex={startIndex}
|
|
||||||
lastPage={lastPage}
|
|
||||||
count={count}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DatasetEntriesPaginator;
|
export default DatasetEntriesPaginator;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Spinner,
|
Spinner,
|
||||||
AspectRatio,
|
AspectRatio,
|
||||||
SkeletonText,
|
SkeletonText,
|
||||||
|
Card,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { RiFlaskLine } from "react-icons/ri";
|
import { RiFlaskLine } from "react-icons/ri";
|
||||||
import { formatTimePast } from "~/utils/dayjs";
|
import { formatTimePast } from "~/utils/dayjs";
|
||||||
@@ -29,17 +30,22 @@ type ExperimentData = {
|
|||||||
|
|
||||||
export const ExperimentCard = ({ exp }: { exp: ExperimentData }) => {
|
export const ExperimentCard = ({ exp }: { exp: ExperimentData }) => {
|
||||||
return (
|
return (
|
||||||
<AspectRatio ratio={1.2} w="full">
|
<Card
|
||||||
|
w="full"
|
||||||
|
h="full"
|
||||||
|
cursor="pointer"
|
||||||
|
p={4}
|
||||||
|
bg="white"
|
||||||
|
borderRadius={4}
|
||||||
|
_hover={{ bg: "gray.100" }}
|
||||||
|
transition="background 0.2s"
|
||||||
|
aspectRatio={1.2}
|
||||||
|
>
|
||||||
<VStack
|
<VStack
|
||||||
as={Link}
|
as={Link}
|
||||||
|
w="full"
|
||||||
|
h="full"
|
||||||
href={{ pathname: "/experiments/[id]", query: { id: exp.id } }}
|
href={{ pathname: "/experiments/[id]", query: { id: exp.id } }}
|
||||||
bg="gray.50"
|
|
||||||
_hover={{ bg: "gray.100" }}
|
|
||||||
transition="background 0.2s"
|
|
||||||
cursor="pointer"
|
|
||||||
borderColor="gray.200"
|
|
||||||
borderWidth={1}
|
|
||||||
p={4}
|
|
||||||
justify="space-between"
|
justify="space-between"
|
||||||
>
|
>
|
||||||
<HStack w="full" color="gray.700" justify="center">
|
<HStack w="full" color="gray.700" justify="center">
|
||||||
@@ -57,7 +63,7 @@ export const ExperimentCard = ({ exp }: { exp: ExperimentData }) => {
|
|||||||
<Text flex={1}>Updated {formatTimePast(exp.updatedAt)}</Text>
|
<Text flex={1}>Updated {formatTimePast(exp.updatedAt)}</Text>
|
||||||
</HStack>
|
</HStack>
|
||||||
</VStack>
|
</VStack>
|
||||||
</AspectRatio>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -89,30 +95,30 @@ export const NewExperimentCard = () => {
|
|||||||
}, [createMutation, router, selectedProjectId]);
|
}, [createMutation, router, selectedProjectId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AspectRatio ratio={1.2} w="full">
|
<Card
|
||||||
<VStack
|
w="full"
|
||||||
align="center"
|
h="full"
|
||||||
justify="center"
|
cursor="pointer"
|
||||||
_hover={{ cursor: "pointer", bg: "gray.50" }}
|
p={4}
|
||||||
transition="background 0.2s"
|
bg="white"
|
||||||
cursor="pointer"
|
borderRadius={4}
|
||||||
borderColor="gray.200"
|
_hover={{ bg: "gray.100" }}
|
||||||
borderWidth={1}
|
transition="background 0.2s"
|
||||||
p={4}
|
aspectRatio={1.2}
|
||||||
onClick={createExperiment}
|
>
|
||||||
>
|
<VStack align="center" justify="center" w="full" h="full" p={4} onClick={createExperiment}>
|
||||||
<Icon as={isLoading ? Spinner : BsPlusSquare} boxSize={8} />
|
<Icon as={isLoading ? Spinner : BsPlusSquare} boxSize={8} />
|
||||||
<Text display={{ base: "none", md: "block" }} ml={2}>
|
<Text display={{ base: "none", md: "block" }} ml={2}>
|
||||||
New Experiment
|
New Experiment
|
||||||
</Text>
|
</Text>
|
||||||
</VStack>
|
</VStack>
|
||||||
</AspectRatio>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ExperimentCardSkeleton = () => (
|
export const ExperimentCardSkeleton = () => (
|
||||||
<AspectRatio ratio={1.2} w="full">
|
<AspectRatio ratio={1.2} w="full">
|
||||||
<VStack align="center" borderColor="gray.200" borderWidth={1} p={4} bg="gray.50">
|
<VStack align="center" borderColor="gray.200" borderWidth={1} p={4} bg="white">
|
||||||
<SkeletonText noOfLines={1} w="80%" />
|
<SkeletonText noOfLines={1} w="80%" />
|
||||||
<SkeletonText noOfLines={2} w="60%" />
|
<SkeletonText noOfLines={2} w="60%" />
|
||||||
<SkeletonText noOfLines={1} w="80%" />
|
<SkeletonText noOfLines={1} w="80%" />
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
import {
|
import {
|
||||||
Heading,
|
Heading,
|
||||||
VStack,
|
VStack,
|
||||||
@@ -9,14 +9,14 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Link as ChakraLink,
|
Link as ChakraLink,
|
||||||
Flex,
|
Flex,
|
||||||
|
useBreakpointValue,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { BsGearFill, BsGithub, BsPersonCircle } from "react-icons/bs";
|
import { BsGearFill, BsGithub, BsPersonCircle } from "react-icons/bs";
|
||||||
import { IoStatsChartOutline } from "react-icons/io5";
|
import { IoStatsChartOutline } from "react-icons/io5";
|
||||||
import { RiDatabase2Line, RiFlaskLine } from "react-icons/ri";
|
import { RiHome3Line, RiDatabase2Line, RiFlaskLine } from "react-icons/ri";
|
||||||
import { signIn, useSession } from "next-auth/react";
|
import { signIn, useSession } from "next-auth/react";
|
||||||
import UserMenu from "./UserMenu";
|
|
||||||
import { env } from "~/env.mjs";
|
import { env } from "~/env.mjs";
|
||||||
import ProjectMenu from "./ProjectMenu";
|
import ProjectMenu from "./ProjectMenu";
|
||||||
import NavSidebarOption from "./NavSidebarOption";
|
import NavSidebarOption from "./NavSidebarOption";
|
||||||
@@ -27,10 +27,16 @@ const Divider = () => <Box h="1px" bgColor="gray.300" w="full" />;
|
|||||||
const NavSidebar = () => {
|
const NavSidebar = () => {
|
||||||
const user = useSession().data;
|
const user = useSession().data;
|
||||||
|
|
||||||
|
// Hack to get around initial flash, see https://github.com/chakra-ui/chakra-ui/issues/6452
|
||||||
|
const isMobile = useBreakpointValue({ base: true, md: false, ssr: false });
|
||||||
|
const renderCount = useRef(0);
|
||||||
|
renderCount.current++;
|
||||||
|
|
||||||
|
const displayLogo = isMobile && renderCount.current > 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VStack
|
<VStack
|
||||||
align="stretch"
|
align="stretch"
|
||||||
bgColor="gray.50"
|
|
||||||
py={2}
|
py={2}
|
||||||
px={2}
|
px={2}
|
||||||
pb={0}
|
pb={0}
|
||||||
@@ -40,32 +46,59 @@ const NavSidebar = () => {
|
|||||||
borderRightWidth={1}
|
borderRightWidth={1}
|
||||||
borderColor="gray.300"
|
borderColor="gray.300"
|
||||||
>
|
>
|
||||||
<HStack
|
{displayLogo && (
|
||||||
as={Link}
|
<>
|
||||||
href="/"
|
<HStack
|
||||||
_hover={{ textDecoration: "none" }}
|
as={Link}
|
||||||
spacing={{ base: 1, md: 0 }}
|
href="/"
|
||||||
mx={2}
|
_hover={{ textDecoration: "none" }}
|
||||||
py={{ base: 1, md: 2 }}
|
spacing={{ base: 1, md: 0 }}
|
||||||
>
|
mx={2}
|
||||||
<Image src="/logo.svg" alt="" boxSize={6} mr={4} ml={{ base: 0.5, md: 0 }} />
|
py={{ base: 1, md: 2 }}
|
||||||
<Heading size="md" fontFamily="inconsolata, monospace">
|
>
|
||||||
OpenPipe
|
<Image src="/logo.svg" alt="" boxSize={6} mr={4} ml={{ base: 0.5, md: 0 }} />
|
||||||
</Heading>
|
<Heading size="md" fontFamily="inconsolata, monospace">
|
||||||
</HStack>
|
OpenPipe
|
||||||
<Divider />
|
</Heading>
|
||||||
|
</HStack>
|
||||||
|
<Divider />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<VStack align="flex-start" overflowY="auto" overflowX="hidden" flex={1}>
|
<VStack align="flex-start" overflowY="auto" overflowX="hidden" flex={1}>
|
||||||
{user != null && (
|
{user != null && (
|
||||||
<>
|
<>
|
||||||
<ProjectMenu />
|
<ProjectMenu />
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
{env.NEXT_PUBLIC_FF_SHOW_LOGGED_CALLS && (
|
{env.NEXT_PUBLIC_FF_SHOW_LOGGED_CALLS && (
|
||||||
<IconLink icon={IoStatsChartOutline} label="Logged Calls" href="/logged-calls" beta />
|
<>
|
||||||
|
<IconLink icon={RiHome3Line} label="Dashboard" href="/dashboard" beta />
|
||||||
|
<IconLink
|
||||||
|
icon={IoStatsChartOutline}
|
||||||
|
label="Request Logs"
|
||||||
|
href="/request-logs"
|
||||||
|
beta
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<IconLink icon={RiFlaskLine} label="Experiments" href="/experiments" />
|
<IconLink icon={RiFlaskLine} label="Experiments" href="/experiments" />
|
||||||
{env.NEXT_PUBLIC_SHOW_DATA && (
|
{env.NEXT_PUBLIC_SHOW_DATA && (
|
||||||
<IconLink icon={RiDatabase2Line} label="Data" href="/data" />
|
<IconLink icon={RiDatabase2Line} label="Data" href="/data" />
|
||||||
)}
|
)}
|
||||||
|
<VStack w="full" alignItems="flex-start" spacing={0} pt={8}>
|
||||||
|
<Text
|
||||||
|
pl={2}
|
||||||
|
pb={2}
|
||||||
|
fontSize="xs"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="gray.500"
|
||||||
|
display={{ base: "none", md: "flex" }}
|
||||||
|
>
|
||||||
|
CONFIGURATION
|
||||||
|
</Text>
|
||||||
|
<IconLink icon={BsGearFill} label="Project Settings" href="/project/settings" />
|
||||||
|
</VStack>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{user === null && (
|
{user === null && (
|
||||||
@@ -87,20 +120,7 @@ const NavSidebar = () => {
|
|||||||
</NavSidebarOption>
|
</NavSidebarOption>
|
||||||
)}
|
)}
|
||||||
</VStack>
|
</VStack>
|
||||||
<VStack w="full" alignItems="flex-start" spacing={0}>
|
|
||||||
<Text
|
|
||||||
pl={2}
|
|
||||||
pb={2}
|
|
||||||
fontSize="xs"
|
|
||||||
fontWeight="bold"
|
|
||||||
color="gray.500"
|
|
||||||
display={{ base: "none", md: "flex" }}
|
|
||||||
>
|
|
||||||
CONFIGURATION
|
|
||||||
</Text>
|
|
||||||
<IconLink icon={BsGearFill} label="Project Settings" href="/project/settings" />
|
|
||||||
</VStack>
|
|
||||||
{user && <UserMenu user={user} borderColor={"gray.200"} />}
|
|
||||||
<Divider />
|
<Divider />
|
||||||
<VStack spacing={0} align="center">
|
<VStack spacing={0} align="center">
|
||||||
<ChakraLink
|
<ChakraLink
|
||||||
@@ -160,7 +180,7 @@ export default function AppShell({
|
|||||||
<title>{title ? `${title} | OpenPipe` : "OpenPipe"}</title>
|
<title>{title ? `${title} | OpenPipe` : "OpenPipe"}</title>
|
||||||
</Head>
|
</Head>
|
||||||
<NavSidebar />
|
<NavSidebar />
|
||||||
<Box h="100%" flex={1} overflowY="auto">
|
<Box h="100%" flex={1} overflowY="auto" bgColor="gray.50">
|
||||||
{children}
|
{children}
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -6,16 +6,18 @@ import {
|
|||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
Flex,
|
Flex,
|
||||||
IconButton,
|
|
||||||
Icon,
|
Icon,
|
||||||
Divider,
|
Divider,
|
||||||
Button,
|
Button,
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
Spinner,
|
Spinner,
|
||||||
|
Link as ChakraLink,
|
||||||
|
Image,
|
||||||
|
Box,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { BsChevronRight, BsGear, BsPlus } from "react-icons/bs";
|
import { BsPlus, BsPersonCircle } from "react-icons/bs";
|
||||||
import { type Project } from "@prisma/client";
|
import { type Project } from "@prisma/client";
|
||||||
|
|
||||||
import { useAppStore } from "~/state/store";
|
import { useAppStore } from "~/state/store";
|
||||||
@@ -23,13 +25,14 @@ import { api } from "~/utils/api";
|
|||||||
import NavSidebarOption from "./NavSidebarOption";
|
import NavSidebarOption from "./NavSidebarOption";
|
||||||
import { useHandledAsyncCallback, useSelectedProject } from "~/utils/hooks";
|
import { useHandledAsyncCallback, useSelectedProject } from "~/utils/hooks";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { useSession, signOut } from "next-auth/react";
|
||||||
|
|
||||||
export default function ProjectMenu() {
|
export default function ProjectMenu() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const utils = api.useContext();
|
const utils = api.useContext();
|
||||||
|
|
||||||
const selectedProjectId = useAppStore((s) => s.selectedProjectId);
|
const selectedProjectId = useAppStore((s) => s.selectedProjectId);
|
||||||
const setselectedProjectId = useAppStore((s) => s.setselectedProjectId);
|
const setSelectedProjectId = useAppStore((s) => s.setSelectedProjectId);
|
||||||
|
|
||||||
const { data: projects } = api.projects.list.useQuery();
|
const { data: projects } = api.projects.list.useQuery();
|
||||||
|
|
||||||
@@ -39,9 +42,9 @@ export default function ProjectMenu() {
|
|||||||
projects[0] &&
|
projects[0] &&
|
||||||
(!selectedProjectId || !projects.find((proj) => proj.id === selectedProjectId))
|
(!selectedProjectId || !projects.find((proj) => proj.id === selectedProjectId))
|
||||||
) {
|
) {
|
||||||
setselectedProjectId(projects[0].id);
|
setSelectedProjectId(projects[0].id);
|
||||||
}
|
}
|
||||||
}, [selectedProjectId, setselectedProjectId, projects]);
|
}, [selectedProjectId, setSelectedProjectId, projects]);
|
||||||
|
|
||||||
const { data: selectedProject } = useSelectedProject();
|
const { data: selectedProject } = useSelectedProject();
|
||||||
|
|
||||||
@@ -49,28 +52,32 @@ export default function ProjectMenu() {
|
|||||||
|
|
||||||
const createMutation = api.projects.create.useMutation();
|
const createMutation = api.projects.create.useMutation();
|
||||||
const [createProject, isLoading] = useHandledAsyncCallback(async () => {
|
const [createProject, isLoading] = useHandledAsyncCallback(async () => {
|
||||||
const newProj = await createMutation.mutateAsync({ name: "New Project" });
|
const newProj = await createMutation.mutateAsync({ name: "Untitled Project" });
|
||||||
await utils.projects.list.invalidate();
|
await utils.projects.list.invalidate();
|
||||||
setselectedProjectId(newProj.id);
|
setSelectedProjectId(newProj.id);
|
||||||
await router.push({ pathname: "/project/settings" });
|
await router.push({ pathname: "/project/settings" });
|
||||||
}, [createMutation, router]);
|
}, [createMutation, router]);
|
||||||
|
|
||||||
|
const user = useSession().data;
|
||||||
|
|
||||||
|
const profileImage = user?.user.image ? (
|
||||||
|
<Image src={user.user.image} alt="profile picture" boxSize={6} borderRadius="50%" />
|
||||||
|
) : (
|
||||||
|
<Icon as={BsPersonCircle} boxSize={6} />
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VStack w="full" alignItems="flex-start" spacing={0}>
|
<VStack w="full" alignItems="flex-start" spacing={0} py={1}>
|
||||||
<Text
|
<Popover
|
||||||
pl={2}
|
placement="bottom"
|
||||||
pb={2}
|
isOpen={popover.isOpen}
|
||||||
fontSize="xs"
|
onOpen={popover.onOpen}
|
||||||
fontWeight="bold"
|
onClose={popover.onClose}
|
||||||
color="gray.500"
|
closeOnBlur
|
||||||
display={{ base: "none", md: "flex" }}
|
|
||||||
>
|
>
|
||||||
PROJECT
|
|
||||||
</Text>
|
|
||||||
<Popover placement="right-end" isOpen={popover.isOpen} onClose={popover.onClose} closeOnBlur>
|
|
||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<NavSidebarOption>
|
<NavSidebarOption>
|
||||||
<HStack w="full" onClick={popover.onToggle}>
|
<HStack w="full">
|
||||||
<Flex
|
<Flex
|
||||||
p={1}
|
p={1}
|
||||||
borderRadius={4}
|
borderRadius={4}
|
||||||
@@ -83,20 +90,35 @@ export default function ProjectMenu() {
|
|||||||
>
|
>
|
||||||
<Text>{selectedProject?.name[0]?.toUpperCase()}</Text>
|
<Text>{selectedProject?.name[0]?.toUpperCase()}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Text fontSize="sm" display={{ base: "none", md: "block" }} py={1} flex={1}>
|
<Text
|
||||||
|
fontSize="sm"
|
||||||
|
display={{ base: "none", md: "block" }}
|
||||||
|
py={1}
|
||||||
|
flex={1}
|
||||||
|
fontWeight="bold"
|
||||||
|
>
|
||||||
{selectedProject?.name}
|
{selectedProject?.name}
|
||||||
</Text>
|
</Text>
|
||||||
<Icon as={BsChevronRight} boxSize={4} color="gray.500" />
|
<Box mr={2}>{profileImage}</Box>
|
||||||
</HStack>
|
</HStack>
|
||||||
</NavSidebarOption>
|
</NavSidebarOption>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent _focusVisible={{ outline: "unset" }} ml={-1} w="auto" minW={100} maxW={280}>
|
<PopoverContent
|
||||||
<VStack alignItems="flex-start" spacing={2} py={4} px={2}>
|
_focusVisible={{ outline: "unset" }}
|
||||||
<Text color="gray.500" fontSize="xs" fontWeight="bold" pb={1}>
|
ml={-1}
|
||||||
PROJECTS
|
w={224}
|
||||||
|
boxShadow="0 0 40px 4px rgba(0, 0, 0, 0.1);"
|
||||||
|
fontSize="sm"
|
||||||
|
>
|
||||||
|
<VStack alignItems="flex-start" spacing={1} py={1}>
|
||||||
|
<Text px={3} py={2}>
|
||||||
|
{user?.user.email}
|
||||||
</Text>
|
</Text>
|
||||||
<Divider />
|
<Divider />
|
||||||
<VStack spacing={0} w="full">
|
<Text alignSelf="flex-start" fontWeight="bold" px={3} pt={2}>
|
||||||
|
Your Projects
|
||||||
|
</Text>
|
||||||
|
<VStack spacing={0} w="full" px={1}>
|
||||||
{projects?.map((proj) => (
|
{projects?.map((proj) => (
|
||||||
<ProjectOption
|
<ProjectOption
|
||||||
key={proj.id}
|
key={proj.id}
|
||||||
@@ -105,19 +127,38 @@ export default function ProjectMenu() {
|
|||||||
onClose={popover.onClose}
|
onClose={popover.onClose}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
<HStack
|
||||||
|
as={Button}
|
||||||
|
variant="ghost"
|
||||||
|
colorScheme="blue"
|
||||||
|
color="blue.400"
|
||||||
|
fontSize="sm"
|
||||||
|
justifyContent="flex-start"
|
||||||
|
onClick={createProject}
|
||||||
|
w="full"
|
||||||
|
borderRadius={4}
|
||||||
|
spacing={0}
|
||||||
|
>
|
||||||
|
<Text>Add project</Text>
|
||||||
|
<Icon as={isLoading ? Spinner : BsPlus} boxSize={4} strokeWidth={0.5} />
|
||||||
|
</HStack>
|
||||||
|
</VStack>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
<VStack w="full" px={1}>
|
||||||
|
<ChakraLink
|
||||||
|
onClick={() => {
|
||||||
|
signOut().catch(console.error);
|
||||||
|
}}
|
||||||
|
_hover={{ bgColor: "gray.200", textDecoration: "none" }}
|
||||||
|
w="full"
|
||||||
|
py={2}
|
||||||
|
px={2}
|
||||||
|
borderRadius={4}
|
||||||
|
>
|
||||||
|
<Text>Sign out</Text>
|
||||||
|
</ChakraLink>
|
||||||
</VStack>
|
</VStack>
|
||||||
<HStack
|
|
||||||
as={Button}
|
|
||||||
variant="ghost"
|
|
||||||
colorScheme="blue"
|
|
||||||
color="blue.400"
|
|
||||||
pr={8}
|
|
||||||
w="full"
|
|
||||||
onClick={createProject}
|
|
||||||
>
|
|
||||||
<Icon as={isLoading ? Spinner : BsPlus} boxSize={6} />
|
|
||||||
<Text>New project</Text>
|
|
||||||
</HStack>
|
|
||||||
</VStack>
|
</VStack>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
@@ -134,38 +175,27 @@ const ProjectOption = ({
|
|||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const setselectedProjectId = useAppStore((s) => s.setselectedProjectId);
|
const setSelectedProjectId = useAppStore((s) => s.setSelectedProjectId);
|
||||||
const [gearHovered, setGearHovered] = useState(false);
|
const [gearHovered, setGearHovered] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack
|
<HStack
|
||||||
as={Link}
|
as={Link}
|
||||||
href="/experiments"
|
href="/experiments"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setselectedProjectId(proj.id);
|
setSelectedProjectId(proj.id);
|
||||||
onClose();
|
onClose();
|
||||||
}}
|
}}
|
||||||
w="full"
|
w="full"
|
||||||
justifyContent="space-between"
|
justifyContent="space-between"
|
||||||
bgColor={isActive ? "gray.100" : "transparent"}
|
|
||||||
_hover={gearHovered ? undefined : { bgColor: "gray.200", textDecoration: "none" }}
|
_hover={gearHovered ? undefined : { bgColor: "gray.200", textDecoration: "none" }}
|
||||||
p={2}
|
color={isActive ? "blue.400" : undefined}
|
||||||
|
py={2}
|
||||||
|
px={4}
|
||||||
borderRadius={4}
|
borderRadius={4}
|
||||||
spacing={4}
|
spacing={4}
|
||||||
>
|
>
|
||||||
<Text>{proj.name}</Text>
|
<Text>{proj.name}</Text>
|
||||||
<IconButton
|
|
||||||
as={Link}
|
|
||||||
href="/project/settings"
|
|
||||||
aria-label={`Open ${proj.name} settings`}
|
|
||||||
icon={<Icon as={BsGear} boxSize={5} strokeWidth={0.5} color="gray.500" />}
|
|
||||||
variant="ghost"
|
|
||||||
size="xs"
|
|
||||||
p={0}
|
|
||||||
onMouseEnter={() => setGearHovered(true)}
|
|
||||||
onMouseLeave={() => setGearHovered(false)}
|
|
||||||
_hover={{ bgColor: isActive ? "gray.300" : "gray.100", transitionDelay: 0 }}
|
|
||||||
borderRadius={4}
|
|
||||||
/>
|
|
||||||
</HStack>
|
</HStack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
30
app/src/components/requestLogs/ActionButton.tsx
Normal file
30
app/src/components/requestLogs/ActionButton.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { Button, HStack, type ButtonProps, Icon, Text } from "@chakra-ui/react";
|
||||||
|
import { type IconType } from "react-icons";
|
||||||
|
|
||||||
|
const ActionButton = ({
|
||||||
|
icon,
|
||||||
|
label,
|
||||||
|
...buttonProps
|
||||||
|
}: { icon: IconType; label: string } & ButtonProps) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
colorScheme="blue"
|
||||||
|
color="black"
|
||||||
|
bgColor="white"
|
||||||
|
borderColor="gray.300"
|
||||||
|
borderRadius={4}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
fontSize="sm"
|
||||||
|
fontWeight="normal"
|
||||||
|
{...buttonProps}
|
||||||
|
>
|
||||||
|
<HStack spacing={1}>
|
||||||
|
{icon && <Icon as={icon} />}
|
||||||
|
<Text>{label}</Text>
|
||||||
|
</HStack>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ActionButton;
|
||||||
55
app/src/components/requestLogs/FormattedJson.tsx
Normal file
55
app/src/components/requestLogs/FormattedJson.tsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { Box, IconButton, useToast } from "@chakra-ui/react";
|
||||||
|
import { CopyIcon } from "lucide-react";
|
||||||
|
import SyntaxHighlighter from "react-syntax-highlighter";
|
||||||
|
import { atelierCaveLight } from "react-syntax-highlighter/dist/cjs/styles/hljs";
|
||||||
|
import stringify from "json-stringify-pretty-compact";
|
||||||
|
|
||||||
|
const FormattedJson = ({ json }: { json: any }) => {
|
||||||
|
const jsonString = stringify(json, { maxLength: 40 });
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
const copyToClipboard = async (text: string) => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
toast({
|
||||||
|
title: "Copied to clipboard",
|
||||||
|
status: "success",
|
||||||
|
duration: 2000,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
toast({
|
||||||
|
title: "Failed to copy to clipboard",
|
||||||
|
status: "error",
|
||||||
|
duration: 2000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box position="relative" fontSize="sm" borderRadius="md" overflow="hidden">
|
||||||
|
<SyntaxHighlighter
|
||||||
|
customStyle={{ overflowX: "unset" }}
|
||||||
|
language="json"
|
||||||
|
style={atelierCaveLight}
|
||||||
|
lineProps={{
|
||||||
|
style: { wordBreak: "break-all", whiteSpace: "pre-wrap" },
|
||||||
|
}}
|
||||||
|
wrapLines
|
||||||
|
>
|
||||||
|
{jsonString}
|
||||||
|
</SyntaxHighlighter>
|
||||||
|
<IconButton
|
||||||
|
aria-label="Copy"
|
||||||
|
icon={<CopyIcon />}
|
||||||
|
position="absolute"
|
||||||
|
top={1}
|
||||||
|
right={1}
|
||||||
|
size="xs"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => void copyToClipboard(jsonString)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { FormattedJson };
|
||||||
16
app/src/components/requestLogs/LoggedCallsPaginator.tsx
Normal file
16
app/src/components/requestLogs/LoggedCallsPaginator.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { type StackProps } from "@chakra-ui/react";
|
||||||
|
|
||||||
|
import { useLoggedCalls } from "~/utils/hooks";
|
||||||
|
import Paginator from "../Paginator";
|
||||||
|
|
||||||
|
const LoggedCallsPaginator = (props: StackProps) => {
|
||||||
|
const { data } = useLoggedCalls();
|
||||||
|
|
||||||
|
if (!data) return null;
|
||||||
|
|
||||||
|
const { count } = data;
|
||||||
|
|
||||||
|
return <Paginator count={count} {...props} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LoggedCallsPaginator;
|
||||||
36
app/src/components/requestLogs/LoggedCallsTable.tsx
Normal file
36
app/src/components/requestLogs/LoggedCallsTable.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { Card, Table, Tbody } from "@chakra-ui/react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useLoggedCalls } from "~/utils/hooks";
|
||||||
|
import { TableHeader, TableRow } from "./TableRow";
|
||||||
|
|
||||||
|
export default function LoggedCallsTable() {
|
||||||
|
const [expandedRow, setExpandedRow] = useState<string | null>(null);
|
||||||
|
const { data: loggedCalls } = useLoggedCalls();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card width="100%" overflow="hidden">
|
||||||
|
<Table>
|
||||||
|
<TableHeader showCheckbox />
|
||||||
|
<Tbody>
|
||||||
|
{loggedCalls?.calls.map((loggedCall) => {
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
key={loggedCall.id}
|
||||||
|
loggedCall={loggedCall}
|
||||||
|
isExpanded={loggedCall.id === expandedRow}
|
||||||
|
onToggle={() => {
|
||||||
|
if (loggedCall.id === expandedRow) {
|
||||||
|
setExpandedRow(null);
|
||||||
|
} else {
|
||||||
|
setExpandedRow(loggedCall.id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
showCheckbox
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Tbody>
|
||||||
|
</Table>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
164
app/src/components/requestLogs/TableRow.tsx
Normal file
164
app/src/components/requestLogs/TableRow.tsx
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Heading,
|
||||||
|
Td,
|
||||||
|
Tr,
|
||||||
|
Thead,
|
||||||
|
Th,
|
||||||
|
Tooltip,
|
||||||
|
Collapse,
|
||||||
|
HStack,
|
||||||
|
VStack,
|
||||||
|
Button,
|
||||||
|
ButtonGroup,
|
||||||
|
Text,
|
||||||
|
Checkbox,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import relativeTime from "dayjs/plugin/relativeTime";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
import { type RouterOutputs } from "~/utils/api";
|
||||||
|
import { FormattedJson } from "./FormattedJson";
|
||||||
|
import { useAppStore } from "~/state/store";
|
||||||
|
import { useLoggedCalls } from "~/utils/hooks";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
|
||||||
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
|
type LoggedCall = RouterOutputs["loggedCalls"]["list"]["calls"][0];
|
||||||
|
|
||||||
|
export const TableHeader = ({ showCheckbox }: { showCheckbox?: boolean }) => {
|
||||||
|
const matchingLogIds = useLoggedCalls().data?.matchingLogIds;
|
||||||
|
const selectedLogIds = useAppStore((s) => s.selectedLogs.selectedLogIds);
|
||||||
|
const addAll = useAppStore((s) => s.selectedLogs.addSelectedLogIds);
|
||||||
|
const clearAll = useAppStore((s) => s.selectedLogs.clearSelectedLogIds);
|
||||||
|
const allSelected = useMemo(() => {
|
||||||
|
if (!matchingLogIds) return false;
|
||||||
|
return matchingLogIds.every((id) => selectedLogIds.has(id));
|
||||||
|
}, [selectedLogIds, matchingLogIds]);
|
||||||
|
return (
|
||||||
|
<Thead>
|
||||||
|
<Tr>
|
||||||
|
{showCheckbox && (
|
||||||
|
<Th>
|
||||||
|
<HStack w={8}>
|
||||||
|
<Checkbox
|
||||||
|
isChecked={allSelected}
|
||||||
|
onChange={() => {
|
||||||
|
allSelected ? clearAll() : addAll(matchingLogIds || []);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text>({selectedLogIds.size})</Text>
|
||||||
|
</HStack>
|
||||||
|
</Th>
|
||||||
|
)}
|
||||||
|
<Th>Time</Th>
|
||||||
|
<Th>Model</Th>
|
||||||
|
<Th isNumeric>Duration</Th>
|
||||||
|
<Th isNumeric>Input tokens</Th>
|
||||||
|
<Th isNumeric>Output tokens</Th>
|
||||||
|
<Th isNumeric>Status</Th>
|
||||||
|
</Tr>
|
||||||
|
</Thead>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TableRow = ({
|
||||||
|
loggedCall,
|
||||||
|
isExpanded,
|
||||||
|
onToggle,
|
||||||
|
showCheckbox,
|
||||||
|
}: {
|
||||||
|
loggedCall: LoggedCall;
|
||||||
|
isExpanded: boolean;
|
||||||
|
onToggle: () => void;
|
||||||
|
showCheckbox?: boolean;
|
||||||
|
}) => {
|
||||||
|
const isError = loggedCall.modelResponse?.statusCode !== 200;
|
||||||
|
const timeAgo = dayjs(loggedCall.requestedAt).fromNow();
|
||||||
|
const fullTime = dayjs(loggedCall.requestedAt).toString();
|
||||||
|
|
||||||
|
const durationCell = (
|
||||||
|
<Td isNumeric>
|
||||||
|
{loggedCall.cacheHit ? (
|
||||||
|
<Text color="gray.500">Cached</Text>
|
||||||
|
) : (
|
||||||
|
((loggedCall.modelResponse?.durationMs ?? 0) / 1000).toFixed(2) + "s"
|
||||||
|
)}
|
||||||
|
</Td>
|
||||||
|
);
|
||||||
|
|
||||||
|
const isChecked = useAppStore((s) => s.selectedLogs.selectedLogIds.has(loggedCall.id));
|
||||||
|
const toggleChecked = useAppStore((s) => s.selectedLogs.toggleSelectedLogId);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Tr
|
||||||
|
onClick={onToggle}
|
||||||
|
key={loggedCall.id}
|
||||||
|
_hover={{ bgColor: "gray.50", cursor: "pointer" }}
|
||||||
|
sx={{
|
||||||
|
"> td": { borderBottom: "none" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{showCheckbox && (
|
||||||
|
<Td>
|
||||||
|
<Checkbox isChecked={isChecked} onChange={() => toggleChecked(loggedCall.id)} />
|
||||||
|
</Td>
|
||||||
|
)}
|
||||||
|
<Td>
|
||||||
|
<Tooltip label={fullTime} placement="top">
|
||||||
|
<Box whiteSpace="nowrap" minW="120px">
|
||||||
|
{timeAgo}
|
||||||
|
</Box>
|
||||||
|
</Tooltip>
|
||||||
|
</Td>
|
||||||
|
<Td width="100%">
|
||||||
|
<HStack justifyContent="flex-start">
|
||||||
|
<Text
|
||||||
|
colorScheme="purple"
|
||||||
|
color="purple.500"
|
||||||
|
borderColor="purple.500"
|
||||||
|
px={1}
|
||||||
|
borderRadius={4}
|
||||||
|
borderWidth={1}
|
||||||
|
fontSize="xs"
|
||||||
|
>
|
||||||
|
{loggedCall.model}
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
</Td>
|
||||||
|
{durationCell}
|
||||||
|
<Td isNumeric>{loggedCall.modelResponse?.inputTokens}</Td>
|
||||||
|
<Td isNumeric>{loggedCall.modelResponse?.outputTokens}</Td>
|
||||||
|
<Td sx={{ color: isError ? "red.500" : "green.500", fontWeight: "semibold" }} isNumeric>
|
||||||
|
{loggedCall.modelResponse?.statusCode ?? "No response"}
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
<Tr>
|
||||||
|
<Td colSpan={8} p={0}>
|
||||||
|
<Collapse in={isExpanded} unmountOnExit={true}>
|
||||||
|
<VStack p={4} align="stretch">
|
||||||
|
<HStack align="stretch">
|
||||||
|
<VStack flex={1} align="stretch">
|
||||||
|
<Heading size="sm">Input</Heading>
|
||||||
|
<FormattedJson json={loggedCall.modelResponse?.reqPayload} />
|
||||||
|
</VStack>
|
||||||
|
<VStack flex={1} align="stretch">
|
||||||
|
<Heading size="sm">Output</Heading>
|
||||||
|
<FormattedJson json={loggedCall.modelResponse?.respPayload} />
|
||||||
|
</VStack>
|
||||||
|
</HStack>
|
||||||
|
<ButtonGroup alignSelf="flex-end">
|
||||||
|
<Button as={Link} colorScheme="blue" href={{ pathname: "/experiments" }}>
|
||||||
|
Experiments
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
</VStack>
|
||||||
|
</Collapse>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -120,9 +120,9 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
"Convert to function call": {
|
"Convert to function call": {
|
||||||
icon: TfiThought,
|
icon: TfiThought,
|
||||||
description: "Use function calls to get output from the model in a more structured way.",
|
description: "Use function calls to get output from the model in a more structured way.",
|
||||||
instructions: `OpenAI functions are a specialized way for an LLM to return output.
|
instructions: `OpenAI functions are a specialized way for an LLM to return its final output.
|
||||||
|
|
||||||
This is what a prompt looks like before adding a function:
|
Example 1 before:
|
||||||
|
|
||||||
definePrompt("openai/ChatCompletion", {
|
definePrompt("openai/ChatCompletion", {
|
||||||
model: "gpt-4",
|
model: "gpt-4",
|
||||||
@@ -139,7 +139,7 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
This is what one looks like after adding a function:
|
Example 1 after:
|
||||||
|
|
||||||
definePrompt("openai/ChatCompletion", {
|
definePrompt("openai/ChatCompletion", {
|
||||||
model: "gpt-4",
|
model: "gpt-4",
|
||||||
@@ -156,7 +156,7 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
],
|
],
|
||||||
functions: [
|
functions: [
|
||||||
{
|
{
|
||||||
name: "extract_sentiment",
|
name: "log_extracted_sentiment",
|
||||||
parameters: {
|
parameters: {
|
||||||
type: "object", // parameters must always be an object with a properties key
|
type: "object", // parameters must always be an object with a properties key
|
||||||
properties: { // properties key is required
|
properties: { // properties key is required
|
||||||
@@ -169,13 +169,13 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
function_call: {
|
function_call: {
|
||||||
name: "extract_sentiment",
|
name: "log_extracted_sentiment",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Here's another example of adding a function:
|
=========
|
||||||
|
|
||||||
Before:
|
Example 2 before:
|
||||||
|
|
||||||
definePrompt("openai/ChatCompletion", {
|
definePrompt("openai/ChatCompletion", {
|
||||||
model: "gpt-3.5-turbo",
|
model: "gpt-3.5-turbo",
|
||||||
@@ -197,7 +197,7 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
temperature: 0,
|
temperature: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
After:
|
Example 2 after:
|
||||||
|
|
||||||
definePrompt("openai/ChatCompletion", {
|
definePrompt("openai/ChatCompletion", {
|
||||||
model: "gpt-3.5-turbo",
|
model: "gpt-3.5-turbo",
|
||||||
@@ -215,7 +215,7 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
temperature: 0,
|
temperature: 0,
|
||||||
functions: [
|
functions: [
|
||||||
{
|
{
|
||||||
name: "score_post",
|
name: "log_post_score",
|
||||||
parameters: {
|
parameters: {
|
||||||
type: "object",
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
@@ -227,13 +227,13 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
function_call: {
|
function_call: {
|
||||||
name: "score_post",
|
name: "log_post_score",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Another example
|
=========
|
||||||
|
|
||||||
Before:
|
Example 3 before:
|
||||||
|
|
||||||
definePrompt("openai/ChatCompletion", {
|
definePrompt("openai/ChatCompletion", {
|
||||||
model: "gpt-3.5-turbo",
|
model: "gpt-3.5-turbo",
|
||||||
@@ -246,7 +246,7 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
After:
|
Example 3 after:
|
||||||
|
|
||||||
definePrompt("openai/ChatCompletion", {
|
definePrompt("openai/ChatCompletion", {
|
||||||
model: "gpt-3.5-turbo",
|
model: "gpt-3.5-turbo",
|
||||||
@@ -258,22 +258,25 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
],
|
],
|
||||||
functions: [
|
functions: [
|
||||||
{
|
{
|
||||||
name: "write_in_language",
|
name: "log_translated_text",
|
||||||
parameters: {
|
parameters: {
|
||||||
type: "object",
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
text: {
|
translated_text: {
|
||||||
type: "string",
|
type: "string",
|
||||||
|
description: "The text, written in the language specified in the prompt",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
function_call: {
|
function_call: {
|
||||||
name: "write_in_language",
|
name: "log_translated_text",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
=========
|
||||||
|
|
||||||
Add an OpenAI function that takes one or more nested parameters that match the expected output from this prompt.`,
|
Add an OpenAI function that takes one or more nested parameters that match the expected output from this prompt.`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { ChakraThemeProvider } from "~/theme/ChakraThemeProvider";
|
|||||||
import { SyncAppStore } from "~/state/sync";
|
import { SyncAppStore } from "~/state/sync";
|
||||||
import NextAdapterApp from "next-query-params/app";
|
import NextAdapterApp from "next-query-params/app";
|
||||||
import { QueryParamProvider } from "use-query-params";
|
import { QueryParamProvider } from "use-query-params";
|
||||||
import { SessionIdentifier } from "~/utils/analytics/clientAnalytics";
|
import { PosthogAppProvider } from "~/utils/analytics/posthog";
|
||||||
|
|
||||||
const MyApp: AppType<{ session: Session | null }> = ({
|
const MyApp: AppType<{ session: Session | null }> = ({
|
||||||
Component,
|
Component,
|
||||||
@@ -34,14 +34,15 @@ const MyApp: AppType<{ session: Session | null }> = ({
|
|||||||
<meta name="twitter:image" content="/og.png" />
|
<meta name="twitter:image" content="/og.png" />
|
||||||
</Head>
|
</Head>
|
||||||
<SessionProvider session={session}>
|
<SessionProvider session={session}>
|
||||||
<SyncAppStore />
|
<PosthogAppProvider>
|
||||||
<Favicon />
|
<SyncAppStore />
|
||||||
<SessionIdentifier />
|
<Favicon />
|
||||||
<ChakraThemeProvider>
|
<ChakraThemeProvider>
|
||||||
<QueryParamProvider adapter={NextAdapterApp}>
|
<QueryParamProvider adapter={NextAdapterApp}>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</QueryParamProvider>
|
</QueryParamProvider>
|
||||||
</ChakraThemeProvider>
|
</ChakraThemeProvider>
|
||||||
|
</PosthogAppProvider>
|
||||||
</SessionProvider>
|
</SessionProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
// A faulty API route to test Sentry's error monitoring
|
|
||||||
// @ts-expect-error just a test file, don't care about types
|
|
||||||
export default function handler(_req, res) {
|
|
||||||
throw new Error("Sentry Example API Route Error");
|
|
||||||
res.status(200).json({ name: "John Doe" });
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,14 @@
|
|||||||
import { type NextApiRequest, type NextApiResponse } from "next";
|
import { type NextApiRequest, type NextApiResponse } from "next";
|
||||||
import cors from "nextjs-cors";
|
import cors from "nextjs-cors";
|
||||||
import { createOpenApiNextHandler } from "trpc-openapi";
|
import { createOpenApiNextHandler } from "trpc-openapi";
|
||||||
import { createProcedureCache } from "trpc-openapi/dist/adapters/node-http/procedures";
|
import { v1ApiRouter } from "~/server/api/external/v1Api.router";
|
||||||
import { appRouter } from "~/server/api/root.router";
|
import { createOpenApiContext } from "~/server/api/external/openApiTrpc";
|
||||||
import { createTRPCContext } from "~/server/api/trpc";
|
|
||||||
|
|
||||||
const openApiHandler = createOpenApiNextHandler({
|
const openApiHandler = createOpenApiNextHandler({
|
||||||
router: appRouter,
|
router: v1ApiRouter,
|
||||||
createContext: createTRPCContext,
|
createContext: createOpenApiContext,
|
||||||
});
|
});
|
||||||
|
|
||||||
const cache = createProcedureCache(appRouter);
|
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
// Setup CORS
|
// Setup CORS
|
||||||
await cors(req, res);
|
await cors(req, res);
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import { type NextApiRequest, type NextApiResponse } from "next";
|
import { type NextApiRequest, type NextApiResponse } from "next";
|
||||||
import { generateOpenApiDocument } from "trpc-openapi";
|
import { generateOpenApiDocument } from "trpc-openapi";
|
||||||
import { appRouter } from "~/server/api/root.router";
|
import { v1ApiRouter } from "~/server/api/external/v1Api.router";
|
||||||
|
|
||||||
export const openApiDocument = generateOpenApiDocument(appRouter, {
|
export const openApiDocument = generateOpenApiDocument(v1ApiRouter, {
|
||||||
title: "OpenPipe API",
|
title: "OpenPipe API",
|
||||||
description: "The public API for reporting API calls to OpenPipe",
|
description: "The public API for reporting API calls to OpenPipe",
|
||||||
version: "0.1.0",
|
version: "0.1.1",
|
||||||
baseUrl: "https://app.openpipe.ai/api",
|
baseUrl: "https://app.openpipe.ai/api/v1",
|
||||||
});
|
});
|
||||||
// Respond with our OpenAPI schema
|
// Respond with our OpenAPI schema
|
||||||
const hander = (req: NextApiRequest, res: NextApiResponse) => {
|
const hander = (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
@@ -15,20 +15,16 @@ import {
|
|||||||
Tr,
|
Tr,
|
||||||
Td,
|
Td,
|
||||||
Divider,
|
Divider,
|
||||||
Breadcrumb,
|
|
||||||
BreadcrumbItem,
|
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { Ban, DollarSign, Hash } from "lucide-react";
|
import { Ban, DollarSign, Hash } from "lucide-react";
|
||||||
|
|
||||||
import AppShell from "~/components/nav/AppShell";
|
import AppShell from "~/components/nav/AppShell";
|
||||||
import PageHeaderContainer from "~/components/nav/PageHeaderContainer";
|
|
||||||
import ProjectBreadcrumbContents from "~/components/nav/ProjectBreadcrumbContents";
|
|
||||||
import { useSelectedProject } from "~/utils/hooks";
|
import { useSelectedProject } from "~/utils/hooks";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import LoggedCallTable from "~/components/dashboard/LoggedCallTable";
|
import LoggedCallsTable from "~/components/dashboard/LoggedCallsTable";
|
||||||
import UsageGraph from "~/components/dashboard/UsageGraph";
|
import UsageGraph from "~/components/dashboard/UsageGraph";
|
||||||
|
|
||||||
export default function LoggedCalls() {
|
export default function Dashboard() {
|
||||||
const { data: selectedProject } = useSelectedProject();
|
const { data: selectedProject } = useSelectedProject();
|
||||||
|
|
||||||
const stats = api.dashboard.stats.useQuery(
|
const stats = api.dashboard.stats.useQuery(
|
||||||
@@ -37,25 +33,15 @@ export default function LoggedCalls() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppShell title="Logged Calls" requireAuth>
|
<AppShell title="Dashboard" requireAuth>
|
||||||
<PageHeaderContainer>
|
<VStack px={8} py={8} alignItems="flex-start" spacing={4}>
|
||||||
<Breadcrumb>
|
|
||||||
<BreadcrumbItem>
|
|
||||||
<ProjectBreadcrumbContents />
|
|
||||||
</BreadcrumbItem>
|
|
||||||
<BreadcrumbItem isCurrentPage>
|
|
||||||
<Text>Logged Calls</Text>
|
|
||||||
</BreadcrumbItem>
|
|
||||||
</Breadcrumb>
|
|
||||||
</PageHeaderContainer>
|
|
||||||
<VStack px={8} pt={4} alignItems="flex-start" spacing={4}>
|
|
||||||
<Text fontSize="2xl" fontWeight="bold">
|
<Text fontSize="2xl" fontWeight="bold">
|
||||||
{selectedProject?.name}
|
Dashboard
|
||||||
</Text>
|
</Text>
|
||||||
<Divider />
|
<Divider />
|
||||||
<VStack margin="auto" spacing={4} align="stretch" w="full">
|
<VStack margin="auto" spacing={4} align="stretch" w="full">
|
||||||
<HStack gap={4} align="start">
|
<HStack gap={4} align="start">
|
||||||
<Card variant="outline" flex={1}>
|
<Card flex={1}>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<Heading as="h3" size="sm">
|
<Heading as="h3" size="sm">
|
||||||
Usage Statistics
|
Usage Statistics
|
||||||
@@ -66,7 +52,7 @@ export default function LoggedCalls() {
|
|||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
<VStack spacing="4" width="300px" align="stretch">
|
<VStack spacing="4" width="300px" align="stretch">
|
||||||
<Card variant="outline">
|
<Card>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<Stat>
|
<Stat>
|
||||||
<HStack>
|
<HStack>
|
||||||
@@ -79,7 +65,7 @@ export default function LoggedCalls() {
|
|||||||
</Stat>
|
</Stat>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
<Card variant="outline">
|
<Card>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<Stat>
|
<Stat>
|
||||||
<HStack>
|
<HStack>
|
||||||
@@ -94,7 +80,7 @@ export default function LoggedCalls() {
|
|||||||
</Stat>
|
</Stat>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
<Card variant="outline" overflow="hidden">
|
<Card overflow="hidden">
|
||||||
<Stat>
|
<Stat>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<HStack>
|
<HStack>
|
||||||
@@ -120,7 +106,7 @@ export default function LoggedCalls() {
|
|||||||
</Card>
|
</Card>
|
||||||
</VStack>
|
</VStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
<LoggedCallTable />
|
<LoggedCallsTable />
|
||||||
</VStack>
|
</VStack>
|
||||||
</VStack>
|
</VStack>
|
||||||
</AppShell>
|
</AppShell>
|
||||||
@@ -62,7 +62,7 @@ export default function Experiment() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
useAppStore.getState().sharedVariantEditor.loadMonaco().catch(console.error);
|
useAppStore.getState().sharedVariantEditor.loadMonaco().catch(console.error);
|
||||||
});
|
}, []);
|
||||||
|
|
||||||
const [label, setLabel] = useState(experiment.data?.label || "");
|
const [label, setLabel] = useState(experiment.data?.label || "");
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export default function Settings() {
|
|||||||
</BreadcrumbItem>
|
</BreadcrumbItem>
|
||||||
</Breadcrumb>
|
</Breadcrumb>
|
||||||
</PageHeaderContainer>
|
</PageHeaderContainer>
|
||||||
<VStack px={8} pt={4} alignItems="flex-start" spacing={4}>
|
<VStack px={8} py={4} alignItems="flex-start" spacing={4}>
|
||||||
<VStack spacing={0} alignItems="flex-start">
|
<VStack spacing={0} alignItems="flex-start">
|
||||||
<Text fontSize="2xl" fontWeight="bold">
|
<Text fontSize="2xl" fontWeight="bold">
|
||||||
Project Settings
|
Project Settings
|
||||||
@@ -80,6 +80,7 @@ export default function Settings() {
|
|||||||
borderWidth={1}
|
borderWidth={1}
|
||||||
borderRadius={4}
|
borderRadius={4}
|
||||||
borderColor="gray.300"
|
borderColor="gray.300"
|
||||||
|
bgColor="white"
|
||||||
p={6}
|
p={6}
|
||||||
spacing={6}
|
spacing={6}
|
||||||
>
|
>
|
||||||
|
|||||||
34
app/src/pages/request-logs/index.tsx
Normal file
34
app/src/pages/request-logs/index.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { Text, VStack, Divider, HStack } from "@chakra-ui/react";
|
||||||
|
|
||||||
|
import AppShell from "~/components/nav/AppShell";
|
||||||
|
import LoggedCallTable from "~/components/requestLogs/LoggedCallsTable";
|
||||||
|
import LoggedCallsPaginator from "~/components/requestLogs/LoggedCallsPaginator";
|
||||||
|
import ActionButton from "~/components/requestLogs/ActionButton";
|
||||||
|
import { useAppStore } from "~/state/store";
|
||||||
|
import { RiFlaskLine } from "react-icons/ri";
|
||||||
|
|
||||||
|
export default function LoggedCalls() {
|
||||||
|
const selectedLogIds = useAppStore((s) => s.selectedLogs.selectedLogIds);
|
||||||
|
return (
|
||||||
|
<AppShell title="Request Logs" requireAuth>
|
||||||
|
<VStack px={8} py={8} alignItems="flex-start" spacing={4} w="full">
|
||||||
|
<Text fontSize="2xl" fontWeight="bold">
|
||||||
|
Request Logs
|
||||||
|
</Text>
|
||||||
|
<Divider />
|
||||||
|
<HStack w="full" justifyContent="flex-end">
|
||||||
|
<ActionButton
|
||||||
|
onClick={() => {
|
||||||
|
console.log("experimenting with these ids", selectedLogIds);
|
||||||
|
}}
|
||||||
|
label="Experiment"
|
||||||
|
icon={RiFlaskLine}
|
||||||
|
isDisabled={selectedLogIds.size === 0}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
<LoggedCallTable />
|
||||||
|
<LoggedCallsPaginator />
|
||||||
|
</VStack>
|
||||||
|
</AppShell>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,11 +4,15 @@ import parserTypescript from "prettier/plugins/typescript";
|
|||||||
// @ts-expect-error for some reason missing from types
|
// @ts-expect-error for some reason missing from types
|
||||||
import parserEstree from "prettier/plugins/estree";
|
import parserEstree from "prettier/plugins/estree";
|
||||||
|
|
||||||
|
// This emits a warning in the browser "Critical dependency: the request of a
|
||||||
|
// dependency is an expression". Unfortunately doesn't seem to be a way to get
|
||||||
|
// around it if we want to use Babel client-side for now. One solution would be
|
||||||
|
// to just do the formatting server-side in a trpc call.
|
||||||
|
// https://github.com/babel/babel/issues/14301
|
||||||
import * as babel from "@babel/standalone";
|
import * as babel from "@babel/standalone";
|
||||||
|
|
||||||
export function stripTypes(tsCode: string): string {
|
export function stripTypes(tsCode: string): string {
|
||||||
const options = {
|
const options = {
|
||||||
presets: ["typescript"],
|
|
||||||
filename: "file.ts",
|
filename: "file.ts",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
95
app/src/server/api/external/openApiTrpc.ts
vendored
Normal file
95
app/src/server/api/external/openApiTrpc.ts
vendored
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import type { ApiKey, Project } from "@prisma/client";
|
||||||
|
import { TRPCError, initTRPC } from "@trpc/server";
|
||||||
|
import { type CreateNextContextOptions } from "@trpc/server/adapters/next";
|
||||||
|
import superjson from "superjson";
|
||||||
|
import { type OpenApiMeta } from "trpc-openapi";
|
||||||
|
import { ZodError } from "zod";
|
||||||
|
import { prisma } from "~/server/db";
|
||||||
|
|
||||||
|
type CreateContextOptions = {
|
||||||
|
key:
|
||||||
|
| (ApiKey & {
|
||||||
|
project: Project;
|
||||||
|
})
|
||||||
|
| null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This helper generates the "internals" for a tRPC context. If you need to use it, you can export
|
||||||
|
* it from here.
|
||||||
|
*
|
||||||
|
* Examples of things you may need it for:
|
||||||
|
* - testing, so we don't have to mock Next.js' req/res
|
||||||
|
* - tRPC's `createSSGHelpers`, where we don't have req/res
|
||||||
|
*
|
||||||
|
* @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts
|
||||||
|
*/
|
||||||
|
export const createInnerTRPCContext = (opts: CreateContextOptions) => {
|
||||||
|
return {
|
||||||
|
key: opts.key,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createOpenApiContext = async (opts: CreateNextContextOptions) => {
|
||||||
|
const { req, res } = opts;
|
||||||
|
|
||||||
|
const apiKey = req.headers.authorization?.split(" ")[1] as string | null;
|
||||||
|
|
||||||
|
if (!apiKey) {
|
||||||
|
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
|
}
|
||||||
|
const key = await prisma.apiKey.findUnique({
|
||||||
|
where: { apiKey },
|
||||||
|
include: { project: true },
|
||||||
|
});
|
||||||
|
if (!key) {
|
||||||
|
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return createInnerTRPCContext({
|
||||||
|
key,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TRPCContext = Awaited<ReturnType<typeof createOpenApiContext>>;
|
||||||
|
|
||||||
|
const t = initTRPC
|
||||||
|
.context<typeof createOpenApiContext>()
|
||||||
|
.meta<OpenApiMeta>()
|
||||||
|
.create({
|
||||||
|
transformer: superjson,
|
||||||
|
errorFormatter({ shape, error }) {
|
||||||
|
return {
|
||||||
|
...shape,
|
||||||
|
data: {
|
||||||
|
...shape.data,
|
||||||
|
zodError: error.cause instanceof ZodError ? error.cause.flatten() : null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const createOpenApiRouter = t.router;
|
||||||
|
|
||||||
|
export const openApiPublicProc = t.procedure;
|
||||||
|
|
||||||
|
/** Reusable middleware that enforces users are logged in before running the procedure. */
|
||||||
|
const enforceApiKey = t.middleware(async ({ ctx, next }) => {
|
||||||
|
if (!ctx.key) {
|
||||||
|
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return next({
|
||||||
|
ctx: { key: ctx.key },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protected (authenticated) procedure
|
||||||
|
*
|
||||||
|
* If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies
|
||||||
|
* the session is valid and guarantees `ctx.session.user` is not null.
|
||||||
|
*
|
||||||
|
* @see https://trpc.io/docs/procedures
|
||||||
|
*/
|
||||||
|
export const openApiProtectedProc = t.procedure.use(enforceApiKey);
|
||||||
@@ -2,9 +2,6 @@ import { type Prisma } from "@prisma/client";
|
|||||||
import { type JsonValue } from "type-fest";
|
import { type JsonValue } from "type-fest";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { TRPCError } from "@trpc/server";
|
|
||||||
|
|
||||||
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
|
|
||||||
import { prisma } from "~/server/db";
|
import { prisma } from "~/server/db";
|
||||||
import { hashRequest } from "~/server/utils/hashObject";
|
import { hashRequest } from "~/server/utils/hashObject";
|
||||||
import modelProvider from "~/modelProviders/openai-ChatCompletion";
|
import modelProvider from "~/modelProviders/openai-ChatCompletion";
|
||||||
@@ -12,6 +9,7 @@ import {
|
|||||||
type ChatCompletion,
|
type ChatCompletion,
|
||||||
type CompletionCreateParams,
|
type CompletionCreateParams,
|
||||||
} from "openai/resources/chat/completions";
|
} from "openai/resources/chat/completions";
|
||||||
|
import { createOpenApiRouter, openApiProtectedProc } from "./openApiTrpc";
|
||||||
|
|
||||||
const reqValidator = z.object({
|
const reqValidator = z.object({
|
||||||
model: z.string(),
|
model: z.string(),
|
||||||
@@ -28,12 +26,12 @@ const respValidator = z.object({
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const externalApiRouter = createTRPCRouter({
|
export const v1ApiRouter = createOpenApiRouter({
|
||||||
checkCache: publicProcedure
|
checkCache: openApiProtectedProc
|
||||||
.meta({
|
.meta({
|
||||||
openapi: {
|
openapi: {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
path: "/v1/check-cache",
|
path: "/check-cache",
|
||||||
description: "Check if a prompt is cached",
|
description: "Check if a prompt is cached",
|
||||||
protect: true,
|
protect: true,
|
||||||
},
|
},
|
||||||
@@ -47,7 +45,8 @@ export const externalApiRouter = createTRPCRouter({
|
|||||||
.optional()
|
.optional()
|
||||||
.describe(
|
.describe(
|
||||||
'Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }',
|
'Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }',
|
||||||
),
|
)
|
||||||
|
.default({}),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(
|
.output(
|
||||||
@@ -56,18 +55,8 @@ export const externalApiRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const apiKey = ctx.apiKey;
|
|
||||||
if (!apiKey) {
|
|
||||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
|
||||||
}
|
|
||||||
const key = await prisma.apiKey.findUnique({
|
|
||||||
where: { apiKey },
|
|
||||||
});
|
|
||||||
if (!key) {
|
|
||||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
|
||||||
}
|
|
||||||
const reqPayload = await reqValidator.spa(input.reqPayload);
|
const reqPayload = await reqValidator.spa(input.reqPayload);
|
||||||
const cacheKey = hashRequest(key.projectId, reqPayload as JsonValue);
|
const cacheKey = hashRequest(ctx.key.projectId, reqPayload as JsonValue);
|
||||||
|
|
||||||
const existingResponse = await prisma.loggedCallModelResponse.findFirst({
|
const existingResponse = await prisma.loggedCallModelResponse.findFirst({
|
||||||
where: { cacheKey },
|
where: { cacheKey },
|
||||||
@@ -79,23 +68,24 @@ export const externalApiRouter = createTRPCRouter({
|
|||||||
|
|
||||||
await prisma.loggedCall.create({
|
await prisma.loggedCall.create({
|
||||||
data: {
|
data: {
|
||||||
projectId: key.projectId,
|
projectId: ctx.key.projectId,
|
||||||
requestedAt: new Date(input.requestedAt),
|
requestedAt: new Date(input.requestedAt),
|
||||||
cacheHit: true,
|
cacheHit: true,
|
||||||
modelResponseId: existingResponse.id,
|
modelResponseId: existingResponse.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await createTags(existingResponse.originalLoggedCallId, input.tags);
|
||||||
return {
|
return {
|
||||||
respPayload: existingResponse.respPayload,
|
respPayload: existingResponse.respPayload,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
||||||
report: publicProcedure
|
report: openApiProtectedProc
|
||||||
.meta({
|
.meta({
|
||||||
openapi: {
|
openapi: {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
path: "/v1/report",
|
path: "/report",
|
||||||
description: "Report an API call",
|
description: "Report an API call",
|
||||||
protect: true,
|
protect: true,
|
||||||
},
|
},
|
||||||
@@ -113,45 +103,38 @@ export const externalApiRouter = createTRPCRouter({
|
|||||||
.optional()
|
.optional()
|
||||||
.describe(
|
.describe(
|
||||||
'Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }',
|
'Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }',
|
||||||
),
|
)
|
||||||
|
.default({}),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.void())
|
.output(z.void())
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
console.log("GOT TAGS", input.tags);
|
|
||||||
const apiKey = ctx.apiKey;
|
|
||||||
if (!apiKey) {
|
|
||||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
|
||||||
}
|
|
||||||
const key = await prisma.apiKey.findUnique({
|
|
||||||
where: { apiKey },
|
|
||||||
});
|
|
||||||
if (!key) {
|
|
||||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
|
||||||
}
|
|
||||||
const reqPayload = await reqValidator.spa(input.reqPayload);
|
const reqPayload = await reqValidator.spa(input.reqPayload);
|
||||||
const respPayload = await respValidator.spa(input.respPayload);
|
const respPayload = await respValidator.spa(input.respPayload);
|
||||||
|
|
||||||
const requestHash = hashRequest(key.projectId, reqPayload as JsonValue);
|
const requestHash = hashRequest(ctx.key.projectId, reqPayload as JsonValue);
|
||||||
|
|
||||||
const newLoggedCallId = uuidv4();
|
const newLoggedCallId = uuidv4();
|
||||||
const newModelResponseId = uuidv4();
|
const newModelResponseId = uuidv4();
|
||||||
|
|
||||||
let usage;
|
let usage;
|
||||||
|
let model;
|
||||||
if (reqPayload.success && respPayload.success) {
|
if (reqPayload.success && respPayload.success) {
|
||||||
usage = modelProvider.getUsage(
|
usage = modelProvider.getUsage(
|
||||||
input.reqPayload as CompletionCreateParams,
|
input.reqPayload as CompletionCreateParams,
|
||||||
input.respPayload as ChatCompletion,
|
input.respPayload as ChatCompletion,
|
||||||
);
|
);
|
||||||
|
model = reqPayload.data.model;
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.$transaction([
|
await prisma.$transaction([
|
||||||
prisma.loggedCall.create({
|
prisma.loggedCall.create({
|
||||||
data: {
|
data: {
|
||||||
id: newLoggedCallId,
|
id: newLoggedCallId,
|
||||||
projectId: key.projectId,
|
projectId: ctx.key.projectId,
|
||||||
requestedAt: new Date(input.requestedAt),
|
requestedAt: new Date(input.requestedAt),
|
||||||
cacheHit: false,
|
cacheHit: false,
|
||||||
|
model,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
prisma.loggedCallModelResponse.create({
|
prisma.loggedCallModelResponse.create({
|
||||||
@@ -182,22 +165,76 @@ export const externalApiRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const tagsToCreate = Object.entries(input.tags ?? {}).map(([name, value]) => ({
|
await createTags(newLoggedCallId, input.tags);
|
||||||
loggedCallId: newLoggedCallId,
|
}),
|
||||||
// sanitize tags
|
localTestingOnlyGetLatestLoggedCall: openApiProtectedProc
|
||||||
name: name.replaceAll(/[^a-zA-Z0-9_]/g, "_"),
|
.meta({
|
||||||
value,
|
openapi: {
|
||||||
}));
|
method: "GET",
|
||||||
|
path: "/local-testing-only-get-latest-logged-call",
|
||||||
if (reqPayload.success) {
|
description: "Get the latest logged call (only for local testing)",
|
||||||
tagsToCreate.push({
|
protect: true, // Make sure to protect this endpoint
|
||||||
loggedCallId: newLoggedCallId,
|
},
|
||||||
name: "$model",
|
})
|
||||||
value: reqPayload.data.model,
|
.input(z.void())
|
||||||
});
|
.output(
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
createdAt: z.date(),
|
||||||
|
cacheHit: z.boolean(),
|
||||||
|
tags: z.record(z.string().nullable()),
|
||||||
|
modelResponse: z
|
||||||
|
.object({
|
||||||
|
id: z.string(),
|
||||||
|
statusCode: z.number().nullable(),
|
||||||
|
errorMessage: z.string().nullable(),
|
||||||
|
reqPayload: z.unknown(),
|
||||||
|
respPayload: z.unknown(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
)
|
||||||
|
.mutation(async ({ ctx }) => {
|
||||||
|
if (process.env.NODE_ENV === "production") {
|
||||||
|
throw new Error("This operation is not allowed in production environment");
|
||||||
}
|
}
|
||||||
await prisma.loggedCallTag.createMany({
|
|
||||||
data: tagsToCreate,
|
const latestLoggedCall = await prisma.loggedCall.findFirst({
|
||||||
|
where: { projectId: ctx.key.projectId },
|
||||||
|
orderBy: { requestedAt: "desc" },
|
||||||
|
select: {
|
||||||
|
createdAt: true,
|
||||||
|
cacheHit: true,
|
||||||
|
tags: true,
|
||||||
|
modelResponse: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
statusCode: true,
|
||||||
|
errorMessage: true,
|
||||||
|
reqPayload: true,
|
||||||
|
respPayload: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
latestLoggedCall && {
|
||||||
|
...latestLoggedCall,
|
||||||
|
tags: Object.fromEntries(latestLoggedCall.tags.map((tag) => [tag.name, tag.value])),
|
||||||
|
}
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function createTags(loggedCallId: string, tags: Record<string, string>) {
|
||||||
|
const tagsToCreate = Object.entries(tags).map(([name, value]) => ({
|
||||||
|
loggedCallId,
|
||||||
|
name: name.replaceAll(/[^a-zA-Z0-9_$]/g, "_"),
|
||||||
|
value,
|
||||||
|
}));
|
||||||
|
await prisma.loggedCallTag.createMany({
|
||||||
|
data: tagsToCreate,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -8,9 +8,9 @@ import { evaluationsRouter } from "./routers/evaluations.router";
|
|||||||
import { worldChampsRouter } from "./routers/worldChamps.router";
|
import { worldChampsRouter } from "./routers/worldChamps.router";
|
||||||
import { datasetsRouter } from "./routers/datasets.router";
|
import { datasetsRouter } from "./routers/datasets.router";
|
||||||
import { datasetEntries } from "./routers/datasetEntries.router";
|
import { datasetEntries } from "./routers/datasetEntries.router";
|
||||||
import { externalApiRouter } from "./routers/externalApi.router";
|
|
||||||
import { projectsRouter } from "./routers/projects.router";
|
import { projectsRouter } from "./routers/projects.router";
|
||||||
import { dashboardRouter } from "./routers/dashboard.router";
|
import { dashboardRouter } from "./routers/dashboard.router";
|
||||||
|
import { loggedCallsRouter } from "./routers/loggedCalls.router";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the primary router for your server.
|
* This is the primary router for your server.
|
||||||
@@ -29,7 +29,7 @@ export const appRouter = createTRPCRouter({
|
|||||||
datasetEntries: datasetEntries,
|
datasetEntries: datasetEntries,
|
||||||
projects: projectsRouter,
|
projects: projectsRouter,
|
||||||
dashboard: dashboardRouter,
|
dashboard: dashboardRouter,
|
||||||
externalApi: externalApiRouter,
|
loggedCalls: loggedCallsRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
// export type definition of API
|
// export type definition of API
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { sql } from "kysely";
|
import { sql } from "kysely";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||||
import { kysely, prisma } from "~/server/db";
|
import { kysely } from "~/server/db";
|
||||||
|
import { requireCanViewProject } from "~/utils/accessControl";
|
||||||
import dayjs from "~/utils/dayjs";
|
import dayjs from "~/utils/dayjs";
|
||||||
|
|
||||||
export const dashboardRouter = createTRPCRouter({
|
export const dashboardRouter = createTRPCRouter({
|
||||||
stats: publicProcedure
|
stats: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
// TODO: actually take startDate into account
|
// TODO: actually take startDate into account
|
||||||
@@ -13,7 +14,8 @@ export const dashboardRouter = createTRPCRouter({
|
|||||||
projectId: z.string(),
|
projectId: z.string(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
|
await requireCanViewProject(input.projectId, ctx);
|
||||||
// Return the stats group by hour
|
// Return the stats group by hour
|
||||||
const periods = await kysely
|
const periods = await kysely
|
||||||
.selectFrom("LoggedCall")
|
.selectFrom("LoggedCall")
|
||||||
@@ -103,16 +105,4 @@ export const dashboardRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return { periods: backfilledPeriods, totals, errors: namedErrors };
|
return { periods: backfilledPeriods, totals, errors: namedErrors };
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// TODO useInfiniteQuery
|
|
||||||
// https://discord.com/channels/966627436387266600/1122258443886153758/1122258443886153758
|
|
||||||
loggedCalls: publicProcedure.input(z.object({})).query(async ({ input }) => {
|
|
||||||
const loggedCalls = await prisma.loggedCall.findMany({
|
|
||||||
orderBy: { requestedAt: "desc" },
|
|
||||||
include: { tags: true, modelResponse: true },
|
|
||||||
take: 20,
|
|
||||||
});
|
|
||||||
|
|
||||||
return loggedCalls;
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,23 +4,21 @@ import { prisma } from "~/server/db";
|
|||||||
import { requireCanModifyDataset, requireCanViewDataset } from "~/utils/accessControl";
|
import { requireCanModifyDataset, requireCanViewDataset } from "~/utils/accessControl";
|
||||||
import { autogenerateDatasetEntries } from "../autogenerate/autogenerateDatasetEntries";
|
import { autogenerateDatasetEntries } from "../autogenerate/autogenerateDatasetEntries";
|
||||||
|
|
||||||
const PAGE_SIZE = 10;
|
|
||||||
|
|
||||||
export const datasetEntries = createTRPCRouter({
|
export const datasetEntries = createTRPCRouter({
|
||||||
list: protectedProcedure
|
list: protectedProcedure
|
||||||
.input(z.object({ datasetId: z.string(), page: z.number() }))
|
.input(z.object({ datasetId: z.string(), page: z.number(), pageSize: z.number() }))
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
await requireCanViewDataset(input.datasetId, ctx);
|
await requireCanViewDataset(input.datasetId, ctx);
|
||||||
|
|
||||||
const { datasetId, page } = input;
|
const { datasetId, page, pageSize } = input;
|
||||||
|
|
||||||
const entries = await prisma.datasetEntry.findMany({
|
const entries = await prisma.datasetEntry.findMany({
|
||||||
where: {
|
where: {
|
||||||
datasetId,
|
datasetId,
|
||||||
},
|
},
|
||||||
orderBy: { createdAt: "desc" },
|
orderBy: { createdAt: "desc" },
|
||||||
skip: (page - 1) * PAGE_SIZE,
|
skip: (page - 1) * pageSize,
|
||||||
take: PAGE_SIZE,
|
take: pageSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
const count = await prisma.datasetEntry.count({
|
const count = await prisma.datasetEntry.count({
|
||||||
@@ -31,8 +29,6 @@ export const datasetEntries = createTRPCRouter({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
entries,
|
entries,
|
||||||
startIndex: (page - 1) * PAGE_SIZE + 1,
|
|
||||||
lastPage: Math.ceil(count / PAGE_SIZE),
|
|
||||||
count,
|
count,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|||||||
33
app/src/server/api/routers/loggedCalls.router.ts
Normal file
33
app/src/server/api/routers/loggedCalls.router.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||||
|
import { prisma } from "~/server/db";
|
||||||
|
import { requireCanViewProject } from "~/utils/accessControl";
|
||||||
|
|
||||||
|
export const loggedCallsRouter = createTRPCRouter({
|
||||||
|
list: protectedProcedure
|
||||||
|
.input(z.object({ projectId: z.string(), page: z.number(), pageSize: z.number() }))
|
||||||
|
.query(async ({ input, ctx }) => {
|
||||||
|
const { projectId, page, pageSize } = input;
|
||||||
|
|
||||||
|
await requireCanViewProject(projectId, ctx);
|
||||||
|
|
||||||
|
const calls = await prisma.loggedCall.findMany({
|
||||||
|
where: { projectId },
|
||||||
|
orderBy: { requestedAt: "desc" },
|
||||||
|
include: { tags: true, modelResponse: true },
|
||||||
|
skip: (page - 1) * pageSize,
|
||||||
|
take: pageSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const matchingLogs = await prisma.loggedCall.findMany({
|
||||||
|
where: { projectId },
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const count = await prisma.loggedCall.count({
|
||||||
|
where: { projectId },
|
||||||
|
});
|
||||||
|
|
||||||
|
return { calls, count, matchingLogIds: matchingLogs.map((log) => log.id) };
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -131,6 +131,8 @@ export const promptVariantsRouter = createTRPCRouter({
|
|||||||
const inputTokens = overallTokens._sum?.inputTokens ?? 0;
|
const inputTokens = overallTokens._sum?.inputTokens ?? 0;
|
||||||
const outputTokens = overallTokens._sum?.outputTokens ?? 0;
|
const outputTokens = overallTokens._sum?.outputTokens ?? 0;
|
||||||
|
|
||||||
|
const awaitingCompletions = outputCount < scenarioCount;
|
||||||
|
|
||||||
const awaitingEvals = !!evalResults.find(
|
const awaitingEvals = !!evalResults.find(
|
||||||
(result) => result.totalCount < scenarioCount * evals.length,
|
(result) => result.totalCount < scenarioCount * evals.length,
|
||||||
);
|
);
|
||||||
@@ -142,6 +144,7 @@ export const promptVariantsRouter = createTRPCRouter({
|
|||||||
overallCost: overallTokens._sum?.cost ?? 0,
|
overallCost: overallTokens._sum?.cost ?? 0,
|
||||||
scenarioCount,
|
scenarioCount,
|
||||||
outputCount,
|
outputCount,
|
||||||
|
awaitingCompletions,
|
||||||
awaitingEvals,
|
awaitingEvals,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -7,15 +7,13 @@ import { runAllEvals } from "~/server/utils/evaluations";
|
|||||||
import { generateNewCell } from "~/server/utils/generateNewCell";
|
import { generateNewCell } from "~/server/utils/generateNewCell";
|
||||||
import { requireCanModifyExperiment, requireCanViewExperiment } from "~/utils/accessControl";
|
import { requireCanModifyExperiment, requireCanViewExperiment } from "~/utils/accessControl";
|
||||||
|
|
||||||
const PAGE_SIZE = 10;
|
|
||||||
|
|
||||||
export const scenariosRouter = createTRPCRouter({
|
export const scenariosRouter = createTRPCRouter({
|
||||||
list: publicProcedure
|
list: publicProcedure
|
||||||
.input(z.object({ experimentId: z.string(), page: z.number() }))
|
.input(z.object({ experimentId: z.string(), page: z.number(), pageSize: z.number() }))
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
await requireCanViewExperiment(input.experimentId, ctx);
|
await requireCanViewExperiment(input.experimentId, ctx);
|
||||||
|
|
||||||
const { experimentId, page } = input;
|
const { experimentId, page, pageSize } = input;
|
||||||
|
|
||||||
const scenarios = await prisma.testScenario.findMany({
|
const scenarios = await prisma.testScenario.findMany({
|
||||||
where: {
|
where: {
|
||||||
@@ -23,8 +21,8 @@ export const scenariosRouter = createTRPCRouter({
|
|||||||
visible: true,
|
visible: true,
|
||||||
},
|
},
|
||||||
orderBy: { sortIndex: "asc" },
|
orderBy: { sortIndex: "asc" },
|
||||||
skip: (page - 1) * PAGE_SIZE,
|
skip: (page - 1) * pageSize,
|
||||||
take: PAGE_SIZE,
|
take: pageSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
const count = await prisma.testScenario.count({
|
const count = await prisma.testScenario.count({
|
||||||
@@ -36,8 +34,6 @@ export const scenariosRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
scenarios,
|
scenarios,
|
||||||
startIndex: (page - 1) * PAGE_SIZE + 1,
|
|
||||||
lastPage: Math.ceil(count / PAGE_SIZE),
|
|
||||||
count,
|
count,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import { capturePath } from "~/utils/analytics/serverAnalytics";
|
|||||||
|
|
||||||
type CreateContextOptions = {
|
type CreateContextOptions = {
|
||||||
session: Session | null;
|
session: Session | null;
|
||||||
apiKey: string | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
@@ -46,7 +45,6 @@ const noOp = () => {};
|
|||||||
export const createInnerTRPCContext = (opts: CreateContextOptions) => {
|
export const createInnerTRPCContext = (opts: CreateContextOptions) => {
|
||||||
return {
|
return {
|
||||||
session: opts.session,
|
session: opts.session,
|
||||||
apiKey: opts.apiKey,
|
|
||||||
prisma,
|
prisma,
|
||||||
markAccessControlRun: noOp,
|
markAccessControlRun: noOp,
|
||||||
};
|
};
|
||||||
@@ -64,11 +62,8 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => {
|
|||||||
// Get the session from the server using the getServerSession wrapper function
|
// Get the session from the server using the getServerSession wrapper function
|
||||||
const session = await getServerAuthSession({ req, res });
|
const session = await getServerAuthSession({ req, res });
|
||||||
|
|
||||||
const apiKey = req.headers.authorization?.split(" ")[1] as string | null;
|
|
||||||
|
|
||||||
return createInnerTRPCContext({
|
return createInnerTRPCContext({
|
||||||
session,
|
session,
|
||||||
apiKey,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
import "dotenv/config";
|
import "dotenv/config";
|
||||||
import { openApiDocument } from "~/pages/api/openapi.json";
|
import { openApiDocument } from "~/pages/api/v1/openapi.json";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { execSync } from "child_process";
|
import { execSync } from "child_process";
|
||||||
|
|
||||||
console.log("Exporting public OpenAPI schema to client-libs/schema.json");
|
|
||||||
|
|
||||||
const scriptPath = import.meta.url.replace("file://", "");
|
const scriptPath = import.meta.url.replace("file://", "");
|
||||||
const clientLibsPath = path.join(path.dirname(scriptPath), "../../../../client-libs");
|
const clientLibsPath = path.join(path.dirname(scriptPath), "../../../../client-libs");
|
||||||
|
|
||||||
const schemaPath = path.join(clientLibsPath, "schema.json");
|
const schemaPath = path.join(clientLibsPath, "openapi.json");
|
||||||
|
|
||||||
|
console.log(`Exporting public OpenAPI schema to ${schemaPath}`);
|
||||||
|
|
||||||
console.log("Exporting schema");
|
|
||||||
fs.writeFileSync(schemaPath, JSON.stringify(openApiDocument, null, 2), "utf-8");
|
fs.writeFileSync(schemaPath, JSON.stringify(openApiDocument, null, 2), "utf-8");
|
||||||
|
|
||||||
console.log("Generating Typescript client");
|
console.log("Generating TypeScript client");
|
||||||
|
|
||||||
const tsClientPath = path.join(clientLibsPath, "typescript/codegen");
|
const tsClientPath = path.join(clientLibsPath, "typescript/src/codegen");
|
||||||
|
|
||||||
fs.rmSync(tsClientPath, { recursive: true, force: true });
|
fs.rmSync(tsClientPath, { recursive: true, force: true });
|
||||||
|
|
||||||
@@ -27,6 +26,8 @@ execSync(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("Done!");
|
console.log("Generating Python client");
|
||||||
|
|
||||||
process.exit(0);
|
execSync(path.join(clientLibsPath, "python/codegen.sh"));
|
||||||
|
|
||||||
|
console.log("Done!");
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ const requestUpdatedPromptFunction = async (
|
|||||||
originalModelProvider.inputSchema,
|
originalModelProvider.inputSchema,
|
||||||
null,
|
null,
|
||||||
2,
|
2,
|
||||||
)}\n\nDo not add any assistant messages.`,
|
)}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: "user",
|
role: "user",
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ import cryptoRandomString from "crypto-random-string";
|
|||||||
|
|
||||||
const KEY_LENGTH = 42;
|
const KEY_LENGTH = 42;
|
||||||
|
|
||||||
export const generateApiKey = () => `opc_${cryptoRandomString({ length: KEY_LENGTH })}`;
|
export const generateApiKey = () => `opk_${cryptoRandomString({ length: KEY_LENGTH })}`;
|
||||||
|
|||||||
@@ -1,13 +1,24 @@
|
|||||||
|
import { type ClientOptions } from "openai";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import OpenAI from "openpipe/src/openai";
|
||||||
|
|
||||||
import { env } from "~/env.mjs";
|
import { env } from "~/env.mjs";
|
||||||
|
|
||||||
import { default as OriginalOpenAI } from "openai";
|
let config: ClientOptions;
|
||||||
// import { OpenAI } from "openpipe";
|
|
||||||
|
|
||||||
const openAIConfig = { apiKey: env.OPENAI_API_KEY ?? "dummy-key" };
|
try {
|
||||||
|
// Allow developers to override the config with a local file
|
||||||
|
const jsonData = fs.readFileSync(
|
||||||
|
path.join(path.dirname(import.meta.url).replace("file://", ""), "./openaiCustomConfig.json"),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
config = JSON.parse(jsonData.toString());
|
||||||
|
} catch (error) {
|
||||||
|
// Set a dummy key so it doesn't fail at build time
|
||||||
|
config = { apiKey: env.OPENAI_API_KEY ?? "dummy-key" };
|
||||||
|
}
|
||||||
|
|
||||||
// Set a dummy key so it doesn't fail at build time
|
// export const openai = env.OPENPIPE_API_KEY ? new OpenAI.OpenAI(config) : new OriginalOpenAI(config);
|
||||||
// export const openai = env.OPENPIPE_API_KEY
|
|
||||||
// ? new OpenAI.OpenAI(openAIConfig)
|
|
||||||
// : new OriginalOpenAI(openAIConfig);
|
|
||||||
|
|
||||||
export const openai = new OriginalOpenAI(openAIConfig);
|
export const openai = new OpenAI(config);
|
||||||
|
|||||||
30
app/src/state/selectedLogsSlice.ts
Normal file
30
app/src/state/selectedLogsSlice.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { type SliceCreator } from "./store";
|
||||||
|
|
||||||
|
export const editorBackground = "#fafafa";
|
||||||
|
|
||||||
|
export type SelectedLogsSlice = {
|
||||||
|
selectedLogIds: Set<string>;
|
||||||
|
toggleSelectedLogId: (id: string) => void;
|
||||||
|
addSelectedLogIds: (ids: string[]) => void;
|
||||||
|
clearSelectedLogIds: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createSelectedLogsSlice: SliceCreator<SelectedLogsSlice> = (set, get) => ({
|
||||||
|
selectedLogIds: new Set(),
|
||||||
|
toggleSelectedLogId: (id: string) =>
|
||||||
|
set((state) => {
|
||||||
|
if (state.selectedLogs.selectedLogIds.has(id)) {
|
||||||
|
state.selectedLogs.selectedLogIds.delete(id);
|
||||||
|
} else {
|
||||||
|
state.selectedLogs.selectedLogIds.add(id);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
addSelectedLogIds: (ids: string[]) =>
|
||||||
|
set((state) => {
|
||||||
|
state.selectedLogs.selectedLogIds = new Set([...state.selectedLogs.selectedLogIds, ...ids]);
|
||||||
|
}),
|
||||||
|
clearSelectedLogIds: () =>
|
||||||
|
set((state) => {
|
||||||
|
state.selectedLogs.selectedLogIds = new Set();
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -81,8 +81,6 @@ export const createVariantEditorSlice: SliceCreator<SharedVariantEditorSlice> =
|
|||||||
};
|
};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
console.log(modelContents);
|
|
||||||
|
|
||||||
const scenariosModel = monaco.editor.getModel(monaco.Uri.parse("file:///scenarios.ts"));
|
const scenariosModel = monaco.editor.getModel(monaco.Uri.parse("file:///scenarios.ts"));
|
||||||
|
|
||||||
if (scenariosModel) {
|
if (scenariosModel) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { type StateCreator, create } from "zustand";
|
import { type StateCreator, create } from "zustand";
|
||||||
import { immer } from "zustand/middleware/immer";
|
import { immer } from "zustand/middleware/immer";
|
||||||
|
import { enableMapSet } from "immer";
|
||||||
import { persist } from "zustand/middleware";
|
import { persist } from "zustand/middleware";
|
||||||
import { createSelectors } from "./createSelectors";
|
import { createSelectors } from "./createSelectors";
|
||||||
import {
|
import {
|
||||||
@@ -8,6 +9,9 @@ import {
|
|||||||
} from "./sharedVariantEditor.slice";
|
} from "./sharedVariantEditor.slice";
|
||||||
import { type APIClient } from "~/utils/api";
|
import { type APIClient } from "~/utils/api";
|
||||||
import { persistOptions, type stateToPersist } from "./persist";
|
import { persistOptions, type stateToPersist } from "./persist";
|
||||||
|
import { type SelectedLogsSlice, createSelectedLogsSlice } from "./selectedLogsSlice";
|
||||||
|
|
||||||
|
enableMapSet();
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
drawerOpen: boolean;
|
drawerOpen: boolean;
|
||||||
@@ -17,7 +21,8 @@ export type State = {
|
|||||||
setApi: (api: APIClient) => void;
|
setApi: (api: APIClient) => void;
|
||||||
sharedVariantEditor: SharedVariantEditorSlice;
|
sharedVariantEditor: SharedVariantEditorSlice;
|
||||||
selectedProjectId: string | null;
|
selectedProjectId: string | null;
|
||||||
setselectedProjectId: (id: string) => void;
|
setSelectedProjectId: (id: string) => void;
|
||||||
|
selectedLogs: SelectedLogsSlice;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SliceCreator<T> = StateCreator<State, [["zustand/immer", never]], [], T>;
|
export type SliceCreator<T> = StateCreator<State, [["zustand/immer", never]], [], T>;
|
||||||
@@ -48,10 +53,11 @@ const useBaseStore = create<
|
|||||||
}),
|
}),
|
||||||
sharedVariantEditor: createVariantEditorSlice(set, get, ...rest),
|
sharedVariantEditor: createVariantEditorSlice(set, get, ...rest),
|
||||||
selectedProjectId: null,
|
selectedProjectId: null,
|
||||||
setselectedProjectId: (id: string) =>
|
setSelectedProjectId: (id: string) =>
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.selectedProjectId = id;
|
state.selectedProjectId = id;
|
||||||
}),
|
}),
|
||||||
|
selectedLogs: createSelectedLogsSlice(set, get, ...rest),
|
||||||
})),
|
})),
|
||||||
persistOptions,
|
persistOptions,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
import { type Session } from "next-auth";
|
|
||||||
import { useSession } from "next-auth/react";
|
|
||||||
import { useEffect } from "react";
|
|
||||||
|
|
||||||
import posthog from "posthog-js";
|
|
||||||
import { env } from "~/env.mjs";
|
|
||||||
|
|
||||||
// Make sure we're in the browser
|
|
||||||
const enableBrowserAnalytics = typeof window !== "undefined";
|
|
||||||
|
|
||||||
if (env.NEXT_PUBLIC_POSTHOG_KEY && enableBrowserAnalytics) {
|
|
||||||
posthog.init(env.NEXT_PUBLIC_POSTHOG_KEY, {
|
|
||||||
api_host: `${env.NEXT_PUBLIC_HOST}/ingest`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const identifySession = (session: Session) => {
|
|
||||||
if (!session.user) return;
|
|
||||||
posthog.identify(session.user.id, {
|
|
||||||
name: session.user.name,
|
|
||||||
email: session.user.email,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SessionIdentifier = () => {
|
|
||||||
const session = useSession().data;
|
|
||||||
useEffect(() => {
|
|
||||||
if (session && enableBrowserAnalytics) identifySession(session);
|
|
||||||
}, [session]);
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
41
app/src/utils/analytics/posthog.tsx
Normal file
41
app/src/utils/analytics/posthog.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
"use client";
|
||||||
|
import { useSession } from "next-auth/react";
|
||||||
|
import React, { type ReactNode, useEffect } from "react";
|
||||||
|
import { PostHogProvider } from "posthog-js/react";
|
||||||
|
|
||||||
|
import posthog from "posthog-js";
|
||||||
|
import { env } from "~/env.mjs";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
// Make sure we're in the browser
|
||||||
|
const inBrowser = typeof window !== "undefined";
|
||||||
|
|
||||||
|
export const PosthogAppProvider = ({ children }: { children: ReactNode }) => {
|
||||||
|
const session = useSession().data;
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Track page views
|
||||||
|
const handleRouteChange = () => posthog?.capture("$pageview");
|
||||||
|
router.events.on("routeChangeComplete", handleRouteChange);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
router.events.off("routeChangeComplete", handleRouteChange);
|
||||||
|
};
|
||||||
|
}, [router.events]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (env.NEXT_PUBLIC_POSTHOG_KEY && inBrowser && session && session.user) {
|
||||||
|
posthog.init(env.NEXT_PUBLIC_POSTHOG_KEY, {
|
||||||
|
api_host: `${env.NEXT_PUBLIC_HOST}/ingest`,
|
||||||
|
});
|
||||||
|
|
||||||
|
posthog.identify(session.user.id, {
|
||||||
|
name: session.user.name,
|
||||||
|
email: session.user.email,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [session]);
|
||||||
|
|
||||||
|
return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
|
||||||
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { type RefObject, useCallback, useEffect, useRef, useState } from "react";
|
import { type RefObject, useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { NumberParam, useQueryParam, withDefault } from "use-query-params";
|
import { NumberParam, useQueryParams } from "use-query-params";
|
||||||
import { useAppStore } from "~/state/store";
|
import { useAppStore } from "~/state/store";
|
||||||
|
|
||||||
export const useExperiments = () => {
|
export const useExperiments = () => {
|
||||||
@@ -46,10 +46,10 @@ export const useDataset = () => {
|
|||||||
|
|
||||||
export const useDatasetEntries = () => {
|
export const useDatasetEntries = () => {
|
||||||
const dataset = useDataset();
|
const dataset = useDataset();
|
||||||
const [page] = usePage();
|
const { page, pageSize } = usePageParams();
|
||||||
|
|
||||||
return api.datasetEntries.list.useQuery(
|
return api.datasetEntries.list.useQuery(
|
||||||
{ datasetId: dataset.data?.id ?? "", page },
|
{ datasetId: dataset.data?.id ?? "", page, pageSize },
|
||||||
{ enabled: dataset.data?.id != null },
|
{ enabled: dataset.data?.id != null },
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -132,14 +132,23 @@ export const useElementDimensions = (): [RefObject<HTMLElement>, Dimensions | un
|
|||||||
return [ref, dimensions];
|
return [ref, dimensions];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const usePage = () => useQueryParam("page", withDefault(NumberParam, 1));
|
export const usePageParams = () => {
|
||||||
|
const [pageParams, setPageParams] = useQueryParams({
|
||||||
|
page: NumberParam,
|
||||||
|
pageSize: NumberParam,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { page, pageSize } = pageParams;
|
||||||
|
|
||||||
|
return { page: page || 1, pageSize: pageSize || 10, setPageParams };
|
||||||
|
};
|
||||||
|
|
||||||
export const useScenarios = () => {
|
export const useScenarios = () => {
|
||||||
const experiment = useExperiment();
|
const experiment = useExperiment();
|
||||||
const [page] = usePage();
|
const { page, pageSize } = usePageParams();
|
||||||
|
|
||||||
return api.scenarios.list.useQuery(
|
return api.scenarios.list.useQuery(
|
||||||
{ experimentId: experiment.data?.id ?? "", page },
|
{ experimentId: experiment.data?.id ?? "", page, pageSize },
|
||||||
{ enabled: experiment.data?.id != null },
|
{ enabled: experiment.data?.id != null },
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -166,3 +175,13 @@ export const useScenarioVars = () => {
|
|||||||
{ enabled: experiment.data?.id != null },
|
{ enabled: experiment.data?.id != null },
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useLoggedCalls = () => {
|
||||||
|
const selectedProjectId = useAppStore((state) => state.selectedProjectId);
|
||||||
|
const { page, pageSize } = usePageParams();
|
||||||
|
|
||||||
|
return api.loggedCalls.list.useQuery(
|
||||||
|
{ projectId: selectedProjectId ?? "", page, pageSize },
|
||||||
|
{ enabled: !!selectedProjectId },
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
9
app/test-docker.sh
Executable file
9
app/test-docker.sh
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
source app/.env
|
||||||
|
|
||||||
|
docker build . --file app/Dockerfile
|
||||||
@@ -25,11 +25,11 @@
|
|||||||
".eslintrc.cjs",
|
".eslintrc.cjs",
|
||||||
"next-env.d.ts",
|
"next-env.d.ts",
|
||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
|
"**/*.mts",
|
||||||
"**/*.tsx",
|
"**/*.tsx",
|
||||||
"**/*.cjs",
|
"**/*.cjs",
|
||||||
"**/*.mjs",
|
"**/*.mjs",
|
||||||
"**/*.js",
|
"**/*.js"
|
||||||
"src/pages/api/sentry-example-api.js"
|
|
||||||
],
|
],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|||||||
275
client-libs/openapi.json
Normal file
275
client-libs/openapi.json
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.0.3",
|
||||||
|
"info": {
|
||||||
|
"title": "OpenPipe API",
|
||||||
|
"description": "The public API for reporting API calls to OpenPipe",
|
||||||
|
"version": "0.1.1"
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "https://app.openpipe.ai/api/v1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/check-cache": {
|
||||||
|
"post": {
|
||||||
|
"operationId": "checkCache",
|
||||||
|
"description": "Check if a prompt is cached",
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Authorization": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"requestedAt": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Unix timestamp in milliseconds"
|
||||||
|
},
|
||||||
|
"reqPayload": {
|
||||||
|
"description": "JSON-encoded request payload"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }",
|
||||||
|
"default": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"requestedAt"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"respPayload": {
|
||||||
|
"description": "JSON-encoded response payload"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"$ref": "#/components/responses/error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/report": {
|
||||||
|
"post": {
|
||||||
|
"operationId": "report",
|
||||||
|
"description": "Report an API call",
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Authorization": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"requestedAt": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Unix timestamp in milliseconds"
|
||||||
|
},
|
||||||
|
"receivedAt": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Unix timestamp in milliseconds"
|
||||||
|
},
|
||||||
|
"reqPayload": {
|
||||||
|
"description": "JSON-encoded request payload"
|
||||||
|
},
|
||||||
|
"respPayload": {
|
||||||
|
"description": "JSON-encoded response payload"
|
||||||
|
},
|
||||||
|
"statusCode": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "HTTP status code of response"
|
||||||
|
},
|
||||||
|
"errorMessage": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "User-friendly error message"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }",
|
||||||
|
"default": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"requestedAt",
|
||||||
|
"receivedAt"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"$ref": "#/components/responses/error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/local-testing-only-get-latest-logged-call": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "localTestingOnlyGetLatestLoggedCall",
|
||||||
|
"description": "Get the latest logged call (only for local testing)",
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Authorization": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"createdAt": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
},
|
||||||
|
"cacheHit": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"modelResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"statusCode": {
|
||||||
|
"type": "number",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"errorMessage": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"reqPayload": {},
|
||||||
|
"respPayload": {}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"statusCode",
|
||||||
|
"errorMessage"
|
||||||
|
],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"createdAt",
|
||||||
|
"cacheHit",
|
||||||
|
"tags",
|
||||||
|
"modelResponse"
|
||||||
|
],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"$ref": "#/components/responses/error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"securitySchemes": {
|
||||||
|
"Authorization": {
|
||||||
|
"type": "http",
|
||||||
|
"scheme": "bearer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"error": {
|
||||||
|
"description": "Error response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"code": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"issues": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"message"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"message",
|
||||||
|
"code"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ set -e
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
poetry run openapi-python-client generate --url http://localhost:3000/api/openapi.json
|
poetry run openapi-python-client generate --path ../openapi.json
|
||||||
|
|
||||||
rm -rf openpipe/api_client
|
rm -rf openpipe/api_client
|
||||||
mv open-pipe-api-client/open_pipe_api_client openpipe/api_client
|
mv open-pipe-api-client/open_pipe_api_client openpipe/api_client
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import httpx
|
|||||||
|
|
||||||
from ... import errors
|
from ... import errors
|
||||||
from ...client import AuthenticatedClient, Client
|
from ...client import AuthenticatedClient, Client
|
||||||
from ...models.external_api_check_cache_json_body import ExternalApiCheckCacheJsonBody
|
from ...models.check_cache_json_body import CheckCacheJsonBody
|
||||||
from ...models.external_api_check_cache_response_200 import ExternalApiCheckCacheResponse200
|
from ...models.check_cache_response_200 import CheckCacheResponse200
|
||||||
from ...types import Response
|
from ...types import Response
|
||||||
|
|
||||||
|
|
||||||
def _get_kwargs(
|
def _get_kwargs(
|
||||||
*,
|
*,
|
||||||
json_body: ExternalApiCheckCacheJsonBody,
|
json_body: CheckCacheJsonBody,
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -20,16 +20,16 @@ def _get_kwargs(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"method": "post",
|
"method": "post",
|
||||||
"url": "/v1/check-cache",
|
"url": "/check-cache",
|
||||||
"json": json_json_body,
|
"json": json_json_body,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _parse_response(
|
def _parse_response(
|
||||||
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
||||||
) -> Optional[ExternalApiCheckCacheResponse200]:
|
) -> Optional[CheckCacheResponse200]:
|
||||||
if response.status_code == HTTPStatus.OK:
|
if response.status_code == HTTPStatus.OK:
|
||||||
response_200 = ExternalApiCheckCacheResponse200.from_dict(response.json())
|
response_200 = CheckCacheResponse200.from_dict(response.json())
|
||||||
|
|
||||||
return response_200
|
return response_200
|
||||||
if client.raise_on_unexpected_status:
|
if client.raise_on_unexpected_status:
|
||||||
@@ -40,7 +40,7 @@ def _parse_response(
|
|||||||
|
|
||||||
def _build_response(
|
def _build_response(
|
||||||
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
||||||
) -> Response[ExternalApiCheckCacheResponse200]:
|
) -> Response[CheckCacheResponse200]:
|
||||||
return Response(
|
return Response(
|
||||||
status_code=HTTPStatus(response.status_code),
|
status_code=HTTPStatus(response.status_code),
|
||||||
content=response.content,
|
content=response.content,
|
||||||
@@ -52,19 +52,19 @@ def _build_response(
|
|||||||
def sync_detailed(
|
def sync_detailed(
|
||||||
*,
|
*,
|
||||||
client: AuthenticatedClient,
|
client: AuthenticatedClient,
|
||||||
json_body: ExternalApiCheckCacheJsonBody,
|
json_body: CheckCacheJsonBody,
|
||||||
) -> Response[ExternalApiCheckCacheResponse200]:
|
) -> Response[CheckCacheResponse200]:
|
||||||
"""Check if a prompt is cached
|
"""Check if a prompt is cached
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
json_body (ExternalApiCheckCacheJsonBody):
|
json_body (CheckCacheJsonBody):
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Response[ExternalApiCheckCacheResponse200]
|
Response[CheckCacheResponse200]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
kwargs = _get_kwargs(
|
kwargs = _get_kwargs(
|
||||||
@@ -81,19 +81,19 @@ def sync_detailed(
|
|||||||
def sync(
|
def sync(
|
||||||
*,
|
*,
|
||||||
client: AuthenticatedClient,
|
client: AuthenticatedClient,
|
||||||
json_body: ExternalApiCheckCacheJsonBody,
|
json_body: CheckCacheJsonBody,
|
||||||
) -> Optional[ExternalApiCheckCacheResponse200]:
|
) -> Optional[CheckCacheResponse200]:
|
||||||
"""Check if a prompt is cached
|
"""Check if a prompt is cached
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
json_body (ExternalApiCheckCacheJsonBody):
|
json_body (CheckCacheJsonBody):
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
ExternalApiCheckCacheResponse200
|
CheckCacheResponse200
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return sync_detailed(
|
return sync_detailed(
|
||||||
@@ -105,19 +105,19 @@ def sync(
|
|||||||
async def asyncio_detailed(
|
async def asyncio_detailed(
|
||||||
*,
|
*,
|
||||||
client: AuthenticatedClient,
|
client: AuthenticatedClient,
|
||||||
json_body: ExternalApiCheckCacheJsonBody,
|
json_body: CheckCacheJsonBody,
|
||||||
) -> Response[ExternalApiCheckCacheResponse200]:
|
) -> Response[CheckCacheResponse200]:
|
||||||
"""Check if a prompt is cached
|
"""Check if a prompt is cached
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
json_body (ExternalApiCheckCacheJsonBody):
|
json_body (CheckCacheJsonBody):
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Response[ExternalApiCheckCacheResponse200]
|
Response[CheckCacheResponse200]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
kwargs = _get_kwargs(
|
kwargs = _get_kwargs(
|
||||||
@@ -132,19 +132,19 @@ async def asyncio_detailed(
|
|||||||
async def asyncio(
|
async def asyncio(
|
||||||
*,
|
*,
|
||||||
client: AuthenticatedClient,
|
client: AuthenticatedClient,
|
||||||
json_body: ExternalApiCheckCacheJsonBody,
|
json_body: CheckCacheJsonBody,
|
||||||
) -> Optional[ExternalApiCheckCacheResponse200]:
|
) -> Optional[CheckCacheResponse200]:
|
||||||
"""Check if a prompt is cached
|
"""Check if a prompt is cached
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
json_body (ExternalApiCheckCacheJsonBody):
|
json_body (CheckCacheJsonBody):
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
ExternalApiCheckCacheResponse200
|
CheckCacheResponse200
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
from http import HTTPStatus
|
||||||
|
from typing import Any, Dict, Optional, Union
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
from ... import errors
|
||||||
|
from ...client import AuthenticatedClient, Client
|
||||||
|
from ...models.local_testing_only_get_latest_logged_call_response_200 import (
|
||||||
|
LocalTestingOnlyGetLatestLoggedCallResponse200,
|
||||||
|
)
|
||||||
|
from ...types import Response
|
||||||
|
|
||||||
|
|
||||||
|
def _get_kwargs() -> Dict[str, Any]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return {
|
||||||
|
"method": "get",
|
||||||
|
"url": "/local-testing-only-get-latest-logged-call",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_response(
|
||||||
|
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
||||||
|
) -> Optional[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]:
|
||||||
|
if response.status_code == HTTPStatus.OK:
|
||||||
|
_response_200 = response.json()
|
||||||
|
response_200: Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]
|
||||||
|
if _response_200 is None:
|
||||||
|
response_200 = None
|
||||||
|
else:
|
||||||
|
response_200 = LocalTestingOnlyGetLatestLoggedCallResponse200.from_dict(_response_200)
|
||||||
|
|
||||||
|
return response_200
|
||||||
|
if client.raise_on_unexpected_status:
|
||||||
|
raise errors.UnexpectedStatus(response.status_code, response.content)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _build_response(
|
||||||
|
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
||||||
|
) -> Response[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]:
|
||||||
|
return Response(
|
||||||
|
status_code=HTTPStatus(response.status_code),
|
||||||
|
content=response.content,
|
||||||
|
headers=response.headers,
|
||||||
|
parsed=_parse_response(client=client, response=response),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def sync_detailed(
|
||||||
|
*,
|
||||||
|
client: AuthenticatedClient,
|
||||||
|
) -> Response[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]:
|
||||||
|
"""Get the latest logged call (only for local testing)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]
|
||||||
|
"""
|
||||||
|
|
||||||
|
kwargs = _get_kwargs()
|
||||||
|
|
||||||
|
response = client.get_httpx_client().request(
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
return _build_response(client=client, response=response)
|
||||||
|
|
||||||
|
|
||||||
|
def sync(
|
||||||
|
*,
|
||||||
|
client: AuthenticatedClient,
|
||||||
|
) -> Optional[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]:
|
||||||
|
"""Get the latest logged call (only for local testing)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]
|
||||||
|
"""
|
||||||
|
|
||||||
|
return sync_detailed(
|
||||||
|
client=client,
|
||||||
|
).parsed
|
||||||
|
|
||||||
|
|
||||||
|
async def asyncio_detailed(
|
||||||
|
*,
|
||||||
|
client: AuthenticatedClient,
|
||||||
|
) -> Response[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]:
|
||||||
|
"""Get the latest logged call (only for local testing)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]
|
||||||
|
"""
|
||||||
|
|
||||||
|
kwargs = _get_kwargs()
|
||||||
|
|
||||||
|
response = await client.get_async_httpx_client().request(**kwargs)
|
||||||
|
|
||||||
|
return _build_response(client=client, response=response)
|
||||||
|
|
||||||
|
|
||||||
|
async def asyncio(
|
||||||
|
*,
|
||||||
|
client: AuthenticatedClient,
|
||||||
|
) -> Optional[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]:
|
||||||
|
"""Get the latest logged call (only for local testing)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]
|
||||||
|
"""
|
||||||
|
|
||||||
|
return (
|
||||||
|
await asyncio_detailed(
|
||||||
|
client=client,
|
||||||
|
)
|
||||||
|
).parsed
|
||||||
@@ -5,13 +5,13 @@ import httpx
|
|||||||
|
|
||||||
from ... import errors
|
from ... import errors
|
||||||
from ...client import AuthenticatedClient, Client
|
from ...client import AuthenticatedClient, Client
|
||||||
from ...models.external_api_report_json_body import ExternalApiReportJsonBody
|
from ...models.report_json_body import ReportJsonBody
|
||||||
from ...types import Response
|
from ...types import Response
|
||||||
|
|
||||||
|
|
||||||
def _get_kwargs(
|
def _get_kwargs(
|
||||||
*,
|
*,
|
||||||
json_body: ExternalApiReportJsonBody,
|
json_body: ReportJsonBody,
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ def _get_kwargs(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"method": "post",
|
"method": "post",
|
||||||
"url": "/v1/report",
|
"url": "/report",
|
||||||
"json": json_json_body,
|
"json": json_json_body,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,12 +45,12 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt
|
|||||||
def sync_detailed(
|
def sync_detailed(
|
||||||
*,
|
*,
|
||||||
client: AuthenticatedClient,
|
client: AuthenticatedClient,
|
||||||
json_body: ExternalApiReportJsonBody,
|
json_body: ReportJsonBody,
|
||||||
) -> Response[Any]:
|
) -> Response[Any]:
|
||||||
"""Report an API call
|
"""Report an API call
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
json_body (ExternalApiReportJsonBody):
|
json_body (ReportJsonBody):
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
@@ -74,12 +74,12 @@ def sync_detailed(
|
|||||||
async def asyncio_detailed(
|
async def asyncio_detailed(
|
||||||
*,
|
*,
|
||||||
client: AuthenticatedClient,
|
client: AuthenticatedClient,
|
||||||
json_body: ExternalApiReportJsonBody,
|
json_body: ReportJsonBody,
|
||||||
) -> Response[Any]:
|
) -> Response[Any]:
|
||||||
"""Report an API call
|
"""Report an API call
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
json_body (ExternalApiReportJsonBody):
|
json_body (ReportJsonBody):
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
@@ -1,15 +1,25 @@
|
|||||||
""" Contains all the data models used in inputs/outputs """
|
""" Contains all the data models used in inputs/outputs """
|
||||||
|
|
||||||
from .external_api_check_cache_json_body import ExternalApiCheckCacheJsonBody
|
from .check_cache_json_body import CheckCacheJsonBody
|
||||||
from .external_api_check_cache_json_body_tags import ExternalApiCheckCacheJsonBodyTags
|
from .check_cache_json_body_tags import CheckCacheJsonBodyTags
|
||||||
from .external_api_check_cache_response_200 import ExternalApiCheckCacheResponse200
|
from .check_cache_response_200 import CheckCacheResponse200
|
||||||
from .external_api_report_json_body import ExternalApiReportJsonBody
|
from .local_testing_only_get_latest_logged_call_response_200 import LocalTestingOnlyGetLatestLoggedCallResponse200
|
||||||
from .external_api_report_json_body_tags import ExternalApiReportJsonBodyTags
|
from .local_testing_only_get_latest_logged_call_response_200_model_response import (
|
||||||
|
LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse,
|
||||||
|
)
|
||||||
|
from .local_testing_only_get_latest_logged_call_response_200_tags import (
|
||||||
|
LocalTestingOnlyGetLatestLoggedCallResponse200Tags,
|
||||||
|
)
|
||||||
|
from .report_json_body import ReportJsonBody
|
||||||
|
from .report_json_body_tags import ReportJsonBodyTags
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
"ExternalApiCheckCacheJsonBody",
|
"CheckCacheJsonBody",
|
||||||
"ExternalApiCheckCacheJsonBodyTags",
|
"CheckCacheJsonBodyTags",
|
||||||
"ExternalApiCheckCacheResponse200",
|
"CheckCacheResponse200",
|
||||||
"ExternalApiReportJsonBody",
|
"LocalTestingOnlyGetLatestLoggedCallResponse200",
|
||||||
"ExternalApiReportJsonBodyTags",
|
"LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse",
|
||||||
|
"LocalTestingOnlyGetLatestLoggedCallResponse200Tags",
|
||||||
|
"ReportJsonBody",
|
||||||
|
"ReportJsonBodyTags",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,25 +5,25 @@ from attrs import define
|
|||||||
from ..types import UNSET, Unset
|
from ..types import UNSET, Unset
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..models.external_api_check_cache_json_body_tags import ExternalApiCheckCacheJsonBodyTags
|
from ..models.check_cache_json_body_tags import CheckCacheJsonBodyTags
|
||||||
|
|
||||||
|
|
||||||
T = TypeVar("T", bound="ExternalApiCheckCacheJsonBody")
|
T = TypeVar("T", bound="CheckCacheJsonBody")
|
||||||
|
|
||||||
|
|
||||||
@define
|
@define
|
||||||
class ExternalApiCheckCacheJsonBody:
|
class CheckCacheJsonBody:
|
||||||
"""
|
"""
|
||||||
Attributes:
|
Attributes:
|
||||||
requested_at (float): Unix timestamp in milliseconds
|
requested_at (float): Unix timestamp in milliseconds
|
||||||
req_payload (Union[Unset, Any]): JSON-encoded request payload
|
req_payload (Union[Unset, Any]): JSON-encoded request payload
|
||||||
tags (Union[Unset, ExternalApiCheckCacheJsonBodyTags]): Extra tags to attach to the call for filtering. Eg {
|
tags (Union[Unset, CheckCacheJsonBodyTags]): Extra tags to attach to the call for filtering. Eg { "userId":
|
||||||
"userId": "123", "promptId": "populate-title" }
|
"123", "promptId": "populate-title" }
|
||||||
"""
|
"""
|
||||||
|
|
||||||
requested_at: float
|
requested_at: float
|
||||||
req_payload: Union[Unset, Any] = UNSET
|
req_payload: Union[Unset, Any] = UNSET
|
||||||
tags: Union[Unset, "ExternalApiCheckCacheJsonBodyTags"] = UNSET
|
tags: Union[Unset, "CheckCacheJsonBodyTags"] = UNSET
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
requested_at = self.requested_at
|
requested_at = self.requested_at
|
||||||
@@ -47,7 +47,7 @@ class ExternalApiCheckCacheJsonBody:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||||
from ..models.external_api_check_cache_json_body_tags import ExternalApiCheckCacheJsonBodyTags
|
from ..models.check_cache_json_body_tags import CheckCacheJsonBodyTags
|
||||||
|
|
||||||
d = src_dict.copy()
|
d = src_dict.copy()
|
||||||
requested_at = d.pop("requestedAt")
|
requested_at = d.pop("requestedAt")
|
||||||
@@ -55,16 +55,16 @@ class ExternalApiCheckCacheJsonBody:
|
|||||||
req_payload = d.pop("reqPayload", UNSET)
|
req_payload = d.pop("reqPayload", UNSET)
|
||||||
|
|
||||||
_tags = d.pop("tags", UNSET)
|
_tags = d.pop("tags", UNSET)
|
||||||
tags: Union[Unset, ExternalApiCheckCacheJsonBodyTags]
|
tags: Union[Unset, CheckCacheJsonBodyTags]
|
||||||
if isinstance(_tags, Unset):
|
if isinstance(_tags, Unset):
|
||||||
tags = UNSET
|
tags = UNSET
|
||||||
else:
|
else:
|
||||||
tags = ExternalApiCheckCacheJsonBodyTags.from_dict(_tags)
|
tags = CheckCacheJsonBodyTags.from_dict(_tags)
|
||||||
|
|
||||||
external_api_check_cache_json_body = cls(
|
check_cache_json_body = cls(
|
||||||
requested_at=requested_at,
|
requested_at=requested_at,
|
||||||
req_payload=req_payload,
|
req_payload=req_payload,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
)
|
)
|
||||||
|
|
||||||
return external_api_check_cache_json_body
|
return check_cache_json_body
|
||||||
@@ -2,11 +2,11 @@ from typing import Any, Dict, List, Type, TypeVar
|
|||||||
|
|
||||||
from attrs import define, field
|
from attrs import define, field
|
||||||
|
|
||||||
T = TypeVar("T", bound="ExternalApiReportJsonBodyTags")
|
T = TypeVar("T", bound="CheckCacheJsonBodyTags")
|
||||||
|
|
||||||
|
|
||||||
@define
|
@define
|
||||||
class ExternalApiReportJsonBodyTags:
|
class CheckCacheJsonBodyTags:
|
||||||
"""Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }"""
|
"""Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }"""
|
||||||
|
|
||||||
additional_properties: Dict[str, str] = field(init=False, factory=dict)
|
additional_properties: Dict[str, str] = field(init=False, factory=dict)
|
||||||
@@ -21,10 +21,10 @@ class ExternalApiReportJsonBodyTags:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||||
d = src_dict.copy()
|
d = src_dict.copy()
|
||||||
external_api_report_json_body_tags = cls()
|
check_cache_json_body_tags = cls()
|
||||||
|
|
||||||
external_api_report_json_body_tags.additional_properties = d
|
check_cache_json_body_tags.additional_properties = d
|
||||||
return external_api_report_json_body_tags
|
return check_cache_json_body_tags
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def additional_keys(self) -> List[str]:
|
def additional_keys(self) -> List[str]:
|
||||||
@@ -4,11 +4,11 @@ from attrs import define
|
|||||||
|
|
||||||
from ..types import UNSET, Unset
|
from ..types import UNSET, Unset
|
||||||
|
|
||||||
T = TypeVar("T", bound="ExternalApiCheckCacheResponse200")
|
T = TypeVar("T", bound="CheckCacheResponse200")
|
||||||
|
|
||||||
|
|
||||||
@define
|
@define
|
||||||
class ExternalApiCheckCacheResponse200:
|
class CheckCacheResponse200:
|
||||||
"""
|
"""
|
||||||
Attributes:
|
Attributes:
|
||||||
resp_payload (Union[Unset, Any]): JSON-encoded response payload
|
resp_payload (Union[Unset, Any]): JSON-encoded response payload
|
||||||
@@ -31,8 +31,8 @@ class ExternalApiCheckCacheResponse200:
|
|||||||
d = src_dict.copy()
|
d = src_dict.copy()
|
||||||
resp_payload = d.pop("respPayload", UNSET)
|
resp_payload = d.pop("respPayload", UNSET)
|
||||||
|
|
||||||
external_api_check_cache_response_200 = cls(
|
check_cache_response_200 = cls(
|
||||||
resp_payload=resp_payload,
|
resp_payload=resp_payload,
|
||||||
)
|
)
|
||||||
|
|
||||||
return external_api_check_cache_response_200
|
return check_cache_response_200
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import datetime
|
||||||
|
from typing import TYPE_CHECKING, Any, Dict, Optional, Type, TypeVar
|
||||||
|
|
||||||
|
from attrs import define
|
||||||
|
from dateutil.parser import isoparse
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from ..models.local_testing_only_get_latest_logged_call_response_200_model_response import (
|
||||||
|
LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse,
|
||||||
|
)
|
||||||
|
from ..models.local_testing_only_get_latest_logged_call_response_200_tags import (
|
||||||
|
LocalTestingOnlyGetLatestLoggedCallResponse200Tags,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
T = TypeVar("T", bound="LocalTestingOnlyGetLatestLoggedCallResponse200")
|
||||||
|
|
||||||
|
|
||||||
|
@define
|
||||||
|
class LocalTestingOnlyGetLatestLoggedCallResponse200:
|
||||||
|
"""
|
||||||
|
Attributes:
|
||||||
|
created_at (datetime.datetime):
|
||||||
|
cache_hit (bool):
|
||||||
|
tags (LocalTestingOnlyGetLatestLoggedCallResponse200Tags):
|
||||||
|
model_response (Optional[LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse]):
|
||||||
|
"""
|
||||||
|
|
||||||
|
created_at: datetime.datetime
|
||||||
|
cache_hit: bool
|
||||||
|
tags: "LocalTestingOnlyGetLatestLoggedCallResponse200Tags"
|
||||||
|
model_response: Optional["LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse"]
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
created_at = self.created_at.isoformat()
|
||||||
|
|
||||||
|
cache_hit = self.cache_hit
|
||||||
|
tags = self.tags.to_dict()
|
||||||
|
|
||||||
|
model_response = self.model_response.to_dict() if self.model_response else None
|
||||||
|
|
||||||
|
field_dict: Dict[str, Any] = {}
|
||||||
|
field_dict.update(
|
||||||
|
{
|
||||||
|
"createdAt": created_at,
|
||||||
|
"cacheHit": cache_hit,
|
||||||
|
"tags": tags,
|
||||||
|
"modelResponse": model_response,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return field_dict
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||||
|
from ..models.local_testing_only_get_latest_logged_call_response_200_model_response import (
|
||||||
|
LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse,
|
||||||
|
)
|
||||||
|
from ..models.local_testing_only_get_latest_logged_call_response_200_tags import (
|
||||||
|
LocalTestingOnlyGetLatestLoggedCallResponse200Tags,
|
||||||
|
)
|
||||||
|
|
||||||
|
d = src_dict.copy()
|
||||||
|
created_at = isoparse(d.pop("createdAt"))
|
||||||
|
|
||||||
|
cache_hit = d.pop("cacheHit")
|
||||||
|
|
||||||
|
tags = LocalTestingOnlyGetLatestLoggedCallResponse200Tags.from_dict(d.pop("tags"))
|
||||||
|
|
||||||
|
_model_response = d.pop("modelResponse")
|
||||||
|
model_response: Optional[LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse]
|
||||||
|
if _model_response is None:
|
||||||
|
model_response = None
|
||||||
|
else:
|
||||||
|
model_response = LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse.from_dict(_model_response)
|
||||||
|
|
||||||
|
local_testing_only_get_latest_logged_call_response_200 = cls(
|
||||||
|
created_at=created_at,
|
||||||
|
cache_hit=cache_hit,
|
||||||
|
tags=tags,
|
||||||
|
model_response=model_response,
|
||||||
|
)
|
||||||
|
|
||||||
|
return local_testing_only_get_latest_logged_call_response_200
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
from typing import Any, Dict, Optional, Type, TypeVar, Union
|
||||||
|
|
||||||
|
from attrs import define
|
||||||
|
|
||||||
|
from ..types import UNSET, Unset
|
||||||
|
|
||||||
|
T = TypeVar("T", bound="LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse")
|
||||||
|
|
||||||
|
|
||||||
|
@define
|
||||||
|
class LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse:
|
||||||
|
"""
|
||||||
|
Attributes:
|
||||||
|
id (str):
|
||||||
|
status_code (Optional[float]):
|
||||||
|
error_message (Optional[str]):
|
||||||
|
req_payload (Union[Unset, Any]):
|
||||||
|
resp_payload (Union[Unset, Any]):
|
||||||
|
"""
|
||||||
|
|
||||||
|
id: str
|
||||||
|
status_code: Optional[float]
|
||||||
|
error_message: Optional[str]
|
||||||
|
req_payload: Union[Unset, Any] = UNSET
|
||||||
|
resp_payload: Union[Unset, Any] = UNSET
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
id = self.id
|
||||||
|
status_code = self.status_code
|
||||||
|
error_message = self.error_message
|
||||||
|
req_payload = self.req_payload
|
||||||
|
resp_payload = self.resp_payload
|
||||||
|
|
||||||
|
field_dict: Dict[str, Any] = {}
|
||||||
|
field_dict.update(
|
||||||
|
{
|
||||||
|
"id": id,
|
||||||
|
"statusCode": status_code,
|
||||||
|
"errorMessage": error_message,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if req_payload is not UNSET:
|
||||||
|
field_dict["reqPayload"] = req_payload
|
||||||
|
if resp_payload is not UNSET:
|
||||||
|
field_dict["respPayload"] = resp_payload
|
||||||
|
|
||||||
|
return field_dict
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||||
|
d = src_dict.copy()
|
||||||
|
id = d.pop("id")
|
||||||
|
|
||||||
|
status_code = d.pop("statusCode")
|
||||||
|
|
||||||
|
error_message = d.pop("errorMessage")
|
||||||
|
|
||||||
|
req_payload = d.pop("reqPayload", UNSET)
|
||||||
|
|
||||||
|
resp_payload = d.pop("respPayload", UNSET)
|
||||||
|
|
||||||
|
local_testing_only_get_latest_logged_call_response_200_model_response = cls(
|
||||||
|
id=id,
|
||||||
|
status_code=status_code,
|
||||||
|
error_message=error_message,
|
||||||
|
req_payload=req_payload,
|
||||||
|
resp_payload=resp_payload,
|
||||||
|
)
|
||||||
|
|
||||||
|
return local_testing_only_get_latest_logged_call_response_200_model_response
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
from typing import Any, Dict, List, Optional, Type, TypeVar
|
||||||
|
|
||||||
|
from attrs import define, field
|
||||||
|
|
||||||
|
T = TypeVar("T", bound="LocalTestingOnlyGetLatestLoggedCallResponse200Tags")
|
||||||
|
|
||||||
|
|
||||||
|
@define
|
||||||
|
class LocalTestingOnlyGetLatestLoggedCallResponse200Tags:
|
||||||
|
""" """
|
||||||
|
|
||||||
|
additional_properties: Dict[str, Optional[str]] = field(init=False, factory=dict)
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
field_dict: Dict[str, Any] = {}
|
||||||
|
field_dict.update(self.additional_properties)
|
||||||
|
field_dict.update({})
|
||||||
|
|
||||||
|
return field_dict
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||||
|
d = src_dict.copy()
|
||||||
|
local_testing_only_get_latest_logged_call_response_200_tags = cls()
|
||||||
|
|
||||||
|
local_testing_only_get_latest_logged_call_response_200_tags.additional_properties = d
|
||||||
|
return local_testing_only_get_latest_logged_call_response_200_tags
|
||||||
|
|
||||||
|
@property
|
||||||
|
def additional_keys(self) -> List[str]:
|
||||||
|
return list(self.additional_properties.keys())
|
||||||
|
|
||||||
|
def __getitem__(self, key: str) -> Optional[str]:
|
||||||
|
return self.additional_properties[key]
|
||||||
|
|
||||||
|
def __setitem__(self, key: str, value: Optional[str]) -> None:
|
||||||
|
self.additional_properties[key] = value
|
||||||
|
|
||||||
|
def __delitem__(self, key: str) -> None:
|
||||||
|
del self.additional_properties[key]
|
||||||
|
|
||||||
|
def __contains__(self, key: str) -> bool:
|
||||||
|
return key in self.additional_properties
|
||||||
@@ -5,14 +5,14 @@ from attrs import define
|
|||||||
from ..types import UNSET, Unset
|
from ..types import UNSET, Unset
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..models.external_api_report_json_body_tags import ExternalApiReportJsonBodyTags
|
from ..models.report_json_body_tags import ReportJsonBodyTags
|
||||||
|
|
||||||
|
|
||||||
T = TypeVar("T", bound="ExternalApiReportJsonBody")
|
T = TypeVar("T", bound="ReportJsonBody")
|
||||||
|
|
||||||
|
|
||||||
@define
|
@define
|
||||||
class ExternalApiReportJsonBody:
|
class ReportJsonBody:
|
||||||
"""
|
"""
|
||||||
Attributes:
|
Attributes:
|
||||||
requested_at (float): Unix timestamp in milliseconds
|
requested_at (float): Unix timestamp in milliseconds
|
||||||
@@ -21,8 +21,8 @@ class ExternalApiReportJsonBody:
|
|||||||
resp_payload (Union[Unset, Any]): JSON-encoded response payload
|
resp_payload (Union[Unset, Any]): JSON-encoded response payload
|
||||||
status_code (Union[Unset, float]): HTTP status code of response
|
status_code (Union[Unset, float]): HTTP status code of response
|
||||||
error_message (Union[Unset, str]): User-friendly error message
|
error_message (Union[Unset, str]): User-friendly error message
|
||||||
tags (Union[Unset, ExternalApiReportJsonBodyTags]): Extra tags to attach to the call for filtering. Eg {
|
tags (Union[Unset, ReportJsonBodyTags]): Extra tags to attach to the call for filtering. Eg { "userId": "123",
|
||||||
"userId": "123", "promptId": "populate-title" }
|
"promptId": "populate-title" }
|
||||||
"""
|
"""
|
||||||
|
|
||||||
requested_at: float
|
requested_at: float
|
||||||
@@ -31,7 +31,7 @@ class ExternalApiReportJsonBody:
|
|||||||
resp_payload: Union[Unset, Any] = UNSET
|
resp_payload: Union[Unset, Any] = UNSET
|
||||||
status_code: Union[Unset, float] = UNSET
|
status_code: Union[Unset, float] = UNSET
|
||||||
error_message: Union[Unset, str] = UNSET
|
error_message: Union[Unset, str] = UNSET
|
||||||
tags: Union[Unset, "ExternalApiReportJsonBodyTags"] = UNSET
|
tags: Union[Unset, "ReportJsonBodyTags"] = UNSET
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
requested_at = self.requested_at
|
requested_at = self.requested_at
|
||||||
@@ -66,7 +66,7 @@ class ExternalApiReportJsonBody:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||||
from ..models.external_api_report_json_body_tags import ExternalApiReportJsonBodyTags
|
from ..models.report_json_body_tags import ReportJsonBodyTags
|
||||||
|
|
||||||
d = src_dict.copy()
|
d = src_dict.copy()
|
||||||
requested_at = d.pop("requestedAt")
|
requested_at = d.pop("requestedAt")
|
||||||
@@ -82,13 +82,13 @@ class ExternalApiReportJsonBody:
|
|||||||
error_message = d.pop("errorMessage", UNSET)
|
error_message = d.pop("errorMessage", UNSET)
|
||||||
|
|
||||||
_tags = d.pop("tags", UNSET)
|
_tags = d.pop("tags", UNSET)
|
||||||
tags: Union[Unset, ExternalApiReportJsonBodyTags]
|
tags: Union[Unset, ReportJsonBodyTags]
|
||||||
if isinstance(_tags, Unset):
|
if isinstance(_tags, Unset):
|
||||||
tags = UNSET
|
tags = UNSET
|
||||||
else:
|
else:
|
||||||
tags = ExternalApiReportJsonBodyTags.from_dict(_tags)
|
tags = ReportJsonBodyTags.from_dict(_tags)
|
||||||
|
|
||||||
external_api_report_json_body = cls(
|
report_json_body = cls(
|
||||||
requested_at=requested_at,
|
requested_at=requested_at,
|
||||||
received_at=received_at,
|
received_at=received_at,
|
||||||
req_payload=req_payload,
|
req_payload=req_payload,
|
||||||
@@ -98,4 +98,4 @@ class ExternalApiReportJsonBody:
|
|||||||
tags=tags,
|
tags=tags,
|
||||||
)
|
)
|
||||||
|
|
||||||
return external_api_report_json_body
|
return report_json_body
|
||||||
@@ -2,11 +2,11 @@ from typing import Any, Dict, List, Type, TypeVar
|
|||||||
|
|
||||||
from attrs import define, field
|
from attrs import define, field
|
||||||
|
|
||||||
T = TypeVar("T", bound="ExternalApiCheckCacheJsonBodyTags")
|
T = TypeVar("T", bound="ReportJsonBodyTags")
|
||||||
|
|
||||||
|
|
||||||
@define
|
@define
|
||||||
class ExternalApiCheckCacheJsonBodyTags:
|
class ReportJsonBodyTags:
|
||||||
"""Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }"""
|
"""Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }"""
|
||||||
|
|
||||||
additional_properties: Dict[str, str] = field(init=False, factory=dict)
|
additional_properties: Dict[str, str] = field(init=False, factory=dict)
|
||||||
@@ -21,10 +21,10 @@ class ExternalApiCheckCacheJsonBodyTags:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||||
d = src_dict.copy()
|
d = src_dict.copy()
|
||||||
external_api_check_cache_json_body_tags = cls()
|
report_json_body_tags = cls()
|
||||||
|
|
||||||
external_api_check_cache_json_body_tags.additional_properties = d
|
report_json_body_tags.additional_properties = d
|
||||||
return external_api_check_cache_json_body_tags
|
return report_json_body_tags
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def additional_keys(self) -> List[str]:
|
def additional_keys(self) -> List[str]:
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
|
||||||
def merge_streamed_chunks(base: Optional[Any], chunk: Any) -> Any:
|
def merge_openai_chunks(base: Optional[Any], chunk: Any) -> Any:
|
||||||
if base is None:
|
if base is None:
|
||||||
return merge_streamed_chunks({**chunk, "choices": []}, chunk)
|
return merge_openai_chunks({**chunk, "choices": []}, chunk)
|
||||||
|
|
||||||
choices = base["choices"].copy()
|
choices = base["choices"].copy()
|
||||||
for choice in chunk["choices"]:
|
for choice in chunk["choices"]:
|
||||||
@@ -34,9 +34,7 @@ def merge_streamed_chunks(base: Optional[Any], chunk: Any) -> Any:
|
|||||||
{**new_choice, "message": {"role": "assistant", **choice["delta"]}}
|
{**new_choice, "message": {"role": "assistant", **choice["delta"]}}
|
||||||
)
|
)
|
||||||
|
|
||||||
merged = {
|
return {
|
||||||
**base,
|
**base,
|
||||||
"choices": choices,
|
"choices": choices,
|
||||||
}
|
}
|
||||||
|
|
||||||
return merged
|
|
||||||
|
|||||||
@@ -3,9 +3,16 @@ from openai.openai_object import OpenAIObject
|
|||||||
import time
|
import time
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
from openpipe.merge_openai_chunks import merge_streamed_chunks
|
from openpipe.merge_openai_chunks import merge_openai_chunks
|
||||||
|
from openpipe.openpipe_meta import OpenPipeMeta
|
||||||
|
|
||||||
from .shared import maybe_check_cache, maybe_check_cache_async, report_async, report
|
from .shared import (
|
||||||
|
_should_check_cache,
|
||||||
|
maybe_check_cache,
|
||||||
|
maybe_check_cache_async,
|
||||||
|
report_async,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class WrappedChatCompletion(original_openai.ChatCompletion):
|
class WrappedChatCompletion(original_openai.ChatCompletion):
|
||||||
@@ -29,9 +36,15 @@ class WrappedChatCompletion(original_openai.ChatCompletion):
|
|||||||
def _gen():
|
def _gen():
|
||||||
assembled_completion = None
|
assembled_completion = None
|
||||||
for chunk in chat_completion:
|
for chunk in chat_completion:
|
||||||
assembled_completion = merge_streamed_chunks(
|
assembled_completion = merge_openai_chunks(
|
||||||
assembled_completion, chunk
|
assembled_completion, chunk
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cache_status = (
|
||||||
|
"MISS" if _should_check_cache(openpipe_options) else "SKIP"
|
||||||
|
)
|
||||||
|
chunk.openpipe = OpenPipeMeta(cache_status=cache_status)
|
||||||
|
|
||||||
yield chunk
|
yield chunk
|
||||||
|
|
||||||
received_at = int(time.time() * 1000)
|
received_at = int(time.time() * 1000)
|
||||||
@@ -58,6 +71,10 @@ class WrappedChatCompletion(original_openai.ChatCompletion):
|
|||||||
status_code=200,
|
status_code=200,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cache_status = (
|
||||||
|
"MISS" if _should_check_cache(openpipe_options) else "SKIP"
|
||||||
|
)
|
||||||
|
chat_completion["openpipe"] = OpenPipeMeta(cache_status=cache_status)
|
||||||
return chat_completion
|
return chat_completion
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
received_at = int(time.time() * 1000)
|
received_at = int(time.time() * 1000)
|
||||||
@@ -96,21 +113,28 @@ class WrappedChatCompletion(original_openai.ChatCompletion):
|
|||||||
requested_at = int(time.time() * 1000)
|
requested_at = int(time.time() * 1000)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
chat_completion = original_openai.ChatCompletion.acreate(*args, **kwargs)
|
chat_completion = await original_openai.ChatCompletion.acreate(
|
||||||
|
*args, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
if inspect.isgenerator(chat_completion):
|
if inspect.isasyncgen(chat_completion):
|
||||||
|
|
||||||
def _gen():
|
async def _gen():
|
||||||
assembled_completion = None
|
assembled_completion = None
|
||||||
for chunk in chat_completion:
|
async for chunk in chat_completion:
|
||||||
assembled_completion = merge_streamed_chunks(
|
assembled_completion = merge_openai_chunks(
|
||||||
assembled_completion, chunk
|
assembled_completion, chunk
|
||||||
)
|
)
|
||||||
|
cache_status = (
|
||||||
|
"MISS" if _should_check_cache(openpipe_options) else "SKIP"
|
||||||
|
)
|
||||||
|
chunk.openpipe = OpenPipeMeta(cache_status=cache_status)
|
||||||
|
|
||||||
yield chunk
|
yield chunk
|
||||||
|
|
||||||
received_at = int(time.time() * 1000)
|
received_at = int(time.time() * 1000)
|
||||||
|
|
||||||
report_async(
|
await report_async(
|
||||||
openpipe_options=openpipe_options,
|
openpipe_options=openpipe_options,
|
||||||
requested_at=requested_at,
|
requested_at=requested_at,
|
||||||
received_at=received_at,
|
received_at=received_at,
|
||||||
@@ -123,7 +147,7 @@ class WrappedChatCompletion(original_openai.ChatCompletion):
|
|||||||
else:
|
else:
|
||||||
received_at = int(time.time() * 1000)
|
received_at = int(time.time() * 1000)
|
||||||
|
|
||||||
report_async(
|
await report_async(
|
||||||
openpipe_options=openpipe_options,
|
openpipe_options=openpipe_options,
|
||||||
requested_at=requested_at,
|
requested_at=requested_at,
|
||||||
received_at=received_at,
|
received_at=received_at,
|
||||||
@@ -132,12 +156,17 @@ class WrappedChatCompletion(original_openai.ChatCompletion):
|
|||||||
status_code=200,
|
status_code=200,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cache_status = (
|
||||||
|
"MISS" if _should_check_cache(openpipe_options) else "SKIP"
|
||||||
|
)
|
||||||
|
chat_completion["openpipe"] = OpenPipeMeta(cache_status=cache_status)
|
||||||
|
|
||||||
return chat_completion
|
return chat_completion
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
received_at = int(time.time() * 1000)
|
received_at = int(time.time() * 1000)
|
||||||
|
|
||||||
if isinstance(e, original_openai.OpenAIError):
|
if isinstance(e, original_openai.OpenAIError):
|
||||||
report_async(
|
await report_async(
|
||||||
openpipe_options=openpipe_options,
|
openpipe_options=openpipe_options,
|
||||||
requested_at=requested_at,
|
requested_at=requested_at,
|
||||||
received_at=received_at,
|
received_at=received_at,
|
||||||
@@ -147,7 +176,7 @@ class WrappedChatCompletion(original_openai.ChatCompletion):
|
|||||||
status_code=e.http_status,
|
status_code=e.http_status,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
report_async(
|
await report_async(
|
||||||
openpipe_options=openpipe_options,
|
openpipe_options=openpipe_options,
|
||||||
requested_at=requested_at,
|
requested_at=requested_at,
|
||||||
received_at=received_at,
|
received_at=received_at,
|
||||||
|
|||||||
7
client-libs/python/openpipe/openpipe_meta.py
Normal file
7
client-libs/python/openpipe/openpipe_meta.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from attr import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class OpenPipeMeta:
|
||||||
|
# Cache status. One of 'HIT', 'MISS', 'SKIP'
|
||||||
|
cache_status: str
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
from openpipe.api_client.api.default import (
|
from openpipe.api_client.api.default import (
|
||||||
external_api_report,
|
report as api_report,
|
||||||
external_api_check_cache,
|
check_cache,
|
||||||
)
|
)
|
||||||
from openpipe.api_client.client import AuthenticatedClient
|
from openpipe.api_client.client import AuthenticatedClient
|
||||||
from openpipe.api_client.models.external_api_report_json_body_tags import (
|
from openpipe.api_client.models.report_json_body_tags import (
|
||||||
ExternalApiReportJsonBodyTags,
|
ReportJsonBodyTags,
|
||||||
)
|
)
|
||||||
import toml
|
import toml
|
||||||
import time
|
import time
|
||||||
@@ -21,7 +21,7 @@ def _get_tags(openpipe_options):
|
|||||||
tags["$sdk"] = "python"
|
tags["$sdk"] = "python"
|
||||||
tags["$sdk_version"] = version
|
tags["$sdk_version"] = version
|
||||||
|
|
||||||
return ExternalApiReportJsonBodyTags.from_dict(tags)
|
return ReportJsonBodyTags.from_dict(tags)
|
||||||
|
|
||||||
|
|
||||||
def _should_check_cache(openpipe_options):
|
def _should_check_cache(openpipe_options):
|
||||||
@@ -31,7 +31,7 @@ def _should_check_cache(openpipe_options):
|
|||||||
|
|
||||||
|
|
||||||
def _process_cache_payload(
|
def _process_cache_payload(
|
||||||
payload: external_api_check_cache.ExternalApiCheckCacheResponse200,
|
payload: check_cache.CheckCacheResponse200,
|
||||||
):
|
):
|
||||||
if not payload or not payload.resp_payload:
|
if not payload or not payload.resp_payload:
|
||||||
return None
|
return None
|
||||||
@@ -47,9 +47,9 @@ def maybe_check_cache(
|
|||||||
if not _should_check_cache(openpipe_options):
|
if not _should_check_cache(openpipe_options):
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
payload = external_api_check_cache.sync(
|
payload = check_cache.sync(
|
||||||
client=configured_client,
|
client=configured_client,
|
||||||
json_body=external_api_check_cache.ExternalApiCheckCacheJsonBody(
|
json_body=check_cache.CheckCacheJsonBody(
|
||||||
req_payload=req_payload,
|
req_payload=req_payload,
|
||||||
requested_at=int(time.time() * 1000),
|
requested_at=int(time.time() * 1000),
|
||||||
tags=_get_tags(openpipe_options),
|
tags=_get_tags(openpipe_options),
|
||||||
@@ -72,9 +72,9 @@ async def maybe_check_cache_async(
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
payload = await external_api_check_cache.asyncio(
|
payload = await check_cache.asyncio(
|
||||||
client=configured_client,
|
client=configured_client,
|
||||||
json_body=external_api_check_cache.ExternalApiCheckCacheJsonBody(
|
json_body=check_cache.CheckCacheJsonBody(
|
||||||
req_payload=req_payload,
|
req_payload=req_payload,
|
||||||
requested_at=int(time.time() * 1000),
|
requested_at=int(time.time() * 1000),
|
||||||
tags=_get_tags(openpipe_options),
|
tags=_get_tags(openpipe_options),
|
||||||
@@ -94,9 +94,9 @@ def report(
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
external_api_report.sync_detailed(
|
api_report.sync_detailed(
|
||||||
client=configured_client,
|
client=configured_client,
|
||||||
json_body=external_api_report.ExternalApiReportJsonBody(
|
json_body=api_report.ReportJsonBody(
|
||||||
**kwargs,
|
**kwargs,
|
||||||
tags=_get_tags(openpipe_options),
|
tags=_get_tags(openpipe_options),
|
||||||
),
|
),
|
||||||
@@ -112,9 +112,9 @@ async def report_async(
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
await external_api_report.asyncio_detailed(
|
await api_report.asyncio_detailed(
|
||||||
client=configured_client,
|
client=configured_client,
|
||||||
json_body=external_api_report.ExternalApiReportJsonBody(
|
json_body=api_report.ReportJsonBody(
|
||||||
**kwargs,
|
**kwargs,
|
||||||
tags=_get_tags(openpipe_options),
|
tags=_get_tags(openpipe_options),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,55 +1,106 @@
|
|||||||
|
from functools import reduce
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from . import openai, configure_openpipe
|
|
||||||
import os
|
import os
|
||||||
import pytest
|
import pytest
|
||||||
|
from . import openai, configure_openpipe, configured_client
|
||||||
|
from .api_client.api.default import local_testing_only_get_latest_logged_call
|
||||||
|
from .merge_openai_chunks import merge_openai_chunks
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
|
||||||
|
def random_string(length):
|
||||||
|
letters = string.ascii_lowercase
|
||||||
|
return "".join(random.choice(letters) for i in range(length))
|
||||||
|
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
openai.api_key = os.getenv("OPENAI_API_KEY")
|
openai.api_key = os.getenv("OPENAI_API_KEY")
|
||||||
|
|
||||||
configure_openpipe(
|
configure_openpipe(
|
||||||
base_url="http://localhost:3000/api", api_key=os.getenv("OPENPIPE_API_KEY")
|
base_url="http://localhost:3000/api/v1", api_key=os.getenv("OPENPIPE_API_KEY")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def last_logged_call():
|
||||||
|
return local_testing_only_get_latest_logged_call.sync(client=configured_client)
|
||||||
|
|
||||||
|
|
||||||
def test_sync():
|
def test_sync():
|
||||||
completion = openai.ChatCompletion.create(
|
completion = openai.ChatCompletion.create(
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo",
|
||||||
messages=[{"role": "system", "content": "count to 10"}],
|
messages=[{"role": "system", "content": "count to 3"}],
|
||||||
)
|
)
|
||||||
|
|
||||||
print(completion.choices[0].message.content)
|
last_logged = last_logged_call()
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.resp_payload["choices"][0]["message"]["content"]
|
||||||
|
== completion.choices[0].message.content
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.req_payload["messages"][0]["content"] == "count to 3"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert completion.openpipe.cache_status == "SKIP"
|
||||||
|
|
||||||
|
|
||||||
def test_streaming():
|
def test_streaming():
|
||||||
completion = openai.ChatCompletion.create(
|
completion = openai.ChatCompletion.create(
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo",
|
||||||
messages=[{"role": "system", "content": "count to 10"}],
|
messages=[{"role": "system", "content": "count to 4"}],
|
||||||
stream=True,
|
stream=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
for chunk in completion:
|
merged = reduce(merge_openai_chunks, completion, None)
|
||||||
print(chunk)
|
last_logged = last_logged_call()
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.resp_payload["choices"][0]["message"]["content"]
|
||||||
|
== merged["choices"][0]["message"]["content"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_async():
|
async def test_async():
|
||||||
acompletion = await openai.ChatCompletion.acreate(
|
completion = await openai.ChatCompletion.acreate(
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo",
|
||||||
messages=[{"role": "user", "content": "count down from 5"}],
|
messages=[{"role": "user", "content": "count down from 5"}],
|
||||||
)
|
)
|
||||||
|
last_logged = last_logged_call()
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.resp_payload["choices"][0]["message"]["content"]
|
||||||
|
== completion.choices[0].message.content
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.req_payload["messages"][0]["content"]
|
||||||
|
== "count down from 5"
|
||||||
|
)
|
||||||
|
|
||||||
print(acompletion.choices[0].message.content)
|
assert completion.openpipe.cache_status == "SKIP"
|
||||||
|
|
||||||
|
|
||||||
async def test_async_streaming():
|
async def test_async_streaming():
|
||||||
acompletion = await openai.ChatCompletion.acreate(
|
completion = await openai.ChatCompletion.acreate(
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo",
|
||||||
messages=[{"role": "user", "content": "count down from 5"}],
|
messages=[{"role": "user", "content": "count down from 5"}],
|
||||||
stream=True,
|
stream=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
async for chunk in acompletion:
|
merged = None
|
||||||
print(chunk)
|
async for chunk in completion:
|
||||||
|
assert chunk.openpipe.cache_status == "SKIP"
|
||||||
|
merged = merge_openai_chunks(merged, chunk)
|
||||||
|
|
||||||
|
last_logged = last_logged_call()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.resp_payload["choices"][0]["message"]["content"]
|
||||||
|
== merged["choices"][0]["message"]["content"]
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.req_payload["messages"][0]["content"]
|
||||||
|
== "count down from 5"
|
||||||
|
)
|
||||||
|
assert merged["openpipe"].cache_status == "SKIP"
|
||||||
|
|
||||||
|
|
||||||
def test_sync_with_tags():
|
def test_sync_with_tags():
|
||||||
@@ -58,31 +109,54 @@ def test_sync_with_tags():
|
|||||||
messages=[{"role": "system", "content": "count to 10"}],
|
messages=[{"role": "system", "content": "count to 10"}],
|
||||||
openpipe={"tags": {"promptId": "testprompt"}},
|
openpipe={"tags": {"promptId": "testprompt"}},
|
||||||
)
|
)
|
||||||
print("finished")
|
|
||||||
|
|
||||||
print(completion.choices[0].message.content)
|
last_logged = last_logged_call()
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.resp_payload["choices"][0]["message"]["content"]
|
||||||
|
== completion.choices[0].message.content
|
||||||
|
)
|
||||||
|
print(last_logged.tags)
|
||||||
|
assert last_logged.tags["promptId"] == "testprompt"
|
||||||
|
assert last_logged.tags["$sdk"] == "python"
|
||||||
|
|
||||||
|
|
||||||
def test_bad_call():
|
def test_bad_call():
|
||||||
completion = openai.ChatCompletion.create(
|
try:
|
||||||
model="gpt-3.5-turbo-blaster",
|
completion = openai.ChatCompletion.create(
|
||||||
messages=[{"role": "system", "content": "count to 10"}],
|
model="gpt-3.5-turbo-blaster",
|
||||||
stream=True,
|
messages=[{"role": "system", "content": "count to 10"}],
|
||||||
|
stream=True,
|
||||||
|
)
|
||||||
|
assert False
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
last_logged = last_logged_call()
|
||||||
|
print(last_logged)
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.error_message
|
||||||
|
== "The model `gpt-3.5-turbo-blaster` does not exist"
|
||||||
)
|
)
|
||||||
|
assert last_logged.model_response.status_code == 404
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.focus
|
|
||||||
async def test_caching():
|
async def test_caching():
|
||||||
|
messages = [{"role": "system", "content": f"repeat '{random_string(10)}'"}]
|
||||||
completion = openai.ChatCompletion.create(
|
completion = openai.ChatCompletion.create(
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo",
|
||||||
messages=[{"role": "system", "content": "count to 10"}],
|
messages=messages,
|
||||||
openpipe={"cache": True},
|
openpipe={"cache": True},
|
||||||
)
|
)
|
||||||
|
assert completion.openpipe.cache_status == "MISS"
|
||||||
|
|
||||||
|
first_logged = last_logged_call()
|
||||||
|
assert (
|
||||||
|
completion.choices[0].message.content
|
||||||
|
== first_logged.model_response.resp_payload["choices"][0]["message"]["content"]
|
||||||
|
)
|
||||||
|
|
||||||
completion2 = await openai.ChatCompletion.acreate(
|
completion2 = await openai.ChatCompletion.acreate(
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo",
|
||||||
messages=[{"role": "system", "content": "count to 10"}],
|
messages=[{"role": "system", "content": "count to 10"}],
|
||||||
openpipe={"cache": True},
|
openpipe={"cache": True},
|
||||||
)
|
)
|
||||||
|
assert completion2.openpipe.cache_status == "HIT"
|
||||||
print(completion2)
|
|
||||||
|
|||||||
@@ -1,133 +0,0 @@
|
|||||||
{
|
|
||||||
"openapi": "3.0.3",
|
|
||||||
"info": {
|
|
||||||
"title": "OpenPipe API",
|
|
||||||
"description": "The public API for reporting API calls to OpenPipe",
|
|
||||||
"version": "0.1.0"
|
|
||||||
},
|
|
||||||
"servers": [{ "url": "https://app.openpipe.ai/api" }],
|
|
||||||
"paths": {
|
|
||||||
"/v1/check-cache": {
|
|
||||||
"post": {
|
|
||||||
"operationId": "externalApi-checkCache",
|
|
||||||
"description": "Check if a prompt is cached",
|
|
||||||
"security": [{ "Authorization": [] }],
|
|
||||||
"requestBody": {
|
|
||||||
"required": true,
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"startTime": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Unix timestamp in milliseconds"
|
|
||||||
},
|
|
||||||
"reqPayload": { "description": "JSON-encoded request payload" },
|
|
||||||
"tags": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": { "type": "string" },
|
|
||||||
"description": "Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["startTime"],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parameters": [],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Successful response",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"respPayload": { "description": "JSON-encoded response payload" }
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default": { "$ref": "#/components/responses/error" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/v1/report": {
|
|
||||||
"post": {
|
|
||||||
"operationId": "externalApi-report",
|
|
||||||
"description": "Report an API call",
|
|
||||||
"security": [{ "Authorization": [] }],
|
|
||||||
"requestBody": {
|
|
||||||
"required": true,
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"startTime": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Unix timestamp in milliseconds"
|
|
||||||
},
|
|
||||||
"endTime": { "type": "number", "description": "Unix timestamp in milliseconds" },
|
|
||||||
"reqPayload": { "description": "JSON-encoded request payload" },
|
|
||||||
"respPayload": { "description": "JSON-encoded response payload" },
|
|
||||||
"respStatus": { "type": "number", "description": "HTTP status code of response" },
|
|
||||||
"error": { "type": "string", "description": "User-friendly error message" },
|
|
||||||
"tags": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": { "type": "string" },
|
|
||||||
"description": "Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["startTime", "endTime"],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parameters": [],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Successful response",
|
|
||||||
"content": { "application/json": { "schema": {} } }
|
|
||||||
},
|
|
||||||
"default": { "$ref": "#/components/responses/error" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"components": {
|
|
||||||
"securitySchemes": { "Authorization": { "type": "http", "scheme": "bearer" } },
|
|
||||||
"responses": {
|
|
||||||
"error": {
|
|
||||||
"description": "Error response",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"message": { "type": "string" },
|
|
||||||
"code": { "type": "string" },
|
|
||||||
"issues": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": { "message": { "type": "string" } },
|
|
||||||
"required": ["message"],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["message", "code"],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
4
client-libs/typescript/.gitignore
vendored
4
client-libs/typescript/.gitignore
vendored
@@ -1,2 +1,2 @@
|
|||||||
node_modules
|
node_modules/
|
||||||
dist
|
dist/
|
||||||
@@ -1,319 +0,0 @@
|
|||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* OpenPipe API
|
|
||||||
* The public API for reporting API calls to OpenPipe
|
|
||||||
*
|
|
||||||
* The version of the OpenAPI document: 0.1.0
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
||||||
* https://openapi-generator.tech
|
|
||||||
* Do not edit the class manually.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import type { Configuration } from './configuration';
|
|
||||||
import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios';
|
|
||||||
import globalAxios from 'axios';
|
|
||||||
// Some imports not used depending on template conditions
|
|
||||||
// @ts-ignore
|
|
||||||
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from './common';
|
|
||||||
import type { RequestArgs } from './base';
|
|
||||||
// @ts-ignore
|
|
||||||
import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError } from './base';
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @interface ExternalApiCheckCache200Response
|
|
||||||
*/
|
|
||||||
export interface ExternalApiCheckCache200Response {
|
|
||||||
/**
|
|
||||||
* JSON-encoded response payload
|
|
||||||
* @type {any}
|
|
||||||
* @memberof ExternalApiCheckCache200Response
|
|
||||||
*/
|
|
||||||
'respPayload'?: any;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @interface ExternalApiCheckCacheDefaultResponse
|
|
||||||
*/
|
|
||||||
export interface ExternalApiCheckCacheDefaultResponse {
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof ExternalApiCheckCacheDefaultResponse
|
|
||||||
*/
|
|
||||||
'message': string;
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof ExternalApiCheckCacheDefaultResponse
|
|
||||||
*/
|
|
||||||
'code': string;
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {Array<ExternalApiCheckCacheDefaultResponseIssuesInner>}
|
|
||||||
* @memberof ExternalApiCheckCacheDefaultResponse
|
|
||||||
*/
|
|
||||||
'issues'?: Array<ExternalApiCheckCacheDefaultResponseIssuesInner>;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @interface ExternalApiCheckCacheDefaultResponseIssuesInner
|
|
||||||
*/
|
|
||||||
export interface ExternalApiCheckCacheDefaultResponseIssuesInner {
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof ExternalApiCheckCacheDefaultResponseIssuesInner
|
|
||||||
*/
|
|
||||||
'message': string;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @interface ExternalApiCheckCacheRequest
|
|
||||||
*/
|
|
||||||
export interface ExternalApiCheckCacheRequest {
|
|
||||||
/**
|
|
||||||
* Unix timestamp in milliseconds
|
|
||||||
* @type {number}
|
|
||||||
* @memberof ExternalApiCheckCacheRequest
|
|
||||||
*/
|
|
||||||
'requestedAt': number;
|
|
||||||
/**
|
|
||||||
* JSON-encoded request payload
|
|
||||||
* @type {any}
|
|
||||||
* @memberof ExternalApiCheckCacheRequest
|
|
||||||
*/
|
|
||||||
'reqPayload'?: any;
|
|
||||||
/**
|
|
||||||
* Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }
|
|
||||||
* @type {{ [key: string]: string; }}
|
|
||||||
* @memberof ExternalApiCheckCacheRequest
|
|
||||||
*/
|
|
||||||
'tags'?: { [key: string]: string; };
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @interface ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
export interface ExternalApiReportRequest {
|
|
||||||
/**
|
|
||||||
* Unix timestamp in milliseconds
|
|
||||||
* @type {number}
|
|
||||||
* @memberof ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
'requestedAt': number;
|
|
||||||
/**
|
|
||||||
* Unix timestamp in milliseconds
|
|
||||||
* @type {number}
|
|
||||||
* @memberof ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
'receivedAt': number;
|
|
||||||
/**
|
|
||||||
* JSON-encoded request payload
|
|
||||||
* @type {any}
|
|
||||||
* @memberof ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
'reqPayload'?: any;
|
|
||||||
/**
|
|
||||||
* JSON-encoded response payload
|
|
||||||
* @type {any}
|
|
||||||
* @memberof ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
'respPayload'?: any;
|
|
||||||
/**
|
|
||||||
* HTTP status code of response
|
|
||||||
* @type {number}
|
|
||||||
* @memberof ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
'statusCode'?: number;
|
|
||||||
/**
|
|
||||||
* User-friendly error message
|
|
||||||
* @type {string}
|
|
||||||
* @memberof ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
'errorMessage'?: string;
|
|
||||||
/**
|
|
||||||
* Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }
|
|
||||||
* @type {{ [key: string]: string; }}
|
|
||||||
* @memberof ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
'tags'?: { [key: string]: string; };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DefaultApi - axios parameter creator
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const DefaultApiAxiosParamCreator = function (configuration?: Configuration) {
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Check if a prompt is cached
|
|
||||||
* @param {ExternalApiCheckCacheRequest} externalApiCheckCacheRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
*/
|
|
||||||
externalApiCheckCache: async (externalApiCheckCacheRequest: ExternalApiCheckCacheRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
|
||||||
// verify required parameter 'externalApiCheckCacheRequest' is not null or undefined
|
|
||||||
assertParamExists('externalApiCheckCache', 'externalApiCheckCacheRequest', externalApiCheckCacheRequest)
|
|
||||||
const localVarPath = `/v1/check-cache`;
|
|
||||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
|
||||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
|
||||||
let baseOptions;
|
|
||||||
if (configuration) {
|
|
||||||
baseOptions = configuration.baseOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
|
||||||
const localVarHeaderParameter = {} as any;
|
|
||||||
const localVarQueryParameter = {} as any;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
|
||||||
|
|
||||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
|
||||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
|
||||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
|
||||||
localVarRequestOptions.data = serializeDataIfNeeded(externalApiCheckCacheRequest, localVarRequestOptions, configuration)
|
|
||||||
|
|
||||||
return {
|
|
||||||
url: toPathString(localVarUrlObj),
|
|
||||||
options: localVarRequestOptions,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Report an API call
|
|
||||||
* @param {ExternalApiReportRequest} externalApiReportRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
*/
|
|
||||||
externalApiReport: async (externalApiReportRequest: ExternalApiReportRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
|
||||||
// verify required parameter 'externalApiReportRequest' is not null or undefined
|
|
||||||
assertParamExists('externalApiReport', 'externalApiReportRequest', externalApiReportRequest)
|
|
||||||
const localVarPath = `/v1/report`;
|
|
||||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
|
||||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
|
||||||
let baseOptions;
|
|
||||||
if (configuration) {
|
|
||||||
baseOptions = configuration.baseOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
|
||||||
const localVarHeaderParameter = {} as any;
|
|
||||||
const localVarQueryParameter = {} as any;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
|
||||||
|
|
||||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
|
||||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
|
||||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
|
||||||
localVarRequestOptions.data = serializeDataIfNeeded(externalApiReportRequest, localVarRequestOptions, configuration)
|
|
||||||
|
|
||||||
return {
|
|
||||||
url: toPathString(localVarUrlObj),
|
|
||||||
options: localVarRequestOptions,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DefaultApi - functional programming interface
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const DefaultApiFp = function(configuration?: Configuration) {
|
|
||||||
const localVarAxiosParamCreator = DefaultApiAxiosParamCreator(configuration)
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Check if a prompt is cached
|
|
||||||
* @param {ExternalApiCheckCacheRequest} externalApiCheckCacheRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
*/
|
|
||||||
async externalApiCheckCache(externalApiCheckCacheRequest: ExternalApiCheckCacheRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<ExternalApiCheckCache200Response>> {
|
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.externalApiCheckCache(externalApiCheckCacheRequest, options);
|
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Report an API call
|
|
||||||
* @param {ExternalApiReportRequest} externalApiReportRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
*/
|
|
||||||
async externalApiReport(externalApiReportRequest: ExternalApiReportRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<any>> {
|
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.externalApiReport(externalApiReportRequest, options);
|
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DefaultApi - factory interface
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const DefaultApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
|
||||||
const localVarFp = DefaultApiFp(configuration)
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Check if a prompt is cached
|
|
||||||
* @param {ExternalApiCheckCacheRequest} externalApiCheckCacheRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
*/
|
|
||||||
externalApiCheckCache(externalApiCheckCacheRequest: ExternalApiCheckCacheRequest, options?: any): AxiosPromise<ExternalApiCheckCache200Response> {
|
|
||||||
return localVarFp.externalApiCheckCache(externalApiCheckCacheRequest, options).then((request) => request(axios, basePath));
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Report an API call
|
|
||||||
* @param {ExternalApiReportRequest} externalApiReportRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
*/
|
|
||||||
externalApiReport(externalApiReportRequest: ExternalApiReportRequest, options?: any): AxiosPromise<any> {
|
|
||||||
return localVarFp.externalApiReport(externalApiReportRequest, options).then((request) => request(axios, basePath));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DefaultApi - object-oriented interface
|
|
||||||
* @export
|
|
||||||
* @class DefaultApi
|
|
||||||
* @extends {BaseAPI}
|
|
||||||
*/
|
|
||||||
export class DefaultApi extends BaseAPI {
|
|
||||||
/**
|
|
||||||
* Check if a prompt is cached
|
|
||||||
* @param {ExternalApiCheckCacheRequest} externalApiCheckCacheRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
* @memberof DefaultApi
|
|
||||||
*/
|
|
||||||
public externalApiCheckCache(externalApiCheckCacheRequest: ExternalApiCheckCacheRequest, options?: AxiosRequestConfig) {
|
|
||||||
return DefaultApiFp(this.configuration).externalApiCheckCache(externalApiCheckCacheRequest, options).then((request) => request(this.axios, this.basePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Report an API call
|
|
||||||
* @param {ExternalApiReportRequest} externalApiReportRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
* @memberof DefaultApi
|
|
||||||
*/
|
|
||||||
public externalApiReport(externalApiReportRequest: ExternalApiReportRequest, options?: AxiosRequestConfig) {
|
|
||||||
return DefaultApiFp(this.configuration).externalApiReport(externalApiReportRequest, options).then((request) => request(this.axios, this.basePath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
import * as openPipeClient from "../codegen";
|
|
||||||
import * as openai from "openai-legacy";
|
|
||||||
import { version } from "../package.json";
|
|
||||||
|
|
||||||
// Anything we don't override we want to pass through to openai directly
|
|
||||||
export * as openAILegacy from "openai-legacy";
|
|
||||||
|
|
||||||
type OPConfigurationParameters = {
|
|
||||||
apiKey?: string;
|
|
||||||
basePath?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class Configuration extends openai.Configuration {
|
|
||||||
public qkConfig?: openPipeClient.Configuration;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
config: openai.ConfigurationParameters & {
|
|
||||||
opParameters?: OPConfigurationParameters;
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
super(config);
|
|
||||||
if (config.opParameters) {
|
|
||||||
this.qkConfig = new openPipeClient.Configuration(config.opParameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type CreateChatCompletion = InstanceType<
|
|
||||||
typeof openai.OpenAIApi
|
|
||||||
>["createChatCompletion"];
|
|
||||||
|
|
||||||
export class OpenAIApi extends openai.OpenAIApi {
|
|
||||||
public openPipeApi?: openPipeClient.DefaultApi;
|
|
||||||
|
|
||||||
constructor(config: Configuration) {
|
|
||||||
super(config);
|
|
||||||
if (config.qkConfig) {
|
|
||||||
this.openPipeApi = new openPipeClient.DefaultApi(config.qkConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async createChatCompletion(
|
|
||||||
createChatCompletionRequest: Parameters<CreateChatCompletion>[0],
|
|
||||||
options?: Parameters<CreateChatCompletion>[1]
|
|
||||||
): ReturnType<CreateChatCompletion> {
|
|
||||||
const requestedAt = Date.now();
|
|
||||||
let resp: Awaited<ReturnType<CreateChatCompletion>> | null = null;
|
|
||||||
let respPayload: openai.CreateChatCompletionResponse | null = null;
|
|
||||||
let statusCode: number | undefined = undefined;
|
|
||||||
let errorMessage: string | undefined;
|
|
||||||
try {
|
|
||||||
resp = await super.createChatCompletion(
|
|
||||||
createChatCompletionRequest,
|
|
||||||
options
|
|
||||||
);
|
|
||||||
respPayload = resp.data;
|
|
||||||
statusCode = resp.status;
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Error in createChatCompletion");
|
|
||||||
if ("isAxiosError" in err && err.isAxiosError) {
|
|
||||||
errorMessage = err.response?.data?.error?.message;
|
|
||||||
respPayload = err.response?.data;
|
|
||||||
statusCode = err.response?.status;
|
|
||||||
} else if ("message" in err) {
|
|
||||||
errorMessage = err.message.toString();
|
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
} finally {
|
|
||||||
this.openPipeApi
|
|
||||||
?.externalApiReport({
|
|
||||||
requestedAt,
|
|
||||||
receivedAt: Date.now(),
|
|
||||||
reqPayload: createChatCompletionRequest,
|
|
||||||
respPayload: respPayload,
|
|
||||||
statusCode: statusCode,
|
|
||||||
errorMessage,
|
|
||||||
tags: {
|
|
||||||
client: "openai-js",
|
|
||||||
clientVersion: version,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("Error reporting to OP", err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("done");
|
|
||||||
return resp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "openpipe",
|
"name": "openpipe",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
"description": "Metrics and auto-evaluation for LLM calls",
|
"description": "Metrics and auto-evaluation for LLM calls",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"build": "tsc"
|
||||||
},
|
},
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.26.0",
|
"axios": "^0.26.0",
|
||||||
"openai-beta": "npm:openai@4.0.0-beta.7",
|
"openai-beta": "npm:openai@4.0.0-beta.7",
|
||||||
|
|||||||
548
client-libs/typescript/pnpm-lock.yaml
generated
548
client-libs/typescript/pnpm-lock.yaml
generated
@@ -1,548 +0,0 @@
|
|||||||
lockfileVersion: '6.0'
|
|
||||||
|
|
||||||
settings:
|
|
||||||
autoInstallPeers: true
|
|
||||||
excludeLinksFromLockfile: false
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
axios:
|
|
||||||
specifier: ^0.26.0
|
|
||||||
version: 0.26.0
|
|
||||||
openai-beta:
|
|
||||||
specifier: npm:openai@4.0.0-beta.7
|
|
||||||
version: /openai@4.0.0-beta.7
|
|
||||||
openai-legacy:
|
|
||||||
specifier: npm:openai@3.3.0
|
|
||||||
version: /openai@3.3.0
|
|
||||||
|
|
||||||
devDependencies:
|
|
||||||
'@types/node':
|
|
||||||
specifier: ^20.4.8
|
|
||||||
version: 20.4.8
|
|
||||||
dotenv:
|
|
||||||
specifier: ^16.3.1
|
|
||||||
version: 16.3.1
|
|
||||||
tsx:
|
|
||||||
specifier: ^3.12.7
|
|
||||||
version: 3.12.7
|
|
||||||
typescript:
|
|
||||||
specifier: ^5.0.4
|
|
||||||
version: 5.0.4
|
|
||||||
|
|
||||||
packages:
|
|
||||||
|
|
||||||
/@esbuild-kit/cjs-loader@2.4.2:
|
|
||||||
resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==}
|
|
||||||
dependencies:
|
|
||||||
'@esbuild-kit/core-utils': 3.1.0
|
|
||||||
get-tsconfig: 4.6.2
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@esbuild-kit/core-utils@3.1.0:
|
|
||||||
resolution: {integrity: sha512-Uuk8RpCg/7fdHSceR1M6XbSZFSuMrxcePFuGgyvsBn+u339dk5OeL4jv2EojwTN2st/unJGsVm4qHWjWNmJ/tw==}
|
|
||||||
dependencies:
|
|
||||||
esbuild: 0.17.19
|
|
||||||
source-map-support: 0.5.21
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@esbuild-kit/esm-loader@2.5.5:
|
|
||||||
resolution: {integrity: sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==}
|
|
||||||
dependencies:
|
|
||||||
'@esbuild-kit/core-utils': 3.1.0
|
|
||||||
get-tsconfig: 4.6.2
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@esbuild/android-arm64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [android]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/android-arm@0.17.19:
|
|
||||||
resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [arm]
|
|
||||||
os: [android]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/android-x64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [android]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/darwin-arm64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [darwin]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/darwin-x64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [darwin]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/freebsd-arm64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [freebsd]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/freebsd-x64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [freebsd]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/linux-arm64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [linux]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/linux-arm@0.17.19:
|
|
||||||
resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [arm]
|
|
||||||
os: [linux]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/linux-ia32@0.17.19:
|
|
||||||
resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [ia32]
|
|
||||||
os: [linux]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/linux-loong64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [loong64]
|
|
||||||
os: [linux]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/linux-mips64el@0.17.19:
|
|
||||||
resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [mips64el]
|
|
||||||
os: [linux]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/linux-ppc64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [ppc64]
|
|
||||||
os: [linux]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/linux-riscv64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [riscv64]
|
|
||||||
os: [linux]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/linux-s390x@0.17.19:
|
|
||||||
resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [s390x]
|
|
||||||
os: [linux]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/linux-x64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [linux]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/netbsd-x64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [netbsd]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/openbsd-x64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [openbsd]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/sunos-x64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [sunos]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/win32-arm64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [win32]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/win32-ia32@0.17.19:
|
|
||||||
resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [ia32]
|
|
||||||
os: [win32]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@esbuild/win32-x64@0.17.19:
|
|
||||||
resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [win32]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/@types/node-fetch@2.6.4:
|
|
||||||
resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
|
|
||||||
dependencies:
|
|
||||||
'@types/node': 20.4.8
|
|
||||||
form-data: 3.0.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/node@18.17.3:
|
|
||||||
resolution: {integrity: sha512-2x8HWtFk0S99zqVQABU9wTpr8wPoaDHZUcAkoTKH+nL7kPv3WUI9cRi/Kk5Mz4xdqXSqTkKP7IWNoQQYCnDsTA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/node@20.4.8:
|
|
||||||
resolution: {integrity: sha512-0mHckf6D2DiIAzh8fM8f3HQCvMKDpK94YQ0DSVkfWTG9BZleYIWudw9cJxX8oCk9bM+vAkDyujDV6dmKHbvQpg==}
|
|
||||||
|
|
||||||
/abort-controller@3.0.0:
|
|
||||||
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
|
||||||
engines: {node: '>=6.5'}
|
|
||||||
dependencies:
|
|
||||||
event-target-shim: 5.0.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/agentkeepalive@4.5.0:
|
|
||||||
resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==}
|
|
||||||
engines: {node: '>= 8.0.0'}
|
|
||||||
dependencies:
|
|
||||||
humanize-ms: 1.2.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/asynckit@0.4.0:
|
|
||||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/axios@0.26.0:
|
|
||||||
resolution: {integrity: sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==}
|
|
||||||
dependencies:
|
|
||||||
follow-redirects: 1.15.2
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- debug
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/base-64@0.1.0:
|
|
||||||
resolution: {integrity: sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/buffer-from@1.1.2:
|
|
||||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/charenc@0.0.2:
|
|
||||||
resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/combined-stream@1.0.8:
|
|
||||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dependencies:
|
|
||||||
delayed-stream: 1.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/crypt@0.0.2:
|
|
||||||
resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/delayed-stream@1.0.0:
|
|
||||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
|
||||||
engines: {node: '>=0.4.0'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/digest-fetch@1.3.0:
|
|
||||||
resolution: {integrity: sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==}
|
|
||||||
dependencies:
|
|
||||||
base-64: 0.1.0
|
|
||||||
md5: 2.3.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/dotenv@16.3.1:
|
|
||||||
resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/esbuild@0.17.19:
|
|
||||||
resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
hasBin: true
|
|
||||||
requiresBuild: true
|
|
||||||
optionalDependencies:
|
|
||||||
'@esbuild/android-arm': 0.17.19
|
|
||||||
'@esbuild/android-arm64': 0.17.19
|
|
||||||
'@esbuild/android-x64': 0.17.19
|
|
||||||
'@esbuild/darwin-arm64': 0.17.19
|
|
||||||
'@esbuild/darwin-x64': 0.17.19
|
|
||||||
'@esbuild/freebsd-arm64': 0.17.19
|
|
||||||
'@esbuild/freebsd-x64': 0.17.19
|
|
||||||
'@esbuild/linux-arm': 0.17.19
|
|
||||||
'@esbuild/linux-arm64': 0.17.19
|
|
||||||
'@esbuild/linux-ia32': 0.17.19
|
|
||||||
'@esbuild/linux-loong64': 0.17.19
|
|
||||||
'@esbuild/linux-mips64el': 0.17.19
|
|
||||||
'@esbuild/linux-ppc64': 0.17.19
|
|
||||||
'@esbuild/linux-riscv64': 0.17.19
|
|
||||||
'@esbuild/linux-s390x': 0.17.19
|
|
||||||
'@esbuild/linux-x64': 0.17.19
|
|
||||||
'@esbuild/netbsd-x64': 0.17.19
|
|
||||||
'@esbuild/openbsd-x64': 0.17.19
|
|
||||||
'@esbuild/sunos-x64': 0.17.19
|
|
||||||
'@esbuild/win32-arm64': 0.17.19
|
|
||||||
'@esbuild/win32-ia32': 0.17.19
|
|
||||||
'@esbuild/win32-x64': 0.17.19
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/event-target-shim@5.0.1:
|
|
||||||
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
|
|
||||||
engines: {node: '>=6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/follow-redirects@1.15.2:
|
|
||||||
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
|
|
||||||
engines: {node: '>=4.0'}
|
|
||||||
peerDependencies:
|
|
||||||
debug: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
debug:
|
|
||||||
optional: true
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/form-data-encoder@1.7.2:
|
|
||||||
resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/form-data@3.0.1:
|
|
||||||
resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
|
|
||||||
engines: {node: '>= 6'}
|
|
||||||
dependencies:
|
|
||||||
asynckit: 0.4.0
|
|
||||||
combined-stream: 1.0.8
|
|
||||||
mime-types: 2.1.35
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/form-data@4.0.0:
|
|
||||||
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
|
||||||
engines: {node: '>= 6'}
|
|
||||||
dependencies:
|
|
||||||
asynckit: 0.4.0
|
|
||||||
combined-stream: 1.0.8
|
|
||||||
mime-types: 2.1.35
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/formdata-node@4.4.1:
|
|
||||||
resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==}
|
|
||||||
engines: {node: '>= 12.20'}
|
|
||||||
dependencies:
|
|
||||||
node-domexception: 1.0.0
|
|
||||||
web-streams-polyfill: 4.0.0-beta.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/fsevents@2.3.2:
|
|
||||||
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
|
||||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
|
||||||
os: [darwin]
|
|
||||||
requiresBuild: true
|
|
||||||
dev: true
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/get-tsconfig@4.6.2:
|
|
||||||
resolution: {integrity: sha512-E5XrT4CbbXcXWy+1jChlZmrmCwd5KGx502kDCXJJ7y898TtWW9FwoG5HfOLVRKmlmDGkWN2HM9Ho+/Y8F0sJDg==}
|
|
||||||
dependencies:
|
|
||||||
resolve-pkg-maps: 1.0.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/humanize-ms@1.2.1:
|
|
||||||
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
|
|
||||||
dependencies:
|
|
||||||
ms: 2.1.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/is-buffer@1.1.6:
|
|
||||||
resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/md5@2.3.0:
|
|
||||||
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
|
|
||||||
dependencies:
|
|
||||||
charenc: 0.0.2
|
|
||||||
crypt: 0.0.2
|
|
||||||
is-buffer: 1.1.6
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/mime-db@1.52.0:
|
|
||||||
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/mime-types@2.1.35:
|
|
||||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dependencies:
|
|
||||||
mime-db: 1.52.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ms@2.1.3:
|
|
||||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/node-domexception@1.0.0:
|
|
||||||
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
|
||||||
engines: {node: '>=10.5.0'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/node-fetch@2.6.12:
|
|
||||||
resolution: {integrity: sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==}
|
|
||||||
engines: {node: 4.x || >=6.0.0}
|
|
||||||
peerDependencies:
|
|
||||||
encoding: ^0.1.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
encoding:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
whatwg-url: 5.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/openai@3.3.0:
|
|
||||||
resolution: {integrity: sha512-uqxI/Au+aPRnsaQRe8CojU0eCR7I0mBiKjD3sNMzY6DaC1ZVrc85u98mtJW6voDug8fgGN+DIZmTDxTthxb7dQ==}
|
|
||||||
dependencies:
|
|
||||||
axios: 0.26.0
|
|
||||||
form-data: 4.0.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- debug
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/openai@4.0.0-beta.7:
|
|
||||||
resolution: {integrity: sha512-jHjwvpMuGkNxiQ3erwLZsOvPEhcVrMtwtfNeYmGCjhbdB+oStVw/7pIhIPkualu8rlhLwgMR7awknIaN3IQcOA==}
|
|
||||||
dependencies:
|
|
||||||
'@types/node': 18.17.3
|
|
||||||
'@types/node-fetch': 2.6.4
|
|
||||||
abort-controller: 3.0.0
|
|
||||||
agentkeepalive: 4.5.0
|
|
||||||
digest-fetch: 1.3.0
|
|
||||||
form-data-encoder: 1.7.2
|
|
||||||
formdata-node: 4.4.1
|
|
||||||
node-fetch: 2.6.12
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- encoding
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/resolve-pkg-maps@1.0.0:
|
|
||||||
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/source-map-support@0.5.21:
|
|
||||||
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
|
|
||||||
dependencies:
|
|
||||||
buffer-from: 1.1.2
|
|
||||||
source-map: 0.6.1
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/source-map@0.6.1:
|
|
||||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
|
||||||
engines: {node: '>=0.10.0'}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/tr46@0.0.3:
|
|
||||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/tsx@3.12.7:
|
|
||||||
resolution: {integrity: sha512-C2Ip+jPmqKd1GWVQDvz/Eyc6QJbGfE7NrR3fx5BpEHMZsEHoIxHL1j+lKdGobr8ovEyqeNkPLSKp6SCSOt7gmw==}
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
'@esbuild-kit/cjs-loader': 2.4.2
|
|
||||||
'@esbuild-kit/core-utils': 3.1.0
|
|
||||||
'@esbuild-kit/esm-loader': 2.5.5
|
|
||||||
optionalDependencies:
|
|
||||||
fsevents: 2.3.2
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/typescript@5.0.4:
|
|
||||||
resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==}
|
|
||||||
engines: {node: '>=12.20'}
|
|
||||||
hasBin: true
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/web-streams-polyfill@4.0.0-beta.3:
|
|
||||||
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
|
|
||||||
engines: {node: '>= 14'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/webidl-conversions@3.0.1:
|
|
||||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/whatwg-url@5.0.0:
|
|
||||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
|
||||||
dependencies:
|
|
||||||
tr46: 0.0.3
|
|
||||||
webidl-conversions: 3.0.1
|
|
||||||
dev: false
|
|
||||||
455
client-libs/typescript/src/codegen/api.ts
Normal file
455
client-libs/typescript/src/codegen/api.ts
Normal file
@@ -0,0 +1,455 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
/**
|
||||||
|
* OpenPipe API
|
||||||
|
* The public API for reporting API calls to OpenPipe
|
||||||
|
*
|
||||||
|
* The version of the OpenAPI document: 0.1.1
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
* https://openapi-generator.tech
|
||||||
|
* Do not edit the class manually.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import type { Configuration } from './configuration';
|
||||||
|
import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios';
|
||||||
|
import globalAxios from 'axios';
|
||||||
|
// Some imports not used depending on template conditions
|
||||||
|
// @ts-ignore
|
||||||
|
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from './common';
|
||||||
|
import type { RequestArgs } from './base';
|
||||||
|
// @ts-ignore
|
||||||
|
import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError } from './base';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface CheckCache200Response
|
||||||
|
*/
|
||||||
|
export interface CheckCache200Response {
|
||||||
|
/**
|
||||||
|
* JSON-encoded response payload
|
||||||
|
* @type {any}
|
||||||
|
* @memberof CheckCache200Response
|
||||||
|
*/
|
||||||
|
'respPayload'?: any;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface CheckCacheDefaultResponse
|
||||||
|
*/
|
||||||
|
export interface CheckCacheDefaultResponse {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof CheckCacheDefaultResponse
|
||||||
|
*/
|
||||||
|
'message': string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof CheckCacheDefaultResponse
|
||||||
|
*/
|
||||||
|
'code': string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<CheckCacheDefaultResponseIssuesInner>}
|
||||||
|
* @memberof CheckCacheDefaultResponse
|
||||||
|
*/
|
||||||
|
'issues'?: Array<CheckCacheDefaultResponseIssuesInner>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface CheckCacheDefaultResponseIssuesInner
|
||||||
|
*/
|
||||||
|
export interface CheckCacheDefaultResponseIssuesInner {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof CheckCacheDefaultResponseIssuesInner
|
||||||
|
*/
|
||||||
|
'message': string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface CheckCacheRequest
|
||||||
|
*/
|
||||||
|
export interface CheckCacheRequest {
|
||||||
|
/**
|
||||||
|
* Unix timestamp in milliseconds
|
||||||
|
* @type {number}
|
||||||
|
* @memberof CheckCacheRequest
|
||||||
|
*/
|
||||||
|
'requestedAt': number;
|
||||||
|
/**
|
||||||
|
* JSON-encoded request payload
|
||||||
|
* @type {any}
|
||||||
|
* @memberof CheckCacheRequest
|
||||||
|
*/
|
||||||
|
'reqPayload'?: any;
|
||||||
|
/**
|
||||||
|
* Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }
|
||||||
|
* @type {{ [key: string]: string; }}
|
||||||
|
* @memberof CheckCacheRequest
|
||||||
|
*/
|
||||||
|
'tags'?: { [key: string]: string; };
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface LocalTestingOnlyGetLatestLoggedCall200Response
|
||||||
|
*/
|
||||||
|
export interface LocalTestingOnlyGetLatestLoggedCall200Response {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof LocalTestingOnlyGetLatestLoggedCall200Response
|
||||||
|
*/
|
||||||
|
'createdAt': string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof LocalTestingOnlyGetLatestLoggedCall200Response
|
||||||
|
*/
|
||||||
|
'cacheHit': boolean;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {{ [key: string]: string; }}
|
||||||
|
* @memberof LocalTestingOnlyGetLatestLoggedCall200Response
|
||||||
|
*/
|
||||||
|
'tags': { [key: string]: string; };
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {LocalTestingOnlyGetLatestLoggedCall200ResponseModelResponse}
|
||||||
|
* @memberof LocalTestingOnlyGetLatestLoggedCall200Response
|
||||||
|
*/
|
||||||
|
'modelResponse': LocalTestingOnlyGetLatestLoggedCall200ResponseModelResponse | null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface LocalTestingOnlyGetLatestLoggedCall200ResponseModelResponse
|
||||||
|
*/
|
||||||
|
export interface LocalTestingOnlyGetLatestLoggedCall200ResponseModelResponse {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof LocalTestingOnlyGetLatestLoggedCall200ResponseModelResponse
|
||||||
|
*/
|
||||||
|
'id': string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof LocalTestingOnlyGetLatestLoggedCall200ResponseModelResponse
|
||||||
|
*/
|
||||||
|
'statusCode': number | null;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof LocalTestingOnlyGetLatestLoggedCall200ResponseModelResponse
|
||||||
|
*/
|
||||||
|
'errorMessage': string | null;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {any}
|
||||||
|
* @memberof LocalTestingOnlyGetLatestLoggedCall200ResponseModelResponse
|
||||||
|
*/
|
||||||
|
'reqPayload'?: any;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {any}
|
||||||
|
* @memberof LocalTestingOnlyGetLatestLoggedCall200ResponseModelResponse
|
||||||
|
*/
|
||||||
|
'respPayload'?: any;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface ReportRequest
|
||||||
|
*/
|
||||||
|
export interface ReportRequest {
|
||||||
|
/**
|
||||||
|
* Unix timestamp in milliseconds
|
||||||
|
* @type {number}
|
||||||
|
* @memberof ReportRequest
|
||||||
|
*/
|
||||||
|
'requestedAt': number;
|
||||||
|
/**
|
||||||
|
* Unix timestamp in milliseconds
|
||||||
|
* @type {number}
|
||||||
|
* @memberof ReportRequest
|
||||||
|
*/
|
||||||
|
'receivedAt': number;
|
||||||
|
/**
|
||||||
|
* JSON-encoded request payload
|
||||||
|
* @type {any}
|
||||||
|
* @memberof ReportRequest
|
||||||
|
*/
|
||||||
|
'reqPayload'?: any;
|
||||||
|
/**
|
||||||
|
* JSON-encoded response payload
|
||||||
|
* @type {any}
|
||||||
|
* @memberof ReportRequest
|
||||||
|
*/
|
||||||
|
'respPayload'?: any;
|
||||||
|
/**
|
||||||
|
* HTTP status code of response
|
||||||
|
* @type {number}
|
||||||
|
* @memberof ReportRequest
|
||||||
|
*/
|
||||||
|
'statusCode'?: number;
|
||||||
|
/**
|
||||||
|
* User-friendly error message
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ReportRequest
|
||||||
|
*/
|
||||||
|
'errorMessage'?: string;
|
||||||
|
/**
|
||||||
|
* Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }
|
||||||
|
* @type {{ [key: string]: string; }}
|
||||||
|
* @memberof ReportRequest
|
||||||
|
*/
|
||||||
|
'tags'?: { [key: string]: string; };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DefaultApi - axios parameter creator
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const DefaultApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Check if a prompt is cached
|
||||||
|
* @param {CheckCacheRequest} checkCacheRequest
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
checkCache: async (checkCacheRequest: CheckCacheRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
|
// verify required parameter 'checkCacheRequest' is not null or undefined
|
||||||
|
assertParamExists('checkCache', 'checkCacheRequest', checkCacheRequest)
|
||||||
|
const localVarPath = `/check-cache`;
|
||||||
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
|
let baseOptions;
|
||||||
|
if (configuration) {
|
||||||
|
baseOptions = configuration.baseOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||||
|
const localVarHeaderParameter = {} as any;
|
||||||
|
const localVarQueryParameter = {} as any;
|
||||||
|
|
||||||
|
// authentication Authorization required
|
||||||
|
// http bearer authentication required
|
||||||
|
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||||
|
|
||||||
|
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||||
|
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||||
|
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||||
|
localVarRequestOptions.data = serializeDataIfNeeded(checkCacheRequest, localVarRequestOptions, configuration)
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: toPathString(localVarUrlObj),
|
||||||
|
options: localVarRequestOptions,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get the latest logged call (only for local testing)
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
localTestingOnlyGetLatestLoggedCall: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
|
const localVarPath = `/local-testing-only-get-latest-logged-call`;
|
||||||
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
|
let baseOptions;
|
||||||
|
if (configuration) {
|
||||||
|
baseOptions = configuration.baseOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||||
|
const localVarHeaderParameter = {} as any;
|
||||||
|
const localVarQueryParameter = {} as any;
|
||||||
|
|
||||||
|
// authentication Authorization required
|
||||||
|
// http bearer authentication required
|
||||||
|
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||||
|
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||||
|
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: toPathString(localVarUrlObj),
|
||||||
|
options: localVarRequestOptions,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Report an API call
|
||||||
|
* @param {ReportRequest} reportRequest
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
report: async (reportRequest: ReportRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
|
// verify required parameter 'reportRequest' is not null or undefined
|
||||||
|
assertParamExists('report', 'reportRequest', reportRequest)
|
||||||
|
const localVarPath = `/report`;
|
||||||
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
|
let baseOptions;
|
||||||
|
if (configuration) {
|
||||||
|
baseOptions = configuration.baseOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||||
|
const localVarHeaderParameter = {} as any;
|
||||||
|
const localVarQueryParameter = {} as any;
|
||||||
|
|
||||||
|
// authentication Authorization required
|
||||||
|
// http bearer authentication required
|
||||||
|
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||||
|
|
||||||
|
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||||
|
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||||
|
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||||
|
localVarRequestOptions.data = serializeDataIfNeeded(reportRequest, localVarRequestOptions, configuration)
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: toPathString(localVarUrlObj),
|
||||||
|
options: localVarRequestOptions,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DefaultApi - functional programming interface
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const DefaultApiFp = function(configuration?: Configuration) {
|
||||||
|
const localVarAxiosParamCreator = DefaultApiAxiosParamCreator(configuration)
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Check if a prompt is cached
|
||||||
|
* @param {CheckCacheRequest} checkCacheRequest
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
async checkCache(checkCacheRequest: CheckCacheRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<CheckCache200Response>> {
|
||||||
|
const localVarAxiosArgs = await localVarAxiosParamCreator.checkCache(checkCacheRequest, options);
|
||||||
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get the latest logged call (only for local testing)
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
async localTestingOnlyGetLatestLoggedCall(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<LocalTestingOnlyGetLatestLoggedCall200Response>> {
|
||||||
|
const localVarAxiosArgs = await localVarAxiosParamCreator.localTestingOnlyGetLatestLoggedCall(options);
|
||||||
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Report an API call
|
||||||
|
* @param {ReportRequest} reportRequest
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
async report(reportRequest: ReportRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<any>> {
|
||||||
|
const localVarAxiosArgs = await localVarAxiosParamCreator.report(reportRequest, options);
|
||||||
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DefaultApi - factory interface
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const DefaultApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||||
|
const localVarFp = DefaultApiFp(configuration)
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Check if a prompt is cached
|
||||||
|
* @param {CheckCacheRequest} checkCacheRequest
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
checkCache(checkCacheRequest: CheckCacheRequest, options?: any): AxiosPromise<CheckCache200Response> {
|
||||||
|
return localVarFp.checkCache(checkCacheRequest, options).then((request) => request(axios, basePath));
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get the latest logged call (only for local testing)
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
localTestingOnlyGetLatestLoggedCall(options?: any): AxiosPromise<LocalTestingOnlyGetLatestLoggedCall200Response> {
|
||||||
|
return localVarFp.localTestingOnlyGetLatestLoggedCall(options).then((request) => request(axios, basePath));
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Report an API call
|
||||||
|
* @param {ReportRequest} reportRequest
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
report(reportRequest: ReportRequest, options?: any): AxiosPromise<any> {
|
||||||
|
return localVarFp.report(reportRequest, options).then((request) => request(axios, basePath));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DefaultApi - object-oriented interface
|
||||||
|
* @export
|
||||||
|
* @class DefaultApi
|
||||||
|
* @extends {BaseAPI}
|
||||||
|
*/
|
||||||
|
export class DefaultApi extends BaseAPI {
|
||||||
|
/**
|
||||||
|
* Check if a prompt is cached
|
||||||
|
* @param {CheckCacheRequest} checkCacheRequest
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
* @memberof DefaultApi
|
||||||
|
*/
|
||||||
|
public checkCache(checkCacheRequest: CheckCacheRequest, options?: AxiosRequestConfig) {
|
||||||
|
return DefaultApiFp(this.configuration).checkCache(checkCacheRequest, options).then((request) => request(this.axios, this.basePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the latest logged call (only for local testing)
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
* @memberof DefaultApi
|
||||||
|
*/
|
||||||
|
public localTestingOnlyGetLatestLoggedCall(options?: AxiosRequestConfig) {
|
||||||
|
return DefaultApiFp(this.configuration).localTestingOnlyGetLatestLoggedCall(options).then((request) => request(this.axios, this.basePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report an API call
|
||||||
|
* @param {ReportRequest} reportRequest
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
* @memberof DefaultApi
|
||||||
|
*/
|
||||||
|
public report(reportRequest: ReportRequest, options?: AxiosRequestConfig) {
|
||||||
|
return DefaultApiFp(this.configuration).report(reportRequest, options).then((request) => request(this.axios, this.basePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* OpenPipe API
|
* OpenPipe API
|
||||||
* The public API for reporting API calls to OpenPipe
|
* The public API for reporting API calls to OpenPipe
|
||||||
*
|
*
|
||||||
* The version of the OpenAPI document: 0.1.0
|
* The version of the OpenAPI document: 0.1.1
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
@@ -19,7 +19,7 @@ import type { Configuration } from './configuration';
|
|||||||
import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios';
|
import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios';
|
||||||
import globalAxios from 'axios';
|
import globalAxios from 'axios';
|
||||||
|
|
||||||
export const BASE_PATH = "https://app.openpipe.ai/api".replace(/\/+$/, "");
|
export const BASE_PATH = "https://app.openpipe.ai/api/v1".replace(/\/+$/, "");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* OpenPipe API
|
* OpenPipe API
|
||||||
* The public API for reporting API calls to OpenPipe
|
* The public API for reporting API calls to OpenPipe
|
||||||
*
|
*
|
||||||
* The version of the OpenAPI document: 0.1.0
|
* The version of the OpenAPI document: 0.1.1
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* OpenPipe API
|
* OpenPipe API
|
||||||
* The public API for reporting API calls to OpenPipe
|
* The public API for reporting API calls to OpenPipe
|
||||||
*
|
*
|
||||||
* The version of the OpenAPI document: 0.1.0
|
* The version of the OpenAPI document: 0.1.1
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* OpenPipe API
|
* OpenPipe API
|
||||||
* The public API for reporting API calls to OpenPipe
|
* The public API for reporting API calls to OpenPipe
|
||||||
*
|
*
|
||||||
* The version of the OpenAPI document: 0.1.0
|
* The version of the OpenAPI document: 0.1.1
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
90
client-libs/typescript/src/openai-legacy/index.ts
Normal file
90
client-libs/typescript/src/openai-legacy/index.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
// import * as openPipeClient from "../codegen";
|
||||||
|
// import * as openai from "openai-legacy";
|
||||||
|
// import { version } from "../package.json";
|
||||||
|
|
||||||
|
// // Anything we don't override we want to pass through to openai directly
|
||||||
|
// export * as openAILegacy from "openai-legacy";
|
||||||
|
|
||||||
|
// type OPConfigurationParameters = {
|
||||||
|
// apiKey?: string;
|
||||||
|
// basePath?: string;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export class Configuration extends openai.Configuration {
|
||||||
|
// public qkConfig?: openPipeClient.Configuration;
|
||||||
|
|
||||||
|
// constructor(
|
||||||
|
// config: openai.ConfigurationParameters & {
|
||||||
|
// opParameters?: OPConfigurationParameters;
|
||||||
|
// }
|
||||||
|
// ) {
|
||||||
|
// super(config);
|
||||||
|
// if (config.opParameters) {
|
||||||
|
// this.qkConfig = new openPipeClient.Configuration(config.opParameters);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// type CreateChatCompletion = InstanceType<
|
||||||
|
// typeof openai.OpenAIApi
|
||||||
|
// >["createChatCompletion"];
|
||||||
|
|
||||||
|
// export class OpenAIApi extends openai.OpenAIApi {
|
||||||
|
// public openPipeApi?: openPipeClient.DefaultApi;
|
||||||
|
|
||||||
|
// constructor(config: Configuration) {
|
||||||
|
// super(config);
|
||||||
|
// if (config.qkConfig) {
|
||||||
|
// this.openPipeApi = new openPipeClient.DefaultApi(config.qkConfig);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public async createChatCompletion(
|
||||||
|
// createChatCompletionRequest: Parameters<CreateChatCompletion>[0],
|
||||||
|
// options?: Parameters<CreateChatCompletion>[1]
|
||||||
|
// ): ReturnType<CreateChatCompletion> {
|
||||||
|
// const requestedAt = Date.now();
|
||||||
|
// let resp: Awaited<ReturnType<CreateChatCompletion>> | null = null;
|
||||||
|
// let respPayload: openai.CreateChatCompletionResponse | null = null;
|
||||||
|
// let statusCode: number | undefined = undefined;
|
||||||
|
// let errorMessage: string | undefined;
|
||||||
|
// try {
|
||||||
|
// resp = await super.createChatCompletion(
|
||||||
|
// createChatCompletionRequest,
|
||||||
|
// options
|
||||||
|
// );
|
||||||
|
// respPayload = resp.data;
|
||||||
|
// statusCode = resp.status;
|
||||||
|
// } catch (err) {
|
||||||
|
// console.error("Error in createChatCompletion");
|
||||||
|
// if ("isAxiosError" in err && err.isAxiosError) {
|
||||||
|
// errorMessage = err.response?.data?.error?.message;
|
||||||
|
// respPayload = err.response?.data;
|
||||||
|
// statusCode = err.response?.status;
|
||||||
|
// } else if ("message" in err) {
|
||||||
|
// errorMessage = err.message.toString();
|
||||||
|
// }
|
||||||
|
// throw err;
|
||||||
|
// } finally {
|
||||||
|
// this.openPipeApi
|
||||||
|
// ?.externalApiReport({
|
||||||
|
// requestedAt,
|
||||||
|
// receivedAt: Date.now(),
|
||||||
|
// reqPayload: createChatCompletionRequest,
|
||||||
|
// respPayload: respPayload,
|
||||||
|
// statusCode: statusCode,
|
||||||
|
// errorMessage,
|
||||||
|
// tags: {
|
||||||
|
// client: "openai-js",
|
||||||
|
// clientVersion: version,
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
// .catch((err) => {
|
||||||
|
// console.error("Error reporting to OP", err);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// console.log("done");
|
||||||
|
// return resp;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user