Compare commits
5 Commits
python-sdk
...
project-me
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e176088e9 | ||
|
|
3cec1f7786 | ||
|
|
b3d8f96fa8 | ||
|
|
54d97ddfa8 | ||
|
|
1f8e3b820f |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
.env
|
||||
.venv/
|
||||
*.pyc
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to rename the column `completionTokens` to `outputTokens` on the `ModelResponse` table.
|
||||
- You are about to rename the column `promptTokens` to `inputTokens` on the `ModelResponse` table.
|
||||
|
||||
*/
|
||||
|
||||
-- Rename completionTokens to outputTokens
|
||||
ALTER TABLE "ModelResponse"
|
||||
RENAME COLUMN "completionTokens" TO "outputTokens";
|
||||
|
||||
-- Rename promptTokens to inputTokens
|
||||
ALTER TABLE "ModelResponse"
|
||||
RENAME COLUMN "promptTokens" TO "inputTokens";
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to rename the column `completionTokens` to `outputTokens` on the `ModelResponse` table.
|
||||
- You are about to rename the column `promptTokens` to `inputTokens` on the `ModelResponse` table.
|
||||
- You are about to rename the column `startTime` on the `LoggedCall` table to `requestedAt`. Ensure compatibility with application logic.
|
||||
- You are about to rename the column `startTime` on the `LoggedCallModelResponse` table to `requestedAt`. Ensure compatibility with application logic.
|
||||
- You are about to rename the column `endTime` on the `LoggedCallModelResponse` table to `receivedAt`. Ensure compatibility with application logic.
|
||||
- You are about to rename the column `error` on the `LoggedCallModelResponse` table to `errorMessage`. Ensure compatibility with application logic.
|
||||
- You are about to rename the column `respStatus` on the `LoggedCallModelResponse` table to `statusCode`. Ensure compatibility with application logic.
|
||||
- You are about to rename the column `totalCost` on the `LoggedCallModelResponse` table to `cost`. Ensure compatibility with application logic.
|
||||
- You are about to rename the column `inputHash` on the `ModelResponse` table to `cacheKey`. Ensure compatibility with application logic.
|
||||
- You are about to rename the column `output` on the `ModelResponse` table to `respPayload`. Ensure compatibility with application logic.
|
||||
|
||||
*/
|
||||
-- DropIndex
|
||||
DROP INDEX "LoggedCall_startTime_idx";
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "ModelResponse_inputHash_idx";
|
||||
|
||||
-- Rename completionTokens to outputTokens
|
||||
ALTER TABLE "ModelResponse"
|
||||
RENAME COLUMN "completionTokens" TO "outputTokens";
|
||||
|
||||
-- Rename promptTokens to inputTokens
|
||||
ALTER TABLE "ModelResponse"
|
||||
RENAME COLUMN "promptTokens" TO "inputTokens";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LoggedCall"
|
||||
RENAME COLUMN "startTime" TO "requestedAt";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LoggedCallModelResponse"
|
||||
RENAME COLUMN "startTime" TO "requestedAt";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LoggedCallModelResponse"
|
||||
RENAME COLUMN "endTime" TO "receivedAt";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LoggedCallModelResponse"
|
||||
RENAME COLUMN "error" TO "errorMessage";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LoggedCallModelResponse"
|
||||
RENAME COLUMN "respStatus" TO "statusCode";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LoggedCallModelResponse"
|
||||
RENAME COLUMN "totalCost" TO "cost";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "ModelResponse"
|
||||
RENAME COLUMN "inputHash" TO "cacheKey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "ModelResponse"
|
||||
RENAME COLUMN "output" TO "respPayload";
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "LoggedCall_requestedAt_idx" ON "LoggedCall"("requestedAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ModelResponse_cacheKey_idx" ON "ModelResponse"("cacheKey");
|
||||
@@ -112,10 +112,10 @@ model ScenarioVariantCell {
|
||||
model ModelResponse {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
|
||||
cacheKey String
|
||||
inputHash String
|
||||
requestedAt DateTime?
|
||||
receivedAt DateTime?
|
||||
respPayload Json?
|
||||
output Json?
|
||||
cost Float?
|
||||
inputTokens Int?
|
||||
outputTokens Int?
|
||||
@@ -131,7 +131,7 @@ model ModelResponse {
|
||||
scenarioVariantCell ScenarioVariantCell @relation(fields: [scenarioVariantCellId], references: [id], onDelete: Cascade)
|
||||
outputEvaluations OutputEvaluation[]
|
||||
|
||||
@@index([cacheKey])
|
||||
@@index([inputHash])
|
||||
}
|
||||
|
||||
enum EvalType {
|
||||
@@ -256,7 +256,7 @@ model WorldChampEntrant {
|
||||
model LoggedCall {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
|
||||
requestedAt DateTime
|
||||
startTime DateTime
|
||||
|
||||
// True if this call was served from the cache, false otherwise
|
||||
cacheHit Boolean
|
||||
@@ -278,7 +278,7 @@ model LoggedCall {
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([requestedAt])
|
||||
@@index([startTime])
|
||||
}
|
||||
|
||||
model LoggedCallModelResponse {
|
||||
@@ -287,14 +287,14 @@ model LoggedCallModelResponse {
|
||||
reqPayload Json
|
||||
|
||||
// The HTTP status returned by the model provider
|
||||
statusCode Int?
|
||||
respStatus Int?
|
||||
respPayload Json?
|
||||
|
||||
// Should be null if the request was successful, and some string if the request failed.
|
||||
errorMessage String?
|
||||
error String?
|
||||
|
||||
requestedAt DateTime
|
||||
receivedAt DateTime
|
||||
startTime DateTime
|
||||
endTime DateTime
|
||||
|
||||
// Note: the function to calculate the cacheKey should include the project
|
||||
// ID so we don't share cached responses between projects, which could be an
|
||||
@@ -308,7 +308,7 @@ model LoggedCallModelResponse {
|
||||
outputTokens Int?
|
||||
finishReason String?
|
||||
completionId String?
|
||||
cost Decimal? @db.Decimal(18, 12)
|
||||
totalCost Decimal? @db.Decimal(18, 12)
|
||||
|
||||
// The LoggedCall that created this LoggedCallModelResponse
|
||||
originalLoggedCallId String @unique @db.Uuid
|
||||
|
||||
@@ -339,17 +339,17 @@ for (let i = 0; i < 1437; i++) {
|
||||
MODEL_RESPONSE_TEMPLATES[Math.floor(Math.random() * MODEL_RESPONSE_TEMPLATES.length)]!;
|
||||
const model = template.reqPayload.model;
|
||||
// choose random time in the last two weeks, with a bias towards the last few days
|
||||
const requestedAt = new Date(Date.now() - Math.pow(Math.random(), 2) * 1000 * 60 * 60 * 24 * 14);
|
||||
const startTime = new Date(Date.now() - Math.pow(Math.random(), 2) * 1000 * 60 * 60 * 24 * 14);
|
||||
// choose random delay anywhere from 2 to 10 seconds later for gpt-4, or 1 to 5 seconds for gpt-3.5
|
||||
const delay =
|
||||
model === "gpt-4" ? 1000 * 2 + Math.random() * 1000 * 8 : 1000 + Math.random() * 1000 * 4;
|
||||
const receivedAt = new Date(requestedAt.getTime() + delay);
|
||||
const endTime = new Date(startTime.getTime() + delay);
|
||||
loggedCallsToCreate.push({
|
||||
id: loggedCallId,
|
||||
cacheHit: false,
|
||||
requestedAt,
|
||||
startTime,
|
||||
projectId: project.id,
|
||||
createdAt: requestedAt,
|
||||
createdAt: startTime,
|
||||
});
|
||||
|
||||
const { promptTokenPrice, completionTokenPrice } =
|
||||
@@ -365,20 +365,21 @@ for (let i = 0; i < 1437; i++) {
|
||||
|
||||
loggedCallModelResponsesToCreate.push({
|
||||
id: loggedCallModelResponseId,
|
||||
requestedAt,
|
||||
receivedAt,
|
||||
startTime,
|
||||
endTime,
|
||||
originalLoggedCallId: loggedCallId,
|
||||
reqPayload: template.reqPayload,
|
||||
respPayload: template.respPayload,
|
||||
statusCode: template.respStatus,
|
||||
errorMessage: template.error,
|
||||
createdAt: requestedAt,
|
||||
respStatus: template.respStatus,
|
||||
error: template.error,
|
||||
createdAt: startTime,
|
||||
cacheKey: hashRequest(project.id, template.reqPayload as JsonValue),
|
||||
durationMs: receivedAt.getTime() - requestedAt.getTime(),
|
||||
durationMs: endTime.getTime() - startTime.getTime(),
|
||||
inputTokens: template.inputTokens,
|
||||
outputTokens: template.outputTokens,
|
||||
finishReason: template.finishReason,
|
||||
cost: template.inputTokens * promptTokenPrice + template.outputTokens * completionTokenPrice,
|
||||
totalCost:
|
||||
template.inputTokens * promptTokenPrice + template.outputTokens * completionTokenPrice,
|
||||
});
|
||||
loggedCallsToUpdate.push({
|
||||
where: {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useEffect, useState } from "react";
|
||||
import { BsPencil, BsX } from "react-icons/bs";
|
||||
import { api } from "~/utils/api";
|
||||
import { useExperiment, useHandledAsyncCallback, useScenarioVars } from "~/utils/hooks";
|
||||
import { maybeReportError } from "~/utils/errorHandling/maybeReportError";
|
||||
import { maybeReportError } from "~/utils/standardResponses";
|
||||
import { FloatingLabelInput } from "./FloatingLabelInput";
|
||||
|
||||
export const ScenarioVar = ({
|
||||
|
||||
@@ -107,7 +107,7 @@ export default function OutputCell({
|
||||
|
||||
if (disabledReason) return <Text color="gray.500">{disabledReason}</Text>;
|
||||
|
||||
const showLogs = !streamedMessage && !mostRecentResponse?.respPayload;
|
||||
const showLogs = !streamedMessage && !mostRecentResponse?.output;
|
||||
|
||||
if (showLogs)
|
||||
return (
|
||||
@@ -160,13 +160,13 @@ export default function OutputCell({
|
||||
</CellWrapper>
|
||||
);
|
||||
|
||||
const normalizedOutput = mostRecentResponse?.respPayload
|
||||
? provider.normalizeOutput(mostRecentResponse?.respPayload)
|
||||
const normalizedOutput = mostRecentResponse?.output
|
||||
? provider.normalizeOutput(mostRecentResponse?.output)
|
||||
: streamedMessage
|
||||
? provider.normalizeOutput(streamedMessage)
|
||||
: null;
|
||||
|
||||
if (mostRecentResponse?.respPayload && normalizedOutput?.type === "json") {
|
||||
if (mostRecentResponse?.output && normalizedOutput?.type === "json") {
|
||||
return (
|
||||
<CellWrapper>
|
||||
<SyntaxHighlighter
|
||||
|
||||
@@ -90,23 +90,15 @@ function TableRow({
|
||||
isExpanded: boolean;
|
||||
onToggle: () => void;
|
||||
}) {
|
||||
const isError = loggedCall.modelResponse?.statusCode !== 200;
|
||||
const timeAgo = dayjs(loggedCall.requestedAt).fromNow();
|
||||
const fullTime = dayjs(loggedCall.requestedAt).toString();
|
||||
const isError = loggedCall.modelResponse?.respStatus !== 200;
|
||||
const timeAgo = dayjs(loggedCall.startTime).fromNow();
|
||||
const fullTime = dayjs(loggedCall.startTime).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
|
||||
@@ -128,11 +120,11 @@ function TableRow({
|
||||
</Tooltip>
|
||||
</Td>
|
||||
<Td width="100%">{model}</Td>
|
||||
{durationCell}
|
||||
<Td isNumeric>{((loggedCall.modelResponse?.durationMs ?? 0) / 1000).toFixed(2)}s</Td>
|
||||
<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"}
|
||||
{loggedCall.modelResponse?.respStatus ?? "No response"}
|
||||
</Td>
|
||||
</Tr>
|
||||
<Tr>
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
import {
|
||||
ResponsiveContainer,
|
||||
LineChart,
|
||||
Line,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
Legend,
|
||||
} from "recharts";
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { useSelectedProject } from "~/utils/hooks";
|
||||
import dayjs from "~/utils/dayjs";
|
||||
import { api } from "~/utils/api";
|
||||
|
||||
export default function UsageGraph() {
|
||||
const { data: selectedProject } = useSelectedProject();
|
||||
|
||||
const stats = api.dashboard.stats.useQuery(
|
||||
{ projectId: selectedProject?.id ?? "" },
|
||||
{ enabled: !!selectedProject },
|
||||
);
|
||||
|
||||
const data = useMemo(() => {
|
||||
return (
|
||||
stats.data?.periods.map(({ period, numQueries, cost }) => ({
|
||||
period,
|
||||
Requests: numQueries,
|
||||
"Total Spent (USD)": parseFloat(cost.toString()),
|
||||
})) || []
|
||||
);
|
||||
}, [stats.data]);
|
||||
|
||||
return (
|
||||
<ResponsiveContainer width="100%" height={400}>
|
||||
<LineChart data={data} margin={{ top: 5, right: 20, left: 10, bottom: 5 }}>
|
||||
<XAxis dataKey="period" tickFormatter={(str: string) => dayjs(str).format("MMM D")} />
|
||||
<YAxis yAxisId="left" dataKey="Requests" orientation="left" stroke="#8884d8" />
|
||||
<YAxis
|
||||
yAxisId="right"
|
||||
dataKey="Total Spent (USD)"
|
||||
orientation="right"
|
||||
unit="$"
|
||||
stroke="#82ca9d"
|
||||
/>
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<CartesianGrid stroke="#f5f5f5" />
|
||||
<Line dataKey="Requests" stroke="#8884d8" yAxisId="left" dot={false} strokeWidth={2} />
|
||||
<Line
|
||||
dataKey="Total Spent (USD)"
|
||||
stroke="#82ca9d"
|
||||
yAxisId="right"
|
||||
dot={false}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import { type CompletionResponse } from "../types";
|
||||
import { isArray, isString, omit } from "lodash-es";
|
||||
import { openai } from "~/server/utils/openai";
|
||||
import { APIError } from "openai";
|
||||
import frontendModelProvider from "./frontend";
|
||||
import modelProvider, { type SupportedModel } from ".";
|
||||
|
||||
const mergeStreamedChunks = (
|
||||
base: ChatCompletion | null,
|
||||
|
||||
@@ -18,15 +18,26 @@ import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
} from "@chakra-ui/react";
|
||||
import {
|
||||
LineChart,
|
||||
Line,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
Legend,
|
||||
ResponsiveContainer,
|
||||
} from "recharts";
|
||||
import { Ban, DollarSign, Hash } from "lucide-react";
|
||||
import { useMemo } from "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 dayjs from "~/utils/dayjs";
|
||||
import { api } from "~/utils/api";
|
||||
import LoggedCallTable from "~/components/dashboard/LoggedCallTable";
|
||||
import UsageGraph from "~/components/dashboard/UsageGraph";
|
||||
|
||||
export default function LoggedCalls() {
|
||||
const { data: selectedProject } = useSelectedProject();
|
||||
@@ -36,8 +47,18 @@ export default function LoggedCalls() {
|
||||
{ enabled: !!selectedProject },
|
||||
);
|
||||
|
||||
const data = useMemo(() => {
|
||||
return (
|
||||
stats.data?.periods.map(({ period, numQueries, totalCost }) => ({
|
||||
period,
|
||||
Requests: numQueries,
|
||||
"Total Spent (USD)": parseFloat(totalCost.toString()),
|
||||
})) || []
|
||||
);
|
||||
}, [stats.data]);
|
||||
|
||||
return (
|
||||
<AppShell title="Logged Calls" requireAuth>
|
||||
<AppShell requireAuth>
|
||||
<PageHeaderContainer>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem>
|
||||
@@ -62,7 +83,39 @@ export default function LoggedCalls() {
|
||||
</Heading>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<UsageGraph />
|
||||
<ResponsiveContainer width="100%" height={400}>
|
||||
<LineChart data={data} margin={{ top: 5, right: 20, left: 10, bottom: 5 }}>
|
||||
<XAxis
|
||||
dataKey="period"
|
||||
tickFormatter={(str: string) => dayjs(str).format("MMM D")}
|
||||
/>
|
||||
<YAxis yAxisId="left" dataKey="Requests" orientation="left" stroke="#8884d8" />
|
||||
<YAxis
|
||||
yAxisId="right"
|
||||
dataKey="Total Spent (USD)"
|
||||
orientation="right"
|
||||
unit="$"
|
||||
stroke="#82ca9d"
|
||||
/>
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<CartesianGrid stroke="#f5f5f5" />
|
||||
<Line
|
||||
dataKey="Requests"
|
||||
stroke="#8884d8"
|
||||
yAxisId="left"
|
||||
dot={false}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<Line
|
||||
dataKey="Total Spent (USD)"
|
||||
stroke="#82ca9d"
|
||||
yAxisId="right"
|
||||
dot={false}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<VStack spacing="4" width="300px" align="stretch">
|
||||
@@ -74,7 +127,7 @@ export default function LoggedCalls() {
|
||||
<Icon as={DollarSign} boxSize={4} color="gray.500" />
|
||||
</HStack>
|
||||
<StatNumber>
|
||||
${parseFloat(stats.data?.totals?.cost?.toString() ?? "0").toFixed(3)}
|
||||
${parseFloat(stats.data?.totals?.totalCost?.toString() ?? "0").toFixed(2)}
|
||||
</StatNumber>
|
||||
</Stat>
|
||||
</CardBody>
|
||||
|
||||
@@ -38,10 +38,7 @@ export default function Settings() {
|
||||
id: selectedProject.id,
|
||||
updates: { name },
|
||||
});
|
||||
await Promise.all([
|
||||
utils.projects.get.invalidate({ id: selectedProject.id }),
|
||||
utils.projects.list.invalidate(),
|
||||
]);
|
||||
await Promise.all([utils.projects.get.invalidate({ id: selectedProject.id })]);
|
||||
}
|
||||
}, [updateMutation, selectedProject]);
|
||||
|
||||
|
||||
@@ -24,9 +24,9 @@ export const dashboardRouter = createTRPCRouter({
|
||||
)
|
||||
.where("projectId", "=", input.projectId)
|
||||
.select(({ fn }) => [
|
||||
sql<Date>`date_trunc('day', "LoggedCallModelResponse"."requestedAt")`.as("period"),
|
||||
sql<Date>`date_trunc('day', "LoggedCallModelResponse"."startTime")`.as("period"),
|
||||
sql<number>`count("LoggedCall"."id")::int`.as("numQueries"),
|
||||
fn.sum(fn.coalesce("LoggedCallModelResponse.cost", sql<number>`0`)).as("cost"),
|
||||
fn.sum(fn.coalesce("LoggedCallModelResponse.totalCost", sql<number>`0`)).as("totalCost"),
|
||||
])
|
||||
.groupBy("period")
|
||||
.orderBy("period")
|
||||
@@ -57,7 +57,7 @@ export const dashboardRouter = createTRPCRouter({
|
||||
backfilledPeriods.unshift({
|
||||
period: dayjs(dayToMatch).toDate(),
|
||||
numQueries: 0,
|
||||
cost: 0,
|
||||
totalCost: 0,
|
||||
});
|
||||
}
|
||||
dayToMatch = dayToMatch.subtract(1, "day");
|
||||
@@ -72,7 +72,7 @@ export const dashboardRouter = createTRPCRouter({
|
||||
)
|
||||
.where("projectId", "=", input.projectId)
|
||||
.select(({ fn }) => [
|
||||
fn.sum(fn.coalesce("LoggedCallModelResponse.cost", sql<number>`0`)).as("cost"),
|
||||
fn.sum(fn.coalesce("LoggedCallModelResponse.totalCost", sql<number>`0`)).as("totalCost"),
|
||||
fn.count("LoggedCall.id").as("numQueries"),
|
||||
])
|
||||
.executeTakeFirst();
|
||||
@@ -85,8 +85,8 @@ export const dashboardRouter = createTRPCRouter({
|
||||
"LoggedCall.id",
|
||||
"LoggedCallModelResponse.originalLoggedCallId",
|
||||
)
|
||||
.select(({ fn }) => [fn.count("LoggedCall.id").as("count"), "statusCode as code"])
|
||||
.where("statusCode", ">", 200)
|
||||
.select(({ fn }) => [fn.count("LoggedCall.id").as("count"), "respStatus as code"])
|
||||
.where("respStatus", ">", 200)
|
||||
.groupBy("code")
|
||||
.orderBy("count", "desc")
|
||||
.execute();
|
||||
@@ -108,7 +108,7 @@ export const dashboardRouter = createTRPCRouter({
|
||||
// https://discord.com/channels/966627436387266600/1122258443886153758/1122258443886153758
|
||||
loggedCalls: publicProcedure.input(z.object({})).query(async ({ input }) => {
|
||||
const loggedCalls = await prisma.loggedCall.findMany({
|
||||
orderBy: { requestedAt: "desc" },
|
||||
orderBy: { startTime: "desc" },
|
||||
include: { tags: true, modelResponse: true },
|
||||
take: 20,
|
||||
});
|
||||
|
||||
@@ -227,7 +227,7 @@ export const experimentsRouter = createTRPCRouter({
|
||||
...modelResponseData,
|
||||
id: newModelResponseId,
|
||||
scenarioVariantCellId: newCellId,
|
||||
respPayload: (modelResponse.respPayload as Prisma.InputJsonValue) ?? undefined,
|
||||
output: (modelResponse.output as Prisma.InputJsonValue) ?? undefined,
|
||||
});
|
||||
for (const evaluation of outputEvaluations) {
|
||||
outputEvaluationsToCreate.push({
|
||||
|
||||
@@ -7,11 +7,6 @@ import { TRPCError } from "@trpc/server";
|
||||
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
|
||||
import { prisma } from "~/server/db";
|
||||
import { hashRequest } from "~/server/utils/hashObject";
|
||||
import modelProvider from "~/modelProviders/openai-ChatCompletion";
|
||||
import {
|
||||
type ChatCompletion,
|
||||
type CompletionCreateParams,
|
||||
} from "openai/resources/chat/completions";
|
||||
|
||||
const reqValidator = z.object({
|
||||
model: z.string(),
|
||||
@@ -21,6 +16,11 @@ const reqValidator = z.object({
|
||||
const respValidator = z.object({
|
||||
id: z.string(),
|
||||
model: z.string(),
|
||||
usage: z.object({
|
||||
total_tokens: z.number(),
|
||||
prompt_tokens: z.number(),
|
||||
completion_tokens: z.number(),
|
||||
}),
|
||||
choices: z.array(
|
||||
z.object({
|
||||
finish_reason: z.string(),
|
||||
@@ -35,12 +35,11 @@ export const externalApiRouter = createTRPCRouter({
|
||||
method: "POST",
|
||||
path: "/v1/check-cache",
|
||||
description: "Check if a prompt is cached",
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
requestedAt: z.number().describe("Unix timestamp in milliseconds"),
|
||||
startTime: z.number().describe("Unix timestamp in milliseconds"),
|
||||
reqPayload: z.unknown().describe("JSON-encoded request payload"),
|
||||
tags: z
|
||||
.record(z.string())
|
||||
@@ -70,9 +69,15 @@ export const externalApiRouter = createTRPCRouter({
|
||||
const cacheKey = hashRequest(key.projectId, reqPayload as JsonValue);
|
||||
|
||||
const existingResponse = await prisma.loggedCallModelResponse.findFirst({
|
||||
where: { cacheKey },
|
||||
include: { originalLoggedCall: true },
|
||||
orderBy: { requestedAt: "desc" },
|
||||
where: {
|
||||
cacheKey,
|
||||
},
|
||||
include: {
|
||||
originalLoggedCall: true,
|
||||
},
|
||||
orderBy: {
|
||||
startTime: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingResponse) return { respPayload: null };
|
||||
@@ -80,7 +85,7 @@ export const externalApiRouter = createTRPCRouter({
|
||||
await prisma.loggedCall.create({
|
||||
data: {
|
||||
projectId: key.projectId,
|
||||
requestedAt: new Date(input.requestedAt),
|
||||
startTime: new Date(input.startTime),
|
||||
cacheHit: true,
|
||||
modelResponseId: existingResponse.id,
|
||||
},
|
||||
@@ -97,17 +102,16 @@ export const externalApiRouter = createTRPCRouter({
|
||||
method: "POST",
|
||||
path: "/v1/report",
|
||||
description: "Report an API call",
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
requestedAt: z.number().describe("Unix timestamp in milliseconds"),
|
||||
receivedAt: z.number().describe("Unix timestamp in milliseconds"),
|
||||
startTime: z.number().describe("Unix timestamp in milliseconds"),
|
||||
endTime: z.number().describe("Unix timestamp in milliseconds"),
|
||||
reqPayload: z.unknown().describe("JSON-encoded request payload"),
|
||||
respPayload: z.unknown().optional().describe("JSON-encoded response payload"),
|
||||
statusCode: z.number().optional().describe("HTTP status code of response"),
|
||||
errorMessage: z.string().optional().describe("User-friendly error message"),
|
||||
respStatus: z.number().optional().describe("HTTP status code of response"),
|
||||
error: z.string().optional().describe("User-friendly error message"),
|
||||
tags: z
|
||||
.record(z.string())
|
||||
.optional()
|
||||
@@ -118,7 +122,6 @@ export const externalApiRouter = createTRPCRouter({
|
||||
)
|
||||
.output(z.void())
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
console.log("GOT TAGS", input.tags);
|
||||
const apiKey = ctx.apiKey;
|
||||
if (!apiKey) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||
@@ -137,20 +140,14 @@ export const externalApiRouter = createTRPCRouter({
|
||||
const newLoggedCallId = uuidv4();
|
||||
const newModelResponseId = uuidv4();
|
||||
|
||||
let usage;
|
||||
if (reqPayload.success && respPayload.success) {
|
||||
usage = modelProvider.getUsage(
|
||||
input.reqPayload as CompletionCreateParams,
|
||||
input.respPayload as ChatCompletion,
|
||||
);
|
||||
}
|
||||
const usage = respPayload.success ? respPayload.data.usage : undefined;
|
||||
|
||||
await prisma.$transaction([
|
||||
prisma.loggedCall.create({
|
||||
data: {
|
||||
id: newLoggedCallId,
|
||||
projectId: key.projectId,
|
||||
requestedAt: new Date(input.requestedAt),
|
||||
startTime: new Date(input.startTime),
|
||||
cacheHit: false,
|
||||
},
|
||||
}),
|
||||
@@ -158,17 +155,20 @@ export const externalApiRouter = createTRPCRouter({
|
||||
data: {
|
||||
id: newModelResponseId,
|
||||
originalLoggedCallId: newLoggedCallId,
|
||||
requestedAt: new Date(input.requestedAt),
|
||||
receivedAt: new Date(input.receivedAt),
|
||||
startTime: new Date(input.startTime),
|
||||
endTime: new Date(input.endTime),
|
||||
reqPayload: input.reqPayload as Prisma.InputJsonValue,
|
||||
respPayload: input.respPayload as Prisma.InputJsonValue,
|
||||
statusCode: input.statusCode,
|
||||
errorMessage: input.errorMessage,
|
||||
durationMs: input.receivedAt - input.requestedAt,
|
||||
cacheKey: respPayload.success ? requestHash : null,
|
||||
inputTokens: usage?.inputTokens,
|
||||
outputTokens: usage?.outputTokens,
|
||||
cost: usage?.cost,
|
||||
respStatus: input.respStatus,
|
||||
error: input.error,
|
||||
durationMs: input.endTime - input.startTime,
|
||||
...(respPayload.success
|
||||
? {
|
||||
cacheKey: requestHash,
|
||||
inputTokens: usage ? usage.prompt_tokens : undefined,
|
||||
outputTokens: usage ? usage.completion_tokens : undefined,
|
||||
}
|
||||
: null),
|
||||
},
|
||||
}),
|
||||
// Avoid foreign key constraint error by updating the logged call after the model response is created
|
||||
@@ -182,22 +182,24 @@ export const externalApiRouter = createTRPCRouter({
|
||||
}),
|
||||
]);
|
||||
|
||||
const tagsToCreate = Object.entries(input.tags ?? {}).map(([name, value]) => ({
|
||||
loggedCallId: newLoggedCallId,
|
||||
// sanitize tags
|
||||
name: name.replaceAll(/[^a-zA-Z0-9_]/g, "_"),
|
||||
value,
|
||||
}));
|
||||
|
||||
if (reqPayload.success) {
|
||||
tagsToCreate.push({
|
||||
if (input.tags) {
|
||||
const tagsToCreate = Object.entries(input.tags).map(([name, value]) => ({
|
||||
loggedCallId: newLoggedCallId,
|
||||
name: "$model",
|
||||
value: reqPayload.data.model,
|
||||
// sanitize tags
|
||||
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,
|
||||
});
|
||||
}
|
||||
await prisma.loggedCallTag.createMany({
|
||||
data: tagsToCreate,
|
||||
});
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import { createTRPCRouter, protectedProcedure, publicProcedure } from "~/server/
|
||||
import { prisma } from "~/server/db";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { generateNewCell } from "~/server/utils/generateNewCell";
|
||||
import { error, success } from "~/utils/errorHandling/standardResponses";
|
||||
import { error, success } from "~/utils/standardResponses";
|
||||
import { recordExperimentUpdated } from "~/server/utils/recordExperimentUpdated";
|
||||
import { reorderPromptVariants } from "~/server/utils/reorderPromptVariants";
|
||||
import { type PromptVariant } from "@prisma/client";
|
||||
@@ -55,7 +55,7 @@ export const promptVariantsRouter = createTRPCRouter({
|
||||
where: {
|
||||
modelResponse: {
|
||||
outdated: false,
|
||||
respPayload: { not: Prisma.AnyNull },
|
||||
output: { not: Prisma.AnyNull },
|
||||
scenarioVariantCell: {
|
||||
promptVariant: {
|
||||
id: input.variantId,
|
||||
@@ -100,7 +100,7 @@ export const promptVariantsRouter = createTRPCRouter({
|
||||
modelResponses: {
|
||||
some: {
|
||||
outdated: false,
|
||||
respPayload: {
|
||||
output: {
|
||||
not: Prisma.AnyNull,
|
||||
},
|
||||
},
|
||||
@@ -111,7 +111,7 @@ export const promptVariantsRouter = createTRPCRouter({
|
||||
const overallTokens = await prisma.modelResponse.aggregate({
|
||||
where: {
|
||||
outdated: false,
|
||||
respPayload: {
|
||||
output: {
|
||||
not: Prisma.AnyNull,
|
||||
},
|
||||
scenarioVariantCell: {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { sql } from "kysely";
|
||||
import { z } from "zod";
|
||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from "~/server/api/trpc";
|
||||
import { kysely, prisma } from "~/server/db";
|
||||
import { error, success } from "~/utils/errorHandling/standardResponses";
|
||||
import { error, success } from "~/utils/standardResponses";
|
||||
import { requireCanModifyExperiment, requireCanViewExperiment } from "~/utils/accessControl";
|
||||
|
||||
export const scenarioVarsRouter = createTRPCRouter({
|
||||
|
||||
@@ -64,7 +64,7 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => {
|
||||
// Get the session from the server using the getServerSession wrapper function
|
||||
const session = await getServerAuthSession({ req, res });
|
||||
|
||||
const apiKey = req.headers.authorization?.split(" ")[1] as string | null;
|
||||
const apiKey = req.headers["x-openpipe-api-key"] as string | null;
|
||||
|
||||
return createInnerTRPCContext({
|
||||
session,
|
||||
|
||||
@@ -99,11 +99,11 @@ export const queryModel = defineTask<QueryModelJob>("queryModel", async (task) =
|
||||
}
|
||||
: null;
|
||||
|
||||
const cacheKey = hashObject(prompt as JsonValue);
|
||||
const inputHash = hashObject(prompt as JsonValue);
|
||||
|
||||
let modelResponse = await prisma.modelResponse.create({
|
||||
data: {
|
||||
cacheKey,
|
||||
inputHash,
|
||||
scenarioVariantCellId: cellId,
|
||||
requestedAt: new Date(),
|
||||
},
|
||||
@@ -114,7 +114,7 @@ export const queryModel = defineTask<QueryModelJob>("queryModel", async (task) =
|
||||
modelResponse = await prisma.modelResponse.update({
|
||||
where: { id: modelResponse.id },
|
||||
data: {
|
||||
respPayload: response.value as Prisma.InputJsonObject,
|
||||
output: response.value as Prisma.InputJsonObject,
|
||||
statusCode: response.statusCode,
|
||||
receivedAt: new Date(),
|
||||
inputTokens: usage?.inputTokens,
|
||||
|
||||
@@ -51,7 +51,7 @@ export const runAllEvals = async (experimentId: string) => {
|
||||
const outputs = await prisma.modelResponse.findMany({
|
||||
where: {
|
||||
outdated: false,
|
||||
respPayload: {
|
||||
output: {
|
||||
not: Prisma.AnyNull,
|
||||
},
|
||||
scenarioVariantCell: {
|
||||
|
||||
@@ -57,7 +57,7 @@ export const generateNewCell = async (
|
||||
return;
|
||||
}
|
||||
|
||||
const cacheKey = hashObject(parsedConstructFn);
|
||||
const inputHash = hashObject(parsedConstructFn);
|
||||
|
||||
cell = await prisma.scenarioVariantCell.create({
|
||||
data: {
|
||||
@@ -73,8 +73,8 @@ export const generateNewCell = async (
|
||||
|
||||
const matchingModelResponse = await prisma.modelResponse.findFirst({
|
||||
where: {
|
||||
cacheKey,
|
||||
respPayload: {
|
||||
inputHash,
|
||||
output: {
|
||||
not: Prisma.AnyNull,
|
||||
},
|
||||
},
|
||||
@@ -92,7 +92,7 @@ export const generateNewCell = async (
|
||||
data: {
|
||||
...omit(matchingModelResponse, ["id", "scenarioVariantCell"]),
|
||||
scenarioVariantCellId: cell.id,
|
||||
respPayload: matchingModelResponse.respPayload as Prisma.InputJsonValue,
|
||||
output: matchingModelResponse.output as Prisma.InputJsonValue,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ export const runOneEval = async (
|
||||
provider: SupportedProvider,
|
||||
): Promise<{ result: number; details?: string }> => {
|
||||
const modelProvider = modelProviders[provider];
|
||||
const message = modelProvider.normalizeOutput(modelResponse.respPayload);
|
||||
const message = modelProvider.normalizeOutput(modelResponse.output);
|
||||
|
||||
if (!message) return { result: 0 };
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
export function error(message: string): { status: "error"; message: string } {
|
||||
return {
|
||||
status: "error",
|
||||
message,
|
||||
};
|
||||
}
|
||||
export function success<T>(payload: T): { status: "success"; payload: T };
|
||||
export function success(payload?: undefined): { status: "success"; payload: undefined };
|
||||
export function success<T>(payload?: T) {
|
||||
return { status: "success", payload };
|
||||
}
|
||||
@@ -1,5 +1,16 @@
|
||||
import { toast } from "~/theme/ChakraThemeProvider";
|
||||
import { type error, type success } from "./standardResponses";
|
||||
|
||||
export function error(message: string): { status: "error"; message: string } {
|
||||
return {
|
||||
status: "error",
|
||||
message,
|
||||
};
|
||||
}
|
||||
export function success<T>(payload: T): { status: "success"; payload: T };
|
||||
export function success(payload?: undefined): { status: "success"; payload: undefined };
|
||||
export function success<T>(payload?: T) {
|
||||
return { status: "success", payload };
|
||||
}
|
||||
|
||||
type SuccessType<T> = ReturnType<typeof success<T>>;
|
||||
type ErrorType = ReturnType<typeof error>;
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,11 +0,0 @@
|
||||
#! /bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
poetry run openapi-python-client generate --url http://localhost:3000/api/openapi.json
|
||||
|
||||
rm -rf openpipe/api_client
|
||||
mv open-pipe-api-client/open_pipe_api_client openpipe/api_client
|
||||
rm -rf open-pipe-api-client
|
||||
@@ -1,10 +0,0 @@
|
||||
from .openai import OpenAIWrapper
|
||||
from .shared import configured_client
|
||||
|
||||
openai = OpenAIWrapper()
|
||||
|
||||
def configure_openpipe(base_url=None, api_key=None):
|
||||
if base_url is not None:
|
||||
configured_client._base_url = base_url
|
||||
if api_key is not None:
|
||||
configured_client.token = api_key
|
||||
@@ -1,7 +0,0 @@
|
||||
""" A client library for accessing OpenPipe API """
|
||||
from .client import AuthenticatedClient, Client
|
||||
|
||||
__all__ = (
|
||||
"AuthenticatedClient",
|
||||
"Client",
|
||||
)
|
||||
@@ -1 +0,0 @@
|
||||
""" Contains methods for accessing the API """
|
||||
@@ -1,155 +0,0 @@
|
||||
from http import HTTPStatus
|
||||
from typing import Any, Dict, Optional, Union
|
||||
|
||||
import httpx
|
||||
|
||||
from ... import errors
|
||||
from ...client import AuthenticatedClient, Client
|
||||
from ...models.external_api_check_cache_json_body import ExternalApiCheckCacheJsonBody
|
||||
from ...models.external_api_check_cache_response_200 import ExternalApiCheckCacheResponse200
|
||||
from ...types import Response
|
||||
|
||||
|
||||
def _get_kwargs(
|
||||
*,
|
||||
json_body: ExternalApiCheckCacheJsonBody,
|
||||
) -> Dict[str, Any]:
|
||||
pass
|
||||
|
||||
json_json_body = json_body.to_dict()
|
||||
|
||||
return {
|
||||
"method": "post",
|
||||
"url": "/v1/check-cache",
|
||||
"json": json_json_body,
|
||||
}
|
||||
|
||||
|
||||
def _parse_response(
|
||||
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
||||
) -> Optional[ExternalApiCheckCacheResponse200]:
|
||||
if response.status_code == HTTPStatus.OK:
|
||||
response_200 = ExternalApiCheckCacheResponse200.from_dict(response.json())
|
||||
|
||||
return response_200
|
||||
if client.raise_on_unexpected_status:
|
||||
raise errors.UnexpectedStatus(response.status_code, response.content)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def _build_response(
|
||||
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
||||
) -> Response[ExternalApiCheckCacheResponse200]:
|
||||
return Response(
|
||||
status_code=HTTPStatus(response.status_code),
|
||||
content=response.content,
|
||||
headers=response.headers,
|
||||
parsed=_parse_response(client=client, response=response),
|
||||
)
|
||||
|
||||
|
||||
def sync_detailed(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
json_body: ExternalApiCheckCacheJsonBody,
|
||||
) -> Response[ExternalApiCheckCacheResponse200]:
|
||||
"""Check if a prompt is cached
|
||||
|
||||
Args:
|
||||
json_body (ExternalApiCheckCacheJsonBody):
|
||||
|
||||
Raises:
|
||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||
|
||||
Returns:
|
||||
Response[ExternalApiCheckCacheResponse200]
|
||||
"""
|
||||
|
||||
kwargs = _get_kwargs(
|
||||
json_body=json_body,
|
||||
)
|
||||
|
||||
response = client.get_httpx_client().request(
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
return _build_response(client=client, response=response)
|
||||
|
||||
|
||||
def sync(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
json_body: ExternalApiCheckCacheJsonBody,
|
||||
) -> Optional[ExternalApiCheckCacheResponse200]:
|
||||
"""Check if a prompt is cached
|
||||
|
||||
Args:
|
||||
json_body (ExternalApiCheckCacheJsonBody):
|
||||
|
||||
Raises:
|
||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||
|
||||
Returns:
|
||||
ExternalApiCheckCacheResponse200
|
||||
"""
|
||||
|
||||
return sync_detailed(
|
||||
client=client,
|
||||
json_body=json_body,
|
||||
).parsed
|
||||
|
||||
|
||||
async def asyncio_detailed(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
json_body: ExternalApiCheckCacheJsonBody,
|
||||
) -> Response[ExternalApiCheckCacheResponse200]:
|
||||
"""Check if a prompt is cached
|
||||
|
||||
Args:
|
||||
json_body (ExternalApiCheckCacheJsonBody):
|
||||
|
||||
Raises:
|
||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||
|
||||
Returns:
|
||||
Response[ExternalApiCheckCacheResponse200]
|
||||
"""
|
||||
|
||||
kwargs = _get_kwargs(
|
||||
json_body=json_body,
|
||||
)
|
||||
|
||||
response = await client.get_async_httpx_client().request(**kwargs)
|
||||
|
||||
return _build_response(client=client, response=response)
|
||||
|
||||
|
||||
async def asyncio(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
json_body: ExternalApiCheckCacheJsonBody,
|
||||
) -> Optional[ExternalApiCheckCacheResponse200]:
|
||||
"""Check if a prompt is cached
|
||||
|
||||
Args:
|
||||
json_body (ExternalApiCheckCacheJsonBody):
|
||||
|
||||
Raises:
|
||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||
|
||||
Returns:
|
||||
ExternalApiCheckCacheResponse200
|
||||
"""
|
||||
|
||||
return (
|
||||
await asyncio_detailed(
|
||||
client=client,
|
||||
json_body=json_body,
|
||||
)
|
||||
).parsed
|
||||
@@ -1,98 +0,0 @@
|
||||
from http import HTTPStatus
|
||||
from typing import Any, Dict, Optional, Union
|
||||
|
||||
import httpx
|
||||
|
||||
from ... import errors
|
||||
from ...client import AuthenticatedClient, Client
|
||||
from ...models.external_api_report_json_body import ExternalApiReportJsonBody
|
||||
from ...types import Response
|
||||
|
||||
|
||||
def _get_kwargs(
|
||||
*,
|
||||
json_body: ExternalApiReportJsonBody,
|
||||
) -> Dict[str, Any]:
|
||||
pass
|
||||
|
||||
json_json_body = json_body.to_dict()
|
||||
|
||||
return {
|
||||
"method": "post",
|
||||
"url": "/v1/report",
|
||||
"json": json_json_body,
|
||||
}
|
||||
|
||||
|
||||
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]:
|
||||
if response.status_code == HTTPStatus.OK:
|
||||
return None
|
||||
if client.raise_on_unexpected_status:
|
||||
raise errors.UnexpectedStatus(response.status_code, response.content)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[Any]:
|
||||
return Response(
|
||||
status_code=HTTPStatus(response.status_code),
|
||||
content=response.content,
|
||||
headers=response.headers,
|
||||
parsed=_parse_response(client=client, response=response),
|
||||
)
|
||||
|
||||
|
||||
def sync_detailed(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
json_body: ExternalApiReportJsonBody,
|
||||
) -> Response[Any]:
|
||||
"""Report an API call
|
||||
|
||||
Args:
|
||||
json_body (ExternalApiReportJsonBody):
|
||||
|
||||
Raises:
|
||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||
|
||||
Returns:
|
||||
Response[Any]
|
||||
"""
|
||||
|
||||
kwargs = _get_kwargs(
|
||||
json_body=json_body,
|
||||
)
|
||||
|
||||
response = client.get_httpx_client().request(
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
return _build_response(client=client, response=response)
|
||||
|
||||
|
||||
async def asyncio_detailed(
|
||||
*,
|
||||
client: AuthenticatedClient,
|
||||
json_body: ExternalApiReportJsonBody,
|
||||
) -> Response[Any]:
|
||||
"""Report an API call
|
||||
|
||||
Args:
|
||||
json_body (ExternalApiReportJsonBody):
|
||||
|
||||
Raises:
|
||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||
|
||||
Returns:
|
||||
Response[Any]
|
||||
"""
|
||||
|
||||
kwargs = _get_kwargs(
|
||||
json_body=json_body,
|
||||
)
|
||||
|
||||
response = await client.get_async_httpx_client().request(**kwargs)
|
||||
|
||||
return _build_response(client=client, response=response)
|
||||
@@ -1,268 +0,0 @@
|
||||
import ssl
|
||||
from typing import Any, Dict, Optional, Union
|
||||
|
||||
import httpx
|
||||
from attrs import define, evolve, field
|
||||
|
||||
|
||||
@define
|
||||
class Client:
|
||||
"""A class for keeping track of data related to the API
|
||||
|
||||
The following are accepted as keyword arguments and will be used to construct httpx Clients internally:
|
||||
|
||||
``base_url``: The base URL for the API, all requests are made to a relative path to this URL
|
||||
|
||||
``cookies``: A dictionary of cookies to be sent with every request
|
||||
|
||||
``headers``: A dictionary of headers to be sent with every request
|
||||
|
||||
``timeout``: The maximum amount of a time a request can take. API functions will raise
|
||||
httpx.TimeoutException if this is exceeded.
|
||||
|
||||
``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production,
|
||||
but can be set to False for testing purposes.
|
||||
|
||||
``follow_redirects``: Whether or not to follow redirects. Default value is False.
|
||||
|
||||
``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor.
|
||||
|
||||
|
||||
Attributes:
|
||||
raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a
|
||||
status code that was not documented in the source OpenAPI document. Can also be provided as a keyword
|
||||
argument to the constructor.
|
||||
"""
|
||||
|
||||
raise_on_unexpected_status: bool = field(default=False, kw_only=True)
|
||||
_base_url: str
|
||||
_cookies: Dict[str, str] = field(factory=dict, kw_only=True)
|
||||
_headers: Dict[str, str] = field(factory=dict, kw_only=True)
|
||||
_timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True)
|
||||
_verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True)
|
||||
_follow_redirects: bool = field(default=False, kw_only=True)
|
||||
_httpx_args: Dict[str, Any] = field(factory=dict, kw_only=True)
|
||||
_client: Optional[httpx.Client] = field(default=None, init=False)
|
||||
_async_client: Optional[httpx.AsyncClient] = field(default=None, init=False)
|
||||
|
||||
def with_headers(self, headers: Dict[str, str]) -> "Client":
|
||||
"""Get a new client matching this one with additional headers"""
|
||||
if self._client is not None:
|
||||
self._client.headers.update(headers)
|
||||
if self._async_client is not None:
|
||||
self._async_client.headers.update(headers)
|
||||
return evolve(self, headers={**self._headers, **headers})
|
||||
|
||||
def with_cookies(self, cookies: Dict[str, str]) -> "Client":
|
||||
"""Get a new client matching this one with additional cookies"""
|
||||
if self._client is not None:
|
||||
self._client.cookies.update(cookies)
|
||||
if self._async_client is not None:
|
||||
self._async_client.cookies.update(cookies)
|
||||
return evolve(self, cookies={**self._cookies, **cookies})
|
||||
|
||||
def with_timeout(self, timeout: httpx.Timeout) -> "Client":
|
||||
"""Get a new client matching this one with a new timeout (in seconds)"""
|
||||
if self._client is not None:
|
||||
self._client.timeout = timeout
|
||||
if self._async_client is not None:
|
||||
self._async_client.timeout = timeout
|
||||
return evolve(self, timeout=timeout)
|
||||
|
||||
def set_httpx_client(self, client: httpx.Client) -> "Client":
|
||||
"""Manually the underlying httpx.Client
|
||||
|
||||
**NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
|
||||
"""
|
||||
self._client = client
|
||||
return self
|
||||
|
||||
def get_httpx_client(self) -> httpx.Client:
|
||||
"""Get the underlying httpx.Client, constructing a new one if not previously set"""
|
||||
if self._client is None:
|
||||
self._client = httpx.Client(
|
||||
base_url=self._base_url,
|
||||
cookies=self._cookies,
|
||||
headers=self._headers,
|
||||
timeout=self._timeout,
|
||||
verify=self._verify_ssl,
|
||||
follow_redirects=self._follow_redirects,
|
||||
**self._httpx_args,
|
||||
)
|
||||
return self._client
|
||||
|
||||
def __enter__(self) -> "Client":
|
||||
"""Enter a context manager for self.client—you cannot enter twice (see httpx docs)"""
|
||||
self.get_httpx_client().__enter__()
|
||||
return self
|
||||
|
||||
def __exit__(self, *args: Any, **kwargs: Any) -> None:
|
||||
"""Exit a context manager for internal httpx.Client (see httpx docs)"""
|
||||
self.get_httpx_client().__exit__(*args, **kwargs)
|
||||
|
||||
def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "Client":
|
||||
"""Manually the underlying httpx.AsyncClient
|
||||
|
||||
**NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
|
||||
"""
|
||||
self._async_client = async_client
|
||||
return self
|
||||
|
||||
def get_async_httpx_client(self) -> httpx.AsyncClient:
|
||||
"""Get the underlying httpx.AsyncClient, constructing a new one if not previously set"""
|
||||
if self._async_client is None:
|
||||
self._async_client = httpx.AsyncClient(
|
||||
base_url=self._base_url,
|
||||
cookies=self._cookies,
|
||||
headers=self._headers,
|
||||
timeout=self._timeout,
|
||||
verify=self._verify_ssl,
|
||||
follow_redirects=self._follow_redirects,
|
||||
**self._httpx_args,
|
||||
)
|
||||
return self._async_client
|
||||
|
||||
async def __aenter__(self) -> "Client":
|
||||
"""Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)"""
|
||||
await self.get_async_httpx_client().__aenter__()
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
|
||||
"""Exit a context manager for underlying httpx.AsyncClient (see httpx docs)"""
|
||||
await self.get_async_httpx_client().__aexit__(*args, **kwargs)
|
||||
|
||||
|
||||
@define
|
||||
class AuthenticatedClient:
|
||||
"""A Client which has been authenticated for use on secured endpoints
|
||||
|
||||
The following are accepted as keyword arguments and will be used to construct httpx Clients internally:
|
||||
|
||||
``base_url``: The base URL for the API, all requests are made to a relative path to this URL
|
||||
|
||||
``cookies``: A dictionary of cookies to be sent with every request
|
||||
|
||||
``headers``: A dictionary of headers to be sent with every request
|
||||
|
||||
``timeout``: The maximum amount of a time a request can take. API functions will raise
|
||||
httpx.TimeoutException if this is exceeded.
|
||||
|
||||
``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production,
|
||||
but can be set to False for testing purposes.
|
||||
|
||||
``follow_redirects``: Whether or not to follow redirects. Default value is False.
|
||||
|
||||
``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor.
|
||||
|
||||
|
||||
Attributes:
|
||||
raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a
|
||||
status code that was not documented in the source OpenAPI document. Can also be provided as a keyword
|
||||
argument to the constructor.
|
||||
token: The token to use for authentication
|
||||
prefix: The prefix to use for the Authorization header
|
||||
auth_header_name: The name of the Authorization header
|
||||
"""
|
||||
|
||||
raise_on_unexpected_status: bool = field(default=False, kw_only=True)
|
||||
_base_url: str
|
||||
_cookies: Dict[str, str] = field(factory=dict, kw_only=True)
|
||||
_headers: Dict[str, str] = field(factory=dict, kw_only=True)
|
||||
_timeout: Optional[httpx.Timeout] = field(default=None, kw_only=True)
|
||||
_verify_ssl: Union[str, bool, ssl.SSLContext] = field(default=True, kw_only=True)
|
||||
_follow_redirects: bool = field(default=False, kw_only=True)
|
||||
_httpx_args: Dict[str, Any] = field(factory=dict, kw_only=True)
|
||||
_client: Optional[httpx.Client] = field(default=None, init=False)
|
||||
_async_client: Optional[httpx.AsyncClient] = field(default=None, init=False)
|
||||
|
||||
token: str
|
||||
prefix: str = "Bearer"
|
||||
auth_header_name: str = "Authorization"
|
||||
|
||||
def with_headers(self, headers: Dict[str, str]) -> "AuthenticatedClient":
|
||||
"""Get a new client matching this one with additional headers"""
|
||||
if self._client is not None:
|
||||
self._client.headers.update(headers)
|
||||
if self._async_client is not None:
|
||||
self._async_client.headers.update(headers)
|
||||
return evolve(self, headers={**self._headers, **headers})
|
||||
|
||||
def with_cookies(self, cookies: Dict[str, str]) -> "AuthenticatedClient":
|
||||
"""Get a new client matching this one with additional cookies"""
|
||||
if self._client is not None:
|
||||
self._client.cookies.update(cookies)
|
||||
if self._async_client is not None:
|
||||
self._async_client.cookies.update(cookies)
|
||||
return evolve(self, cookies={**self._cookies, **cookies})
|
||||
|
||||
def with_timeout(self, timeout: httpx.Timeout) -> "AuthenticatedClient":
|
||||
"""Get a new client matching this one with a new timeout (in seconds)"""
|
||||
if self._client is not None:
|
||||
self._client.timeout = timeout
|
||||
if self._async_client is not None:
|
||||
self._async_client.timeout = timeout
|
||||
return evolve(self, timeout=timeout)
|
||||
|
||||
def set_httpx_client(self, client: httpx.Client) -> "AuthenticatedClient":
|
||||
"""Manually the underlying httpx.Client
|
||||
|
||||
**NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
|
||||
"""
|
||||
self._client = client
|
||||
return self
|
||||
|
||||
def get_httpx_client(self) -> httpx.Client:
|
||||
"""Get the underlying httpx.Client, constructing a new one if not previously set"""
|
||||
if self._client is None:
|
||||
self._headers[self.auth_header_name] = f"{self.prefix} {self.token}" if self.prefix else self.token
|
||||
self._client = httpx.Client(
|
||||
base_url=self._base_url,
|
||||
cookies=self._cookies,
|
||||
headers=self._headers,
|
||||
timeout=self._timeout,
|
||||
verify=self._verify_ssl,
|
||||
follow_redirects=self._follow_redirects,
|
||||
**self._httpx_args,
|
||||
)
|
||||
return self._client
|
||||
|
||||
def __enter__(self) -> "AuthenticatedClient":
|
||||
"""Enter a context manager for self.client—you cannot enter twice (see httpx docs)"""
|
||||
self.get_httpx_client().__enter__()
|
||||
return self
|
||||
|
||||
def __exit__(self, *args: Any, **kwargs: Any) -> None:
|
||||
"""Exit a context manager for internal httpx.Client (see httpx docs)"""
|
||||
self.get_httpx_client().__exit__(*args, **kwargs)
|
||||
|
||||
def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "AuthenticatedClient":
|
||||
"""Manually the underlying httpx.AsyncClient
|
||||
|
||||
**NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
|
||||
"""
|
||||
self._async_client = async_client
|
||||
return self
|
||||
|
||||
def get_async_httpx_client(self) -> httpx.AsyncClient:
|
||||
"""Get the underlying httpx.AsyncClient, constructing a new one if not previously set"""
|
||||
if self._async_client is None:
|
||||
self._headers[self.auth_header_name] = f"{self.prefix} {self.token}" if self.prefix else self.token
|
||||
self._async_client = httpx.AsyncClient(
|
||||
base_url=self._base_url,
|
||||
cookies=self._cookies,
|
||||
headers=self._headers,
|
||||
timeout=self._timeout,
|
||||
verify=self._verify_ssl,
|
||||
follow_redirects=self._follow_redirects,
|
||||
**self._httpx_args,
|
||||
)
|
||||
return self._async_client
|
||||
|
||||
async def __aenter__(self) -> "AuthenticatedClient":
|
||||
"""Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)"""
|
||||
await self.get_async_httpx_client().__aenter__()
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
|
||||
"""Exit a context manager for underlying httpx.AsyncClient (see httpx docs)"""
|
||||
await self.get_async_httpx_client().__aexit__(*args, **kwargs)
|
||||
@@ -1,14 +0,0 @@
|
||||
""" Contains shared errors types that can be raised from API functions """
|
||||
|
||||
|
||||
class UnexpectedStatus(Exception):
|
||||
"""Raised by api functions when the response status an undocumented status and Client.raise_on_unexpected_status is True"""
|
||||
|
||||
def __init__(self, status_code: int, content: bytes):
|
||||
self.status_code = status_code
|
||||
self.content = content
|
||||
|
||||
super().__init__(f"Unexpected status code: {status_code}")
|
||||
|
||||
|
||||
__all__ = ["UnexpectedStatus"]
|
||||
@@ -1,15 +0,0 @@
|
||||
""" Contains all the data models used in inputs/outputs """
|
||||
|
||||
from .external_api_check_cache_json_body import ExternalApiCheckCacheJsonBody
|
||||
from .external_api_check_cache_json_body_tags import ExternalApiCheckCacheJsonBodyTags
|
||||
from .external_api_check_cache_response_200 import ExternalApiCheckCacheResponse200
|
||||
from .external_api_report_json_body import ExternalApiReportJsonBody
|
||||
from .external_api_report_json_body_tags import ExternalApiReportJsonBodyTags
|
||||
|
||||
__all__ = (
|
||||
"ExternalApiCheckCacheJsonBody",
|
||||
"ExternalApiCheckCacheJsonBodyTags",
|
||||
"ExternalApiCheckCacheResponse200",
|
||||
"ExternalApiReportJsonBody",
|
||||
"ExternalApiReportJsonBodyTags",
|
||||
)
|
||||
@@ -1,70 +0,0 @@
|
||||
from typing import TYPE_CHECKING, Any, Dict, Type, TypeVar, Union
|
||||
|
||||
from attrs import define
|
||||
|
||||
from ..types import UNSET, Unset
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..models.external_api_check_cache_json_body_tags import ExternalApiCheckCacheJsonBodyTags
|
||||
|
||||
|
||||
T = TypeVar("T", bound="ExternalApiCheckCacheJsonBody")
|
||||
|
||||
|
||||
@define
|
||||
class ExternalApiCheckCacheJsonBody:
|
||||
"""
|
||||
Attributes:
|
||||
requested_at (float): Unix timestamp in milliseconds
|
||||
req_payload (Union[Unset, Any]): JSON-encoded request payload
|
||||
tags (Union[Unset, ExternalApiCheckCacheJsonBodyTags]): Extra tags to attach to the call for filtering. Eg {
|
||||
"userId": "123", "promptId": "populate-title" }
|
||||
"""
|
||||
|
||||
requested_at: float
|
||||
req_payload: Union[Unset, Any] = UNSET
|
||||
tags: Union[Unset, "ExternalApiCheckCacheJsonBodyTags"] = UNSET
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
requested_at = self.requested_at
|
||||
req_payload = self.req_payload
|
||||
tags: Union[Unset, Dict[str, Any]] = UNSET
|
||||
if not isinstance(self.tags, Unset):
|
||||
tags = self.tags.to_dict()
|
||||
|
||||
field_dict: Dict[str, Any] = {}
|
||||
field_dict.update(
|
||||
{
|
||||
"requestedAt": requested_at,
|
||||
}
|
||||
)
|
||||
if req_payload is not UNSET:
|
||||
field_dict["reqPayload"] = req_payload
|
||||
if tags is not UNSET:
|
||||
field_dict["tags"] = tags
|
||||
|
||||
return field_dict
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||
from ..models.external_api_check_cache_json_body_tags import ExternalApiCheckCacheJsonBodyTags
|
||||
|
||||
d = src_dict.copy()
|
||||
requested_at = d.pop("requestedAt")
|
||||
|
||||
req_payload = d.pop("reqPayload", UNSET)
|
||||
|
||||
_tags = d.pop("tags", UNSET)
|
||||
tags: Union[Unset, ExternalApiCheckCacheJsonBodyTags]
|
||||
if isinstance(_tags, Unset):
|
||||
tags = UNSET
|
||||
else:
|
||||
tags = ExternalApiCheckCacheJsonBodyTags.from_dict(_tags)
|
||||
|
||||
external_api_check_cache_json_body = cls(
|
||||
requested_at=requested_at,
|
||||
req_payload=req_payload,
|
||||
tags=tags,
|
||||
)
|
||||
|
||||
return external_api_check_cache_json_body
|
||||
@@ -1,43 +0,0 @@
|
||||
from typing import Any, Dict, List, Type, TypeVar
|
||||
|
||||
from attrs import define, field
|
||||
|
||||
T = TypeVar("T", bound="ExternalApiCheckCacheJsonBodyTags")
|
||||
|
||||
|
||||
@define
|
||||
class ExternalApiCheckCacheJsonBodyTags:
|
||||
"""Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }"""
|
||||
|
||||
additional_properties: Dict[str, str] = field(init=False, factory=dict)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
field_dict: Dict[str, Any] = {}
|
||||
field_dict.update(self.additional_properties)
|
||||
field_dict.update({})
|
||||
|
||||
return field_dict
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||
d = src_dict.copy()
|
||||
external_api_check_cache_json_body_tags = cls()
|
||||
|
||||
external_api_check_cache_json_body_tags.additional_properties = d
|
||||
return external_api_check_cache_json_body_tags
|
||||
|
||||
@property
|
||||
def additional_keys(self) -> List[str]:
|
||||
return list(self.additional_properties.keys())
|
||||
|
||||
def __getitem__(self, key: str) -> str:
|
||||
return self.additional_properties[key]
|
||||
|
||||
def __setitem__(self, key: str, value: str) -> None:
|
||||
self.additional_properties[key] = value
|
||||
|
||||
def __delitem__(self, key: str) -> None:
|
||||
del self.additional_properties[key]
|
||||
|
||||
def __contains__(self, key: str) -> bool:
|
||||
return key in self.additional_properties
|
||||
@@ -1,38 +0,0 @@
|
||||
from typing import Any, Dict, Type, TypeVar, Union
|
||||
|
||||
from attrs import define
|
||||
|
||||
from ..types import UNSET, Unset
|
||||
|
||||
T = TypeVar("T", bound="ExternalApiCheckCacheResponse200")
|
||||
|
||||
|
||||
@define
|
||||
class ExternalApiCheckCacheResponse200:
|
||||
"""
|
||||
Attributes:
|
||||
resp_payload (Union[Unset, Any]): JSON-encoded response payload
|
||||
"""
|
||||
|
||||
resp_payload: Union[Unset, Any] = UNSET
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
resp_payload = self.resp_payload
|
||||
|
||||
field_dict: Dict[str, Any] = {}
|
||||
field_dict.update({})
|
||||
if resp_payload is not UNSET:
|
||||
field_dict["respPayload"] = resp_payload
|
||||
|
||||
return field_dict
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||
d = src_dict.copy()
|
||||
resp_payload = d.pop("respPayload", UNSET)
|
||||
|
||||
external_api_check_cache_response_200 = cls(
|
||||
resp_payload=resp_payload,
|
||||
)
|
||||
|
||||
return external_api_check_cache_response_200
|
||||
@@ -1,101 +0,0 @@
|
||||
from typing import TYPE_CHECKING, Any, Dict, Type, TypeVar, Union
|
||||
|
||||
from attrs import define
|
||||
|
||||
from ..types import UNSET, Unset
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..models.external_api_report_json_body_tags import ExternalApiReportJsonBodyTags
|
||||
|
||||
|
||||
T = TypeVar("T", bound="ExternalApiReportJsonBody")
|
||||
|
||||
|
||||
@define
|
||||
class ExternalApiReportJsonBody:
|
||||
"""
|
||||
Attributes:
|
||||
requested_at (float): Unix timestamp in milliseconds
|
||||
received_at (float): Unix timestamp in milliseconds
|
||||
req_payload (Union[Unset, Any]): JSON-encoded request payload
|
||||
resp_payload (Union[Unset, Any]): JSON-encoded response payload
|
||||
status_code (Union[Unset, float]): HTTP status code of response
|
||||
error_message (Union[Unset, str]): User-friendly error message
|
||||
tags (Union[Unset, ExternalApiReportJsonBodyTags]): Extra tags to attach to the call for filtering. Eg {
|
||||
"userId": "123", "promptId": "populate-title" }
|
||||
"""
|
||||
|
||||
requested_at: float
|
||||
received_at: float
|
||||
req_payload: Union[Unset, Any] = UNSET
|
||||
resp_payload: Union[Unset, Any] = UNSET
|
||||
status_code: Union[Unset, float] = UNSET
|
||||
error_message: Union[Unset, str] = UNSET
|
||||
tags: Union[Unset, "ExternalApiReportJsonBodyTags"] = UNSET
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
requested_at = self.requested_at
|
||||
received_at = self.received_at
|
||||
req_payload = self.req_payload
|
||||
resp_payload = self.resp_payload
|
||||
status_code = self.status_code
|
||||
error_message = self.error_message
|
||||
tags: Union[Unset, Dict[str, Any]] = UNSET
|
||||
if not isinstance(self.tags, Unset):
|
||||
tags = self.tags.to_dict()
|
||||
|
||||
field_dict: Dict[str, Any] = {}
|
||||
field_dict.update(
|
||||
{
|
||||
"requestedAt": requested_at,
|
||||
"receivedAt": received_at,
|
||||
}
|
||||
)
|
||||
if req_payload is not UNSET:
|
||||
field_dict["reqPayload"] = req_payload
|
||||
if resp_payload is not UNSET:
|
||||
field_dict["respPayload"] = resp_payload
|
||||
if status_code is not UNSET:
|
||||
field_dict["statusCode"] = status_code
|
||||
if error_message is not UNSET:
|
||||
field_dict["errorMessage"] = error_message
|
||||
if tags is not UNSET:
|
||||
field_dict["tags"] = tags
|
||||
|
||||
return field_dict
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||
from ..models.external_api_report_json_body_tags import ExternalApiReportJsonBodyTags
|
||||
|
||||
d = src_dict.copy()
|
||||
requested_at = d.pop("requestedAt")
|
||||
|
||||
received_at = d.pop("receivedAt")
|
||||
|
||||
req_payload = d.pop("reqPayload", UNSET)
|
||||
|
||||
resp_payload = d.pop("respPayload", UNSET)
|
||||
|
||||
status_code = d.pop("statusCode", UNSET)
|
||||
|
||||
error_message = d.pop("errorMessage", UNSET)
|
||||
|
||||
_tags = d.pop("tags", UNSET)
|
||||
tags: Union[Unset, ExternalApiReportJsonBodyTags]
|
||||
if isinstance(_tags, Unset):
|
||||
tags = UNSET
|
||||
else:
|
||||
tags = ExternalApiReportJsonBodyTags.from_dict(_tags)
|
||||
|
||||
external_api_report_json_body = cls(
|
||||
requested_at=requested_at,
|
||||
received_at=received_at,
|
||||
req_payload=req_payload,
|
||||
resp_payload=resp_payload,
|
||||
status_code=status_code,
|
||||
error_message=error_message,
|
||||
tags=tags,
|
||||
)
|
||||
|
||||
return external_api_report_json_body
|
||||
@@ -1,43 +0,0 @@
|
||||
from typing import Any, Dict, List, Type, TypeVar
|
||||
|
||||
from attrs import define, field
|
||||
|
||||
T = TypeVar("T", bound="ExternalApiReportJsonBodyTags")
|
||||
|
||||
|
||||
@define
|
||||
class ExternalApiReportJsonBodyTags:
|
||||
"""Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }"""
|
||||
|
||||
additional_properties: Dict[str, str] = field(init=False, factory=dict)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
field_dict: Dict[str, Any] = {}
|
||||
field_dict.update(self.additional_properties)
|
||||
field_dict.update({})
|
||||
|
||||
return field_dict
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||
d = src_dict.copy()
|
||||
external_api_report_json_body_tags = cls()
|
||||
|
||||
external_api_report_json_body_tags.additional_properties = d
|
||||
return external_api_report_json_body_tags
|
||||
|
||||
@property
|
||||
def additional_keys(self) -> List[str]:
|
||||
return list(self.additional_properties.keys())
|
||||
|
||||
def __getitem__(self, key: str) -> str:
|
||||
return self.additional_properties[key]
|
||||
|
||||
def __setitem__(self, key: str, value: str) -> None:
|
||||
self.additional_properties[key] = value
|
||||
|
||||
def __delitem__(self, key: str) -> None:
|
||||
del self.additional_properties[key]
|
||||
|
||||
def __contains__(self, key: str) -> bool:
|
||||
return key in self.additional_properties
|
||||
@@ -1 +0,0 @@
|
||||
# Marker file for PEP 561
|
||||
@@ -1,44 +0,0 @@
|
||||
""" Contains some shared types for properties """
|
||||
from http import HTTPStatus
|
||||
from typing import BinaryIO, Generic, Literal, MutableMapping, Optional, Tuple, TypeVar
|
||||
|
||||
from attrs import define
|
||||
|
||||
|
||||
class Unset:
|
||||
def __bool__(self) -> Literal[False]:
|
||||
return False
|
||||
|
||||
|
||||
UNSET: Unset = Unset()
|
||||
|
||||
FileJsonType = Tuple[Optional[str], BinaryIO, Optional[str]]
|
||||
|
||||
|
||||
@define
|
||||
class File:
|
||||
"""Contains information for file uploads"""
|
||||
|
||||
payload: BinaryIO
|
||||
file_name: Optional[str] = None
|
||||
mime_type: Optional[str] = None
|
||||
|
||||
def to_tuple(self) -> FileJsonType:
|
||||
"""Return a tuple representation that httpx will accept for multipart/form-data"""
|
||||
return self.file_name, self.payload, self.mime_type
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
@define
|
||||
class Response(Generic[T]):
|
||||
"""A response from an endpoint"""
|
||||
|
||||
status_code: HTTPStatus
|
||||
content: bytes
|
||||
headers: MutableMapping[str, str]
|
||||
parsed: Optional[T]
|
||||
|
||||
|
||||
__all__ = ["File", "Response", "FileJsonType"]
|
||||
@@ -1,42 +0,0 @@
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
def merge_streamed_chunks(base: Optional[Any], chunk: Any) -> Any:
|
||||
if base is None:
|
||||
return merge_streamed_chunks({**chunk, "choices": []}, chunk)
|
||||
|
||||
choices = base["choices"].copy()
|
||||
for choice in chunk["choices"]:
|
||||
base_choice = next((c for c in choices if c["index"] == choice["index"]), None)
|
||||
|
||||
if base_choice:
|
||||
base_choice["finish_reason"] = (
|
||||
choice.get("finish_reason") or base_choice["finish_reason"]
|
||||
)
|
||||
base_choice["message"] = base_choice.get("message") or {"role": "assistant"}
|
||||
|
||||
if choice.get("delta") and choice["delta"].get("content"):
|
||||
base_choice["message"]["content"] = (
|
||||
base_choice["message"].get("content") or ""
|
||||
) + (choice["delta"].get("content") or "")
|
||||
if choice.get("delta") and choice["delta"].get("function_call"):
|
||||
fn_call = base_choice["message"].get("function_call") or {}
|
||||
fn_call["name"] = (fn_call.get("name") or "") + (
|
||||
choice["delta"]["function_call"].get("name") or ""
|
||||
)
|
||||
fn_call["arguments"] = (fn_call.get("arguments") or "") + (
|
||||
choice["delta"]["function_call"].get("arguments") or ""
|
||||
)
|
||||
else:
|
||||
# Here, we'll have to handle the omitted property "delta" manually
|
||||
new_choice = {k: v for k, v in choice.items() if k != "delta"}
|
||||
choices.append(
|
||||
{**new_choice, "message": {"role": "assistant", **choice["delta"]}}
|
||||
)
|
||||
|
||||
merged = {
|
||||
**base,
|
||||
"choices": choices,
|
||||
}
|
||||
|
||||
return merged
|
||||
@@ -1,168 +0,0 @@
|
||||
import openai as original_openai
|
||||
from openai.openai_object import OpenAIObject
|
||||
import time
|
||||
import inspect
|
||||
|
||||
from openpipe.merge_openai_chunks import merge_streamed_chunks
|
||||
|
||||
from .shared import maybe_check_cache, maybe_check_cache_async, report_async, report
|
||||
|
||||
|
||||
class WrappedChatCompletion(original_openai.ChatCompletion):
|
||||
@classmethod
|
||||
def create(cls, *args, **kwargs):
|
||||
openpipe_options = kwargs.pop("openpipe", {})
|
||||
|
||||
cached_response = maybe_check_cache(
|
||||
openpipe_options=openpipe_options, req_payload=kwargs
|
||||
)
|
||||
if cached_response:
|
||||
return OpenAIObject.construct_from(cached_response, api_key=None)
|
||||
|
||||
requested_at = int(time.time() * 1000)
|
||||
|
||||
try:
|
||||
chat_completion = original_openai.ChatCompletion.create(*args, **kwargs)
|
||||
|
||||
if inspect.isgenerator(chat_completion):
|
||||
|
||||
def _gen():
|
||||
assembled_completion = None
|
||||
for chunk in chat_completion:
|
||||
assembled_completion = merge_streamed_chunks(
|
||||
assembled_completion, chunk
|
||||
)
|
||||
yield chunk
|
||||
|
||||
received_at = int(time.time() * 1000)
|
||||
|
||||
report(
|
||||
openpipe_options=openpipe_options,
|
||||
requested_at=requested_at,
|
||||
received_at=received_at,
|
||||
req_payload=kwargs,
|
||||
resp_payload=assembled_completion,
|
||||
status_code=200,
|
||||
)
|
||||
|
||||
return _gen()
|
||||
else:
|
||||
received_at = int(time.time() * 1000)
|
||||
|
||||
report(
|
||||
openpipe_options=openpipe_options,
|
||||
requested_at=requested_at,
|
||||
received_at=received_at,
|
||||
req_payload=kwargs,
|
||||
resp_payload=chat_completion,
|
||||
status_code=200,
|
||||
)
|
||||
|
||||
return chat_completion
|
||||
except Exception as e:
|
||||
received_at = int(time.time() * 1000)
|
||||
|
||||
if isinstance(e, original_openai.OpenAIError):
|
||||
report(
|
||||
openpipe_options=openpipe_options,
|
||||
requested_at=requested_at,
|
||||
received_at=received_at,
|
||||
req_payload=kwargs,
|
||||
resp_payload=e.json_body,
|
||||
error_message=str(e),
|
||||
status_code=e.http_status,
|
||||
)
|
||||
else:
|
||||
report(
|
||||
openpipe_options=openpipe_options,
|
||||
requested_at=requested_at,
|
||||
received_at=received_at,
|
||||
req_payload=kwargs,
|
||||
error_message=str(e),
|
||||
)
|
||||
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
async def acreate(cls, *args, **kwargs):
|
||||
openpipe_options = kwargs.pop("openpipe", {})
|
||||
|
||||
cached_response = await maybe_check_cache_async(
|
||||
openpipe_options=openpipe_options, req_payload=kwargs
|
||||
)
|
||||
if cached_response:
|
||||
return OpenAIObject.construct_from(cached_response, api_key=None)
|
||||
|
||||
requested_at = int(time.time() * 1000)
|
||||
|
||||
try:
|
||||
chat_completion = original_openai.ChatCompletion.acreate(*args, **kwargs)
|
||||
|
||||
if inspect.isgenerator(chat_completion):
|
||||
|
||||
def _gen():
|
||||
assembled_completion = None
|
||||
for chunk in chat_completion:
|
||||
assembled_completion = merge_streamed_chunks(
|
||||
assembled_completion, chunk
|
||||
)
|
||||
yield chunk
|
||||
|
||||
received_at = int(time.time() * 1000)
|
||||
|
||||
report_async(
|
||||
openpipe_options=openpipe_options,
|
||||
requested_at=requested_at,
|
||||
received_at=received_at,
|
||||
req_payload=kwargs,
|
||||
resp_payload=assembled_completion,
|
||||
status_code=200,
|
||||
)
|
||||
|
||||
return _gen()
|
||||
else:
|
||||
received_at = int(time.time() * 1000)
|
||||
|
||||
report_async(
|
||||
openpipe_options=openpipe_options,
|
||||
requested_at=requested_at,
|
||||
received_at=received_at,
|
||||
req_payload=kwargs,
|
||||
resp_payload=chat_completion,
|
||||
status_code=200,
|
||||
)
|
||||
|
||||
return chat_completion
|
||||
except Exception as e:
|
||||
received_at = int(time.time() * 1000)
|
||||
|
||||
if isinstance(e, original_openai.OpenAIError):
|
||||
report_async(
|
||||
openpipe_options=openpipe_options,
|
||||
requested_at=requested_at,
|
||||
received_at=received_at,
|
||||
req_payload=kwargs,
|
||||
resp_payload=e.json_body,
|
||||
error_message=str(e),
|
||||
status_code=e.http_status,
|
||||
)
|
||||
else:
|
||||
report_async(
|
||||
openpipe_options=openpipe_options,
|
||||
requested_at=requested_at,
|
||||
received_at=received_at,
|
||||
req_payload=kwargs,
|
||||
error_message=str(e),
|
||||
)
|
||||
|
||||
raise e
|
||||
|
||||
|
||||
class OpenAIWrapper:
|
||||
ChatCompletion = WrappedChatCompletion()
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(original_openai, name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
return setattr(original_openai, name, value)
|
||||
@@ -1,125 +0,0 @@
|
||||
from openpipe.api_client.api.default import (
|
||||
external_api_report,
|
||||
external_api_check_cache,
|
||||
)
|
||||
from openpipe.api_client.client import AuthenticatedClient
|
||||
from openpipe.api_client.models.external_api_report_json_body_tags import (
|
||||
ExternalApiReportJsonBodyTags,
|
||||
)
|
||||
import toml
|
||||
import time
|
||||
|
||||
version = toml.load("pyproject.toml")["tool"]["poetry"]["version"]
|
||||
|
||||
configured_client = AuthenticatedClient(
|
||||
base_url="https://app.openpipe.ai/api/v1", token=""
|
||||
)
|
||||
|
||||
|
||||
def _get_tags(openpipe_options):
|
||||
tags = openpipe_options.get("tags") or {}
|
||||
tags["$sdk"] = "python"
|
||||
tags["$sdk_version"] = version
|
||||
|
||||
return ExternalApiReportJsonBodyTags.from_dict(tags)
|
||||
|
||||
|
||||
def _should_check_cache(openpipe_options):
|
||||
if configured_client.token == "":
|
||||
return False
|
||||
return openpipe_options.get("cache", False)
|
||||
|
||||
|
||||
def _process_cache_payload(
|
||||
payload: external_api_check_cache.ExternalApiCheckCacheResponse200,
|
||||
):
|
||||
if not payload or not payload.resp_payload:
|
||||
return None
|
||||
payload.resp_payload["openpipe"] = {"cache_status": "HIT"}
|
||||
|
||||
return payload.resp_payload
|
||||
|
||||
|
||||
def maybe_check_cache(
|
||||
openpipe_options={},
|
||||
req_payload={},
|
||||
):
|
||||
if not _should_check_cache(openpipe_options):
|
||||
return None
|
||||
try:
|
||||
payload = external_api_check_cache.sync(
|
||||
client=configured_client,
|
||||
json_body=external_api_check_cache.ExternalApiCheckCacheJsonBody(
|
||||
req_payload=req_payload,
|
||||
requested_at=int(time.time() * 1000),
|
||||
tags=_get_tags(openpipe_options),
|
||||
),
|
||||
)
|
||||
return _process_cache_payload(payload)
|
||||
|
||||
except Exception as e:
|
||||
# We don't want to break client apps if our API is down for some reason
|
||||
print(f"Error reporting to OpenPipe: {e}")
|
||||
print(e)
|
||||
return None
|
||||
|
||||
|
||||
async def maybe_check_cache_async(
|
||||
openpipe_options={},
|
||||
req_payload={},
|
||||
):
|
||||
if not _should_check_cache(openpipe_options):
|
||||
return None
|
||||
|
||||
try:
|
||||
payload = await external_api_check_cache.asyncio(
|
||||
client=configured_client,
|
||||
json_body=external_api_check_cache.ExternalApiCheckCacheJsonBody(
|
||||
req_payload=req_payload,
|
||||
requested_at=int(time.time() * 1000),
|
||||
tags=_get_tags(openpipe_options),
|
||||
),
|
||||
)
|
||||
return _process_cache_payload(payload)
|
||||
|
||||
except Exception as e:
|
||||
# We don't want to break client apps if our API is down for some reason
|
||||
print(f"Error reporting to OpenPipe: {e}")
|
||||
print(e)
|
||||
return None
|
||||
|
||||
|
||||
def report(
|
||||
openpipe_options={},
|
||||
**kwargs,
|
||||
):
|
||||
try:
|
||||
external_api_report.sync_detailed(
|
||||
client=configured_client,
|
||||
json_body=external_api_report.ExternalApiReportJsonBody(
|
||||
**kwargs,
|
||||
tags=_get_tags(openpipe_options),
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
# We don't want to break client apps if our API is down for some reason
|
||||
print(f"Error reporting to OpenPipe: {e}")
|
||||
print(e)
|
||||
|
||||
|
||||
async def report_async(
|
||||
openpipe_options={},
|
||||
**kwargs,
|
||||
):
|
||||
try:
|
||||
await external_api_report.asyncio_detailed(
|
||||
client=configured_client,
|
||||
json_body=external_api_report.ExternalApiReportJsonBody(
|
||||
**kwargs,
|
||||
tags=_get_tags(openpipe_options),
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
# We don't want to break client apps if our API is down for some reason
|
||||
print(f"Error reporting to OpenPipe: {e}")
|
||||
print(e)
|
||||
@@ -1,88 +0,0 @@
|
||||
from dotenv import load_dotenv
|
||||
from . import openai, configure_openpipe
|
||||
import os
|
||||
import pytest
|
||||
|
||||
load_dotenv()
|
||||
|
||||
openai.api_key = os.getenv("OPENAI_API_KEY")
|
||||
|
||||
configure_openpipe(
|
||||
base_url="http://localhost:3000/api", api_key=os.getenv("OPENPIPE_API_KEY")
|
||||
)
|
||||
|
||||
|
||||
def test_sync():
|
||||
completion = openai.ChatCompletion.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "system", "content": "count to 10"}],
|
||||
)
|
||||
|
||||
print(completion.choices[0].message.content)
|
||||
|
||||
|
||||
def test_streaming():
|
||||
completion = openai.ChatCompletion.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "system", "content": "count to 10"}],
|
||||
stream=True,
|
||||
)
|
||||
|
||||
for chunk in completion:
|
||||
print(chunk)
|
||||
|
||||
|
||||
async def test_async():
|
||||
acompletion = await openai.ChatCompletion.acreate(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "user", "content": "count down from 5"}],
|
||||
)
|
||||
|
||||
print(acompletion.choices[0].message.content)
|
||||
|
||||
|
||||
async def test_async_streaming():
|
||||
acompletion = await openai.ChatCompletion.acreate(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "user", "content": "count down from 5"}],
|
||||
stream=True,
|
||||
)
|
||||
|
||||
async for chunk in acompletion:
|
||||
print(chunk)
|
||||
|
||||
|
||||
def test_sync_with_tags():
|
||||
completion = openai.ChatCompletion.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "system", "content": "count to 10"}],
|
||||
openpipe={"tags": {"promptId": "testprompt"}},
|
||||
)
|
||||
print("finished")
|
||||
|
||||
print(completion.choices[0].message.content)
|
||||
|
||||
|
||||
def test_bad_call():
|
||||
completion = openai.ChatCompletion.create(
|
||||
model="gpt-3.5-turbo-blaster",
|
||||
messages=[{"role": "system", "content": "count to 10"}],
|
||||
stream=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.focus
|
||||
async def test_caching():
|
||||
completion = openai.ChatCompletion.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "system", "content": "count to 10"}],
|
||||
openpipe={"cache": True},
|
||||
)
|
||||
|
||||
completion2 = await openai.ChatCompletion.acreate(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "system", "content": "count to 10"}],
|
||||
openpipe={"cache": True},
|
||||
)
|
||||
|
||||
print(completion2)
|
||||
1370
client-libs/python/poetry.lock
generated
1370
client-libs/python/poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,35 +0,0 @@
|
||||
[tool.poetry]
|
||||
name = "openpipe"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["Kyle Corbitt <kyle@corbt.com>"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.9"
|
||||
openai = "^0.27.8"
|
||||
httpx = "^0.24.1"
|
||||
attrs = "^23.1.0"
|
||||
python-dateutil = "^2.8.2"
|
||||
toml = "^0.10.2"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
openapi-python-client = "^0.15.0"
|
||||
black = "^23.7.0"
|
||||
isort = "^5.12.0"
|
||||
autoflake = "^2.2.0"
|
||||
pytest = "^7.4.0"
|
||||
python-dotenv = "^1.0.0"
|
||||
pytest-asyncio = "^0.21.1"
|
||||
pytest-watch = "^4.2.0"
|
||||
pytest-testmon = "^2.0.12"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
asyncio_mode = "auto"
|
||||
markers = "focus"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
@@ -5,13 +5,16 @@
|
||||
"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": [] }],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
@@ -23,14 +26,20 @@
|
||||
"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": [
|
||||
"startTime"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
@@ -45,14 +54,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 +73,6 @@
|
||||
"post": {
|
||||
"operationId": "externalApi-report",
|
||||
"description": "Report an API call",
|
||||
"security": [{ "Authorization": [] }],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
@@ -72,18 +84,36 @@
|
||||
"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" },
|
||||
"endTime": {
|
||||
"type": "number",
|
||||
"description": "Unix timestamp in milliseconds"
|
||||
},
|
||||
"reqPayload": {
|
||||
"description": "JSON-encoded request payload"
|
||||
},
|
||||
"respPayload": {
|
||||
"description": "JSON-encoded response payload"
|
||||
},
|
||||
"respStatus": {
|
||||
"type": "number",
|
||||
"description": "HTTP status code of response"
|
||||
},
|
||||
"error": {
|
||||
"type": "string",
|
||||
"description": "User-friendly error message"
|
||||
},
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "type": "string" },
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }"
|
||||
}
|
||||
},
|
||||
"required": ["startTime", "endTime"],
|
||||
"required": [
|
||||
"startTime",
|
||||
"endTime"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
@@ -93,15 +123,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 +151,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 +184,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
272
client-libs/typescript/codegen/api.js
Normal file
272
client-libs/typescript/codegen/api.js
Normal file
@@ -0,0 +1,272 @@
|
||||
"use strict";
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* OpenPipe API
|
||||
* The public API for reporting API calls to OpenPipe
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = function (d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
||||
return extendStatics(d, b);
|
||||
};
|
||||
return function (d, b) {
|
||||
if (typeof b !== "function" && b !== null)
|
||||
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var __assign = (this && this.__assign) || function () {
|
||||
__assign = Object.assign || function(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||
t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DefaultApi = exports.DefaultApiFactory = exports.DefaultApiFp = exports.DefaultApiAxiosParamCreator = void 0;
|
||||
var axios_1 = require("axios");
|
||||
// Some imports not used depending on template conditions
|
||||
// @ts-ignore
|
||||
var common_1 = require("./common");
|
||||
// @ts-ignore
|
||||
var base_1 = require("./base");
|
||||
/**
|
||||
* DefaultApi - axios parameter creator
|
||||
* @export
|
||||
*/
|
||||
var DefaultApiAxiosParamCreator = function (configuration) {
|
||||
var _this = this;
|
||||
return {
|
||||
/**
|
||||
* Check if a prompt is cached
|
||||
* @param {ExternalApiCheckCacheRequest} externalApiCheckCacheRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
externalApiCheckCache: function (externalApiCheckCacheRequest, options) {
|
||||
if (options === void 0) { options = {}; }
|
||||
return __awaiter(_this, void 0, void 0, function () {
|
||||
var localVarPath, localVarUrlObj, baseOptions, localVarRequestOptions, localVarHeaderParameter, localVarQueryParameter, headersFromBaseOptions;
|
||||
return __generator(this, function (_a) {
|
||||
// verify required parameter 'externalApiCheckCacheRequest' is not null or undefined
|
||||
(0, common_1.assertParamExists)('externalApiCheckCache', 'externalApiCheckCacheRequest', externalApiCheckCacheRequest);
|
||||
localVarPath = "/v1/check-cache";
|
||||
localVarUrlObj = new URL(localVarPath, common_1.DUMMY_BASE_URL);
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
localVarRequestOptions = __assign(__assign({ method: 'POST' }, baseOptions), options);
|
||||
localVarHeaderParameter = {};
|
||||
localVarQueryParameter = {};
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||
(0, common_1.setSearchParams)(localVarUrlObj, localVarQueryParameter);
|
||||
headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = __assign(__assign(__assign({}, localVarHeaderParameter), headersFromBaseOptions), options.headers);
|
||||
localVarRequestOptions.data = (0, common_1.serializeDataIfNeeded)(externalApiCheckCacheRequest, localVarRequestOptions, configuration);
|
||||
return [2 /*return*/, {
|
||||
url: (0, common_1.toPathString)(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
}];
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Report an API call
|
||||
* @param {ExternalApiReportRequest} externalApiReportRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
externalApiReport: function (externalApiReportRequest, options) {
|
||||
if (options === void 0) { options = {}; }
|
||||
return __awaiter(_this, void 0, void 0, function () {
|
||||
var localVarPath, localVarUrlObj, baseOptions, localVarRequestOptions, localVarHeaderParameter, localVarQueryParameter, headersFromBaseOptions;
|
||||
return __generator(this, function (_a) {
|
||||
// verify required parameter 'externalApiReportRequest' is not null or undefined
|
||||
(0, common_1.assertParamExists)('externalApiReport', 'externalApiReportRequest', externalApiReportRequest);
|
||||
localVarPath = "/v1/report";
|
||||
localVarUrlObj = new URL(localVarPath, common_1.DUMMY_BASE_URL);
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
localVarRequestOptions = __assign(__assign({ method: 'POST' }, baseOptions), options);
|
||||
localVarHeaderParameter = {};
|
||||
localVarQueryParameter = {};
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||
(0, common_1.setSearchParams)(localVarUrlObj, localVarQueryParameter);
|
||||
headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = __assign(__assign(__assign({}, localVarHeaderParameter), headersFromBaseOptions), options.headers);
|
||||
localVarRequestOptions.data = (0, common_1.serializeDataIfNeeded)(externalApiReportRequest, localVarRequestOptions, configuration);
|
||||
return [2 /*return*/, {
|
||||
url: (0, common_1.toPathString)(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
}];
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
exports.DefaultApiAxiosParamCreator = DefaultApiAxiosParamCreator;
|
||||
/**
|
||||
* DefaultApi - functional programming interface
|
||||
* @export
|
||||
*/
|
||||
var DefaultApiFp = function (configuration) {
|
||||
var localVarAxiosParamCreator = (0, exports.DefaultApiAxiosParamCreator)(configuration);
|
||||
return {
|
||||
/**
|
||||
* Check if a prompt is cached
|
||||
* @param {ExternalApiCheckCacheRequest} externalApiCheckCacheRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
externalApiCheckCache: function (externalApiCheckCacheRequest, options) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var localVarAxiosArgs;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, localVarAxiosParamCreator.externalApiCheckCache(externalApiCheckCacheRequest, options)];
|
||||
case 1:
|
||||
localVarAxiosArgs = _a.sent();
|
||||
return [2 /*return*/, (0, common_1.createRequestFunction)(localVarAxiosArgs, axios_1.default, base_1.BASE_PATH, configuration)];
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Report an API call
|
||||
* @param {ExternalApiReportRequest} externalApiReportRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
externalApiReport: function (externalApiReportRequest, options) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var localVarAxiosArgs;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, localVarAxiosParamCreator.externalApiReport(externalApiReportRequest, options)];
|
||||
case 1:
|
||||
localVarAxiosArgs = _a.sent();
|
||||
return [2 /*return*/, (0, common_1.createRequestFunction)(localVarAxiosArgs, axios_1.default, base_1.BASE_PATH, configuration)];
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
exports.DefaultApiFp = DefaultApiFp;
|
||||
/**
|
||||
* DefaultApi - factory interface
|
||||
* @export
|
||||
*/
|
||||
var DefaultApiFactory = function (configuration, basePath, axios) {
|
||||
var localVarFp = (0, exports.DefaultApiFp)(configuration);
|
||||
return {
|
||||
/**
|
||||
* Check if a prompt is cached
|
||||
* @param {ExternalApiCheckCacheRequest} externalApiCheckCacheRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
externalApiCheckCache: function (externalApiCheckCacheRequest, options) {
|
||||
return localVarFp.externalApiCheckCache(externalApiCheckCacheRequest, options).then(function (request) { return request(axios, basePath); });
|
||||
},
|
||||
/**
|
||||
* Report an API call
|
||||
* @param {ExternalApiReportRequest} externalApiReportRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
externalApiReport: function (externalApiReportRequest, options) {
|
||||
return localVarFp.externalApiReport(externalApiReportRequest, options).then(function (request) { return request(axios, basePath); });
|
||||
},
|
||||
};
|
||||
};
|
||||
exports.DefaultApiFactory = DefaultApiFactory;
|
||||
/**
|
||||
* DefaultApi - object-oriented interface
|
||||
* @export
|
||||
* @class DefaultApi
|
||||
* @extends {BaseAPI}
|
||||
*/
|
||||
var DefaultApi = /** @class */ (function (_super) {
|
||||
__extends(DefaultApi, _super);
|
||||
function DefaultApi() {
|
||||
return _super !== null && _super.apply(this, arguments) || this;
|
||||
}
|
||||
/**
|
||||
* Check if a prompt is cached
|
||||
* @param {ExternalApiCheckCacheRequest} externalApiCheckCacheRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof DefaultApi
|
||||
*/
|
||||
DefaultApi.prototype.externalApiCheckCache = function (externalApiCheckCacheRequest, options) {
|
||||
var _this = this;
|
||||
return (0, exports.DefaultApiFp)(this.configuration).externalApiCheckCache(externalApiCheckCacheRequest, options).then(function (request) { return request(_this.axios, _this.basePath); });
|
||||
};
|
||||
/**
|
||||
* Report an API call
|
||||
* @param {ExternalApiReportRequest} externalApiReportRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof DefaultApi
|
||||
*/
|
||||
DefaultApi.prototype.externalApiReport = function (externalApiReportRequest, options) {
|
||||
var _this = this;
|
||||
return (0, exports.DefaultApiFp)(this.configuration).externalApiReport(externalApiReportRequest, options).then(function (request) { return request(_this.axios, _this.basePath); });
|
||||
};
|
||||
return DefaultApi;
|
||||
}(base_1.BaseAPI));
|
||||
exports.DefaultApi = DefaultApi;
|
||||
@@ -85,7 +85,7 @@ export interface ExternalApiCheckCacheRequest {
|
||||
* @type {number}
|
||||
* @memberof ExternalApiCheckCacheRequest
|
||||
*/
|
||||
'requestedAt': number;
|
||||
'startTime': number;
|
||||
/**
|
||||
* JSON-encoded request payload
|
||||
* @type {any}
|
||||
@@ -110,13 +110,13 @@ export interface ExternalApiReportRequest {
|
||||
* @type {number}
|
||||
* @memberof ExternalApiReportRequest
|
||||
*/
|
||||
'requestedAt': number;
|
||||
'startTime': number;
|
||||
/**
|
||||
* Unix timestamp in milliseconds
|
||||
* @type {number}
|
||||
* @memberof ExternalApiReportRequest
|
||||
*/
|
||||
'receivedAt': number;
|
||||
'endTime': number;
|
||||
/**
|
||||
* JSON-encoded request payload
|
||||
* @type {any}
|
||||
@@ -134,13 +134,13 @@ export interface ExternalApiReportRequest {
|
||||
* @type {number}
|
||||
* @memberof ExternalApiReportRequest
|
||||
*/
|
||||
'statusCode'?: number;
|
||||
'respStatus'?: number;
|
||||
/**
|
||||
* User-friendly error message
|
||||
* @type {string}
|
||||
* @memberof ExternalApiReportRequest
|
||||
*/
|
||||
'errorMessage'?: string;
|
||||
'error'?: string;
|
||||
/**
|
||||
* Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }
|
||||
* @type {{ [key: string]: string; }}
|
||||
|
||||
80
client-libs/typescript/codegen/base.js
Normal file
80
client-libs/typescript/codegen/base.js
Normal file
@@ -0,0 +1,80 @@
|
||||
"use strict";
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* OpenPipe API
|
||||
* The public API for reporting API calls to OpenPipe
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = function (d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
||||
return extendStatics(d, b);
|
||||
};
|
||||
return function (d, b) {
|
||||
if (typeof b !== "function" && b !== null)
|
||||
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.RequiredError = exports.BaseAPI = exports.COLLECTION_FORMATS = exports.BASE_PATH = void 0;
|
||||
var axios_1 = require("axios");
|
||||
exports.BASE_PATH = "https://app.openpipe.ai/api".replace(/\/+$/, "");
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
exports.COLLECTION_FORMATS = {
|
||||
csv: ",",
|
||||
ssv: " ",
|
||||
tsv: "\t",
|
||||
pipes: "|",
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @class BaseAPI
|
||||
*/
|
||||
var BaseAPI = /** @class */ (function () {
|
||||
function BaseAPI(configuration, basePath, axios) {
|
||||
if (basePath === void 0) { basePath = exports.BASE_PATH; }
|
||||
if (axios === void 0) { axios = axios_1.default; }
|
||||
this.basePath = basePath;
|
||||
this.axios = axios;
|
||||
if (configuration) {
|
||||
this.configuration = configuration;
|
||||
this.basePath = configuration.basePath || this.basePath;
|
||||
}
|
||||
}
|
||||
return BaseAPI;
|
||||
}());
|
||||
exports.BaseAPI = BaseAPI;
|
||||
;
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @class RequiredError
|
||||
* @extends {Error}
|
||||
*/
|
||||
var RequiredError = /** @class */ (function (_super) {
|
||||
__extends(RequiredError, _super);
|
||||
function RequiredError(field, msg) {
|
||||
var _this = _super.call(this, msg) || this;
|
||||
_this.field = field;
|
||||
_this.name = "RequiredError";
|
||||
return _this;
|
||||
}
|
||||
return RequiredError;
|
||||
}(Error));
|
||||
exports.RequiredError = RequiredError;
|
||||
252
client-libs/typescript/codegen/common.js
Normal file
252
client-libs/typescript/codegen/common.js
Normal file
@@ -0,0 +1,252 @@
|
||||
"use strict";
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* OpenPipe API
|
||||
* The public API for reporting API calls to OpenPipe
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
var __assign = (this && this.__assign) || function () {
|
||||
__assign = Object.assign || function(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||
t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createRequestFunction = exports.toPathString = exports.serializeDataIfNeeded = exports.setSearchParams = exports.setOAuthToObject = exports.setBearerAuthToObject = exports.setBasicAuthToObject = exports.setApiKeyToObject = exports.assertParamExists = exports.DUMMY_BASE_URL = void 0;
|
||||
var base_1 = require("./base");
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
exports.DUMMY_BASE_URL = 'https://example.com';
|
||||
/**
|
||||
*
|
||||
* @throws {RequiredError}
|
||||
* @export
|
||||
*/
|
||||
var assertParamExists = function (functionName, paramName, paramValue) {
|
||||
if (paramValue === null || paramValue === undefined) {
|
||||
throw new base_1.RequiredError(paramName, "Required parameter ".concat(paramName, " was null or undefined when calling ").concat(functionName, "."));
|
||||
}
|
||||
};
|
||||
exports.assertParamExists = assertParamExists;
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
var setApiKeyToObject = function (object, keyParamName, configuration) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var localVarApiKeyValue, _a;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
if (!(configuration && configuration.apiKey)) return [3 /*break*/, 5];
|
||||
if (!(typeof configuration.apiKey === 'function')) return [3 /*break*/, 2];
|
||||
return [4 /*yield*/, configuration.apiKey(keyParamName)];
|
||||
case 1:
|
||||
_a = _b.sent();
|
||||
return [3 /*break*/, 4];
|
||||
case 2: return [4 /*yield*/, configuration.apiKey];
|
||||
case 3:
|
||||
_a = _b.sent();
|
||||
_b.label = 4;
|
||||
case 4:
|
||||
localVarApiKeyValue = _a;
|
||||
object[keyParamName] = localVarApiKeyValue;
|
||||
_b.label = 5;
|
||||
case 5: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
exports.setApiKeyToObject = setApiKeyToObject;
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
var setBasicAuthToObject = function (object, configuration) {
|
||||
if (configuration && (configuration.username || configuration.password)) {
|
||||
object["auth"] = { username: configuration.username, password: configuration.password };
|
||||
}
|
||||
};
|
||||
exports.setBasicAuthToObject = setBasicAuthToObject;
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
var setBearerAuthToObject = function (object, configuration) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var accessToken, _a;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
if (!(configuration && configuration.accessToken)) return [3 /*break*/, 5];
|
||||
if (!(typeof configuration.accessToken === 'function')) return [3 /*break*/, 2];
|
||||
return [4 /*yield*/, configuration.accessToken()];
|
||||
case 1:
|
||||
_a = _b.sent();
|
||||
return [3 /*break*/, 4];
|
||||
case 2: return [4 /*yield*/, configuration.accessToken];
|
||||
case 3:
|
||||
_a = _b.sent();
|
||||
_b.label = 4;
|
||||
case 4:
|
||||
accessToken = _a;
|
||||
object["Authorization"] = "Bearer " + accessToken;
|
||||
_b.label = 5;
|
||||
case 5: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
exports.setBearerAuthToObject = setBearerAuthToObject;
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
var setOAuthToObject = function (object, name, scopes, configuration) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var localVarAccessTokenValue, _a;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
if (!(configuration && configuration.accessToken)) return [3 /*break*/, 5];
|
||||
if (!(typeof configuration.accessToken === 'function')) return [3 /*break*/, 2];
|
||||
return [4 /*yield*/, configuration.accessToken(name, scopes)];
|
||||
case 1:
|
||||
_a = _b.sent();
|
||||
return [3 /*break*/, 4];
|
||||
case 2: return [4 /*yield*/, configuration.accessToken];
|
||||
case 3:
|
||||
_a = _b.sent();
|
||||
_b.label = 4;
|
||||
case 4:
|
||||
localVarAccessTokenValue = _a;
|
||||
object["Authorization"] = "Bearer " + localVarAccessTokenValue;
|
||||
_b.label = 5;
|
||||
case 5: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
exports.setOAuthToObject = setOAuthToObject;
|
||||
function setFlattenedQueryParams(urlSearchParams, parameter, key) {
|
||||
if (key === void 0) { key = ""; }
|
||||
if (parameter == null)
|
||||
return;
|
||||
if (typeof parameter === "object") {
|
||||
if (Array.isArray(parameter)) {
|
||||
parameter.forEach(function (item) { return setFlattenedQueryParams(urlSearchParams, item, key); });
|
||||
}
|
||||
else {
|
||||
Object.keys(parameter).forEach(function (currentKey) {
|
||||
return setFlattenedQueryParams(urlSearchParams, parameter[currentKey], "".concat(key).concat(key !== '' ? '.' : '').concat(currentKey));
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (urlSearchParams.has(key)) {
|
||||
urlSearchParams.append(key, parameter);
|
||||
}
|
||||
else {
|
||||
urlSearchParams.set(key, parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
var setSearchParams = function (url) {
|
||||
var objects = [];
|
||||
for (var _i = 1; _i < arguments.length; _i++) {
|
||||
objects[_i - 1] = arguments[_i];
|
||||
}
|
||||
var searchParams = new URLSearchParams(url.search);
|
||||
setFlattenedQueryParams(searchParams, objects);
|
||||
url.search = searchParams.toString();
|
||||
};
|
||||
exports.setSearchParams = setSearchParams;
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
var serializeDataIfNeeded = function (value, requestOptions, configuration) {
|
||||
var nonString = typeof value !== 'string';
|
||||
var needsSerialization = nonString && configuration && configuration.isJsonMime
|
||||
? configuration.isJsonMime(requestOptions.headers['Content-Type'])
|
||||
: nonString;
|
||||
return needsSerialization
|
||||
? JSON.stringify(value !== undefined ? value : {})
|
||||
: (value || "");
|
||||
};
|
||||
exports.serializeDataIfNeeded = serializeDataIfNeeded;
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
var toPathString = function (url) {
|
||||
return url.pathname + url.search + url.hash;
|
||||
};
|
||||
exports.toPathString = toPathString;
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
var createRequestFunction = function (axiosArgs, globalAxios, BASE_PATH, configuration) {
|
||||
return function (axios, basePath) {
|
||||
if (axios === void 0) { axios = globalAxios; }
|
||||
if (basePath === void 0) { basePath = BASE_PATH; }
|
||||
var axiosRequestArgs = __assign(__assign({}, axiosArgs.options), { url: ((configuration === null || configuration === void 0 ? void 0 : configuration.basePath) || basePath) + axiosArgs.url });
|
||||
return axios.request(axiosRequestArgs);
|
||||
};
|
||||
};
|
||||
exports.createRequestFunction = createRequestFunction;
|
||||
44
client-libs/typescript/codegen/configuration.js
Normal file
44
client-libs/typescript/codegen/configuration.js
Normal file
@@ -0,0 +1,44 @@
|
||||
"use strict";
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* OpenPipe API
|
||||
* The public API for reporting API calls to OpenPipe
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Configuration = void 0;
|
||||
var Configuration = /** @class */ (function () {
|
||||
function Configuration(param) {
|
||||
if (param === void 0) { param = {}; }
|
||||
this.apiKey = param.apiKey;
|
||||
this.username = param.username;
|
||||
this.password = param.password;
|
||||
this.accessToken = param.accessToken;
|
||||
this.basePath = param.basePath;
|
||||
this.baseOptions = param.baseOptions;
|
||||
this.formDataCtor = param.formDataCtor;
|
||||
}
|
||||
/**
|
||||
* Check if the given MIME is a JSON MIME.
|
||||
* JSON MIME examples:
|
||||
* application/json
|
||||
* application/json; charset=UTF8
|
||||
* APPLICATION/JSON
|
||||
* application/vnd.company+json
|
||||
* @param mime - MIME (Multipurpose Internet Mail Extensions)
|
||||
* @return True if the given MIME is JSON, false otherwise.
|
||||
*/
|
||||
Configuration.prototype.isJsonMime = function (mime) {
|
||||
var jsonMime = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i');
|
||||
return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json');
|
||||
};
|
||||
return Configuration;
|
||||
}());
|
||||
exports.Configuration = Configuration;
|
||||
31
client-libs/typescript/codegen/index.js
Normal file
31
client-libs/typescript/codegen/index.js
Normal file
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* OpenPipe API
|
||||
* The public API for reporting API calls to OpenPipe
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__exportStar(require("./api"), exports);
|
||||
__exportStar(require("./configuration"), exports);
|
||||
@@ -13,11 +13,7 @@ type OPConfigurationParameters = {
|
||||
export class Configuration extends openai.Configuration {
|
||||
public qkConfig?: openPipeClient.Configuration;
|
||||
|
||||
constructor(
|
||||
config: openai.ConfigurationParameters & {
|
||||
opParameters?: OPConfigurationParameters;
|
||||
}
|
||||
) {
|
||||
constructor(config: openai.ConfigurationParameters & { opParameters?: OPConfigurationParameters }) {
|
||||
super(config);
|
||||
if (config.opParameters) {
|
||||
this.qkConfig = new openPipeClient.Configuration(config.opParameters);
|
||||
@@ -25,9 +21,7 @@ export class Configuration extends openai.Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
type CreateChatCompletion = InstanceType<
|
||||
typeof openai.OpenAIApi
|
||||
>["createChatCompletion"];
|
||||
type CreateChatCompletion = InstanceType<typeof openai.OpenAIApi>["createChatCompletion"];
|
||||
|
||||
export class OpenAIApi extends openai.OpenAIApi {
|
||||
public openPipeApi?: openPipeClient.DefaultApi;
|
||||
@@ -43,44 +37,41 @@ export class OpenAIApi extends openai.OpenAIApi {
|
||||
createChatCompletionRequest: Parameters<CreateChatCompletion>[0],
|
||||
options?: Parameters<CreateChatCompletion>[1]
|
||||
): ReturnType<CreateChatCompletion> {
|
||||
const requestedAt = Date.now();
|
||||
const startTime = Date.now();
|
||||
let resp: Awaited<ReturnType<CreateChatCompletion>> | null = null;
|
||||
let respPayload: openai.CreateChatCompletionResponse | null = null;
|
||||
let statusCode: number | undefined = undefined;
|
||||
let errorMessage: string | undefined;
|
||||
let respStatus: number | undefined = undefined;
|
||||
let error: string | undefined;
|
||||
try {
|
||||
resp = await super.createChatCompletion(
|
||||
createChatCompletionRequest,
|
||||
options
|
||||
);
|
||||
resp = await super.createChatCompletion(createChatCompletionRequest, options);
|
||||
respPayload = resp.data;
|
||||
statusCode = resp.status;
|
||||
respStatus = resp.status;
|
||||
} catch (err) {
|
||||
console.error("Error in createChatCompletion");
|
||||
if ("isAxiosError" in err && err.isAxiosError) {
|
||||
errorMessage = err.response?.data?.error?.message;
|
||||
error = err.response?.data?.error?.message;
|
||||
respPayload = err.response?.data;
|
||||
statusCode = err.response?.status;
|
||||
respStatus = err.response?.status;
|
||||
} else if ("message" in err) {
|
||||
errorMessage = err.message.toString();
|
||||
error = err.message.toString();
|
||||
}
|
||||
throw err;
|
||||
} finally {
|
||||
this.openPipeApi
|
||||
?.externalApiReport({
|
||||
requestedAt,
|
||||
receivedAt: Date.now(),
|
||||
startTime,
|
||||
endTime: Date.now(),
|
||||
reqPayload: createChatCompletionRequest,
|
||||
respPayload: respPayload,
|
||||
statusCode: statusCode,
|
||||
errorMessage,
|
||||
respStatus: respStatus,
|
||||
error,
|
||||
tags: {
|
||||
client: "openai-js",
|
||||
clientVersion: version,
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Error reporting to OP", err);
|
||||
console.error("Error reporting to QK", err);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ export class OpenAI extends openai.OpenAI {
|
||||
|
||||
constructor({
|
||||
openPipeApiKey = readEnv("OPENPIPE_API_KEY"),
|
||||
openPipeBaseUrl = readEnv("OPENPIPE_BASE_URL") ?? `https://app.openpipe.ai/v1`,
|
||||
openPipeBaseUrl = readEnv("OPENPIPE_BASE_URL") ??
|
||||
`https://app.openpipe.ai/v1`,
|
||||
...opts
|
||||
}: ClientOptions = {}) {
|
||||
super({ ...opts });
|
||||
@@ -25,7 +26,7 @@ export class OpenAI extends openai.OpenAI {
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: openPipeBaseUrl,
|
||||
headers: {
|
||||
Authorization: `Bearer ${openPipeApiKey}`,
|
||||
'x-openpipe-api-key': openPipeApiKey,
|
||||
},
|
||||
});
|
||||
this.openPipeApi = new openPipeClient.DefaultApi(
|
||||
@@ -87,18 +88,18 @@ class ExtendedCompletions extends openai.OpenAI.Chat.Completions {
|
||||
console.log("Doing post API call for Streaming...");
|
||||
return result;
|
||||
} else {
|
||||
const requestedAt = Date.now();
|
||||
const startTime = Date.now();
|
||||
const result = await super.create(
|
||||
params as CompletionCreateParams.CreateChatCompletionRequestNonStreaming,
|
||||
options
|
||||
);
|
||||
await this.openaiInstance.openPipeApi?.externalApiReport({
|
||||
requestedAt,
|
||||
receivedAt: Date.now(),
|
||||
startTime,
|
||||
endTime: Date.now(),
|
||||
reqPayload: params,
|
||||
respPayload: result,
|
||||
statusCode: 200,
|
||||
errorMessage: undefined,
|
||||
respStatus: 200,
|
||||
error: undefined,
|
||||
tags,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user