Compare commits
28 Commits
python-sdk
...
styling
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e56c93c3f | ||
|
|
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 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
.env
|
||||
.venv/
|
||||
*.pyc
|
||||
node_modules/
|
||||
*.tsbuildinfo
|
||||
|
||||
@@ -6,7 +6,7 @@ const config = {
|
||||
overrides: [
|
||||
{
|
||||
extends: ["plugin:@typescript-eslint/recommended-requiring-type-checking"],
|
||||
files: ["*.ts", "*.tsx"],
|
||||
files: ["*.mts", "*.ts", "*.tsx"],
|
||||
parserOptions: {
|
||||
project: path.join(__dirname, "tsconfig.json"),
|
||||
},
|
||||
|
||||
3
app/.gitignore
vendored
3
app/.gitignore
vendored
@@ -44,3 +44,6 @@ yarn-error.log*
|
||||
|
||||
# Sentry Auth Token
|
||||
.sentryclirc
|
||||
|
||||
# custom openai intialization
|
||||
src/server/utils/openaiCustomConfig.json
|
||||
|
||||
3
app/@types/nextjs-routes.d.ts
vendored
3
app/@types/nextjs-routes.d.ts
vendored
@@ -18,13 +18,14 @@ declare module "nextjs-routes" {
|
||||
| StaticRoute<"/api/openapi">
|
||||
| StaticRoute<"/api/sentry-example-api">
|
||||
| DynamicRoute<"/api/trpc/[trpc]", { "trpc": string }>
|
||||
| StaticRoute<"/dashboard">
|
||||
| DynamicRoute<"/data/[id]", { "id": string }>
|
||||
| StaticRoute<"/data">
|
||||
| DynamicRoute<"/experiments/[id]", { "id": string }>
|
||||
| StaticRoute<"/experiments">
|
||||
| StaticRoute<"/">
|
||||
| StaticRoute<"/logged-calls">
|
||||
| StaticRoute<"/project/settings">
|
||||
| StaticRoute<"/request-logs">
|
||||
| StaticRoute<"/sentry-example-page">
|
||||
| StaticRoute<"/world-champs">
|
||||
| StaticRoute<"/world-champs/signup">;
|
||||
|
||||
@@ -36,6 +36,8 @@ let config = {
|
||||
});
|
||||
return config;
|
||||
},
|
||||
|
||||
transpilePackages: ["openpipe"],
|
||||
};
|
||||
|
||||
config = nextRoutes()(config);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "openpipe",
|
||||
"name": "openpipe-app",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"version": "0.1.0",
|
||||
"license": "Apache-2.0",
|
||||
@@ -16,7 +17,7 @@
|
||||
"postinstall": "prisma generate",
|
||||
"lint": "next lint",
|
||||
"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",
|
||||
"check": "concurrently 'pnpm lint' 'pnpm tsc' 'pnpm prettier . --check'",
|
||||
"test": "pnpm vitest"
|
||||
@@ -24,7 +25,6 @@
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.5.8",
|
||||
"@apidevtools/json-schema-ref-parser": "^10.1.0",
|
||||
"@babel/preset-typescript": "^7.22.5",
|
||||
"@babel/standalone": "^7.22.9",
|
||||
"@chakra-ui/anatomy": "^2.2.0",
|
||||
"@chakra-ui/next-js": "^2.1.4",
|
||||
@@ -100,7 +100,8 @@
|
||||
"uuid": "^9.0.0",
|
||||
"vite-tsconfig-paths": "^4.2.0",
|
||||
"zod": "^3.21.4",
|
||||
"zustand": "^4.3.9"
|
||||
"zustand": "^4.3.9",
|
||||
"openpipe": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openapi-contrib/openapi-schema-to-json-schema": "^4.0.5",
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "LoggedCall" ADD COLUMN "model" TEXT;
|
||||
@@ -273,7 +273,8 @@ model LoggedCall {
|
||||
projectId String @db.Uuid
|
||||
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
|
||||
tags LoggedCallTag[]
|
||||
model String?
|
||||
tags LoggedCallTag[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@ -33,25 +33,11 @@ export default function AddVariantButton() {
|
||||
<Flex w="100%" justifyContent="flex-end">
|
||||
<ActionButton
|
||||
onClick={onClick}
|
||||
py={5}
|
||||
py={7}
|
||||
leftIcon={<Icon as={loading ? Spinner : BsPlus} boxSize={6} mr={loading ? 1 : 0} />}
|
||||
>
|
||||
<Text display={{ base: "none", md: "flex" }}>Add Variant</Text>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
import { type StackProps } from "@chakra-ui/react";
|
||||
|
||||
import { useScenarios } from "~/utils/hooks";
|
||||
import Paginator from "../Paginator";
|
||||
|
||||
const ScenarioPaginator = () => {
|
||||
const ScenarioPaginator = (props: StackProps) => {
|
||||
const { data } = useScenarios();
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
const { scenarios, startIndex, lastPage, count } = data;
|
||||
const { count } = data;
|
||||
|
||||
return (
|
||||
<Paginator
|
||||
numItemsLoaded={scenarios.length}
|
||||
startIndex={startIndex}
|
||||
lastPage={lastPage}
|
||||
count={count}
|
||||
/>
|
||||
);
|
||||
return <Paginator count={count} condense {...props} />;
|
||||
};
|
||||
|
||||
export default ScenarioPaginator;
|
||||
|
||||
@@ -10,6 +10,8 @@ const ScenarioRow = (props: {
|
||||
variants: PromptVariant[];
|
||||
canHide: boolean;
|
||||
rowStart: number;
|
||||
isFirst: boolean;
|
||||
isLast: boolean;
|
||||
}) => {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
@@ -21,10 +23,14 @@ const ScenarioRow = (props: {
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
sx={isHovered ? highlightStyle : undefined}
|
||||
borderLeftWidth={1}
|
||||
{...borders}
|
||||
bgColor="white"
|
||||
rowStart={props.rowStart}
|
||||
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} />
|
||||
</GridItem>
|
||||
@@ -34,8 +40,12 @@ const ScenarioRow = (props: {
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
sx={isHovered ? highlightStyle : undefined}
|
||||
bgColor="white"
|
||||
rowStart={props.rowStart}
|
||||
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}
|
||||
>
|
||||
<OutputCell key={variant.id} scenario={props.scenario} variant={variant} />
|
||||
|
||||
@@ -48,7 +48,7 @@ export const ScenariosHeader = () => {
|
||||
);
|
||||
|
||||
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">
|
||||
Scenarios ({scenarios.data?.count})
|
||||
</Text>
|
||||
@@ -57,11 +57,16 @@ export const ScenariosHeader = () => {
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
mt={1}
|
||||
ml={2}
|
||||
variant="ghost"
|
||||
aria-label="Edit Scenarios"
|
||||
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
|
||||
icon={<Icon as={BsPlus} boxSize={6} mx="-5px" />}
|
||||
onClick={() => onAddScenario(false)}
|
||||
|
||||
@@ -53,20 +53,29 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
|
||||
colStart: i + 2,
|
||||
borderLeftWidth: i === 0 ? 1 : 0,
|
||||
marginLeft: i === 0 ? "-1px" : 0,
|
||||
backgroundColor: "gray.100",
|
||||
backgroundColor: "white",
|
||||
};
|
||||
const isFirst = i === 0;
|
||||
const isLast = i === variants.data.length - 1;
|
||||
return (
|
||||
<Fragment key={variant.uiId}>
|
||||
<VariantHeader
|
||||
variant={variant}
|
||||
canHide={variants.data.length > 1}
|
||||
rowStart={1}
|
||||
borderTopLeftRadius={isFirst ? 8 : 0}
|
||||
borderTopRightRadius={isLast ? 8 : 0}
|
||||
{...sharedProps}
|
||||
/>
|
||||
<GridItem rowStart={2} {...sharedProps}>
|
||||
<VariantEditor variant={variant} />
|
||||
</GridItem>
|
||||
<GridItem rowStart={3} {...sharedProps}>
|
||||
<GridItem
|
||||
rowStart={3}
|
||||
{...sharedProps}
|
||||
borderBottomLeftRadius={isFirst ? 8 : 0}
|
||||
borderBottomRightRadius={isLast ? 8 : 0}
|
||||
>
|
||||
<VariantStats variant={variant} />
|
||||
</GridItem>
|
||||
</Fragment>
|
||||
@@ -77,7 +86,6 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
|
||||
colSpan={allCols - 1}
|
||||
rowStart={variantHeaderRows + 1}
|
||||
colStart={1}
|
||||
{...borders}
|
||||
borderRightWidth={0}
|
||||
>
|
||||
<ScenariosHeader />
|
||||
@@ -90,6 +98,8 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
|
||||
scenario={scenario}
|
||||
variants={variants.data}
|
||||
canHide={visibleScenariosCount > 1}
|
||||
isFirst={i === 0}
|
||||
isLast={i === visibleScenariosCount - 1}
|
||||
/>
|
||||
))}
|
||||
<GridItem
|
||||
|
||||
@@ -1,77 +1,117 @@
|
||||
import { Box, HStack, IconButton } from "@chakra-ui/react";
|
||||
import {
|
||||
BsChevronDoubleLeft,
|
||||
BsChevronDoubleRight,
|
||||
BsChevronLeft,
|
||||
BsChevronRight,
|
||||
} from "react-icons/bs";
|
||||
import { usePage } from "~/utils/hooks";
|
||||
import { HStack, IconButton, Text, Select, type StackProps, Icon } from "@chakra-ui/react";
|
||||
import React, { useCallback } from "react";
|
||||
import { FiChevronsLeft, FiChevronsRight, FiChevronLeft, FiChevronRight } from "react-icons/fi";
|
||||
import { usePageParams } from "~/utils/hooks";
|
||||
|
||||
const pageSizeOptions = [10, 25, 50, 100];
|
||||
|
||||
const Paginator = ({
|
||||
numItemsLoaded,
|
||||
startIndex,
|
||||
lastPage,
|
||||
count,
|
||||
}: {
|
||||
numItemsLoaded: number;
|
||||
startIndex: number;
|
||||
lastPage: number;
|
||||
count: number;
|
||||
}) => {
|
||||
const [page, setPage] = usePage();
|
||||
condense,
|
||||
...props
|
||||
}: { count: number; condense?: boolean } & StackProps) => {
|
||||
const { page, pageSize, setPageParams } = usePageParams();
|
||||
|
||||
const lastPage = Math.ceil(count / pageSize);
|
||||
|
||||
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 = () => {
|
||||
if (page < lastPage) {
|
||||
setPage(page + 1, "replace");
|
||||
setPageParams({ page: page + 1 }, "replace");
|
||||
}
|
||||
};
|
||||
|
||||
const prevPage = () => {
|
||||
if (page > 1) {
|
||||
setPage(page - 1, "replace");
|
||||
setPageParams({ page: page - 1 }, "replace");
|
||||
}
|
||||
};
|
||||
|
||||
const goToLastPage = () => setPage(lastPage, "replace");
|
||||
const goToFirstPage = () => setPage(1, "replace");
|
||||
const goToLastPage = () => setPageParams({ page: lastPage }, "replace");
|
||||
const goToFirstPage = () => setPageParams({ page: 1 }, "replace");
|
||||
|
||||
return (
|
||||
<HStack pt={4}>
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={goToFirstPage}
|
||||
isDisabled={page === 1}
|
||||
aria-label="Go to first page"
|
||||
icon={<BsChevronDoubleLeft />}
|
||||
/>
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={prevPage}
|
||||
isDisabled={page === 1}
|
||||
aria-label="Previous page"
|
||||
icon={<BsChevronLeft />}
|
||||
/>
|
||||
<Box>
|
||||
{startIndex}-{startIndex + numItemsLoaded - 1} / {count}
|
||||
</Box>
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={nextPage}
|
||||
isDisabled={page === lastPage}
|
||||
aria-label="Next page"
|
||||
icon={<BsChevronRight />}
|
||||
/>
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={goToLastPage}
|
||||
isDisabled={page === lastPage}
|
||||
aria-label="Go to last page"
|
||||
icon={<BsChevronDoubleRight />}
|
||||
/>
|
||||
<HStack
|
||||
pt={4}
|
||||
spacing={8}
|
||||
justifyContent={condense ? "flex-start" : "space-between"}
|
||||
alignItems="center"
|
||||
w="full"
|
||||
{...props}
|
||||
>
|
||||
{!condense && (
|
||||
<>
|
||||
<HStack>
|
||||
<Text>Rows</Text>
|
||||
<Select
|
||||
value={pageSize}
|
||||
onChange={(e) => updatePageSize(parseInt(e.target.value))}
|
||||
w={20}
|
||||
backgroundColor="white"
|
||||
>
|
||||
{pageSizeOptions.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</HStack>
|
||||
<Text>
|
||||
Page {page} of {lastPage}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
|
||||
<HStack>
|
||||
<IconButton
|
||||
variant="outline"
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -75,7 +75,7 @@ export default function VariantHeader(
|
||||
padding={0}
|
||||
sx={{
|
||||
position: "sticky",
|
||||
top: "0",
|
||||
top: "-2",
|
||||
// Ensure that the menu always appears above the sticky header of other variants
|
||||
zIndex: menuOpen ? "dropdown" : 10,
|
||||
}}
|
||||
@@ -84,6 +84,7 @@ export default function VariantHeader(
|
||||
>
|
||||
<HStack
|
||||
spacing={2}
|
||||
py={2}
|
||||
alignItems="flex-start"
|
||||
minH={headerMinHeight}
|
||||
draggable={!isInputHovered}
|
||||
@@ -102,7 +103,9 @@ export default function VariantHeader(
|
||||
setIsDragTarget(false);
|
||||
}}
|
||||
onDrop={onReorder}
|
||||
backgroundColor={isDragTarget ? "gray.200" : "gray.100"}
|
||||
backgroundColor={isDragTarget ? "gray.200" : "white"}
|
||||
borderTopLeftRadius={gridItemProps.borderTopLeftRadius}
|
||||
borderTopRightRadius={gridItemProps.borderTopRightRadius}
|
||||
h="full"
|
||||
>
|
||||
<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 Paginator from "../Paginator";
|
||||
|
||||
const DatasetEntriesPaginator = () => {
|
||||
const DatasetEntriesPaginator = (props: StackProps) => {
|
||||
const { data } = useDatasetEntries();
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
const { entries, startIndex, lastPage, count } = data;
|
||||
const { count } = data;
|
||||
|
||||
return (
|
||||
<Paginator
|
||||
numItemsLoaded={entries.length}
|
||||
startIndex={startIndex}
|
||||
lastPage={lastPage}
|
||||
count={count}
|
||||
/>
|
||||
);
|
||||
return <Paginator count={count} {...props} />;
|
||||
};
|
||||
|
||||
export default DatasetEntriesPaginator;
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
Spinner,
|
||||
AspectRatio,
|
||||
SkeletonText,
|
||||
Card,
|
||||
} from "@chakra-ui/react";
|
||||
import { RiFlaskLine } from "react-icons/ri";
|
||||
import { formatTimePast } from "~/utils/dayjs";
|
||||
@@ -29,17 +30,22 @@ type ExperimentData = {
|
||||
|
||||
export const ExperimentCard = ({ exp }: { exp: ExperimentData }) => {
|
||||
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
|
||||
as={Link}
|
||||
w="full"
|
||||
h="full"
|
||||
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"
|
||||
>
|
||||
<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>
|
||||
</HStack>
|
||||
</VStack>
|
||||
</AspectRatio>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -89,30 +95,30 @@ export const NewExperimentCard = () => {
|
||||
}, [createMutation, router, selectedProjectId]);
|
||||
|
||||
return (
|
||||
<AspectRatio ratio={1.2} w="full">
|
||||
<VStack
|
||||
align="center"
|
||||
justify="center"
|
||||
_hover={{ cursor: "pointer", bg: "gray.50" }}
|
||||
transition="background 0.2s"
|
||||
cursor="pointer"
|
||||
borderColor="gray.200"
|
||||
borderWidth={1}
|
||||
p={4}
|
||||
onClick={createExperiment}
|
||||
>
|
||||
<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 align="center" justify="center" w="full" h="full" p={4} onClick={createExperiment}>
|
||||
<Icon as={isLoading ? Spinner : BsPlusSquare} boxSize={8} />
|
||||
<Text display={{ base: "none", md: "block" }} ml={2}>
|
||||
New Experiment
|
||||
</Text>
|
||||
</VStack>
|
||||
</AspectRatio>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export const ExperimentCardSkeleton = () => (
|
||||
<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={2} w="60%" />
|
||||
<SkeletonText noOfLines={1} w="80%" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import {
|
||||
Heading,
|
||||
VStack,
|
||||
@@ -9,14 +9,14 @@ import {
|
||||
Box,
|
||||
Link as ChakraLink,
|
||||
Flex,
|
||||
useBreakpointValue,
|
||||
} from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import Link from "next/link";
|
||||
import { BsGearFill, BsGithub, BsPersonCircle } from "react-icons/bs";
|
||||
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 UserMenu from "./UserMenu";
|
||||
import { env } from "~/env.mjs";
|
||||
import ProjectMenu from "./ProjectMenu";
|
||||
import NavSidebarOption from "./NavSidebarOption";
|
||||
@@ -27,10 +27,16 @@ const Divider = () => <Box h="1px" bgColor="gray.300" w="full" />;
|
||||
const NavSidebar = () => {
|
||||
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 (
|
||||
<VStack
|
||||
align="stretch"
|
||||
bgColor="gray.50"
|
||||
py={2}
|
||||
px={2}
|
||||
pb={0}
|
||||
@@ -40,32 +46,59 @@ const NavSidebar = () => {
|
||||
borderRightWidth={1}
|
||||
borderColor="gray.300"
|
||||
>
|
||||
<HStack
|
||||
as={Link}
|
||||
href="/"
|
||||
_hover={{ textDecoration: "none" }}
|
||||
spacing={{ base: 1, md: 0 }}
|
||||
mx={2}
|
||||
py={{ base: 1, md: 2 }}
|
||||
>
|
||||
<Image src="/logo.svg" alt="" boxSize={6} mr={4} ml={{ base: 0.5, md: 0 }} />
|
||||
<Heading size="md" fontFamily="inconsolata, monospace">
|
||||
OpenPipe
|
||||
</Heading>
|
||||
</HStack>
|
||||
<Divider />
|
||||
{displayLogo && (
|
||||
<>
|
||||
<HStack
|
||||
as={Link}
|
||||
href="/"
|
||||
_hover={{ textDecoration: "none" }}
|
||||
spacing={{ base: 1, md: 0 }}
|
||||
mx={2}
|
||||
py={{ base: 1, md: 2 }}
|
||||
>
|
||||
<Image src="/logo.svg" alt="" boxSize={6} mr={4} ml={{ base: 0.5, md: 0 }} />
|
||||
<Heading size="md" fontFamily="inconsolata, monospace">
|
||||
OpenPipe
|
||||
</Heading>
|
||||
</HStack>
|
||||
<Divider />
|
||||
</>
|
||||
)}
|
||||
|
||||
<VStack align="flex-start" overflowY="auto" overflowX="hidden" flex={1}>
|
||||
{user != null && (
|
||||
<>
|
||||
<ProjectMenu />
|
||||
<Divider />
|
||||
|
||||
{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" />
|
||||
{env.NEXT_PUBLIC_SHOW_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 && (
|
||||
@@ -87,20 +120,7 @@ const NavSidebar = () => {
|
||||
</NavSidebarOption>
|
||||
)}
|
||||
</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 />
|
||||
<VStack spacing={0} align="center">
|
||||
<ChakraLink
|
||||
@@ -160,7 +180,7 @@ export default function AppShell({
|
||||
<title>{title ? `${title} | OpenPipe` : "OpenPipe"}</title>
|
||||
</Head>
|
||||
<NavSidebar />
|
||||
<Box h="100%" flex={1} overflowY="auto">
|
||||
<Box h="100%" flex={1} overflowY="auto" bgColor="gray.50">
|
||||
{children}
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
@@ -6,16 +6,18 @@ import {
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
Flex,
|
||||
IconButton,
|
||||
Icon,
|
||||
Divider,
|
||||
Button,
|
||||
useDisclosure,
|
||||
Spinner,
|
||||
Link as ChakraLink,
|
||||
Image,
|
||||
Box,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
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 { useAppStore } from "~/state/store";
|
||||
@@ -23,13 +25,14 @@ import { api } from "~/utils/api";
|
||||
import NavSidebarOption from "./NavSidebarOption";
|
||||
import { useHandledAsyncCallback, useSelectedProject } from "~/utils/hooks";
|
||||
import { useRouter } from "next/router";
|
||||
import { useSession, signOut } from "next-auth/react";
|
||||
|
||||
export default function ProjectMenu() {
|
||||
const router = useRouter();
|
||||
const utils = api.useContext();
|
||||
|
||||
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();
|
||||
|
||||
@@ -39,9 +42,9 @@ export default function ProjectMenu() {
|
||||
projects[0] &&
|
||||
(!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();
|
||||
|
||||
@@ -49,28 +52,32 @@ export default function ProjectMenu() {
|
||||
|
||||
const createMutation = api.projects.create.useMutation();
|
||||
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();
|
||||
setselectedProjectId(newProj.id);
|
||||
setSelectedProjectId(newProj.id);
|
||||
await router.push({ pathname: "/project/settings" });
|
||||
}, [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 (
|
||||
<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" }}
|
||||
<VStack w="full" alignItems="flex-start" spacing={0} py={1}>
|
||||
<Popover
|
||||
placement="bottom"
|
||||
isOpen={popover.isOpen}
|
||||
onOpen={popover.onOpen}
|
||||
onClose={popover.onClose}
|
||||
closeOnBlur
|
||||
>
|
||||
PROJECT
|
||||
</Text>
|
||||
<Popover placement="right-end" isOpen={popover.isOpen} onClose={popover.onClose} closeOnBlur>
|
||||
<PopoverTrigger>
|
||||
<NavSidebarOption>
|
||||
<HStack w="full" onClick={popover.onToggle}>
|
||||
<HStack w="full">
|
||||
<Flex
|
||||
p={1}
|
||||
borderRadius={4}
|
||||
@@ -83,20 +90,35 @@ export default function ProjectMenu() {
|
||||
>
|
||||
<Text>{selectedProject?.name[0]?.toUpperCase()}</Text>
|
||||
</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}
|
||||
</Text>
|
||||
<Icon as={BsChevronRight} boxSize={4} color="gray.500" />
|
||||
<Box mr={2}>{profileImage}</Box>
|
||||
</HStack>
|
||||
</NavSidebarOption>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent _focusVisible={{ outline: "unset" }} ml={-1} w="auto" minW={100} maxW={280}>
|
||||
<VStack alignItems="flex-start" spacing={2} py={4} px={2}>
|
||||
<Text color="gray.500" fontSize="xs" fontWeight="bold" pb={1}>
|
||||
PROJECTS
|
||||
<PopoverContent
|
||||
_focusVisible={{ outline: "unset" }}
|
||||
ml={-1}
|
||||
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>
|
||||
<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) => (
|
||||
<ProjectOption
|
||||
key={proj.id}
|
||||
@@ -105,19 +127,38 @@ export default function ProjectMenu() {
|
||||
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>
|
||||
<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>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
@@ -134,38 +175,27 @@ const ProjectOption = ({
|
||||
isActive: boolean;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const setselectedProjectId = useAppStore((s) => s.setselectedProjectId);
|
||||
const setSelectedProjectId = useAppStore((s) => s.setSelectedProjectId);
|
||||
const [gearHovered, setGearHovered] = useState(false);
|
||||
|
||||
return (
|
||||
<HStack
|
||||
as={Link}
|
||||
href="/experiments"
|
||||
onClick={() => {
|
||||
setselectedProjectId(proj.id);
|
||||
setSelectedProjectId(proj.id);
|
||||
onClose();
|
||||
}}
|
||||
w="full"
|
||||
justifyContent="space-between"
|
||||
bgColor={isActive ? "gray.100" : "transparent"}
|
||||
_hover={gearHovered ? undefined : { bgColor: "gray.200", textDecoration: "none" }}
|
||||
p={2}
|
||||
color={isActive ? "blue.400" : undefined}
|
||||
py={2}
|
||||
px={4}
|
||||
borderRadius={4}
|
||||
spacing={4}
|
||||
>
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
||||
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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -8,7 +8,7 @@ import { ChakraThemeProvider } from "~/theme/ChakraThemeProvider";
|
||||
import { SyncAppStore } from "~/state/sync";
|
||||
import NextAdapterApp from "next-query-params/app";
|
||||
import { QueryParamProvider } from "use-query-params";
|
||||
import { SessionIdentifier } from "~/utils/analytics/clientAnalytics";
|
||||
import { PosthogAppProvider } from "~/utils/analytics/posthog";
|
||||
|
||||
const MyApp: AppType<{ session: Session | null }> = ({
|
||||
Component,
|
||||
@@ -34,14 +34,15 @@ const MyApp: AppType<{ session: Session | null }> = ({
|
||||
<meta name="twitter:image" content="/og.png" />
|
||||
</Head>
|
||||
<SessionProvider session={session}>
|
||||
<SyncAppStore />
|
||||
<Favicon />
|
||||
<SessionIdentifier />
|
||||
<ChakraThemeProvider>
|
||||
<QueryParamProvider adapter={NextAdapterApp}>
|
||||
<Component {...pageProps} />
|
||||
</QueryParamProvider>
|
||||
</ChakraThemeProvider>
|
||||
<PosthogAppProvider>
|
||||
<SyncAppStore />
|
||||
<Favicon />
|
||||
<ChakraThemeProvider>
|
||||
<QueryParamProvider adapter={NextAdapterApp}>
|
||||
<Component {...pageProps} />
|
||||
</QueryParamProvider>
|
||||
</ChakraThemeProvider>
|
||||
</PosthogAppProvider>
|
||||
</SessionProvider>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -15,20 +15,16 @@ import {
|
||||
Tr,
|
||||
Td,
|
||||
Divider,
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
} from "@chakra-ui/react";
|
||||
import { Ban, DollarSign, Hash } from "lucide-react";
|
||||
|
||||
import AppShell from "~/components/nav/AppShell";
|
||||
import PageHeaderContainer from "~/components/nav/PageHeaderContainer";
|
||||
import ProjectBreadcrumbContents from "~/components/nav/ProjectBreadcrumbContents";
|
||||
import { useSelectedProject } from "~/utils/hooks";
|
||||
import { api } from "~/utils/api";
|
||||
import LoggedCallTable from "~/components/dashboard/LoggedCallTable";
|
||||
import LoggedCallsTable from "~/components/dashboard/LoggedCallsTable";
|
||||
import UsageGraph from "~/components/dashboard/UsageGraph";
|
||||
|
||||
export default function LoggedCalls() {
|
||||
export default function Dashboard() {
|
||||
const { data: selectedProject } = useSelectedProject();
|
||||
|
||||
const stats = api.dashboard.stats.useQuery(
|
||||
@@ -37,25 +33,15 @@ export default function LoggedCalls() {
|
||||
);
|
||||
|
||||
return (
|
||||
<AppShell title="Logged Calls" requireAuth>
|
||||
<PageHeaderContainer>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem>
|
||||
<ProjectBreadcrumbContents />
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbItem isCurrentPage>
|
||||
<Text>Logged Calls</Text>
|
||||
</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
</PageHeaderContainer>
|
||||
<VStack px={8} pt={4} alignItems="flex-start" spacing={4}>
|
||||
<AppShell title="Dashboard" requireAuth>
|
||||
<VStack px={8} py={8} alignItems="flex-start" spacing={4}>
|
||||
<Text fontSize="2xl" fontWeight="bold">
|
||||
{selectedProject?.name}
|
||||
Dashboard
|
||||
</Text>
|
||||
<Divider />
|
||||
<VStack margin="auto" spacing={4} align="stretch" w="full">
|
||||
<HStack gap={4} align="start">
|
||||
<Card variant="outline" flex={1}>
|
||||
<Card flex={1}>
|
||||
<CardHeader>
|
||||
<Heading as="h3" size="sm">
|
||||
Usage Statistics
|
||||
@@ -66,7 +52,7 @@ export default function LoggedCalls() {
|
||||
</CardBody>
|
||||
</Card>
|
||||
<VStack spacing="4" width="300px" align="stretch">
|
||||
<Card variant="outline">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<Stat>
|
||||
<HStack>
|
||||
@@ -79,7 +65,7 @@ export default function LoggedCalls() {
|
||||
</Stat>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<Card variant="outline">
|
||||
<Card>
|
||||
<CardBody>
|
||||
<Stat>
|
||||
<HStack>
|
||||
@@ -94,7 +80,7 @@ export default function LoggedCalls() {
|
||||
</Stat>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<Card variant="outline" overflow="hidden">
|
||||
<Card overflow="hidden">
|
||||
<Stat>
|
||||
<CardHeader>
|
||||
<HStack>
|
||||
@@ -120,7 +106,7 @@ export default function LoggedCalls() {
|
||||
</Card>
|
||||
</VStack>
|
||||
</HStack>
|
||||
<LoggedCallTable />
|
||||
<LoggedCallsTable />
|
||||
</VStack>
|
||||
</VStack>
|
||||
</AppShell>
|
||||
@@ -62,7 +62,7 @@ export default function Experiment() {
|
||||
|
||||
useEffect(() => {
|
||||
useAppStore.getState().sharedVariantEditor.loadMonaco().catch(console.error);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const [label, setLabel] = useState(experiment.data?.label || "");
|
||||
useEffect(() => {
|
||||
|
||||
@@ -65,7 +65,7 @@ export default function Settings() {
|
||||
</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
</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">
|
||||
<Text fontSize="2xl" fontWeight="bold">
|
||||
Project Settings
|
||||
@@ -80,6 +80,7 @@ export default function Settings() {
|
||||
borderWidth={1}
|
||||
borderRadius={4}
|
||||
borderColor="gray.300"
|
||||
bgColor="white"
|
||||
p={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
|
||||
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";
|
||||
|
||||
export function stripTypes(tsCode: string): string {
|
||||
const options = {
|
||||
presets: ["typescript"],
|
||||
filename: "file.ts",
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { datasetEntries } from "./routers/datasetEntries.router";
|
||||
import { externalApiRouter } from "./routers/externalApi.router";
|
||||
import { projectsRouter } from "./routers/projects.router";
|
||||
import { dashboardRouter } from "./routers/dashboard.router";
|
||||
import { loggedCallsRouter } from "./routers/loggedCalls.router";
|
||||
|
||||
/**
|
||||
* This is the primary router for your server.
|
||||
@@ -29,6 +30,7 @@ export const appRouter = createTRPCRouter({
|
||||
datasetEntries: datasetEntries,
|
||||
projects: projectsRouter,
|
||||
dashboard: dashboardRouter,
|
||||
loggedCalls: loggedCallsRouter,
|
||||
externalApi: externalApiRouter,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { sql } from "kysely";
|
||||
import { z } from "zod";
|
||||
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
|
||||
import { kysely, prisma } from "~/server/db";
|
||||
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||
import { kysely } from "~/server/db";
|
||||
import { requireCanViewProject } from "~/utils/accessControl";
|
||||
import dayjs from "~/utils/dayjs";
|
||||
|
||||
export const dashboardRouter = createTRPCRouter({
|
||||
stats: publicProcedure
|
||||
stats: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
// TODO: actually take startDate into account
|
||||
@@ -13,7 +14,8 @@ export const dashboardRouter = createTRPCRouter({
|
||||
projectId: z.string(),
|
||||
}),
|
||||
)
|
||||
.query(async ({ input }) => {
|
||||
.query(async ({ input, ctx }) => {
|
||||
await requireCanViewProject(input.projectId, ctx);
|
||||
// Return the stats group by hour
|
||||
const periods = await kysely
|
||||
.selectFrom("LoggedCall")
|
||||
@@ -103,16 +105,4 @@ export const dashboardRouter = createTRPCRouter({
|
||||
|
||||
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 { autogenerateDatasetEntries } from "../autogenerate/autogenerateDatasetEntries";
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
|
||||
export const datasetEntries = createTRPCRouter({
|
||||
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 }) => {
|
||||
await requireCanViewDataset(input.datasetId, ctx);
|
||||
|
||||
const { datasetId, page } = input;
|
||||
const { datasetId, page, pageSize } = input;
|
||||
|
||||
const entries = await prisma.datasetEntry.findMany({
|
||||
where: {
|
||||
datasetId,
|
||||
},
|
||||
orderBy: { createdAt: "desc" },
|
||||
skip: (page - 1) * PAGE_SIZE,
|
||||
take: PAGE_SIZE,
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
});
|
||||
|
||||
const count = await prisma.datasetEntry.count({
|
||||
@@ -31,8 +29,6 @@ export const datasetEntries = createTRPCRouter({
|
||||
|
||||
return {
|
||||
entries,
|
||||
startIndex: (page - 1) * PAGE_SIZE + 1,
|
||||
lastPage: Math.ceil(count / PAGE_SIZE),
|
||||
count,
|
||||
};
|
||||
}),
|
||||
|
||||
@@ -138,11 +138,13 @@ export const externalApiRouter = createTRPCRouter({
|
||||
const newModelResponseId = uuidv4();
|
||||
|
||||
let usage;
|
||||
let model;
|
||||
if (reqPayload.success && respPayload.success) {
|
||||
usage = modelProvider.getUsage(
|
||||
input.reqPayload as CompletionCreateParams,
|
||||
input.respPayload as ChatCompletion,
|
||||
);
|
||||
model = reqPayload.data.model;
|
||||
}
|
||||
|
||||
await prisma.$transaction([
|
||||
@@ -152,6 +154,7 @@ export const externalApiRouter = createTRPCRouter({
|
||||
projectId: key.projectId,
|
||||
requestedAt: new Date(input.requestedAt),
|
||||
cacheHit: false,
|
||||
model,
|
||||
},
|
||||
}),
|
||||
prisma.loggedCallModelResponse.create({
|
||||
@@ -188,14 +191,6 @@ export const externalApiRouter = createTRPCRouter({
|
||||
name: name.replaceAll(/[^a-zA-Z0-9_]/g, "_"),
|
||||
value,
|
||||
}));
|
||||
|
||||
if (reqPayload.success) {
|
||||
tagsToCreate.push({
|
||||
loggedCallId: newLoggedCallId,
|
||||
name: "$model",
|
||||
value: reqPayload.data.model,
|
||||
});
|
||||
}
|
||||
await prisma.loggedCallTag.createMany({
|
||||
data: tagsToCreate,
|
||||
});
|
||||
|
||||
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) };
|
||||
}),
|
||||
});
|
||||
@@ -7,15 +7,13 @@ import { runAllEvals } from "~/server/utils/evaluations";
|
||||
import { generateNewCell } from "~/server/utils/generateNewCell";
|
||||
import { requireCanModifyExperiment, requireCanViewExperiment } from "~/utils/accessControl";
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
|
||||
export const scenariosRouter = createTRPCRouter({
|
||||
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 }) => {
|
||||
await requireCanViewExperiment(input.experimentId, ctx);
|
||||
|
||||
const { experimentId, page } = input;
|
||||
const { experimentId, page, pageSize } = input;
|
||||
|
||||
const scenarios = await prisma.testScenario.findMany({
|
||||
where: {
|
||||
@@ -23,8 +21,8 @@ export const scenariosRouter = createTRPCRouter({
|
||||
visible: true,
|
||||
},
|
||||
orderBy: { sortIndex: "asc" },
|
||||
skip: (page - 1) * PAGE_SIZE,
|
||||
take: PAGE_SIZE,
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
});
|
||||
|
||||
const count = await prisma.testScenario.count({
|
||||
@@ -36,8 +34,6 @@ export const scenariosRouter = createTRPCRouter({
|
||||
|
||||
return {
|
||||
scenarios,
|
||||
startIndex: (page - 1) * PAGE_SIZE + 1,
|
||||
lastPage: Math.ceil(count / PAGE_SIZE),
|
||||
count,
|
||||
};
|
||||
}),
|
||||
|
||||
@@ -4,19 +4,18 @@ import fs from "fs";
|
||||
import path from "path";
|
||||
import { execSync } from "child_process";
|
||||
|
||||
console.log("Exporting public OpenAPI schema to client-libs/schema.json");
|
||||
|
||||
const scriptPath = import.meta.url.replace("file://", "");
|
||||
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");
|
||||
|
||||
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 });
|
||||
|
||||
@@ -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!");
|
||||
|
||||
@@ -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 { default as OriginalOpenAI } from "openai";
|
||||
// import { OpenAI } from "openpipe";
|
||||
let config: ClientOptions;
|
||||
|
||||
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(openAIConfig)
|
||||
// : new OriginalOpenAI(openAIConfig);
|
||||
// export const openai = env.OPENPIPE_API_KEY ? new OpenAI.OpenAI(config) : new OriginalOpenAI(config);
|
||||
|
||||
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"));
|
||||
|
||||
if (scenariosModel) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { type StateCreator, create } from "zustand";
|
||||
import { immer } from "zustand/middleware/immer";
|
||||
import { enableMapSet } from "immer";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { createSelectors } from "./createSelectors";
|
||||
import {
|
||||
@@ -8,6 +9,9 @@ import {
|
||||
} from "./sharedVariantEditor.slice";
|
||||
import { type APIClient } from "~/utils/api";
|
||||
import { persistOptions, type stateToPersist } from "./persist";
|
||||
import { type SelectedLogsSlice, createSelectedLogsSlice } from "./selectedLogsSlice";
|
||||
|
||||
enableMapSet();
|
||||
|
||||
export type State = {
|
||||
drawerOpen: boolean;
|
||||
@@ -17,7 +21,8 @@ export type State = {
|
||||
setApi: (api: APIClient) => void;
|
||||
sharedVariantEditor: SharedVariantEditorSlice;
|
||||
selectedProjectId: string | null;
|
||||
setselectedProjectId: (id: string) => void;
|
||||
setSelectedProjectId: (id: string) => void;
|
||||
selectedLogs: SelectedLogsSlice;
|
||||
};
|
||||
|
||||
export type SliceCreator<T> = StateCreator<State, [["zustand/immer", never]], [], T>;
|
||||
@@ -48,10 +53,11 @@ const useBaseStore = create<
|
||||
}),
|
||||
sharedVariantEditor: createVariantEditorSlice(set, get, ...rest),
|
||||
selectedProjectId: null,
|
||||
setselectedProjectId: (id: string) =>
|
||||
setSelectedProjectId: (id: string) =>
|
||||
set((state) => {
|
||||
state.selectedProjectId = id;
|
||||
}),
|
||||
selectedLogs: createSelectedLogsSlice(set, get, ...rest),
|
||||
})),
|
||||
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 { type RefObject, useCallback, useEffect, useRef, useState } from "react";
|
||||
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";
|
||||
|
||||
export const useExperiments = () => {
|
||||
@@ -46,10 +46,10 @@ export const useDataset = () => {
|
||||
|
||||
export const useDatasetEntries = () => {
|
||||
const dataset = useDataset();
|
||||
const [page] = usePage();
|
||||
const { page, pageSize } = usePageParams();
|
||||
|
||||
return api.datasetEntries.list.useQuery(
|
||||
{ datasetId: dataset.data?.id ?? "", page },
|
||||
{ datasetId: dataset.data?.id ?? "", page, pageSize },
|
||||
{ enabled: dataset.data?.id != null },
|
||||
);
|
||||
};
|
||||
@@ -132,14 +132,23 @@ export const useElementDimensions = (): [RefObject<HTMLElement>, Dimensions | un
|
||||
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 = () => {
|
||||
const experiment = useExperiment();
|
||||
const [page] = usePage();
|
||||
const { page, pageSize } = usePageParams();
|
||||
|
||||
return api.scenarios.list.useQuery(
|
||||
{ experimentId: experiment.data?.id ?? "", page },
|
||||
{ experimentId: experiment.data?.id ?? "", page, pageSize },
|
||||
{ enabled: experiment.data?.id != null },
|
||||
);
|
||||
};
|
||||
@@ -166,3 +175,13 @@ export const useScenarioVars = () => {
|
||||
{ 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 },
|
||||
);
|
||||
};
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
".eslintrc.cjs",
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.mts",
|
||||
"**/*.tsx",
|
||||
"**/*.cjs",
|
||||
"**/*.mjs",
|
||||
"**/*.js",
|
||||
"src/pages/api/sentry-example-api.js"
|
||||
"**/*.js"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -5,13 +5,21 @@
|
||||
"description": "The public API for reporting API calls to OpenPipe",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"servers": [{ "url": "https://app.openpipe.ai/api" }],
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://app.openpipe.ai/api"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/v1/check-cache": {
|
||||
"post": {
|
||||
"operationId": "externalApi-checkCache",
|
||||
"description": "Check if a prompt is cached",
|
||||
"security": [{ "Authorization": [] }],
|
||||
"security": [
|
||||
{
|
||||
"Authorization": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
@@ -19,18 +27,24 @@
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"startTime": {
|
||||
"requestedAt": {
|
||||
"type": "number",
|
||||
"description": "Unix timestamp in milliseconds"
|
||||
},
|
||||
"reqPayload": { "description": "JSON-encoded request payload" },
|
||||
"reqPayload": {
|
||||
"description": "JSON-encoded request payload"
|
||||
},
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "type": "string" },
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }"
|
||||
}
|
||||
},
|
||||
"required": ["startTime"],
|
||||
"required": [
|
||||
"requestedAt"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
@@ -45,14 +59,18 @@
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"respPayload": { "description": "JSON-encoded response payload" }
|
||||
"respPayload": {
|
||||
"description": "JSON-encoded response payload"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": { "$ref": "#/components/responses/error" }
|
||||
"default": {
|
||||
"$ref": "#/components/responses/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -60,7 +78,11 @@
|
||||
"post": {
|
||||
"operationId": "externalApi-report",
|
||||
"description": "Report an API call",
|
||||
"security": [{ "Authorization": [] }],
|
||||
"security": [
|
||||
{
|
||||
"Authorization": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
@@ -68,22 +90,40 @@
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"startTime": {
|
||||
"requestedAt": {
|
||||
"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" },
|
||||
"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" },
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }"
|
||||
}
|
||||
},
|
||||
"required": ["startTime", "endTime"],
|
||||
"required": [
|
||||
"requestedAt",
|
||||
"receivedAt"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
@@ -93,15 +133,26 @@
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful response",
|
||||
"content": { "application/json": { "schema": {} } }
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": { "$ref": "#/components/responses/error" }
|
||||
"default": {
|
||||
"$ref": "#/components/responses/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": { "Authorization": { "type": "http", "scheme": "bearer" } },
|
||||
"securitySchemes": {
|
||||
"Authorization": {
|
||||
"type": "http",
|
||||
"scheme": "bearer"
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"error": {
|
||||
"description": "Error response",
|
||||
@@ -110,19 +161,32 @@
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": { "type": "string" },
|
||||
"code": { "type": "string" },
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"code": {
|
||||
"type": "string"
|
||||
},
|
||||
"issues": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": { "message": { "type": "string" } },
|
||||
"required": ["message"],
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["message", "code"],
|
||||
"required": [
|
||||
"message",
|
||||
"code"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
@@ -130,4 +194,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ set -e
|
||||
|
||||
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
|
||||
mv open-pipe-api-client/open_pipe_api_client openpipe/api_client
|
||||
|
||||
4
client-libs/typescript/.gitignore
vendored
4
client-libs/typescript/.gitignore
vendored
@@ -1,2 +1,2 @@
|
||||
node_modules
|
||||
dist
|
||||
node_modules/
|
||||
dist/
|
||||
@@ -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",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"description": "Metrics and auto-evaluation for LLM calls",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"build": "tsc"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"axios": "^0.26.0",
|
||||
"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
|
||||
@@ -176,6 +176,10 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication Authorization required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||
@@ -211,6 +215,10 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication Authorization required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||
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;
|
||||
// }
|
||||
// }
|
||||
@@ -3,7 +3,6 @@ import { readEnv, type RequestOptions } from "openai-beta/core";
|
||||
import { CompletionCreateParams } from "openai-beta/resources/chat/completions";
|
||||
import axios from "axios";
|
||||
|
||||
export * as openai from "openai-beta";
|
||||
import * as openPipeClient from "../codegen";
|
||||
|
||||
interface ClientOptions extends openai.ClientOptions {
|
||||
@@ -11,7 +10,7 @@ interface ClientOptions extends openai.ClientOptions {
|
||||
openPipeBaseUrl?: string;
|
||||
}
|
||||
|
||||
export class OpenAI extends openai.OpenAI {
|
||||
export default class OpenAI extends openai.OpenAI {
|
||||
public openPipeApi?: openPipeClient.DefaultApi;
|
||||
|
||||
constructor({
|
||||
@@ -74,37 +73,37 @@ class ExtendedCompletions extends openai.OpenAI.Chat.Completions {
|
||||
options?: RequestOptions,
|
||||
tags?: Record<string, string>
|
||||
): Promise<any> {
|
||||
// Your pre API call logic here
|
||||
console.log("Doing pre API call...");
|
||||
// // Your pre API call logic here
|
||||
// console.log("Doing pre API call...");
|
||||
|
||||
// Determine the type of request
|
||||
if (params.hasOwnProperty("stream") && params.stream === true) {
|
||||
const result = await super.create(
|
||||
params as CompletionCreateParams.CreateChatCompletionRequestStreaming,
|
||||
options
|
||||
);
|
||||
// Your post API call logic here
|
||||
console.log("Doing post API call for Streaming...");
|
||||
return result;
|
||||
} else {
|
||||
const requestedAt = Date.now();
|
||||
const result = await super.create(
|
||||
params as CompletionCreateParams.CreateChatCompletionRequestNonStreaming,
|
||||
options
|
||||
);
|
||||
await this.openaiInstance.openPipeApi?.externalApiReport({
|
||||
requestedAt,
|
||||
receivedAt: Date.now(),
|
||||
reqPayload: params,
|
||||
respPayload: result,
|
||||
statusCode: 200,
|
||||
errorMessage: undefined,
|
||||
tags,
|
||||
});
|
||||
// // Determine the type of request
|
||||
// if (params.hasOwnProperty("stream") && params.stream === true) {
|
||||
// const result = await super.create(
|
||||
// params as CompletionCreateParams.CreateChatCompletionRequestStreaming,
|
||||
// options
|
||||
// );
|
||||
// // Your post API call logic here
|
||||
// console.log("Doing post API call for Streaming...");
|
||||
// return result;
|
||||
// } else {
|
||||
// const requestedAt = Date.now();
|
||||
const result = await super.create(
|
||||
params as CompletionCreateParams.CreateChatCompletionRequestNonStreaming,
|
||||
options
|
||||
);
|
||||
return result;
|
||||
// await this.openaiInstance.openPipeApi?.externalApiReport({
|
||||
// requestedAt,
|
||||
// receivedAt: Date.now(),
|
||||
// reqPayload: params,
|
||||
// respPayload: result,
|
||||
// statusCode: 200,
|
||||
// errorMessage: undefined,
|
||||
// tags,
|
||||
// });
|
||||
|
||||
// Your post API call logic here
|
||||
console.log("Doing post API call for NonStreaming...");
|
||||
return result;
|
||||
}
|
||||
// console.log("GOT RESULT", result);
|
||||
// return result;
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"noImplicitAny": true,
|
||||
"target": "es2017",
|
||||
"lib": ["esnext"],
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"incremental": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"baseUrl": ".",
|
||||
"outDir": "dist",
|
||||
"rootDir": ".",
|
||||
"skipLibCheck": true
|
||||
"paths": {
|
||||
"~/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["dist", "node_modules"],
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
1565
app/pnpm-lock.yaml → pnpm-lock.yaml
generated
1565
app/pnpm-lock.yaml → pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
3
pnpm-workspace.yaml
Normal file
3
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
- app
|
||||
- client-libs/typescript
|
||||
Reference in New Issue
Block a user