Compare commits
11 Commits
styling
...
more-js-ap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f4e7f7e2e | ||
|
|
5da62fdc29 | ||
|
|
754e273049 | ||
|
|
2863dc2f89 | ||
|
|
c4cef35717 | ||
|
|
8552baf632 | ||
|
|
f41e2229ca | ||
|
|
e649f42c9c | ||
|
|
99f305483b | ||
|
|
b28f4cad57 | ||
|
|
df4a3a0950 |
5
.dockerignore
Normal file
5
.dockerignore
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
**/node_modules/
|
||||||
|
.git
|
||||||
|
**/.venv/
|
||||||
|
**/.env*
|
||||||
|
**/.next/
|
||||||
@@ -32,5 +32,5 @@ NEXT_PUBLIC_HOST="http://localhost:3000"
|
|||||||
GITHUB_CLIENT_ID="your_client_id"
|
GITHUB_CLIENT_ID="your_client_id"
|
||||||
GITHUB_CLIENT_SECRET="your_secret"
|
GITHUB_CLIENT_SECRET="your_secret"
|
||||||
|
|
||||||
OPENPIPE_BASE_URL="http://localhost:3000/api"
|
OPENPIPE_BASE_URL="http://localhost:3000/api/v1"
|
||||||
OPENPIPE_API_KEY="your_key"
|
OPENPIPE_API_KEY="your_key"
|
||||||
|
|||||||
5
app/@types/nextjs-routes.d.ts
vendored
5
app/@types/nextjs-routes.d.ts
vendored
@@ -12,12 +12,11 @@ declare module "nextjs-routes" {
|
|||||||
|
|
||||||
export type Route =
|
export type Route =
|
||||||
| StaticRoute<"/account/signin">
|
| StaticRoute<"/account/signin">
|
||||||
| DynamicRoute<"/api/[...trpc]", { "trpc": string[] }>
|
|
||||||
| DynamicRoute<"/api/auth/[...nextauth]", { "nextauth": string[] }>
|
| DynamicRoute<"/api/auth/[...nextauth]", { "nextauth": string[] }>
|
||||||
| StaticRoute<"/api/experiments/og-image">
|
| StaticRoute<"/api/experiments/og-image">
|
||||||
| StaticRoute<"/api/openapi">
|
|
||||||
| StaticRoute<"/api/sentry-example-api">
|
|
||||||
| DynamicRoute<"/api/trpc/[trpc]", { "trpc": string }>
|
| DynamicRoute<"/api/trpc/[trpc]", { "trpc": string }>
|
||||||
|
| DynamicRoute<"/api/v1/[...trpc]", { "trpc": string[] }>
|
||||||
|
| StaticRoute<"/api/v1/openapi">
|
||||||
| StaticRoute<"/dashboard">
|
| StaticRoute<"/dashboard">
|
||||||
| DynamicRoute<"/data/[id]", { "id": string }>
|
| DynamicRoute<"/data/[id]", { "id": string }>
|
||||||
| StaticRoute<"/data">
|
| StaticRoute<"/data">
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ RUN yarn global add pnpm
|
|||||||
# DEPS
|
# DEPS
|
||||||
FROM base as deps
|
FROM base as deps
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /code
|
||||||
|
|
||||||
COPY prisma ./
|
COPY app/prisma app/package.json ./app/
|
||||||
|
COPY client-libs/typescript/package.json ./client-libs/typescript/
|
||||||
|
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||||
|
|
||||||
COPY package.json pnpm-lock.yaml ./
|
RUN cd app && pnpm install --frozen-lockfile
|
||||||
|
|
||||||
RUN pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
# BUILDER
|
# BUILDER
|
||||||
FROM base as builder
|
FROM base as builder
|
||||||
@@ -25,22 +25,24 @@ ARG NEXT_PUBLIC_SENTRY_DSN
|
|||||||
ARG SENTRY_AUTH_TOKEN
|
ARG SENTRY_AUTH_TOKEN
|
||||||
ARG NEXT_PUBLIC_FF_SHOW_LOGGED_CALLS
|
ARG NEXT_PUBLIC_FF_SHOW_LOGGED_CALLS
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /code
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
COPY --from=deps /code/node_modules ./node_modules
|
||||||
|
COPY --from=deps /code/app/node_modules ./app/node_modules
|
||||||
|
COPY --from=deps /code/client-libs/typescript/node_modules ./client-libs/typescript/node_modules
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN SKIP_ENV_VALIDATION=1 pnpm build
|
RUN cd app && SKIP_ENV_VALIDATION=1 pnpm build
|
||||||
|
|
||||||
# RUNNER
|
# RUNNER
|
||||||
FROM base as runner
|
FROM base as runner
|
||||||
WORKDIR /app
|
WORKDIR /code/app
|
||||||
|
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
|
||||||
COPY --from=builder /app/ ./
|
COPY --from=builder /code/ /code/
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
ENV PORT 3000
|
ENV PORT 3000
|
||||||
|
|
||||||
# Run the "run-prod.sh" script
|
# Run the "run-prod.sh" script
|
||||||
CMD /app/run-prod.sh
|
CMD /code/app/run-prod.sh
|
||||||
@@ -72,6 +72,7 @@
|
|||||||
"nextjs-cors": "^2.1.2",
|
"nextjs-cors": "^2.1.2",
|
||||||
"nextjs-routes": "^2.0.1",
|
"nextjs-routes": "^2.0.1",
|
||||||
"openai": "4.0.0-beta.7",
|
"openai": "4.0.0-beta.7",
|
||||||
|
"openpipe": "workspace:*",
|
||||||
"pg": "^8.11.2",
|
"pg": "^8.11.2",
|
||||||
"pluralize": "^8.0.0",
|
"pluralize": "^8.0.0",
|
||||||
"posthog-js": "^1.75.3",
|
"posthog-js": "^1.75.3",
|
||||||
@@ -100,8 +101,7 @@
|
|||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"vite-tsconfig-paths": "^4.2.0",
|
"vite-tsconfig-paths": "^4.2.0",
|
||||||
"zod": "^3.21.4",
|
"zod": "^3.21.4",
|
||||||
"zustand": "^4.3.9",
|
"zustand": "^4.3.9"
|
||||||
"openpipe": "workspace:*"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@openapi-contrib/openapi-schema-to-json-schema": "^4.0.5",
|
"@openapi-contrib/openapi-schema-to-json-schema": "^4.0.5",
|
||||||
@@ -129,6 +129,7 @@
|
|||||||
"eslint-plugin-unused-imports": "^2.0.0",
|
"eslint-plugin-unused-imports": "^2.0.0",
|
||||||
"monaco-editor": "^0.40.0",
|
"monaco-editor": "^0.40.0",
|
||||||
"openapi-typescript": "^6.3.4",
|
"openapi-typescript": "^6.3.4",
|
||||||
|
"openapi-typescript-codegen": "^0.25.0",
|
||||||
"prisma": "^4.14.0",
|
"prisma": "^4.14.0",
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
|
|||||||
@@ -341,7 +341,7 @@ model ApiKey {
|
|||||||
apiKey String @unique
|
apiKey String @unique
|
||||||
|
|
||||||
projectId String @db.Uuid
|
projectId String @db.Uuid
|
||||||
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { prisma } from "~/server/db";
|
|||||||
import dedent from "dedent";
|
import dedent from "dedent";
|
||||||
import { generateNewCell } from "~/server/utils/generateNewCell";
|
import { generateNewCell } from "~/server/utils/generateNewCell";
|
||||||
import { promptConstructorVersion } from "~/promptConstructor/version";
|
import { promptConstructorVersion } from "~/promptConstructor/version";
|
||||||
|
import { env } from "~/env.mjs";
|
||||||
|
|
||||||
const defaultId = "11111111-1111-1111-1111-111111111111";
|
const defaultId = "11111111-1111-1111-1111-111111111111";
|
||||||
|
|
||||||
@@ -16,6 +17,16 @@ const project =
|
|||||||
data: { id: defaultId },
|
data: { id: defaultId },
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
if (env.OPENPIPE_API_KEY) {
|
||||||
|
await prisma.apiKey.create({
|
||||||
|
data: {
|
||||||
|
projectId: project.id,
|
||||||
|
name: "Default API Key",
|
||||||
|
apiKey: env.OPENPIPE_API_KEY,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await prisma.experiment.deleteMany({
|
await prisma.experiment.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
id: defaultId,
|
id: defaultId,
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default function OutputCell({
|
|||||||
|
|
||||||
if (!templateHasVariables) disabledReason = "Add a value to the scenario variables to see output";
|
if (!templateHasVariables) disabledReason = "Add a value to the scenario variables to see output";
|
||||||
|
|
||||||
const [refetchInterval, setRefetchInterval] = useState(0);
|
const [refetchInterval, setRefetchInterval] = useState<number | false>(false);
|
||||||
const { data: cell, isLoading: queryLoading } = api.scenarioVariantCells.get.useQuery(
|
const { data: cell, isLoading: queryLoading } = api.scenarioVariantCells.get.useQuery(
|
||||||
{ scenarioId: scenario.id, variantId: variant.id },
|
{ scenarioId: scenario.id, variantId: variant.id },
|
||||||
{ refetchInterval },
|
{ refetchInterval },
|
||||||
@@ -64,7 +64,8 @@ export default function OutputCell({
|
|||||||
cell.retrievalStatus === "PENDING" ||
|
cell.retrievalStatus === "PENDING" ||
|
||||||
cell.retrievalStatus === "IN_PROGRESS" ||
|
cell.retrievalStatus === "IN_PROGRESS" ||
|
||||||
hardRefetching;
|
hardRefetching;
|
||||||
useEffect(() => setRefetchInterval(awaitingOutput ? 1000 : 0), [awaitingOutput]);
|
|
||||||
|
useEffect(() => setRefetchInterval(awaitingOutput ? 1000 : false), [awaitingOutput]);
|
||||||
|
|
||||||
// TODO: disconnect from socket if we're not streaming anymore
|
// TODO: disconnect from socket if we're not streaming anymore
|
||||||
const streamedMessage = useSocket<OutputSchema>(cell?.id);
|
const streamedMessage = useSocket<OutputSchema>(cell?.id);
|
||||||
@@ -120,8 +121,13 @@ export default function OutputCell({
|
|||||||
? response.receivedAt.getTime()
|
? response.receivedAt.getTime()
|
||||||
: Date.now();
|
: Date.now();
|
||||||
if (response.requestedAt) {
|
if (response.requestedAt) {
|
||||||
numWaitingMessages = Math.floor(
|
numWaitingMessages = Math.min(
|
||||||
|
Math.floor(
|
||||||
(relativeWaitingTime - response.requestedAt.getTime()) / WAITING_MESSAGE_INTERVAL,
|
(relativeWaitingTime - response.requestedAt.getTime()) / WAITING_MESSAGE_INTERVAL,
|
||||||
|
),
|
||||||
|
// Don't try to render more than 15, it'll use too much CPU and
|
||||||
|
// break the page
|
||||||
|
15,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -21,14 +21,18 @@ export default function VariantStats(props: { variant: PromptVariant }) {
|
|||||||
outputTokens: 0,
|
outputTokens: 0,
|
||||||
scenarioCount: 0,
|
scenarioCount: 0,
|
||||||
outputCount: 0,
|
outputCount: 0,
|
||||||
|
awaitingCompletions: false,
|
||||||
awaitingEvals: false,
|
awaitingEvals: false,
|
||||||
},
|
},
|
||||||
refetchInterval,
|
refetchInterval,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Poll every two seconds while we are waiting for LLM retrievals to finish
|
// Poll every five seconds while we are waiting for LLM retrievals to finish
|
||||||
useEffect(() => setRefetchInterval(data.awaitingEvals ? 5000 : 0), [data.awaitingEvals]);
|
useEffect(
|
||||||
|
() => setRefetchInterval(data.awaitingCompletions || data.awaitingEvals ? 5000 : 0),
|
||||||
|
[data.awaitingCompletions, data.awaitingEvals],
|
||||||
|
);
|
||||||
|
|
||||||
const [passColor, neutralColor, failColor] = useToken("colors", [
|
const [passColor, neutralColor, failColor] = useToken("colors", [
|
||||||
"green.500",
|
"green.500",
|
||||||
|
|||||||
@@ -1,54 +1,10 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
import {
|
import { isArray, isString } from "lodash-es";
|
||||||
type ChatCompletionChunk,
|
|
||||||
type ChatCompletion,
|
|
||||||
type CompletionCreateParams,
|
|
||||||
} from "openai/resources/chat";
|
|
||||||
import { type CompletionResponse } from "../types";
|
|
||||||
import { isArray, isString, omit } from "lodash-es";
|
|
||||||
import { openai } from "~/server/utils/openai";
|
|
||||||
import { APIError } from "openai";
|
import { APIError } from "openai";
|
||||||
|
import { type ChatCompletion, type CompletionCreateParams } from "openai/resources/chat";
|
||||||
const mergeStreamedChunks = (
|
import mergeChunks from "openpipe/src/openai/mergeChunks";
|
||||||
base: ChatCompletion | null,
|
import { openai } from "~/server/utils/openai";
|
||||||
chunk: ChatCompletionChunk,
|
import { type CompletionResponse } from "../types";
|
||||||
): ChatCompletion => {
|
|
||||||
if (base === null) {
|
|
||||||
return mergeStreamedChunks({ ...chunk, choices: [] }, chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
const choices = [...base.choices];
|
|
||||||
for (const choice of chunk.choices) {
|
|
||||||
const baseChoice = choices.find((c) => c.index === choice.index);
|
|
||||||
if (baseChoice) {
|
|
||||||
baseChoice.finish_reason = choice.finish_reason ?? baseChoice.finish_reason;
|
|
||||||
baseChoice.message = baseChoice.message ?? { role: "assistant" };
|
|
||||||
|
|
||||||
if (choice.delta?.content)
|
|
||||||
baseChoice.message.content =
|
|
||||||
((baseChoice.message.content as string) ?? "") + (choice.delta.content ?? "");
|
|
||||||
if (choice.delta?.function_call) {
|
|
||||||
const fnCall = baseChoice.message.function_call ?? {};
|
|
||||||
fnCall.name =
|
|
||||||
((fnCall.name as string) ?? "") + ((choice.delta.function_call.name as string) ?? "");
|
|
||||||
fnCall.arguments =
|
|
||||||
((fnCall.arguments as string) ?? "") +
|
|
||||||
((choice.delta.function_call.arguments as string) ?? "");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// @ts-expect-error the types are correctly telling us that finish_reason
|
|
||||||
// could be null, but don't want to fix it right now.
|
|
||||||
choices.push({ ...omit(choice, "delta"), message: { role: "assistant", ...choice.delta } });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const merged: ChatCompletion = {
|
|
||||||
...base,
|
|
||||||
choices,
|
|
||||||
};
|
|
||||||
|
|
||||||
return merged;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function getCompletion(
|
export async function getCompletion(
|
||||||
input: CompletionCreateParams,
|
input: CompletionCreateParams,
|
||||||
@@ -59,7 +15,6 @@ export async function getCompletion(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (onStream) {
|
if (onStream) {
|
||||||
console.log("got started");
|
|
||||||
const resp = await openai.chat.completions.create(
|
const resp = await openai.chat.completions.create(
|
||||||
{ ...input, stream: true },
|
{ ...input, stream: true },
|
||||||
{
|
{
|
||||||
@@ -67,11 +22,9 @@ export async function getCompletion(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
for await (const part of resp) {
|
for await (const part of resp) {
|
||||||
console.log("got part", part);
|
finalCompletion = mergeChunks(finalCompletion, part);
|
||||||
finalCompletion = mergeStreamedChunks(finalCompletion, part);
|
|
||||||
onStream(finalCompletion);
|
onStream(finalCompletion);
|
||||||
}
|
}
|
||||||
console.log("got final", finalCompletion);
|
|
||||||
if (!finalCompletion) {
|
if (!finalCompletion) {
|
||||||
return {
|
return {
|
||||||
type: "error",
|
type: "error",
|
||||||
|
|||||||
@@ -120,9 +120,9 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
"Convert to function call": {
|
"Convert to function call": {
|
||||||
icon: TfiThought,
|
icon: TfiThought,
|
||||||
description: "Use function calls to get output from the model in a more structured way.",
|
description: "Use function calls to get output from the model in a more structured way.",
|
||||||
instructions: `OpenAI functions are a specialized way for an LLM to return output.
|
instructions: `OpenAI functions are a specialized way for an LLM to return its final output.
|
||||||
|
|
||||||
This is what a prompt looks like before adding a function:
|
Example 1 before:
|
||||||
|
|
||||||
definePrompt("openai/ChatCompletion", {
|
definePrompt("openai/ChatCompletion", {
|
||||||
model: "gpt-4",
|
model: "gpt-4",
|
||||||
@@ -139,7 +139,7 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
This is what one looks like after adding a function:
|
Example 1 after:
|
||||||
|
|
||||||
definePrompt("openai/ChatCompletion", {
|
definePrompt("openai/ChatCompletion", {
|
||||||
model: "gpt-4",
|
model: "gpt-4",
|
||||||
@@ -156,7 +156,7 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
],
|
],
|
||||||
functions: [
|
functions: [
|
||||||
{
|
{
|
||||||
name: "extract_sentiment",
|
name: "log_extracted_sentiment",
|
||||||
parameters: {
|
parameters: {
|
||||||
type: "object", // parameters must always be an object with a properties key
|
type: "object", // parameters must always be an object with a properties key
|
||||||
properties: { // properties key is required
|
properties: { // properties key is required
|
||||||
@@ -169,13 +169,13 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
function_call: {
|
function_call: {
|
||||||
name: "extract_sentiment",
|
name: "log_extracted_sentiment",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Here's another example of adding a function:
|
=========
|
||||||
|
|
||||||
Before:
|
Example 2 before:
|
||||||
|
|
||||||
definePrompt("openai/ChatCompletion", {
|
definePrompt("openai/ChatCompletion", {
|
||||||
model: "gpt-3.5-turbo",
|
model: "gpt-3.5-turbo",
|
||||||
@@ -197,7 +197,7 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
temperature: 0,
|
temperature: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
After:
|
Example 2 after:
|
||||||
|
|
||||||
definePrompt("openai/ChatCompletion", {
|
definePrompt("openai/ChatCompletion", {
|
||||||
model: "gpt-3.5-turbo",
|
model: "gpt-3.5-turbo",
|
||||||
@@ -215,7 +215,7 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
temperature: 0,
|
temperature: 0,
|
||||||
functions: [
|
functions: [
|
||||||
{
|
{
|
||||||
name: "score_post",
|
name: "log_post_score",
|
||||||
parameters: {
|
parameters: {
|
||||||
type: "object",
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
@@ -227,13 +227,13 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
function_call: {
|
function_call: {
|
||||||
name: "score_post",
|
name: "log_post_score",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Another example
|
=========
|
||||||
|
|
||||||
Before:
|
Example 3 before:
|
||||||
|
|
||||||
definePrompt("openai/ChatCompletion", {
|
definePrompt("openai/ChatCompletion", {
|
||||||
model: "gpt-3.5-turbo",
|
model: "gpt-3.5-turbo",
|
||||||
@@ -246,7 +246,7 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
After:
|
Example 3 after:
|
||||||
|
|
||||||
definePrompt("openai/ChatCompletion", {
|
definePrompt("openai/ChatCompletion", {
|
||||||
model: "gpt-3.5-turbo",
|
model: "gpt-3.5-turbo",
|
||||||
@@ -258,22 +258,25 @@ export const refinementActions: Record<string, RefinementAction> = {
|
|||||||
],
|
],
|
||||||
functions: [
|
functions: [
|
||||||
{
|
{
|
||||||
name: "write_in_language",
|
name: "log_translated_text",
|
||||||
parameters: {
|
parameters: {
|
||||||
type: "object",
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
text: {
|
translated_text: {
|
||||||
type: "string",
|
type: "string",
|
||||||
|
description: "The text, written in the language specified in the prompt",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
function_call: {
|
function_call: {
|
||||||
name: "write_in_language",
|
name: "log_translated_text",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
=========
|
||||||
|
|
||||||
Add an OpenAI function that takes one or more nested parameters that match the expected output from this prompt.`,
|
Add an OpenAI function that takes one or more nested parameters that match the expected output from this prompt.`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
// A faulty API route to test Sentry's error monitoring
|
|
||||||
// @ts-expect-error just a test file, don't care about types
|
|
||||||
export default function handler(_req, res) {
|
|
||||||
throw new Error("Sentry Example API Route Error");
|
|
||||||
res.status(200).json({ name: "John Doe" });
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,14 @@
|
|||||||
import { type NextApiRequest, type NextApiResponse } from "next";
|
import { type NextApiRequest, type NextApiResponse } from "next";
|
||||||
import cors from "nextjs-cors";
|
import cors from "nextjs-cors";
|
||||||
import { createOpenApiNextHandler } from "trpc-openapi";
|
import { createOpenApiNextHandler } from "trpc-openapi";
|
||||||
import { createProcedureCache } from "trpc-openapi/dist/adapters/node-http/procedures";
|
import { v1ApiRouter } from "~/server/api/external/v1Api.router";
|
||||||
import { appRouter } from "~/server/api/root.router";
|
import { createOpenApiContext } from "~/server/api/external/openApiTrpc";
|
||||||
import { createTRPCContext } from "~/server/api/trpc";
|
|
||||||
|
|
||||||
const openApiHandler = createOpenApiNextHandler({
|
const openApiHandler = createOpenApiNextHandler({
|
||||||
router: appRouter,
|
router: v1ApiRouter,
|
||||||
createContext: createTRPCContext,
|
createContext: createOpenApiContext,
|
||||||
});
|
});
|
||||||
|
|
||||||
const cache = createProcedureCache(appRouter);
|
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
// Setup CORS
|
// Setup CORS
|
||||||
await cors(req, res);
|
await cors(req, res);
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import { type NextApiRequest, type NextApiResponse } from "next";
|
import { type NextApiRequest, type NextApiResponse } from "next";
|
||||||
import { generateOpenApiDocument } from "trpc-openapi";
|
import { generateOpenApiDocument } from "trpc-openapi";
|
||||||
import { appRouter } from "~/server/api/root.router";
|
import { v1ApiRouter } from "~/server/api/external/v1Api.router";
|
||||||
|
|
||||||
export const openApiDocument = generateOpenApiDocument(appRouter, {
|
export const openApiDocument = generateOpenApiDocument(v1ApiRouter, {
|
||||||
title: "OpenPipe API",
|
title: "OpenPipe API",
|
||||||
description: "The public API for reporting API calls to OpenPipe",
|
description: "The public API for reporting API calls to OpenPipe",
|
||||||
version: "0.1.0",
|
version: "0.1.1",
|
||||||
baseUrl: "https://app.openpipe.ai/api",
|
baseUrl: "https://app.openpipe.ai/api/v1",
|
||||||
});
|
});
|
||||||
// Respond with our OpenAPI schema
|
// Respond with our OpenAPI schema
|
||||||
const hander = (req: NextApiRequest, res: NextApiResponse) => {
|
const hander = (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
95
app/src/server/api/external/openApiTrpc.ts
vendored
Normal file
95
app/src/server/api/external/openApiTrpc.ts
vendored
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import type { ApiKey, Project } from "@prisma/client";
|
||||||
|
import { TRPCError, initTRPC } from "@trpc/server";
|
||||||
|
import { type CreateNextContextOptions } from "@trpc/server/adapters/next";
|
||||||
|
import superjson from "superjson";
|
||||||
|
import { type OpenApiMeta } from "trpc-openapi";
|
||||||
|
import { ZodError } from "zod";
|
||||||
|
import { prisma } from "~/server/db";
|
||||||
|
|
||||||
|
type CreateContextOptions = {
|
||||||
|
key:
|
||||||
|
| (ApiKey & {
|
||||||
|
project: Project;
|
||||||
|
})
|
||||||
|
| null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This helper generates the "internals" for a tRPC context. If you need to use it, you can export
|
||||||
|
* it from here.
|
||||||
|
*
|
||||||
|
* Examples of things you may need it for:
|
||||||
|
* - testing, so we don't have to mock Next.js' req/res
|
||||||
|
* - tRPC's `createSSGHelpers`, where we don't have req/res
|
||||||
|
*
|
||||||
|
* @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts
|
||||||
|
*/
|
||||||
|
export const createInnerTRPCContext = (opts: CreateContextOptions) => {
|
||||||
|
return {
|
||||||
|
key: opts.key,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createOpenApiContext = async (opts: CreateNextContextOptions) => {
|
||||||
|
const { req, res } = opts;
|
||||||
|
|
||||||
|
const apiKey = req.headers.authorization?.split(" ")[1] as string | null;
|
||||||
|
|
||||||
|
if (!apiKey) {
|
||||||
|
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
|
}
|
||||||
|
const key = await prisma.apiKey.findUnique({
|
||||||
|
where: { apiKey },
|
||||||
|
include: { project: true },
|
||||||
|
});
|
||||||
|
if (!key) {
|
||||||
|
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return createInnerTRPCContext({
|
||||||
|
key,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TRPCContext = Awaited<ReturnType<typeof createOpenApiContext>>;
|
||||||
|
|
||||||
|
const t = initTRPC
|
||||||
|
.context<typeof createOpenApiContext>()
|
||||||
|
.meta<OpenApiMeta>()
|
||||||
|
.create({
|
||||||
|
transformer: superjson,
|
||||||
|
errorFormatter({ shape, error }) {
|
||||||
|
return {
|
||||||
|
...shape,
|
||||||
|
data: {
|
||||||
|
...shape.data,
|
||||||
|
zodError: error.cause instanceof ZodError ? error.cause.flatten() : null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const createOpenApiRouter = t.router;
|
||||||
|
|
||||||
|
export const openApiPublicProc = t.procedure;
|
||||||
|
|
||||||
|
/** Reusable middleware that enforces users are logged in before running the procedure. */
|
||||||
|
const enforceApiKey = t.middleware(async ({ ctx, next }) => {
|
||||||
|
if (!ctx.key) {
|
||||||
|
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return next({
|
||||||
|
ctx: { key: ctx.key },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protected (authenticated) procedure
|
||||||
|
*
|
||||||
|
* If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies
|
||||||
|
* the session is valid and guarantees `ctx.session.user` is not null.
|
||||||
|
*
|
||||||
|
* @see https://trpc.io/docs/procedures
|
||||||
|
*/
|
||||||
|
export const openApiProtectedProc = t.procedure.use(enforceApiKey);
|
||||||
@@ -2,9 +2,6 @@ import { type Prisma } from "@prisma/client";
|
|||||||
import { type JsonValue } from "type-fest";
|
import { type JsonValue } from "type-fest";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { TRPCError } from "@trpc/server";
|
|
||||||
|
|
||||||
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
|
|
||||||
import { prisma } from "~/server/db";
|
import { prisma } from "~/server/db";
|
||||||
import { hashRequest } from "~/server/utils/hashObject";
|
import { hashRequest } from "~/server/utils/hashObject";
|
||||||
import modelProvider from "~/modelProviders/openai-ChatCompletion";
|
import modelProvider from "~/modelProviders/openai-ChatCompletion";
|
||||||
@@ -12,6 +9,7 @@ import {
|
|||||||
type ChatCompletion,
|
type ChatCompletion,
|
||||||
type CompletionCreateParams,
|
type CompletionCreateParams,
|
||||||
} from "openai/resources/chat/completions";
|
} from "openai/resources/chat/completions";
|
||||||
|
import { createOpenApiRouter, openApiProtectedProc } from "./openApiTrpc";
|
||||||
|
|
||||||
const reqValidator = z.object({
|
const reqValidator = z.object({
|
||||||
model: z.string(),
|
model: z.string(),
|
||||||
@@ -28,12 +26,12 @@ const respValidator = z.object({
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const externalApiRouter = createTRPCRouter({
|
export const v1ApiRouter = createOpenApiRouter({
|
||||||
checkCache: publicProcedure
|
checkCache: openApiProtectedProc
|
||||||
.meta({
|
.meta({
|
||||||
openapi: {
|
openapi: {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
path: "/v1/check-cache",
|
path: "/check-cache",
|
||||||
description: "Check if a prompt is cached",
|
description: "Check if a prompt is cached",
|
||||||
protect: true,
|
protect: true,
|
||||||
},
|
},
|
||||||
@@ -47,7 +45,8 @@ export const externalApiRouter = createTRPCRouter({
|
|||||||
.optional()
|
.optional()
|
||||||
.describe(
|
.describe(
|
||||||
'Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }',
|
'Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }',
|
||||||
),
|
)
|
||||||
|
.default({}),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(
|
.output(
|
||||||
@@ -56,18 +55,8 @@ export const externalApiRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const apiKey = ctx.apiKey;
|
|
||||||
if (!apiKey) {
|
|
||||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
|
||||||
}
|
|
||||||
const key = await prisma.apiKey.findUnique({
|
|
||||||
where: { apiKey },
|
|
||||||
});
|
|
||||||
if (!key) {
|
|
||||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
|
||||||
}
|
|
||||||
const reqPayload = await reqValidator.spa(input.reqPayload);
|
const reqPayload = await reqValidator.spa(input.reqPayload);
|
||||||
const cacheKey = hashRequest(key.projectId, reqPayload as JsonValue);
|
const cacheKey = hashRequest(ctx.key.projectId, reqPayload as JsonValue);
|
||||||
|
|
||||||
const existingResponse = await prisma.loggedCallModelResponse.findFirst({
|
const existingResponse = await prisma.loggedCallModelResponse.findFirst({
|
||||||
where: { cacheKey },
|
where: { cacheKey },
|
||||||
@@ -79,23 +68,24 @@ export const externalApiRouter = createTRPCRouter({
|
|||||||
|
|
||||||
await prisma.loggedCall.create({
|
await prisma.loggedCall.create({
|
||||||
data: {
|
data: {
|
||||||
projectId: key.projectId,
|
projectId: ctx.key.projectId,
|
||||||
requestedAt: new Date(input.requestedAt),
|
requestedAt: new Date(input.requestedAt),
|
||||||
cacheHit: true,
|
cacheHit: true,
|
||||||
modelResponseId: existingResponse.id,
|
modelResponseId: existingResponse.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await createTags(existingResponse.originalLoggedCallId, input.tags);
|
||||||
return {
|
return {
|
||||||
respPayload: existingResponse.respPayload,
|
respPayload: existingResponse.respPayload,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
||||||
report: publicProcedure
|
report: openApiProtectedProc
|
||||||
.meta({
|
.meta({
|
||||||
openapi: {
|
openapi: {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
path: "/v1/report",
|
path: "/report",
|
||||||
description: "Report an API call",
|
description: "Report an API call",
|
||||||
protect: true,
|
protect: true,
|
||||||
},
|
},
|
||||||
@@ -113,26 +103,16 @@ export const externalApiRouter = createTRPCRouter({
|
|||||||
.optional()
|
.optional()
|
||||||
.describe(
|
.describe(
|
||||||
'Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }',
|
'Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }',
|
||||||
),
|
)
|
||||||
|
.default({}),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.void())
|
.output(z.object({ status: z.literal("ok") }))
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
console.log("GOT TAGS", input.tags);
|
|
||||||
const apiKey = ctx.apiKey;
|
|
||||||
if (!apiKey) {
|
|
||||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
|
||||||
}
|
|
||||||
const key = await prisma.apiKey.findUnique({
|
|
||||||
where: { apiKey },
|
|
||||||
});
|
|
||||||
if (!key) {
|
|
||||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
|
||||||
}
|
|
||||||
const reqPayload = await reqValidator.spa(input.reqPayload);
|
const reqPayload = await reqValidator.spa(input.reqPayload);
|
||||||
const respPayload = await respValidator.spa(input.respPayload);
|
const respPayload = await respValidator.spa(input.respPayload);
|
||||||
|
|
||||||
const requestHash = hashRequest(key.projectId, reqPayload as JsonValue);
|
const requestHash = hashRequest(ctx.key.projectId, reqPayload as JsonValue);
|
||||||
|
|
||||||
const newLoggedCallId = uuidv4();
|
const newLoggedCallId = uuidv4();
|
||||||
const newModelResponseId = uuidv4();
|
const newModelResponseId = uuidv4();
|
||||||
@@ -151,7 +131,7 @@ export const externalApiRouter = createTRPCRouter({
|
|||||||
prisma.loggedCall.create({
|
prisma.loggedCall.create({
|
||||||
data: {
|
data: {
|
||||||
id: newLoggedCallId,
|
id: newLoggedCallId,
|
||||||
projectId: key.projectId,
|
projectId: ctx.key.projectId,
|
||||||
requestedAt: new Date(input.requestedAt),
|
requestedAt: new Date(input.requestedAt),
|
||||||
cacheHit: false,
|
cacheHit: false,
|
||||||
model,
|
model,
|
||||||
@@ -185,14 +165,77 @@ export const externalApiRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const tagsToCreate = Object.entries(input.tags ?? {}).map(([name, value]) => ({
|
await createTags(newLoggedCallId, input.tags);
|
||||||
loggedCallId: newLoggedCallId,
|
return { status: "ok" };
|
||||||
// sanitize tags
|
}),
|
||||||
name: name.replaceAll(/[^a-zA-Z0-9_]/g, "_"),
|
localTestingOnlyGetLatestLoggedCall: openApiProtectedProc
|
||||||
|
.meta({
|
||||||
|
openapi: {
|
||||||
|
method: "GET",
|
||||||
|
path: "/local-testing-only-get-latest-logged-call",
|
||||||
|
description: "Get the latest logged call (only for local testing)",
|
||||||
|
protect: true, // Make sure to protect this endpoint
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.input(z.void())
|
||||||
|
.output(
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
createdAt: z.date(),
|
||||||
|
cacheHit: z.boolean(),
|
||||||
|
tags: z.record(z.string().nullable()),
|
||||||
|
modelResponse: z
|
||||||
|
.object({
|
||||||
|
id: z.string(),
|
||||||
|
statusCode: z.number().nullable(),
|
||||||
|
errorMessage: z.string().nullable(),
|
||||||
|
reqPayload: z.unknown(),
|
||||||
|
respPayload: z.unknown(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
)
|
||||||
|
.mutation(async ({ ctx }) => {
|
||||||
|
if (process.env.NODE_ENV === "production") {
|
||||||
|
throw new Error("This operation is not allowed in production environment");
|
||||||
|
}
|
||||||
|
|
||||||
|
const latestLoggedCall = await prisma.loggedCall.findFirst({
|
||||||
|
where: { projectId: ctx.key.projectId },
|
||||||
|
orderBy: { requestedAt: "desc" },
|
||||||
|
select: {
|
||||||
|
createdAt: true,
|
||||||
|
cacheHit: true,
|
||||||
|
tags: true,
|
||||||
|
modelResponse: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
statusCode: true,
|
||||||
|
errorMessage: true,
|
||||||
|
reqPayload: true,
|
||||||
|
respPayload: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
latestLoggedCall && {
|
||||||
|
...latestLoggedCall,
|
||||||
|
tags: Object.fromEntries(latestLoggedCall.tags.map((tag) => [tag.name, tag.value])),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
async function createTags(loggedCallId: string, tags: Record<string, string>) {
|
||||||
|
const tagsToCreate = Object.entries(tags).map(([name, value]) => ({
|
||||||
|
loggedCallId,
|
||||||
|
name: name.replaceAll(/[^a-zA-Z0-9_$]/g, "_"),
|
||||||
value,
|
value,
|
||||||
}));
|
}));
|
||||||
await prisma.loggedCallTag.createMany({
|
await prisma.loggedCallTag.createMany({
|
||||||
data: tagsToCreate,
|
data: tagsToCreate,
|
||||||
});
|
});
|
||||||
}),
|
}
|
||||||
});
|
|
||||||
@@ -8,7 +8,6 @@ import { evaluationsRouter } from "./routers/evaluations.router";
|
|||||||
import { worldChampsRouter } from "./routers/worldChamps.router";
|
import { worldChampsRouter } from "./routers/worldChamps.router";
|
||||||
import { datasetsRouter } from "./routers/datasets.router";
|
import { datasetsRouter } from "./routers/datasets.router";
|
||||||
import { datasetEntries } from "./routers/datasetEntries.router";
|
import { datasetEntries } from "./routers/datasetEntries.router";
|
||||||
import { externalApiRouter } from "./routers/externalApi.router";
|
|
||||||
import { projectsRouter } from "./routers/projects.router";
|
import { projectsRouter } from "./routers/projects.router";
|
||||||
import { dashboardRouter } from "./routers/dashboard.router";
|
import { dashboardRouter } from "./routers/dashboard.router";
|
||||||
import { loggedCallsRouter } from "./routers/loggedCalls.router";
|
import { loggedCallsRouter } from "./routers/loggedCalls.router";
|
||||||
@@ -31,7 +30,6 @@ export const appRouter = createTRPCRouter({
|
|||||||
projects: projectsRouter,
|
projects: projectsRouter,
|
||||||
dashboard: dashboardRouter,
|
dashboard: dashboardRouter,
|
||||||
loggedCalls: loggedCallsRouter,
|
loggedCalls: loggedCallsRouter,
|
||||||
externalApi: externalApiRouter,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// export type definition of API
|
// export type definition of API
|
||||||
|
|||||||
@@ -131,6 +131,8 @@ export const promptVariantsRouter = createTRPCRouter({
|
|||||||
const inputTokens = overallTokens._sum?.inputTokens ?? 0;
|
const inputTokens = overallTokens._sum?.inputTokens ?? 0;
|
||||||
const outputTokens = overallTokens._sum?.outputTokens ?? 0;
|
const outputTokens = overallTokens._sum?.outputTokens ?? 0;
|
||||||
|
|
||||||
|
const awaitingCompletions = outputCount < scenarioCount;
|
||||||
|
|
||||||
const awaitingEvals = !!evalResults.find(
|
const awaitingEvals = !!evalResults.find(
|
||||||
(result) => result.totalCount < scenarioCount * evals.length,
|
(result) => result.totalCount < scenarioCount * evals.length,
|
||||||
);
|
);
|
||||||
@@ -142,6 +144,7 @@ export const promptVariantsRouter = createTRPCRouter({
|
|||||||
overallCost: overallTokens._sum?.cost ?? 0,
|
overallCost: overallTokens._sum?.cost ?? 0,
|
||||||
scenarioCount,
|
scenarioCount,
|
||||||
outputCount,
|
outputCount,
|
||||||
|
awaitingCompletions,
|
||||||
awaitingEvals,
|
awaitingEvals,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import { capturePath } from "~/utils/analytics/serverAnalytics";
|
|||||||
|
|
||||||
type CreateContextOptions = {
|
type CreateContextOptions = {
|
||||||
session: Session | null;
|
session: Session | null;
|
||||||
apiKey: string | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
@@ -46,7 +45,6 @@ const noOp = () => {};
|
|||||||
export const createInnerTRPCContext = (opts: CreateContextOptions) => {
|
export const createInnerTRPCContext = (opts: CreateContextOptions) => {
|
||||||
return {
|
return {
|
||||||
session: opts.session,
|
session: opts.session,
|
||||||
apiKey: opts.apiKey,
|
|
||||||
prisma,
|
prisma,
|
||||||
markAccessControlRun: noOp,
|
markAccessControlRun: noOp,
|
||||||
};
|
};
|
||||||
@@ -64,11 +62,8 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => {
|
|||||||
// Get the session from the server using the getServerSession wrapper function
|
// Get the session from the server using the getServerSession wrapper function
|
||||||
const session = await getServerAuthSession({ req, res });
|
const session = await getServerAuthSession({ req, res });
|
||||||
|
|
||||||
const apiKey = req.headers.authorization?.split(" ")[1] as string | null;
|
|
||||||
|
|
||||||
return createInnerTRPCContext({
|
return createInnerTRPCContext({
|
||||||
session,
|
session,
|
||||||
apiKey,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import "dotenv/config";
|
import "dotenv/config";
|
||||||
import { openApiDocument } from "~/pages/api/openapi.json";
|
import { openApiDocument } from "~/pages/api/v1/openapi.json";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { execSync } from "child_process";
|
import { execSync } from "child_process";
|
||||||
|
import { generate } from "openapi-typescript-codegen";
|
||||||
|
|
||||||
const scriptPath = import.meta.url.replace("file://", "");
|
const scriptPath = import.meta.url.replace("file://", "");
|
||||||
const clientLibsPath = path.join(path.dirname(scriptPath), "../../../../client-libs");
|
const clientLibsPath = path.join(path.dirname(scriptPath), "../../../../client-libs");
|
||||||
@@ -18,13 +19,20 @@ console.log("Generating TypeScript client");
|
|||||||
const tsClientPath = path.join(clientLibsPath, "typescript/src/codegen");
|
const tsClientPath = path.join(clientLibsPath, "typescript/src/codegen");
|
||||||
|
|
||||||
fs.rmSync(tsClientPath, { recursive: true, force: true });
|
fs.rmSync(tsClientPath, { recursive: true, force: true });
|
||||||
|
fs.mkdirSync(tsClientPath, { recursive: true });
|
||||||
|
|
||||||
execSync(
|
await generate({
|
||||||
`pnpm dlx @openapitools/openapi-generator-cli generate -i "${schemaPath}" -g typescript-axios -o "${tsClientPath}"`,
|
input: openApiDocument,
|
||||||
{
|
output: tsClientPath,
|
||||||
stdio: "inherit",
|
clientName: "OPClient",
|
||||||
},
|
httpClient: "node",
|
||||||
);
|
});
|
||||||
|
// execSync(
|
||||||
|
// `pnpm run openapi generate --input "${schemaPath}" --output "${tsClientPath}" --name OPClient --client node`,
|
||||||
|
// {
|
||||||
|
// stdio: "inherit",
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
|
||||||
console.log("Generating Python client");
|
console.log("Generating Python client");
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ const requestUpdatedPromptFunction = async (
|
|||||||
originalModelProvider.inputSchema,
|
originalModelProvider.inputSchema,
|
||||||
null,
|
null,
|
||||||
2,
|
2,
|
||||||
)}\n\nDo not add any assistant messages.`,
|
)}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: "user",
|
role: "user",
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ import cryptoRandomString from "crypto-random-string";
|
|||||||
|
|
||||||
const KEY_LENGTH = 42;
|
const KEY_LENGTH = 42;
|
||||||
|
|
||||||
export const generateApiKey = () => `opc_${cryptoRandomString({ length: KEY_LENGTH })}`;
|
export const generateApiKey = () => `opk_${cryptoRandomString({ length: KEY_LENGTH })}`;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { type ClientOptions } from "openai";
|
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import OpenAI from "openpipe/src/openai";
|
import OpenAI, { type ClientOptions } from "openpipe/src/openai";
|
||||||
|
|
||||||
import { env } from "~/env.mjs";
|
import { env } from "~/env.mjs";
|
||||||
|
|
||||||
@@ -16,7 +15,13 @@ try {
|
|||||||
config = JSON.parse(jsonData.toString());
|
config = JSON.parse(jsonData.toString());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Set a dummy key so it doesn't fail at build time
|
// Set a dummy key so it doesn't fail at build time
|
||||||
config = { apiKey: env.OPENAI_API_KEY ?? "dummy-key" };
|
config = {
|
||||||
|
apiKey: env.OPENAI_API_KEY ?? "dummy-key",
|
||||||
|
openpipe: {
|
||||||
|
apiKey: env.OPENPIPE_API_KEY,
|
||||||
|
baseUrl: "http://localhost:3000/api/v1",
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// export const openai = env.OPENPIPE_API_KEY ? new OpenAI.OpenAI(config) : new OriginalOpenAI(config);
|
// export const openai = env.OPENPIPE_API_KEY ? new OpenAI.OpenAI(config) : new OriginalOpenAI(config);
|
||||||
|
|||||||
9
app/test-docker.sh
Executable file
9
app/test-docker.sh
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
source app/.env
|
||||||
|
|
||||||
|
docker build . --file app/Dockerfile
|
||||||
@@ -3,17 +3,17 @@
|
|||||||
"info": {
|
"info": {
|
||||||
"title": "OpenPipe API",
|
"title": "OpenPipe API",
|
||||||
"description": "The public API for reporting API calls to OpenPipe",
|
"description": "The public API for reporting API calls to OpenPipe",
|
||||||
"version": "0.1.0"
|
"version": "0.1.1"
|
||||||
},
|
},
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
"url": "https://app.openpipe.ai/api"
|
"url": "https://app.openpipe.ai/api/v1"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"/v1/check-cache": {
|
"/check-cache": {
|
||||||
"post": {
|
"post": {
|
||||||
"operationId": "externalApi-checkCache",
|
"operationId": "checkCache",
|
||||||
"description": "Check if a prompt is cached",
|
"description": "Check if a prompt is cached",
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
@@ -39,7 +39,8 @@
|
|||||||
"additionalProperties": {
|
"additionalProperties": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"description": "Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }"
|
"description": "Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }",
|
||||||
|
"default": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
@@ -74,9 +75,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/v1/report": {
|
"/report": {
|
||||||
"post": {
|
"post": {
|
||||||
"operationId": "externalApi-report",
|
"operationId": "report",
|
||||||
"description": "Report an API call",
|
"description": "Report an API call",
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
@@ -117,7 +118,8 @@
|
|||||||
"additionalProperties": {
|
"additionalProperties": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"description": "Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }"
|
"description": "Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }",
|
||||||
|
"default": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
@@ -135,7 +137,97 @@
|
|||||||
"description": "Successful response",
|
"description": "Successful response",
|
||||||
"content": {
|
"content": {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
"schema": {}
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ok"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"status"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"$ref": "#/components/responses/error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/local-testing-only-get-latest-logged-call": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "localTestingOnlyGetLatestLoggedCall",
|
||||||
|
"description": "Get the latest logged call (only for local testing)",
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Authorization": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"createdAt": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
},
|
||||||
|
"cacheHit": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"modelResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"statusCode": {
|
||||||
|
"type": "number",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"errorMessage": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"reqPayload": {},
|
||||||
|
"respPayload": {}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"statusCode",
|
||||||
|
"errorMessage"
|
||||||
|
],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"createdAt",
|
||||||
|
"cacheHit",
|
||||||
|
"tags",
|
||||||
|
"modelResponse"
|
||||||
|
],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import httpx
|
|||||||
|
|
||||||
from ... import errors
|
from ... import errors
|
||||||
from ...client import AuthenticatedClient, Client
|
from ...client import AuthenticatedClient, Client
|
||||||
from ...models.external_api_check_cache_json_body import ExternalApiCheckCacheJsonBody
|
from ...models.check_cache_json_body import CheckCacheJsonBody
|
||||||
from ...models.external_api_check_cache_response_200 import ExternalApiCheckCacheResponse200
|
from ...models.check_cache_response_200 import CheckCacheResponse200
|
||||||
from ...types import Response
|
from ...types import Response
|
||||||
|
|
||||||
|
|
||||||
def _get_kwargs(
|
def _get_kwargs(
|
||||||
*,
|
*,
|
||||||
json_body: ExternalApiCheckCacheJsonBody,
|
json_body: CheckCacheJsonBody,
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -20,16 +20,16 @@ def _get_kwargs(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"method": "post",
|
"method": "post",
|
||||||
"url": "/v1/check-cache",
|
"url": "/check-cache",
|
||||||
"json": json_json_body,
|
"json": json_json_body,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _parse_response(
|
def _parse_response(
|
||||||
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
||||||
) -> Optional[ExternalApiCheckCacheResponse200]:
|
) -> Optional[CheckCacheResponse200]:
|
||||||
if response.status_code == HTTPStatus.OK:
|
if response.status_code == HTTPStatus.OK:
|
||||||
response_200 = ExternalApiCheckCacheResponse200.from_dict(response.json())
|
response_200 = CheckCacheResponse200.from_dict(response.json())
|
||||||
|
|
||||||
return response_200
|
return response_200
|
||||||
if client.raise_on_unexpected_status:
|
if client.raise_on_unexpected_status:
|
||||||
@@ -40,7 +40,7 @@ def _parse_response(
|
|||||||
|
|
||||||
def _build_response(
|
def _build_response(
|
||||||
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
||||||
) -> Response[ExternalApiCheckCacheResponse200]:
|
) -> Response[CheckCacheResponse200]:
|
||||||
return Response(
|
return Response(
|
||||||
status_code=HTTPStatus(response.status_code),
|
status_code=HTTPStatus(response.status_code),
|
||||||
content=response.content,
|
content=response.content,
|
||||||
@@ -52,19 +52,19 @@ def _build_response(
|
|||||||
def sync_detailed(
|
def sync_detailed(
|
||||||
*,
|
*,
|
||||||
client: AuthenticatedClient,
|
client: AuthenticatedClient,
|
||||||
json_body: ExternalApiCheckCacheJsonBody,
|
json_body: CheckCacheJsonBody,
|
||||||
) -> Response[ExternalApiCheckCacheResponse200]:
|
) -> Response[CheckCacheResponse200]:
|
||||||
"""Check if a prompt is cached
|
"""Check if a prompt is cached
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
json_body (ExternalApiCheckCacheJsonBody):
|
json_body (CheckCacheJsonBody):
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Response[ExternalApiCheckCacheResponse200]
|
Response[CheckCacheResponse200]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
kwargs = _get_kwargs(
|
kwargs = _get_kwargs(
|
||||||
@@ -81,19 +81,19 @@ def sync_detailed(
|
|||||||
def sync(
|
def sync(
|
||||||
*,
|
*,
|
||||||
client: AuthenticatedClient,
|
client: AuthenticatedClient,
|
||||||
json_body: ExternalApiCheckCacheJsonBody,
|
json_body: CheckCacheJsonBody,
|
||||||
) -> Optional[ExternalApiCheckCacheResponse200]:
|
) -> Optional[CheckCacheResponse200]:
|
||||||
"""Check if a prompt is cached
|
"""Check if a prompt is cached
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
json_body (ExternalApiCheckCacheJsonBody):
|
json_body (CheckCacheJsonBody):
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
ExternalApiCheckCacheResponse200
|
CheckCacheResponse200
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return sync_detailed(
|
return sync_detailed(
|
||||||
@@ -105,19 +105,19 @@ def sync(
|
|||||||
async def asyncio_detailed(
|
async def asyncio_detailed(
|
||||||
*,
|
*,
|
||||||
client: AuthenticatedClient,
|
client: AuthenticatedClient,
|
||||||
json_body: ExternalApiCheckCacheJsonBody,
|
json_body: CheckCacheJsonBody,
|
||||||
) -> Response[ExternalApiCheckCacheResponse200]:
|
) -> Response[CheckCacheResponse200]:
|
||||||
"""Check if a prompt is cached
|
"""Check if a prompt is cached
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
json_body (ExternalApiCheckCacheJsonBody):
|
json_body (CheckCacheJsonBody):
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Response[ExternalApiCheckCacheResponse200]
|
Response[CheckCacheResponse200]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
kwargs = _get_kwargs(
|
kwargs = _get_kwargs(
|
||||||
@@ -132,19 +132,19 @@ async def asyncio_detailed(
|
|||||||
async def asyncio(
|
async def asyncio(
|
||||||
*,
|
*,
|
||||||
client: AuthenticatedClient,
|
client: AuthenticatedClient,
|
||||||
json_body: ExternalApiCheckCacheJsonBody,
|
json_body: CheckCacheJsonBody,
|
||||||
) -> Optional[ExternalApiCheckCacheResponse200]:
|
) -> Optional[CheckCacheResponse200]:
|
||||||
"""Check if a prompt is cached
|
"""Check if a prompt is cached
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
json_body (ExternalApiCheckCacheJsonBody):
|
json_body (CheckCacheJsonBody):
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
ExternalApiCheckCacheResponse200
|
CheckCacheResponse200
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -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)
|
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
from http import HTTPStatus
|
||||||
|
from typing import Any, Dict, Optional, Union
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
from ... import errors
|
||||||
|
from ...client import AuthenticatedClient, Client
|
||||||
|
from ...models.local_testing_only_get_latest_logged_call_response_200 import (
|
||||||
|
LocalTestingOnlyGetLatestLoggedCallResponse200,
|
||||||
|
)
|
||||||
|
from ...types import Response
|
||||||
|
|
||||||
|
|
||||||
|
def _get_kwargs() -> Dict[str, Any]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return {
|
||||||
|
"method": "get",
|
||||||
|
"url": "/local-testing-only-get-latest-logged-call",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_response(
|
||||||
|
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
||||||
|
) -> Optional[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]:
|
||||||
|
if response.status_code == HTTPStatus.OK:
|
||||||
|
_response_200 = response.json()
|
||||||
|
response_200: Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]
|
||||||
|
if _response_200 is None:
|
||||||
|
response_200 = None
|
||||||
|
else:
|
||||||
|
response_200 = LocalTestingOnlyGetLatestLoggedCallResponse200.from_dict(_response_200)
|
||||||
|
|
||||||
|
return response_200
|
||||||
|
if client.raise_on_unexpected_status:
|
||||||
|
raise errors.UnexpectedStatus(response.status_code, response.content)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _build_response(
|
||||||
|
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
||||||
|
) -> Response[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]:
|
||||||
|
return Response(
|
||||||
|
status_code=HTTPStatus(response.status_code),
|
||||||
|
content=response.content,
|
||||||
|
headers=response.headers,
|
||||||
|
parsed=_parse_response(client=client, response=response),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def sync_detailed(
|
||||||
|
*,
|
||||||
|
client: AuthenticatedClient,
|
||||||
|
) -> Response[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]:
|
||||||
|
"""Get the latest logged call (only for local testing)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]
|
||||||
|
"""
|
||||||
|
|
||||||
|
kwargs = _get_kwargs()
|
||||||
|
|
||||||
|
response = client.get_httpx_client().request(
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
return _build_response(client=client, response=response)
|
||||||
|
|
||||||
|
|
||||||
|
def sync(
|
||||||
|
*,
|
||||||
|
client: AuthenticatedClient,
|
||||||
|
) -> Optional[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]:
|
||||||
|
"""Get the latest logged call (only for local testing)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]
|
||||||
|
"""
|
||||||
|
|
||||||
|
return sync_detailed(
|
||||||
|
client=client,
|
||||||
|
).parsed
|
||||||
|
|
||||||
|
|
||||||
|
async def asyncio_detailed(
|
||||||
|
*,
|
||||||
|
client: AuthenticatedClient,
|
||||||
|
) -> Response[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]:
|
||||||
|
"""Get the latest logged call (only for local testing)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]
|
||||||
|
"""
|
||||||
|
|
||||||
|
kwargs = _get_kwargs()
|
||||||
|
|
||||||
|
response = await client.get_async_httpx_client().request(**kwargs)
|
||||||
|
|
||||||
|
return _build_response(client=client, response=response)
|
||||||
|
|
||||||
|
|
||||||
|
async def asyncio(
|
||||||
|
*,
|
||||||
|
client: AuthenticatedClient,
|
||||||
|
) -> Optional[Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]]:
|
||||||
|
"""Get the latest logged call (only for local testing)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||||
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[LocalTestingOnlyGetLatestLoggedCallResponse200]
|
||||||
|
"""
|
||||||
|
|
||||||
|
return (
|
||||||
|
await asyncio_detailed(
|
||||||
|
client=client,
|
||||||
|
)
|
||||||
|
).parsed
|
||||||
155
client-libs/python/openpipe/api_client/api/default/report.py
Normal file
155
client-libs/python/openpipe/api_client/api/default/report.py
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
from http import HTTPStatus
|
||||||
|
from typing import Any, Dict, Optional, Union
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
from ... import errors
|
||||||
|
from ...client import AuthenticatedClient, Client
|
||||||
|
from ...models.report_json_body import ReportJsonBody
|
||||||
|
from ...models.report_response_200 import ReportResponse200
|
||||||
|
from ...types import Response
|
||||||
|
|
||||||
|
|
||||||
|
def _get_kwargs(
|
||||||
|
*,
|
||||||
|
json_body: ReportJsonBody,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
json_json_body = json_body.to_dict()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"method": "post",
|
||||||
|
"url": "/report",
|
||||||
|
"json": json_json_body,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_response(
|
||||||
|
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
||||||
|
) -> Optional[ReportResponse200]:
|
||||||
|
if response.status_code == HTTPStatus.OK:
|
||||||
|
response_200 = ReportResponse200.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[ReportResponse200]:
|
||||||
|
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: ReportJsonBody,
|
||||||
|
) -> Response[ReportResponse200]:
|
||||||
|
"""Report an API call
|
||||||
|
|
||||||
|
Args:
|
||||||
|
json_body (ReportJsonBody):
|
||||||
|
|
||||||
|
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[ReportResponse200]
|
||||||
|
"""
|
||||||
|
|
||||||
|
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: ReportJsonBody,
|
||||||
|
) -> Optional[ReportResponse200]:
|
||||||
|
"""Report an API call
|
||||||
|
|
||||||
|
Args:
|
||||||
|
json_body (ReportJsonBody):
|
||||||
|
|
||||||
|
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:
|
||||||
|
ReportResponse200
|
||||||
|
"""
|
||||||
|
|
||||||
|
return sync_detailed(
|
||||||
|
client=client,
|
||||||
|
json_body=json_body,
|
||||||
|
).parsed
|
||||||
|
|
||||||
|
|
||||||
|
async def asyncio_detailed(
|
||||||
|
*,
|
||||||
|
client: AuthenticatedClient,
|
||||||
|
json_body: ReportJsonBody,
|
||||||
|
) -> Response[ReportResponse200]:
|
||||||
|
"""Report an API call
|
||||||
|
|
||||||
|
Args:
|
||||||
|
json_body (ReportJsonBody):
|
||||||
|
|
||||||
|
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[ReportResponse200]
|
||||||
|
"""
|
||||||
|
|
||||||
|
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: ReportJsonBody,
|
||||||
|
) -> Optional[ReportResponse200]:
|
||||||
|
"""Report an API call
|
||||||
|
|
||||||
|
Args:
|
||||||
|
json_body (ReportJsonBody):
|
||||||
|
|
||||||
|
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:
|
||||||
|
ReportResponse200
|
||||||
|
"""
|
||||||
|
|
||||||
|
return (
|
||||||
|
await asyncio_detailed(
|
||||||
|
client=client,
|
||||||
|
json_body=json_body,
|
||||||
|
)
|
||||||
|
).parsed
|
||||||
@@ -1,15 +1,29 @@
|
|||||||
""" Contains all the data models used in inputs/outputs """
|
""" Contains all the data models used in inputs/outputs """
|
||||||
|
|
||||||
from .external_api_check_cache_json_body import ExternalApiCheckCacheJsonBody
|
from .check_cache_json_body import CheckCacheJsonBody
|
||||||
from .external_api_check_cache_json_body_tags import ExternalApiCheckCacheJsonBodyTags
|
from .check_cache_json_body_tags import CheckCacheJsonBodyTags
|
||||||
from .external_api_check_cache_response_200 import ExternalApiCheckCacheResponse200
|
from .check_cache_response_200 import CheckCacheResponse200
|
||||||
from .external_api_report_json_body import ExternalApiReportJsonBody
|
from .local_testing_only_get_latest_logged_call_response_200 import LocalTestingOnlyGetLatestLoggedCallResponse200
|
||||||
from .external_api_report_json_body_tags import ExternalApiReportJsonBodyTags
|
from .local_testing_only_get_latest_logged_call_response_200_model_response import (
|
||||||
|
LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse,
|
||||||
|
)
|
||||||
|
from .local_testing_only_get_latest_logged_call_response_200_tags import (
|
||||||
|
LocalTestingOnlyGetLatestLoggedCallResponse200Tags,
|
||||||
|
)
|
||||||
|
from .report_json_body import ReportJsonBody
|
||||||
|
from .report_json_body_tags import ReportJsonBodyTags
|
||||||
|
from .report_response_200 import ReportResponse200
|
||||||
|
from .report_response_200_status import ReportResponse200Status
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
"ExternalApiCheckCacheJsonBody",
|
"CheckCacheJsonBody",
|
||||||
"ExternalApiCheckCacheJsonBodyTags",
|
"CheckCacheJsonBodyTags",
|
||||||
"ExternalApiCheckCacheResponse200",
|
"CheckCacheResponse200",
|
||||||
"ExternalApiReportJsonBody",
|
"LocalTestingOnlyGetLatestLoggedCallResponse200",
|
||||||
"ExternalApiReportJsonBodyTags",
|
"LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse",
|
||||||
|
"LocalTestingOnlyGetLatestLoggedCallResponse200Tags",
|
||||||
|
"ReportJsonBody",
|
||||||
|
"ReportJsonBodyTags",
|
||||||
|
"ReportResponse200",
|
||||||
|
"ReportResponse200Status",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,25 +5,25 @@ from attrs import define
|
|||||||
from ..types import UNSET, Unset
|
from ..types import UNSET, Unset
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..models.external_api_check_cache_json_body_tags import ExternalApiCheckCacheJsonBodyTags
|
from ..models.check_cache_json_body_tags import CheckCacheJsonBodyTags
|
||||||
|
|
||||||
|
|
||||||
T = TypeVar("T", bound="ExternalApiCheckCacheJsonBody")
|
T = TypeVar("T", bound="CheckCacheJsonBody")
|
||||||
|
|
||||||
|
|
||||||
@define
|
@define
|
||||||
class ExternalApiCheckCacheJsonBody:
|
class CheckCacheJsonBody:
|
||||||
"""
|
"""
|
||||||
Attributes:
|
Attributes:
|
||||||
requested_at (float): Unix timestamp in milliseconds
|
requested_at (float): Unix timestamp in milliseconds
|
||||||
req_payload (Union[Unset, Any]): JSON-encoded request payload
|
req_payload (Union[Unset, Any]): JSON-encoded request payload
|
||||||
tags (Union[Unset, ExternalApiCheckCacheJsonBodyTags]): Extra tags to attach to the call for filtering. Eg {
|
tags (Union[Unset, CheckCacheJsonBodyTags]): Extra tags to attach to the call for filtering. Eg { "userId":
|
||||||
"userId": "123", "promptId": "populate-title" }
|
"123", "promptId": "populate-title" }
|
||||||
"""
|
"""
|
||||||
|
|
||||||
requested_at: float
|
requested_at: float
|
||||||
req_payload: Union[Unset, Any] = UNSET
|
req_payload: Union[Unset, Any] = UNSET
|
||||||
tags: Union[Unset, "ExternalApiCheckCacheJsonBodyTags"] = UNSET
|
tags: Union[Unset, "CheckCacheJsonBodyTags"] = UNSET
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
requested_at = self.requested_at
|
requested_at = self.requested_at
|
||||||
@@ -47,7 +47,7 @@ class ExternalApiCheckCacheJsonBody:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||||
from ..models.external_api_check_cache_json_body_tags import ExternalApiCheckCacheJsonBodyTags
|
from ..models.check_cache_json_body_tags import CheckCacheJsonBodyTags
|
||||||
|
|
||||||
d = src_dict.copy()
|
d = src_dict.copy()
|
||||||
requested_at = d.pop("requestedAt")
|
requested_at = d.pop("requestedAt")
|
||||||
@@ -55,16 +55,16 @@ class ExternalApiCheckCacheJsonBody:
|
|||||||
req_payload = d.pop("reqPayload", UNSET)
|
req_payload = d.pop("reqPayload", UNSET)
|
||||||
|
|
||||||
_tags = d.pop("tags", UNSET)
|
_tags = d.pop("tags", UNSET)
|
||||||
tags: Union[Unset, ExternalApiCheckCacheJsonBodyTags]
|
tags: Union[Unset, CheckCacheJsonBodyTags]
|
||||||
if isinstance(_tags, Unset):
|
if isinstance(_tags, Unset):
|
||||||
tags = UNSET
|
tags = UNSET
|
||||||
else:
|
else:
|
||||||
tags = ExternalApiCheckCacheJsonBodyTags.from_dict(_tags)
|
tags = CheckCacheJsonBodyTags.from_dict(_tags)
|
||||||
|
|
||||||
external_api_check_cache_json_body = cls(
|
check_cache_json_body = cls(
|
||||||
requested_at=requested_at,
|
requested_at=requested_at,
|
||||||
req_payload=req_payload,
|
req_payload=req_payload,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
)
|
)
|
||||||
|
|
||||||
return external_api_check_cache_json_body
|
return check_cache_json_body
|
||||||
@@ -2,11 +2,11 @@ from typing import Any, Dict, List, Type, TypeVar
|
|||||||
|
|
||||||
from attrs import define, field
|
from attrs import define, field
|
||||||
|
|
||||||
T = TypeVar("T", bound="ExternalApiReportJsonBodyTags")
|
T = TypeVar("T", bound="CheckCacheJsonBodyTags")
|
||||||
|
|
||||||
|
|
||||||
@define
|
@define
|
||||||
class ExternalApiReportJsonBodyTags:
|
class CheckCacheJsonBodyTags:
|
||||||
"""Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }"""
|
"""Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }"""
|
||||||
|
|
||||||
additional_properties: Dict[str, str] = field(init=False, factory=dict)
|
additional_properties: Dict[str, str] = field(init=False, factory=dict)
|
||||||
@@ -21,10 +21,10 @@ class ExternalApiReportJsonBodyTags:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||||
d = src_dict.copy()
|
d = src_dict.copy()
|
||||||
external_api_report_json_body_tags = cls()
|
check_cache_json_body_tags = cls()
|
||||||
|
|
||||||
external_api_report_json_body_tags.additional_properties = d
|
check_cache_json_body_tags.additional_properties = d
|
||||||
return external_api_report_json_body_tags
|
return check_cache_json_body_tags
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def additional_keys(self) -> List[str]:
|
def additional_keys(self) -> List[str]:
|
||||||
@@ -4,11 +4,11 @@ from attrs import define
|
|||||||
|
|
||||||
from ..types import UNSET, Unset
|
from ..types import UNSET, Unset
|
||||||
|
|
||||||
T = TypeVar("T", bound="ExternalApiCheckCacheResponse200")
|
T = TypeVar("T", bound="CheckCacheResponse200")
|
||||||
|
|
||||||
|
|
||||||
@define
|
@define
|
||||||
class ExternalApiCheckCacheResponse200:
|
class CheckCacheResponse200:
|
||||||
"""
|
"""
|
||||||
Attributes:
|
Attributes:
|
||||||
resp_payload (Union[Unset, Any]): JSON-encoded response payload
|
resp_payload (Union[Unset, Any]): JSON-encoded response payload
|
||||||
@@ -31,8 +31,8 @@ class ExternalApiCheckCacheResponse200:
|
|||||||
d = src_dict.copy()
|
d = src_dict.copy()
|
||||||
resp_payload = d.pop("respPayload", UNSET)
|
resp_payload = d.pop("respPayload", UNSET)
|
||||||
|
|
||||||
external_api_check_cache_response_200 = cls(
|
check_cache_response_200 = cls(
|
||||||
resp_payload=resp_payload,
|
resp_payload=resp_payload,
|
||||||
)
|
)
|
||||||
|
|
||||||
return external_api_check_cache_response_200
|
return check_cache_response_200
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import datetime
|
||||||
|
from typing import TYPE_CHECKING, Any, Dict, Optional, Type, TypeVar
|
||||||
|
|
||||||
|
from attrs import define
|
||||||
|
from dateutil.parser import isoparse
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from ..models.local_testing_only_get_latest_logged_call_response_200_model_response import (
|
||||||
|
LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse,
|
||||||
|
)
|
||||||
|
from ..models.local_testing_only_get_latest_logged_call_response_200_tags import (
|
||||||
|
LocalTestingOnlyGetLatestLoggedCallResponse200Tags,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
T = TypeVar("T", bound="LocalTestingOnlyGetLatestLoggedCallResponse200")
|
||||||
|
|
||||||
|
|
||||||
|
@define
|
||||||
|
class LocalTestingOnlyGetLatestLoggedCallResponse200:
|
||||||
|
"""
|
||||||
|
Attributes:
|
||||||
|
created_at (datetime.datetime):
|
||||||
|
cache_hit (bool):
|
||||||
|
tags (LocalTestingOnlyGetLatestLoggedCallResponse200Tags):
|
||||||
|
model_response (Optional[LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse]):
|
||||||
|
"""
|
||||||
|
|
||||||
|
created_at: datetime.datetime
|
||||||
|
cache_hit: bool
|
||||||
|
tags: "LocalTestingOnlyGetLatestLoggedCallResponse200Tags"
|
||||||
|
model_response: Optional["LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse"]
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
created_at = self.created_at.isoformat()
|
||||||
|
|
||||||
|
cache_hit = self.cache_hit
|
||||||
|
tags = self.tags.to_dict()
|
||||||
|
|
||||||
|
model_response = self.model_response.to_dict() if self.model_response else None
|
||||||
|
|
||||||
|
field_dict: Dict[str, Any] = {}
|
||||||
|
field_dict.update(
|
||||||
|
{
|
||||||
|
"createdAt": created_at,
|
||||||
|
"cacheHit": cache_hit,
|
||||||
|
"tags": tags,
|
||||||
|
"modelResponse": model_response,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return field_dict
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||||
|
from ..models.local_testing_only_get_latest_logged_call_response_200_model_response import (
|
||||||
|
LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse,
|
||||||
|
)
|
||||||
|
from ..models.local_testing_only_get_latest_logged_call_response_200_tags import (
|
||||||
|
LocalTestingOnlyGetLatestLoggedCallResponse200Tags,
|
||||||
|
)
|
||||||
|
|
||||||
|
d = src_dict.copy()
|
||||||
|
created_at = isoparse(d.pop("createdAt"))
|
||||||
|
|
||||||
|
cache_hit = d.pop("cacheHit")
|
||||||
|
|
||||||
|
tags = LocalTestingOnlyGetLatestLoggedCallResponse200Tags.from_dict(d.pop("tags"))
|
||||||
|
|
||||||
|
_model_response = d.pop("modelResponse")
|
||||||
|
model_response: Optional[LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse]
|
||||||
|
if _model_response is None:
|
||||||
|
model_response = None
|
||||||
|
else:
|
||||||
|
model_response = LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse.from_dict(_model_response)
|
||||||
|
|
||||||
|
local_testing_only_get_latest_logged_call_response_200 = cls(
|
||||||
|
created_at=created_at,
|
||||||
|
cache_hit=cache_hit,
|
||||||
|
tags=tags,
|
||||||
|
model_response=model_response,
|
||||||
|
)
|
||||||
|
|
||||||
|
return local_testing_only_get_latest_logged_call_response_200
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
from typing import Any, Dict, Optional, Type, TypeVar, Union
|
||||||
|
|
||||||
|
from attrs import define
|
||||||
|
|
||||||
|
from ..types import UNSET, Unset
|
||||||
|
|
||||||
|
T = TypeVar("T", bound="LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse")
|
||||||
|
|
||||||
|
|
||||||
|
@define
|
||||||
|
class LocalTestingOnlyGetLatestLoggedCallResponse200ModelResponse:
|
||||||
|
"""
|
||||||
|
Attributes:
|
||||||
|
id (str):
|
||||||
|
status_code (Optional[float]):
|
||||||
|
error_message (Optional[str]):
|
||||||
|
req_payload (Union[Unset, Any]):
|
||||||
|
resp_payload (Union[Unset, Any]):
|
||||||
|
"""
|
||||||
|
|
||||||
|
id: str
|
||||||
|
status_code: Optional[float]
|
||||||
|
error_message: Optional[str]
|
||||||
|
req_payload: Union[Unset, Any] = UNSET
|
||||||
|
resp_payload: Union[Unset, Any] = UNSET
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
id = self.id
|
||||||
|
status_code = self.status_code
|
||||||
|
error_message = self.error_message
|
||||||
|
req_payload = self.req_payload
|
||||||
|
resp_payload = self.resp_payload
|
||||||
|
|
||||||
|
field_dict: Dict[str, Any] = {}
|
||||||
|
field_dict.update(
|
||||||
|
{
|
||||||
|
"id": id,
|
||||||
|
"statusCode": status_code,
|
||||||
|
"errorMessage": error_message,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if req_payload is not UNSET:
|
||||||
|
field_dict["reqPayload"] = req_payload
|
||||||
|
if resp_payload is not UNSET:
|
||||||
|
field_dict["respPayload"] = resp_payload
|
||||||
|
|
||||||
|
return field_dict
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||||
|
d = src_dict.copy()
|
||||||
|
id = d.pop("id")
|
||||||
|
|
||||||
|
status_code = d.pop("statusCode")
|
||||||
|
|
||||||
|
error_message = d.pop("errorMessage")
|
||||||
|
|
||||||
|
req_payload = d.pop("reqPayload", UNSET)
|
||||||
|
|
||||||
|
resp_payload = d.pop("respPayload", UNSET)
|
||||||
|
|
||||||
|
local_testing_only_get_latest_logged_call_response_200_model_response = cls(
|
||||||
|
id=id,
|
||||||
|
status_code=status_code,
|
||||||
|
error_message=error_message,
|
||||||
|
req_payload=req_payload,
|
||||||
|
resp_payload=resp_payload,
|
||||||
|
)
|
||||||
|
|
||||||
|
return local_testing_only_get_latest_logged_call_response_200_model_response
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
from typing import Any, Dict, List, Optional, Type, TypeVar
|
||||||
|
|
||||||
|
from attrs import define, field
|
||||||
|
|
||||||
|
T = TypeVar("T", bound="LocalTestingOnlyGetLatestLoggedCallResponse200Tags")
|
||||||
|
|
||||||
|
|
||||||
|
@define
|
||||||
|
class LocalTestingOnlyGetLatestLoggedCallResponse200Tags:
|
||||||
|
""" """
|
||||||
|
|
||||||
|
additional_properties: Dict[str, Optional[str]] = field(init=False, factory=dict)
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
field_dict: Dict[str, Any] = {}
|
||||||
|
field_dict.update(self.additional_properties)
|
||||||
|
field_dict.update({})
|
||||||
|
|
||||||
|
return field_dict
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||||
|
d = src_dict.copy()
|
||||||
|
local_testing_only_get_latest_logged_call_response_200_tags = cls()
|
||||||
|
|
||||||
|
local_testing_only_get_latest_logged_call_response_200_tags.additional_properties = d
|
||||||
|
return local_testing_only_get_latest_logged_call_response_200_tags
|
||||||
|
|
||||||
|
@property
|
||||||
|
def additional_keys(self) -> List[str]:
|
||||||
|
return list(self.additional_properties.keys())
|
||||||
|
|
||||||
|
def __getitem__(self, key: str) -> Optional[str]:
|
||||||
|
return self.additional_properties[key]
|
||||||
|
|
||||||
|
def __setitem__(self, key: str, value: Optional[str]) -> None:
|
||||||
|
self.additional_properties[key] = value
|
||||||
|
|
||||||
|
def __delitem__(self, key: str) -> None:
|
||||||
|
del self.additional_properties[key]
|
||||||
|
|
||||||
|
def __contains__(self, key: str) -> bool:
|
||||||
|
return key in self.additional_properties
|
||||||
@@ -5,14 +5,14 @@ from attrs import define
|
|||||||
from ..types import UNSET, Unset
|
from ..types import UNSET, Unset
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..models.external_api_report_json_body_tags import ExternalApiReportJsonBodyTags
|
from ..models.report_json_body_tags import ReportJsonBodyTags
|
||||||
|
|
||||||
|
|
||||||
T = TypeVar("T", bound="ExternalApiReportJsonBody")
|
T = TypeVar("T", bound="ReportJsonBody")
|
||||||
|
|
||||||
|
|
||||||
@define
|
@define
|
||||||
class ExternalApiReportJsonBody:
|
class ReportJsonBody:
|
||||||
"""
|
"""
|
||||||
Attributes:
|
Attributes:
|
||||||
requested_at (float): Unix timestamp in milliseconds
|
requested_at (float): Unix timestamp in milliseconds
|
||||||
@@ -21,8 +21,8 @@ class ExternalApiReportJsonBody:
|
|||||||
resp_payload (Union[Unset, Any]): JSON-encoded response payload
|
resp_payload (Union[Unset, Any]): JSON-encoded response payload
|
||||||
status_code (Union[Unset, float]): HTTP status code of response
|
status_code (Union[Unset, float]): HTTP status code of response
|
||||||
error_message (Union[Unset, str]): User-friendly error message
|
error_message (Union[Unset, str]): User-friendly error message
|
||||||
tags (Union[Unset, ExternalApiReportJsonBodyTags]): Extra tags to attach to the call for filtering. Eg {
|
tags (Union[Unset, ReportJsonBodyTags]): Extra tags to attach to the call for filtering. Eg { "userId": "123",
|
||||||
"userId": "123", "promptId": "populate-title" }
|
"promptId": "populate-title" }
|
||||||
"""
|
"""
|
||||||
|
|
||||||
requested_at: float
|
requested_at: float
|
||||||
@@ -31,7 +31,7 @@ class ExternalApiReportJsonBody:
|
|||||||
resp_payload: Union[Unset, Any] = UNSET
|
resp_payload: Union[Unset, Any] = UNSET
|
||||||
status_code: Union[Unset, float] = UNSET
|
status_code: Union[Unset, float] = UNSET
|
||||||
error_message: Union[Unset, str] = UNSET
|
error_message: Union[Unset, str] = UNSET
|
||||||
tags: Union[Unset, "ExternalApiReportJsonBodyTags"] = UNSET
|
tags: Union[Unset, "ReportJsonBodyTags"] = UNSET
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
requested_at = self.requested_at
|
requested_at = self.requested_at
|
||||||
@@ -66,7 +66,7 @@ class ExternalApiReportJsonBody:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||||
from ..models.external_api_report_json_body_tags import ExternalApiReportJsonBodyTags
|
from ..models.report_json_body_tags import ReportJsonBodyTags
|
||||||
|
|
||||||
d = src_dict.copy()
|
d = src_dict.copy()
|
||||||
requested_at = d.pop("requestedAt")
|
requested_at = d.pop("requestedAt")
|
||||||
@@ -82,13 +82,13 @@ class ExternalApiReportJsonBody:
|
|||||||
error_message = d.pop("errorMessage", UNSET)
|
error_message = d.pop("errorMessage", UNSET)
|
||||||
|
|
||||||
_tags = d.pop("tags", UNSET)
|
_tags = d.pop("tags", UNSET)
|
||||||
tags: Union[Unset, ExternalApiReportJsonBodyTags]
|
tags: Union[Unset, ReportJsonBodyTags]
|
||||||
if isinstance(_tags, Unset):
|
if isinstance(_tags, Unset):
|
||||||
tags = UNSET
|
tags = UNSET
|
||||||
else:
|
else:
|
||||||
tags = ExternalApiReportJsonBodyTags.from_dict(_tags)
|
tags = ReportJsonBodyTags.from_dict(_tags)
|
||||||
|
|
||||||
external_api_report_json_body = cls(
|
report_json_body = cls(
|
||||||
requested_at=requested_at,
|
requested_at=requested_at,
|
||||||
received_at=received_at,
|
received_at=received_at,
|
||||||
req_payload=req_payload,
|
req_payload=req_payload,
|
||||||
@@ -98,4 +98,4 @@ class ExternalApiReportJsonBody:
|
|||||||
tags=tags,
|
tags=tags,
|
||||||
)
|
)
|
||||||
|
|
||||||
return external_api_report_json_body
|
return report_json_body
|
||||||
@@ -2,11 +2,11 @@ from typing import Any, Dict, List, Type, TypeVar
|
|||||||
|
|
||||||
from attrs import define, field
|
from attrs import define, field
|
||||||
|
|
||||||
T = TypeVar("T", bound="ExternalApiCheckCacheJsonBodyTags")
|
T = TypeVar("T", bound="ReportJsonBodyTags")
|
||||||
|
|
||||||
|
|
||||||
@define
|
@define
|
||||||
class ExternalApiCheckCacheJsonBodyTags:
|
class ReportJsonBodyTags:
|
||||||
"""Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }"""
|
"""Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }"""
|
||||||
|
|
||||||
additional_properties: Dict[str, str] = field(init=False, factory=dict)
|
additional_properties: Dict[str, str] = field(init=False, factory=dict)
|
||||||
@@ -21,10 +21,10 @@ class ExternalApiCheckCacheJsonBodyTags:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||||
d = src_dict.copy()
|
d = src_dict.copy()
|
||||||
external_api_check_cache_json_body_tags = cls()
|
report_json_body_tags = cls()
|
||||||
|
|
||||||
external_api_check_cache_json_body_tags.additional_properties = d
|
report_json_body_tags.additional_properties = d
|
||||||
return external_api_check_cache_json_body_tags
|
return report_json_body_tags
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def additional_keys(self) -> List[str]:
|
def additional_keys(self) -> List[str]:
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
from typing import Any, Dict, Type, TypeVar
|
||||||
|
|
||||||
|
from attrs import define
|
||||||
|
|
||||||
|
from ..models.report_response_200_status import ReportResponse200Status
|
||||||
|
|
||||||
|
T = TypeVar("T", bound="ReportResponse200")
|
||||||
|
|
||||||
|
|
||||||
|
@define
|
||||||
|
class ReportResponse200:
|
||||||
|
"""
|
||||||
|
Attributes:
|
||||||
|
status (ReportResponse200Status):
|
||||||
|
"""
|
||||||
|
|
||||||
|
status: ReportResponse200Status
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
status = self.status.value
|
||||||
|
|
||||||
|
field_dict: Dict[str, Any] = {}
|
||||||
|
field_dict.update(
|
||||||
|
{
|
||||||
|
"status": status,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return field_dict
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||||
|
d = src_dict.copy()
|
||||||
|
status = ReportResponse200Status(d.pop("status"))
|
||||||
|
|
||||||
|
report_response_200 = cls(
|
||||||
|
status=status,
|
||||||
|
)
|
||||||
|
|
||||||
|
return report_response_200
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class ReportResponse200Status(str, Enum):
|
||||||
|
OK = "ok"
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return str(self.value)
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
|
||||||
def merge_streamed_chunks(base: Optional[Any], chunk: Any) -> Any:
|
def merge_openai_chunks(base: Optional[Any], chunk: Any) -> Any:
|
||||||
if base is None:
|
if base is None:
|
||||||
return merge_streamed_chunks({**chunk, "choices": []}, chunk)
|
return merge_openai_chunks({**chunk, "choices": []}, chunk)
|
||||||
|
|
||||||
choices = base["choices"].copy()
|
choices = base["choices"].copy()
|
||||||
for choice in chunk["choices"]:
|
for choice in chunk["choices"]:
|
||||||
@@ -34,9 +34,7 @@ def merge_streamed_chunks(base: Optional[Any], chunk: Any) -> Any:
|
|||||||
{**new_choice, "message": {"role": "assistant", **choice["delta"]}}
|
{**new_choice, "message": {"role": "assistant", **choice["delta"]}}
|
||||||
)
|
)
|
||||||
|
|
||||||
merged = {
|
return {
|
||||||
**base,
|
**base,
|
||||||
"choices": choices,
|
"choices": choices,
|
||||||
}
|
}
|
||||||
|
|
||||||
return merged
|
|
||||||
|
|||||||
@@ -3,9 +3,16 @@ from openai.openai_object import OpenAIObject
|
|||||||
import time
|
import time
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
from openpipe.merge_openai_chunks import merge_streamed_chunks
|
from openpipe.merge_openai_chunks import merge_openai_chunks
|
||||||
|
from openpipe.openpipe_meta import OpenPipeMeta
|
||||||
|
|
||||||
from .shared import maybe_check_cache, maybe_check_cache_async, report_async, report
|
from .shared import (
|
||||||
|
_should_check_cache,
|
||||||
|
maybe_check_cache,
|
||||||
|
maybe_check_cache_async,
|
||||||
|
report_async,
|
||||||
|
report,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class WrappedChatCompletion(original_openai.ChatCompletion):
|
class WrappedChatCompletion(original_openai.ChatCompletion):
|
||||||
@@ -29,9 +36,15 @@ class WrappedChatCompletion(original_openai.ChatCompletion):
|
|||||||
def _gen():
|
def _gen():
|
||||||
assembled_completion = None
|
assembled_completion = None
|
||||||
for chunk in chat_completion:
|
for chunk in chat_completion:
|
||||||
assembled_completion = merge_streamed_chunks(
|
assembled_completion = merge_openai_chunks(
|
||||||
assembled_completion, chunk
|
assembled_completion, chunk
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cache_status = (
|
||||||
|
"MISS" if _should_check_cache(openpipe_options) else "SKIP"
|
||||||
|
)
|
||||||
|
chunk.openpipe = OpenPipeMeta(cache_status=cache_status)
|
||||||
|
|
||||||
yield chunk
|
yield chunk
|
||||||
|
|
||||||
received_at = int(time.time() * 1000)
|
received_at = int(time.time() * 1000)
|
||||||
@@ -58,6 +71,10 @@ class WrappedChatCompletion(original_openai.ChatCompletion):
|
|||||||
status_code=200,
|
status_code=200,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cache_status = (
|
||||||
|
"MISS" if _should_check_cache(openpipe_options) else "SKIP"
|
||||||
|
)
|
||||||
|
chat_completion["openpipe"] = OpenPipeMeta(cache_status=cache_status)
|
||||||
return chat_completion
|
return chat_completion
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
received_at = int(time.time() * 1000)
|
received_at = int(time.time() * 1000)
|
||||||
@@ -96,21 +113,28 @@ class WrappedChatCompletion(original_openai.ChatCompletion):
|
|||||||
requested_at = int(time.time() * 1000)
|
requested_at = int(time.time() * 1000)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
chat_completion = original_openai.ChatCompletion.acreate(*args, **kwargs)
|
chat_completion = await original_openai.ChatCompletion.acreate(
|
||||||
|
*args, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
if inspect.isgenerator(chat_completion):
|
if inspect.isasyncgen(chat_completion):
|
||||||
|
|
||||||
def _gen():
|
async def _gen():
|
||||||
assembled_completion = None
|
assembled_completion = None
|
||||||
for chunk in chat_completion:
|
async for chunk in chat_completion:
|
||||||
assembled_completion = merge_streamed_chunks(
|
assembled_completion = merge_openai_chunks(
|
||||||
assembled_completion, chunk
|
assembled_completion, chunk
|
||||||
)
|
)
|
||||||
|
cache_status = (
|
||||||
|
"MISS" if _should_check_cache(openpipe_options) else "SKIP"
|
||||||
|
)
|
||||||
|
chunk.openpipe = OpenPipeMeta(cache_status=cache_status)
|
||||||
|
|
||||||
yield chunk
|
yield chunk
|
||||||
|
|
||||||
received_at = int(time.time() * 1000)
|
received_at = int(time.time() * 1000)
|
||||||
|
|
||||||
report_async(
|
await report_async(
|
||||||
openpipe_options=openpipe_options,
|
openpipe_options=openpipe_options,
|
||||||
requested_at=requested_at,
|
requested_at=requested_at,
|
||||||
received_at=received_at,
|
received_at=received_at,
|
||||||
@@ -123,7 +147,7 @@ class WrappedChatCompletion(original_openai.ChatCompletion):
|
|||||||
else:
|
else:
|
||||||
received_at = int(time.time() * 1000)
|
received_at = int(time.time() * 1000)
|
||||||
|
|
||||||
report_async(
|
await report_async(
|
||||||
openpipe_options=openpipe_options,
|
openpipe_options=openpipe_options,
|
||||||
requested_at=requested_at,
|
requested_at=requested_at,
|
||||||
received_at=received_at,
|
received_at=received_at,
|
||||||
@@ -132,12 +156,17 @@ class WrappedChatCompletion(original_openai.ChatCompletion):
|
|||||||
status_code=200,
|
status_code=200,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cache_status = (
|
||||||
|
"MISS" if _should_check_cache(openpipe_options) else "SKIP"
|
||||||
|
)
|
||||||
|
chat_completion["openpipe"] = OpenPipeMeta(cache_status=cache_status)
|
||||||
|
|
||||||
return chat_completion
|
return chat_completion
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
received_at = int(time.time() * 1000)
|
received_at = int(time.time() * 1000)
|
||||||
|
|
||||||
if isinstance(e, original_openai.OpenAIError):
|
if isinstance(e, original_openai.OpenAIError):
|
||||||
report_async(
|
await report_async(
|
||||||
openpipe_options=openpipe_options,
|
openpipe_options=openpipe_options,
|
||||||
requested_at=requested_at,
|
requested_at=requested_at,
|
||||||
received_at=received_at,
|
received_at=received_at,
|
||||||
@@ -147,7 +176,7 @@ class WrappedChatCompletion(original_openai.ChatCompletion):
|
|||||||
status_code=e.http_status,
|
status_code=e.http_status,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
report_async(
|
await report_async(
|
||||||
openpipe_options=openpipe_options,
|
openpipe_options=openpipe_options,
|
||||||
requested_at=requested_at,
|
requested_at=requested_at,
|
||||||
received_at=received_at,
|
received_at=received_at,
|
||||||
|
|||||||
7
client-libs/python/openpipe/openpipe_meta.py
Normal file
7
client-libs/python/openpipe/openpipe_meta.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from attr import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class OpenPipeMeta:
|
||||||
|
# Cache status. One of 'HIT', 'MISS', 'SKIP'
|
||||||
|
cache_status: str
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
from openpipe.api_client.api.default import (
|
from openpipe.api_client.api.default import (
|
||||||
external_api_report,
|
report as api_report,
|
||||||
external_api_check_cache,
|
check_cache,
|
||||||
)
|
)
|
||||||
from openpipe.api_client.client import AuthenticatedClient
|
from openpipe.api_client.client import AuthenticatedClient
|
||||||
from openpipe.api_client.models.external_api_report_json_body_tags import (
|
from openpipe.api_client.models.report_json_body_tags import (
|
||||||
ExternalApiReportJsonBodyTags,
|
ReportJsonBodyTags,
|
||||||
)
|
)
|
||||||
import toml
|
import toml
|
||||||
import time
|
import time
|
||||||
@@ -19,9 +19,9 @@ configured_client = AuthenticatedClient(
|
|||||||
def _get_tags(openpipe_options):
|
def _get_tags(openpipe_options):
|
||||||
tags = openpipe_options.get("tags") or {}
|
tags = openpipe_options.get("tags") or {}
|
||||||
tags["$sdk"] = "python"
|
tags["$sdk"] = "python"
|
||||||
tags["$sdk_version"] = version
|
tags["$sdk.version"] = version
|
||||||
|
|
||||||
return ExternalApiReportJsonBodyTags.from_dict(tags)
|
return ReportJsonBodyTags.from_dict(tags)
|
||||||
|
|
||||||
|
|
||||||
def _should_check_cache(openpipe_options):
|
def _should_check_cache(openpipe_options):
|
||||||
@@ -31,7 +31,7 @@ def _should_check_cache(openpipe_options):
|
|||||||
|
|
||||||
|
|
||||||
def _process_cache_payload(
|
def _process_cache_payload(
|
||||||
payload: external_api_check_cache.ExternalApiCheckCacheResponse200,
|
payload: check_cache.CheckCacheResponse200,
|
||||||
):
|
):
|
||||||
if not payload or not payload.resp_payload:
|
if not payload or not payload.resp_payload:
|
||||||
return None
|
return None
|
||||||
@@ -47,9 +47,9 @@ def maybe_check_cache(
|
|||||||
if not _should_check_cache(openpipe_options):
|
if not _should_check_cache(openpipe_options):
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
payload = external_api_check_cache.sync(
|
payload = check_cache.sync(
|
||||||
client=configured_client,
|
client=configured_client,
|
||||||
json_body=external_api_check_cache.ExternalApiCheckCacheJsonBody(
|
json_body=check_cache.CheckCacheJsonBody(
|
||||||
req_payload=req_payload,
|
req_payload=req_payload,
|
||||||
requested_at=int(time.time() * 1000),
|
requested_at=int(time.time() * 1000),
|
||||||
tags=_get_tags(openpipe_options),
|
tags=_get_tags(openpipe_options),
|
||||||
@@ -72,9 +72,9 @@ async def maybe_check_cache_async(
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
payload = await external_api_check_cache.asyncio(
|
payload = await check_cache.asyncio(
|
||||||
client=configured_client,
|
client=configured_client,
|
||||||
json_body=external_api_check_cache.ExternalApiCheckCacheJsonBody(
|
json_body=check_cache.CheckCacheJsonBody(
|
||||||
req_payload=req_payload,
|
req_payload=req_payload,
|
||||||
requested_at=int(time.time() * 1000),
|
requested_at=int(time.time() * 1000),
|
||||||
tags=_get_tags(openpipe_options),
|
tags=_get_tags(openpipe_options),
|
||||||
@@ -94,9 +94,9 @@ def report(
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
external_api_report.sync_detailed(
|
api_report.sync_detailed(
|
||||||
client=configured_client,
|
client=configured_client,
|
||||||
json_body=external_api_report.ExternalApiReportJsonBody(
|
json_body=api_report.ReportJsonBody(
|
||||||
**kwargs,
|
**kwargs,
|
||||||
tags=_get_tags(openpipe_options),
|
tags=_get_tags(openpipe_options),
|
||||||
),
|
),
|
||||||
@@ -112,9 +112,9 @@ async def report_async(
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
await external_api_report.asyncio_detailed(
|
await api_report.asyncio_detailed(
|
||||||
client=configured_client,
|
client=configured_client,
|
||||||
json_body=external_api_report.ExternalApiReportJsonBody(
|
json_body=api_report.ReportJsonBody(
|
||||||
**kwargs,
|
**kwargs,
|
||||||
tags=_get_tags(openpipe_options),
|
tags=_get_tags(openpipe_options),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,55 +1,106 @@
|
|||||||
|
from functools import reduce
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from . import openai, configure_openpipe
|
|
||||||
import os
|
import os
|
||||||
import pytest
|
import pytest
|
||||||
|
from . import openai, configure_openpipe, configured_client
|
||||||
|
from .api_client.api.default import local_testing_only_get_latest_logged_call
|
||||||
|
from .merge_openai_chunks import merge_openai_chunks
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
|
||||||
|
def random_string(length):
|
||||||
|
letters = string.ascii_lowercase
|
||||||
|
return "".join(random.choice(letters) for i in range(length))
|
||||||
|
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
openai.api_key = os.getenv("OPENAI_API_KEY")
|
openai.api_key = os.getenv("OPENAI_API_KEY")
|
||||||
|
|
||||||
configure_openpipe(
|
configure_openpipe(
|
||||||
base_url="http://localhost:3000/api", api_key=os.getenv("OPENPIPE_API_KEY")
|
base_url="http://localhost:3000/api/v1", api_key=os.getenv("OPENPIPE_API_KEY")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def last_logged_call():
|
||||||
|
return local_testing_only_get_latest_logged_call.sync(client=configured_client)
|
||||||
|
|
||||||
|
|
||||||
def test_sync():
|
def test_sync():
|
||||||
completion = openai.ChatCompletion.create(
|
completion = openai.ChatCompletion.create(
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo",
|
||||||
messages=[{"role": "system", "content": "count to 10"}],
|
messages=[{"role": "system", "content": "count to 3"}],
|
||||||
)
|
)
|
||||||
|
|
||||||
print(completion.choices[0].message.content)
|
last_logged = last_logged_call()
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.resp_payload["choices"][0]["message"]["content"]
|
||||||
|
== completion.choices[0].message.content
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.req_payload["messages"][0]["content"] == "count to 3"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert completion.openpipe.cache_status == "SKIP"
|
||||||
|
|
||||||
|
|
||||||
def test_streaming():
|
def test_streaming():
|
||||||
completion = openai.ChatCompletion.create(
|
completion = openai.ChatCompletion.create(
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo",
|
||||||
messages=[{"role": "system", "content": "count to 10"}],
|
messages=[{"role": "system", "content": "count to 4"}],
|
||||||
stream=True,
|
stream=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
for chunk in completion:
|
merged = reduce(merge_openai_chunks, completion, None)
|
||||||
print(chunk)
|
last_logged = last_logged_call()
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.resp_payload["choices"][0]["message"]["content"]
|
||||||
|
== merged["choices"][0]["message"]["content"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_async():
|
async def test_async():
|
||||||
acompletion = await openai.ChatCompletion.acreate(
|
completion = await openai.ChatCompletion.acreate(
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo",
|
||||||
messages=[{"role": "user", "content": "count down from 5"}],
|
messages=[{"role": "user", "content": "count down from 5"}],
|
||||||
)
|
)
|
||||||
|
last_logged = last_logged_call()
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.resp_payload["choices"][0]["message"]["content"]
|
||||||
|
== completion.choices[0].message.content
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.req_payload["messages"][0]["content"]
|
||||||
|
== "count down from 5"
|
||||||
|
)
|
||||||
|
|
||||||
print(acompletion.choices[0].message.content)
|
assert completion.openpipe.cache_status == "SKIP"
|
||||||
|
|
||||||
|
|
||||||
async def test_async_streaming():
|
async def test_async_streaming():
|
||||||
acompletion = await openai.ChatCompletion.acreate(
|
completion = await openai.ChatCompletion.acreate(
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo",
|
||||||
messages=[{"role": "user", "content": "count down from 5"}],
|
messages=[{"role": "user", "content": "count down from 5"}],
|
||||||
stream=True,
|
stream=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
async for chunk in acompletion:
|
merged = None
|
||||||
print(chunk)
|
async for chunk in completion:
|
||||||
|
assert chunk.openpipe.cache_status == "SKIP"
|
||||||
|
merged = merge_openai_chunks(merged, chunk)
|
||||||
|
|
||||||
|
last_logged = last_logged_call()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.resp_payload["choices"][0]["message"]["content"]
|
||||||
|
== merged["choices"][0]["message"]["content"]
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.req_payload["messages"][0]["content"]
|
||||||
|
== "count down from 5"
|
||||||
|
)
|
||||||
|
assert merged["openpipe"].cache_status == "SKIP"
|
||||||
|
|
||||||
|
|
||||||
def test_sync_with_tags():
|
def test_sync_with_tags():
|
||||||
@@ -58,31 +109,54 @@ def test_sync_with_tags():
|
|||||||
messages=[{"role": "system", "content": "count to 10"}],
|
messages=[{"role": "system", "content": "count to 10"}],
|
||||||
openpipe={"tags": {"promptId": "testprompt"}},
|
openpipe={"tags": {"promptId": "testprompt"}},
|
||||||
)
|
)
|
||||||
print("finished")
|
|
||||||
|
|
||||||
print(completion.choices[0].message.content)
|
last_logged = last_logged_call()
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.resp_payload["choices"][0]["message"]["content"]
|
||||||
|
== completion.choices[0].message.content
|
||||||
|
)
|
||||||
|
print(last_logged.tags)
|
||||||
|
assert last_logged.tags["promptId"] == "testprompt"
|
||||||
|
assert last_logged.tags["$sdk"] == "python"
|
||||||
|
|
||||||
|
|
||||||
def test_bad_call():
|
def test_bad_call():
|
||||||
|
try:
|
||||||
completion = openai.ChatCompletion.create(
|
completion = openai.ChatCompletion.create(
|
||||||
model="gpt-3.5-turbo-blaster",
|
model="gpt-3.5-turbo-blaster",
|
||||||
messages=[{"role": "system", "content": "count to 10"}],
|
messages=[{"role": "system", "content": "count to 10"}],
|
||||||
stream=True,
|
stream=True,
|
||||||
)
|
)
|
||||||
|
assert False
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
last_logged = last_logged_call()
|
||||||
|
print(last_logged)
|
||||||
|
assert (
|
||||||
|
last_logged.model_response.error_message
|
||||||
|
== "The model `gpt-3.5-turbo-blaster` does not exist"
|
||||||
|
)
|
||||||
|
assert last_logged.model_response.status_code == 404
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.focus
|
|
||||||
async def test_caching():
|
async def test_caching():
|
||||||
|
messages = [{"role": "system", "content": f"repeat '{random_string(10)}'"}]
|
||||||
completion = openai.ChatCompletion.create(
|
completion = openai.ChatCompletion.create(
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo",
|
||||||
messages=[{"role": "system", "content": "count to 10"}],
|
messages=messages,
|
||||||
openpipe={"cache": True},
|
openpipe={"cache": True},
|
||||||
)
|
)
|
||||||
|
assert completion.openpipe.cache_status == "MISS"
|
||||||
|
|
||||||
|
first_logged = last_logged_call()
|
||||||
|
assert (
|
||||||
|
completion.choices[0].message.content
|
||||||
|
== first_logged.model_response.resp_payload["choices"][0]["message"]["content"]
|
||||||
|
)
|
||||||
|
|
||||||
completion2 = await openai.ChatCompletion.acreate(
|
completion2 = await openai.ChatCompletion.acreate(
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo",
|
||||||
messages=[{"role": "system", "content": "count to 10"}],
|
messages=messages,
|
||||||
openpipe={"cache": True},
|
openpipe={"cache": True},
|
||||||
)
|
)
|
||||||
|
assert completion2.openpipe.cache_status == "HIT"
|
||||||
print(completion2)
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
// main.ts or index.ts at the root level
|
// main.ts or index.ts at the root level
|
||||||
export * as OpenAI from './openai';
|
export * as OpenAI from "./src/openai";
|
||||||
export * as OpenAILegacy from './openai-legacy';
|
export * as OpenAILegacy from "./src/openai-legacy";
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Metrics and auto-evaluation for LLM calls",
|
"description": "Metrics and auto-evaluation for LLM calls",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc"
|
"build": "tsc",
|
||||||
|
"test": "vitest"
|
||||||
},
|
},
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
@@ -12,7 +13,8 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.26.0",
|
"form-data": "^4.0.0",
|
||||||
|
"node-fetch": "^3.3.2",
|
||||||
"openai-beta": "npm:openai@4.0.0-beta.7",
|
"openai-beta": "npm:openai@4.0.0-beta.7",
|
||||||
"openai-legacy": "npm:openai@3.3.0"
|
"openai-legacy": "npm:openai@3.3.0"
|
||||||
},
|
},
|
||||||
@@ -20,6 +22,7 @@
|
|||||||
"@types/node": "^20.4.8",
|
"@types/node": "^20.4.8",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"tsx": "^3.12.7",
|
"tsx": "^3.12.7",
|
||||||
"typescript": "^5.0.4"
|
"typescript": "^5.0.4",
|
||||||
|
"vitest": "^0.33.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
wwwroot/*.js
|
|
||||||
node_modules
|
|
||||||
typings
|
|
||||||
dist
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# OpenAPI Generator Ignore
|
|
||||||
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
|
|
||||||
|
|
||||||
# Use this file to prevent files from being overwritten by the generator.
|
|
||||||
# The patterns follow closely to .gitignore or .dockerignore.
|
|
||||||
|
|
||||||
# As an example, the C# client generator defines ApiClient.cs.
|
|
||||||
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
|
|
||||||
#ApiClient.cs
|
|
||||||
|
|
||||||
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
|
|
||||||
#foo/*/qux
|
|
||||||
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
|
|
||||||
|
|
||||||
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
|
|
||||||
#foo/**/qux
|
|
||||||
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
|
|
||||||
|
|
||||||
# You can also negate patterns with an exclamation (!).
|
|
||||||
# For example, you can ignore all files in a docs folder with the file extension .md:
|
|
||||||
#docs/*.md
|
|
||||||
# Then explicitly reverse the ignore rule for a single file:
|
|
||||||
#!docs/README.md
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
.gitignore
|
|
||||||
.npmignore
|
|
||||||
.openapi-generator-ignore
|
|
||||||
api.ts
|
|
||||||
base.ts
|
|
||||||
common.ts
|
|
||||||
configuration.ts
|
|
||||||
git_push.sh
|
|
||||||
index.ts
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
6.6.0
|
|
||||||
35
client-libs/typescript/src/codegen/OPClient.ts
Normal file
35
client-libs/typescript/src/codegen/OPClient.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { BaseHttpRequest } from './core/BaseHttpRequest';
|
||||||
|
import type { OpenAPIConfig } from './core/OpenAPI';
|
||||||
|
import { NodeHttpRequest } from './core/NodeHttpRequest';
|
||||||
|
|
||||||
|
import { DefaultService } from './services/DefaultService';
|
||||||
|
|
||||||
|
type HttpRequestConstructor = new (config: OpenAPIConfig) => BaseHttpRequest;
|
||||||
|
|
||||||
|
export class OPClient {
|
||||||
|
|
||||||
|
public readonly default: DefaultService;
|
||||||
|
|
||||||
|
public readonly request: BaseHttpRequest;
|
||||||
|
|
||||||
|
constructor(config?: Partial<OpenAPIConfig>, HttpRequest: HttpRequestConstructor = NodeHttpRequest) {
|
||||||
|
this.request = new HttpRequest({
|
||||||
|
BASE: config?.BASE ?? 'https://app.openpipe.ai/api/v1',
|
||||||
|
VERSION: config?.VERSION ?? '0.1.1',
|
||||||
|
WITH_CREDENTIALS: config?.WITH_CREDENTIALS ?? false,
|
||||||
|
CREDENTIALS: config?.CREDENTIALS ?? 'include',
|
||||||
|
TOKEN: config?.TOKEN,
|
||||||
|
USERNAME: config?.USERNAME,
|
||||||
|
PASSWORD: config?.PASSWORD,
|
||||||
|
HEADERS: config?.HEADERS,
|
||||||
|
ENCODE_PATH: config?.ENCODE_PATH,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.default = new DefaultService(this.request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,327 +0,0 @@
|
|||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* OpenPipe API
|
|
||||||
* The public API for reporting API calls to OpenPipe
|
|
||||||
*
|
|
||||||
* The version of the OpenAPI document: 0.1.0
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
||||||
* https://openapi-generator.tech
|
|
||||||
* Do not edit the class manually.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import type { Configuration } from './configuration';
|
|
||||||
import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios';
|
|
||||||
import globalAxios from 'axios';
|
|
||||||
// Some imports not used depending on template conditions
|
|
||||||
// @ts-ignore
|
|
||||||
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from './common';
|
|
||||||
import type { RequestArgs } from './base';
|
|
||||||
// @ts-ignore
|
|
||||||
import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError } from './base';
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @interface ExternalApiCheckCache200Response
|
|
||||||
*/
|
|
||||||
export interface ExternalApiCheckCache200Response {
|
|
||||||
/**
|
|
||||||
* JSON-encoded response payload
|
|
||||||
* @type {any}
|
|
||||||
* @memberof ExternalApiCheckCache200Response
|
|
||||||
*/
|
|
||||||
'respPayload'?: any;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @interface ExternalApiCheckCacheDefaultResponse
|
|
||||||
*/
|
|
||||||
export interface ExternalApiCheckCacheDefaultResponse {
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof ExternalApiCheckCacheDefaultResponse
|
|
||||||
*/
|
|
||||||
'message': string;
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof ExternalApiCheckCacheDefaultResponse
|
|
||||||
*/
|
|
||||||
'code': string;
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {Array<ExternalApiCheckCacheDefaultResponseIssuesInner>}
|
|
||||||
* @memberof ExternalApiCheckCacheDefaultResponse
|
|
||||||
*/
|
|
||||||
'issues'?: Array<ExternalApiCheckCacheDefaultResponseIssuesInner>;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @interface ExternalApiCheckCacheDefaultResponseIssuesInner
|
|
||||||
*/
|
|
||||||
export interface ExternalApiCheckCacheDefaultResponseIssuesInner {
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof ExternalApiCheckCacheDefaultResponseIssuesInner
|
|
||||||
*/
|
|
||||||
'message': string;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @interface ExternalApiCheckCacheRequest
|
|
||||||
*/
|
|
||||||
export interface ExternalApiCheckCacheRequest {
|
|
||||||
/**
|
|
||||||
* Unix timestamp in milliseconds
|
|
||||||
* @type {number}
|
|
||||||
* @memberof ExternalApiCheckCacheRequest
|
|
||||||
*/
|
|
||||||
'requestedAt': number;
|
|
||||||
/**
|
|
||||||
* JSON-encoded request payload
|
|
||||||
* @type {any}
|
|
||||||
* @memberof ExternalApiCheckCacheRequest
|
|
||||||
*/
|
|
||||||
'reqPayload'?: any;
|
|
||||||
/**
|
|
||||||
* Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }
|
|
||||||
* @type {{ [key: string]: string; }}
|
|
||||||
* @memberof ExternalApiCheckCacheRequest
|
|
||||||
*/
|
|
||||||
'tags'?: { [key: string]: string; };
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @interface ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
export interface ExternalApiReportRequest {
|
|
||||||
/**
|
|
||||||
* Unix timestamp in milliseconds
|
|
||||||
* @type {number}
|
|
||||||
* @memberof ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
'requestedAt': number;
|
|
||||||
/**
|
|
||||||
* Unix timestamp in milliseconds
|
|
||||||
* @type {number}
|
|
||||||
* @memberof ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
'receivedAt': number;
|
|
||||||
/**
|
|
||||||
* JSON-encoded request payload
|
|
||||||
* @type {any}
|
|
||||||
* @memberof ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
'reqPayload'?: any;
|
|
||||||
/**
|
|
||||||
* JSON-encoded response payload
|
|
||||||
* @type {any}
|
|
||||||
* @memberof ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
'respPayload'?: any;
|
|
||||||
/**
|
|
||||||
* HTTP status code of response
|
|
||||||
* @type {number}
|
|
||||||
* @memberof ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
'statusCode'?: number;
|
|
||||||
/**
|
|
||||||
* User-friendly error message
|
|
||||||
* @type {string}
|
|
||||||
* @memberof ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
'errorMessage'?: string;
|
|
||||||
/**
|
|
||||||
* Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"promptId\": \"populate-title\" }
|
|
||||||
* @type {{ [key: string]: string; }}
|
|
||||||
* @memberof ExternalApiReportRequest
|
|
||||||
*/
|
|
||||||
'tags'?: { [key: string]: string; };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DefaultApi - axios parameter creator
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const DefaultApiAxiosParamCreator = function (configuration?: Configuration) {
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Check if a prompt is cached
|
|
||||||
* @param {ExternalApiCheckCacheRequest} externalApiCheckCacheRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
*/
|
|
||||||
externalApiCheckCache: async (externalApiCheckCacheRequest: ExternalApiCheckCacheRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
|
||||||
// verify required parameter 'externalApiCheckCacheRequest' is not null or undefined
|
|
||||||
assertParamExists('externalApiCheckCache', 'externalApiCheckCacheRequest', externalApiCheckCacheRequest)
|
|
||||||
const localVarPath = `/v1/check-cache`;
|
|
||||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
|
||||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
|
||||||
let baseOptions;
|
|
||||||
if (configuration) {
|
|
||||||
baseOptions = configuration.baseOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
|
||||||
const localVarHeaderParameter = {} as any;
|
|
||||||
const localVarQueryParameter = {} as any;
|
|
||||||
|
|
||||||
// authentication Authorization required
|
|
||||||
// http bearer authentication required
|
|
||||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
|
||||||
|
|
||||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
|
||||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
|
||||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
|
||||||
localVarRequestOptions.data = serializeDataIfNeeded(externalApiCheckCacheRequest, localVarRequestOptions, configuration)
|
|
||||||
|
|
||||||
return {
|
|
||||||
url: toPathString(localVarUrlObj),
|
|
||||||
options: localVarRequestOptions,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Report an API call
|
|
||||||
* @param {ExternalApiReportRequest} externalApiReportRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
*/
|
|
||||||
externalApiReport: async (externalApiReportRequest: ExternalApiReportRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
|
||||||
// verify required parameter 'externalApiReportRequest' is not null or undefined
|
|
||||||
assertParamExists('externalApiReport', 'externalApiReportRequest', externalApiReportRequest)
|
|
||||||
const localVarPath = `/v1/report`;
|
|
||||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
|
||||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
|
||||||
let baseOptions;
|
|
||||||
if (configuration) {
|
|
||||||
baseOptions = configuration.baseOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
|
||||||
const localVarHeaderParameter = {} as any;
|
|
||||||
const localVarQueryParameter = {} as any;
|
|
||||||
|
|
||||||
// authentication Authorization required
|
|
||||||
// http bearer authentication required
|
|
||||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
|
||||||
|
|
||||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
|
||||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
|
||||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
|
||||||
localVarRequestOptions.data = serializeDataIfNeeded(externalApiReportRequest, localVarRequestOptions, configuration)
|
|
||||||
|
|
||||||
return {
|
|
||||||
url: toPathString(localVarUrlObj),
|
|
||||||
options: localVarRequestOptions,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DefaultApi - functional programming interface
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const DefaultApiFp = function(configuration?: Configuration) {
|
|
||||||
const localVarAxiosParamCreator = DefaultApiAxiosParamCreator(configuration)
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Check if a prompt is cached
|
|
||||||
* @param {ExternalApiCheckCacheRequest} externalApiCheckCacheRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
*/
|
|
||||||
async externalApiCheckCache(externalApiCheckCacheRequest: ExternalApiCheckCacheRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<ExternalApiCheckCache200Response>> {
|
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.externalApiCheckCache(externalApiCheckCacheRequest, options);
|
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Report an API call
|
|
||||||
* @param {ExternalApiReportRequest} externalApiReportRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
*/
|
|
||||||
async externalApiReport(externalApiReportRequest: ExternalApiReportRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<any>> {
|
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.externalApiReport(externalApiReportRequest, options);
|
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DefaultApi - factory interface
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const DefaultApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
|
||||||
const localVarFp = DefaultApiFp(configuration)
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Check if a prompt is cached
|
|
||||||
* @param {ExternalApiCheckCacheRequest} externalApiCheckCacheRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
*/
|
|
||||||
externalApiCheckCache(externalApiCheckCacheRequest: ExternalApiCheckCacheRequest, options?: any): AxiosPromise<ExternalApiCheckCache200Response> {
|
|
||||||
return localVarFp.externalApiCheckCache(externalApiCheckCacheRequest, options).then((request) => request(axios, basePath));
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Report an API call
|
|
||||||
* @param {ExternalApiReportRequest} externalApiReportRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
*/
|
|
||||||
externalApiReport(externalApiReportRequest: ExternalApiReportRequest, options?: any): AxiosPromise<any> {
|
|
||||||
return localVarFp.externalApiReport(externalApiReportRequest, options).then((request) => request(axios, basePath));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DefaultApi - object-oriented interface
|
|
||||||
* @export
|
|
||||||
* @class DefaultApi
|
|
||||||
* @extends {BaseAPI}
|
|
||||||
*/
|
|
||||||
export class DefaultApi extends BaseAPI {
|
|
||||||
/**
|
|
||||||
* Check if a prompt is cached
|
|
||||||
* @param {ExternalApiCheckCacheRequest} externalApiCheckCacheRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
* @memberof DefaultApi
|
|
||||||
*/
|
|
||||||
public externalApiCheckCache(externalApiCheckCacheRequest: ExternalApiCheckCacheRequest, options?: AxiosRequestConfig) {
|
|
||||||
return DefaultApiFp(this.configuration).externalApiCheckCache(externalApiCheckCacheRequest, options).then((request) => request(this.axios, this.basePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Report an API call
|
|
||||||
* @param {ExternalApiReportRequest} externalApiReportRequest
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
* @memberof DefaultApi
|
|
||||||
*/
|
|
||||||
public externalApiReport(externalApiReportRequest: ExternalApiReportRequest, options?: AxiosRequestConfig) {
|
|
||||||
return DefaultApiFp(this.configuration).externalApiReport(externalApiReportRequest, options).then((request) => request(this.axios, this.basePath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* OpenPipe API
|
|
||||||
* The public API for reporting API calls to OpenPipe
|
|
||||||
*
|
|
||||||
* The version of the OpenAPI document: 0.1.0
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
||||||
* https://openapi-generator.tech
|
|
||||||
* Do not edit the class manually.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import type { Configuration } from './configuration';
|
|
||||||
// Some imports not used depending on template conditions
|
|
||||||
// @ts-ignore
|
|
||||||
import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios';
|
|
||||||
import globalAxios from 'axios';
|
|
||||||
|
|
||||||
export const BASE_PATH = "https://app.openpipe.ai/api".replace(/\/+$/, "");
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const COLLECTION_FORMATS = {
|
|
||||||
csv: ",",
|
|
||||||
ssv: " ",
|
|
||||||
tsv: "\t",
|
|
||||||
pipes: "|",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @interface RequestArgs
|
|
||||||
*/
|
|
||||||
export interface RequestArgs {
|
|
||||||
url: string;
|
|
||||||
options: AxiosRequestConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @class BaseAPI
|
|
||||||
*/
|
|
||||||
export class BaseAPI {
|
|
||||||
protected configuration: Configuration | undefined;
|
|
||||||
|
|
||||||
constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) {
|
|
||||||
if (configuration) {
|
|
||||||
this.configuration = configuration;
|
|
||||||
this.basePath = configuration.basePath || this.basePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @class RequiredError
|
|
||||||
* @extends {Error}
|
|
||||||
*/
|
|
||||||
export class RequiredError extends Error {
|
|
||||||
constructor(public field: string, msg?: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "RequiredError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* OpenPipe API
|
|
||||||
* The public API for reporting API calls to OpenPipe
|
|
||||||
*
|
|
||||||
* The version of the OpenAPI document: 0.1.0
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
||||||
* https://openapi-generator.tech
|
|
||||||
* Do not edit the class manually.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import type { Configuration } from "./configuration";
|
|
||||||
import type { RequestArgs } from "./base";
|
|
||||||
import type { AxiosInstance, AxiosResponse } from 'axios';
|
|
||||||
import { RequiredError } from "./base";
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const DUMMY_BASE_URL = 'https://example.com'
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @throws {RequiredError}
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) {
|
|
||||||
if (paramValue === null || paramValue === undefined) {
|
|
||||||
throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) {
|
|
||||||
if (configuration && configuration.apiKey) {
|
|
||||||
const localVarApiKeyValue = typeof configuration.apiKey === 'function'
|
|
||||||
? await configuration.apiKey(keyParamName)
|
|
||||||
: await configuration.apiKey;
|
|
||||||
object[keyParamName] = localVarApiKeyValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const setBasicAuthToObject = function (object: any, configuration?: Configuration) {
|
|
||||||
if (configuration && (configuration.username || configuration.password)) {
|
|
||||||
object["auth"] = { username: configuration.username, password: configuration.password };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) {
|
|
||||||
if (configuration && configuration.accessToken) {
|
|
||||||
const accessToken = typeof configuration.accessToken === 'function'
|
|
||||||
? await configuration.accessToken()
|
|
||||||
: await configuration.accessToken;
|
|
||||||
object["Authorization"] = "Bearer " + accessToken;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) {
|
|
||||||
if (configuration && configuration.accessToken) {
|
|
||||||
const localVarAccessTokenValue = typeof configuration.accessToken === 'function'
|
|
||||||
? await configuration.accessToken(name, scopes)
|
|
||||||
: await configuration.accessToken;
|
|
||||||
object["Authorization"] = "Bearer " + localVarAccessTokenValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setFlattenedQueryParams(urlSearchParams: URLSearchParams, parameter: any, key: string = ""): void {
|
|
||||||
if (parameter == null) return;
|
|
||||||
if (typeof parameter === "object") {
|
|
||||||
if (Array.isArray(parameter)) {
|
|
||||||
(parameter as any[]).forEach(item => setFlattenedQueryParams(urlSearchParams, item, key));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Object.keys(parameter).forEach(currentKey =>
|
|
||||||
setFlattenedQueryParams(urlSearchParams, parameter[currentKey], `${key}${key !== '' ? '.' : ''}${currentKey}`)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (urlSearchParams.has(key)) {
|
|
||||||
urlSearchParams.append(key, parameter);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
urlSearchParams.set(key, parameter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const setSearchParams = function (url: URL, ...objects: any[]) {
|
|
||||||
const searchParams = new URLSearchParams(url.search);
|
|
||||||
setFlattenedQueryParams(searchParams, objects);
|
|
||||||
url.search = searchParams.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) {
|
|
||||||
const nonString = typeof value !== 'string';
|
|
||||||
const needsSerialization = nonString && configuration && configuration.isJsonMime
|
|
||||||
? configuration.isJsonMime(requestOptions.headers['Content-Type'])
|
|
||||||
: nonString;
|
|
||||||
return needsSerialization
|
|
||||||
? JSON.stringify(value !== undefined ? value : {})
|
|
||||||
: (value || "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const toPathString = function (url: URL) {
|
|
||||||
return url.pathname + url.search + url.hash
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) {
|
|
||||||
return <T = unknown, R = AxiosResponse<T>>(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
|
|
||||||
const axiosRequestArgs = {...axiosArgs.options, url: (configuration?.basePath || basePath) + axiosArgs.url};
|
|
||||||
return axios.request<T, R>(axiosRequestArgs);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* OpenPipe API
|
|
||||||
* The public API for reporting API calls to OpenPipe
|
|
||||||
*
|
|
||||||
* The version of the OpenAPI document: 0.1.0
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
||||||
* https://openapi-generator.tech
|
|
||||||
* Do not edit the class manually.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
export interface ConfigurationParameters {
|
|
||||||
apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
|
|
||||||
username?: string;
|
|
||||||
password?: string;
|
|
||||||
accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
|
|
||||||
basePath?: string;
|
|
||||||
baseOptions?: any;
|
|
||||||
formDataCtor?: new () => any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Configuration {
|
|
||||||
/**
|
|
||||||
* parameter for apiKey security
|
|
||||||
* @param name security name
|
|
||||||
* @memberof Configuration
|
|
||||||
*/
|
|
||||||
apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
|
|
||||||
/**
|
|
||||||
* parameter for basic security
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof Configuration
|
|
||||||
*/
|
|
||||||
username?: string;
|
|
||||||
/**
|
|
||||||
* parameter for basic security
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof Configuration
|
|
||||||
*/
|
|
||||||
password?: string;
|
|
||||||
/**
|
|
||||||
* parameter for oauth2 security
|
|
||||||
* @param name security name
|
|
||||||
* @param scopes oauth2 scope
|
|
||||||
* @memberof Configuration
|
|
||||||
*/
|
|
||||||
accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
|
|
||||||
/**
|
|
||||||
* override base path
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof Configuration
|
|
||||||
*/
|
|
||||||
basePath?: string;
|
|
||||||
/**
|
|
||||||
* base options for axios calls
|
|
||||||
*
|
|
||||||
* @type {any}
|
|
||||||
* @memberof Configuration
|
|
||||||
*/
|
|
||||||
baseOptions?: any;
|
|
||||||
/**
|
|
||||||
* The FormData constructor that will be used to create multipart form data
|
|
||||||
* requests. You can inject this here so that execution environments that
|
|
||||||
* do not support the FormData class can still run the generated client.
|
|
||||||
*
|
|
||||||
* @type {new () => FormData}
|
|
||||||
*/
|
|
||||||
formDataCtor?: new () => any;
|
|
||||||
|
|
||||||
constructor(param: ConfigurationParameters = {}) {
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
public isJsonMime(mime: string): boolean {
|
|
||||||
const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i');
|
|
||||||
return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
25
client-libs/typescript/src/codegen/core/ApiError.ts
Normal file
25
client-libs/typescript/src/codegen/core/ApiError.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||||
|
import type { ApiResult } from './ApiResult';
|
||||||
|
|
||||||
|
export class ApiError extends Error {
|
||||||
|
public readonly url: string;
|
||||||
|
public readonly status: number;
|
||||||
|
public readonly statusText: string;
|
||||||
|
public readonly body: any;
|
||||||
|
public readonly request: ApiRequestOptions;
|
||||||
|
|
||||||
|
constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
|
||||||
|
super(message);
|
||||||
|
|
||||||
|
this.name = 'ApiError';
|
||||||
|
this.url = response.url;
|
||||||
|
this.status = response.status;
|
||||||
|
this.statusText = response.statusText;
|
||||||
|
this.body = response.body;
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
client-libs/typescript/src/codegen/core/ApiRequestOptions.ts
Normal file
17
client-libs/typescript/src/codegen/core/ApiRequestOptions.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ApiRequestOptions = {
|
||||||
|
readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH';
|
||||||
|
readonly url: string;
|
||||||
|
readonly path?: Record<string, any>;
|
||||||
|
readonly cookies?: Record<string, any>;
|
||||||
|
readonly headers?: Record<string, any>;
|
||||||
|
readonly query?: Record<string, any>;
|
||||||
|
readonly formData?: Record<string, any>;
|
||||||
|
readonly body?: any;
|
||||||
|
readonly mediaType?: string;
|
||||||
|
readonly responseHeader?: string;
|
||||||
|
readonly errors?: Record<number, string>;
|
||||||
|
};
|
||||||
11
client-libs/typescript/src/codegen/core/ApiResult.ts
Normal file
11
client-libs/typescript/src/codegen/core/ApiResult.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type ApiResult = {
|
||||||
|
readonly url: string;
|
||||||
|
readonly ok: boolean;
|
||||||
|
readonly status: number;
|
||||||
|
readonly statusText: string;
|
||||||
|
readonly body: any;
|
||||||
|
};
|
||||||
14
client-libs/typescript/src/codegen/core/BaseHttpRequest.ts
Normal file
14
client-libs/typescript/src/codegen/core/BaseHttpRequest.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||||
|
import type { CancelablePromise } from './CancelablePromise';
|
||||||
|
import type { OpenAPIConfig } from './OpenAPI';
|
||||||
|
|
||||||
|
export abstract class BaseHttpRequest {
|
||||||
|
|
||||||
|
constructor(public readonly config: OpenAPIConfig) {}
|
||||||
|
|
||||||
|
public abstract request<T>(options: ApiRequestOptions): CancelablePromise<T>;
|
||||||
|
}
|
||||||
131
client-libs/typescript/src/codegen/core/CancelablePromise.ts
Normal file
131
client-libs/typescript/src/codegen/core/CancelablePromise.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export class CancelError extends Error {
|
||||||
|
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'CancelError';
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isCancelled(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OnCancel {
|
||||||
|
readonly isResolved: boolean;
|
||||||
|
readonly isRejected: boolean;
|
||||||
|
readonly isCancelled: boolean;
|
||||||
|
|
||||||
|
(cancelHandler: () => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CancelablePromise<T> implements Promise<T> {
|
||||||
|
#isResolved: boolean;
|
||||||
|
#isRejected: boolean;
|
||||||
|
#isCancelled: boolean;
|
||||||
|
readonly #cancelHandlers: (() => void)[];
|
||||||
|
readonly #promise: Promise<T>;
|
||||||
|
#resolve?: (value: T | PromiseLike<T>) => void;
|
||||||
|
#reject?: (reason?: any) => void;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
executor: (
|
||||||
|
resolve: (value: T | PromiseLike<T>) => void,
|
||||||
|
reject: (reason?: any) => void,
|
||||||
|
onCancel: OnCancel
|
||||||
|
) => void
|
||||||
|
) {
|
||||||
|
this.#isResolved = false;
|
||||||
|
this.#isRejected = false;
|
||||||
|
this.#isCancelled = false;
|
||||||
|
this.#cancelHandlers = [];
|
||||||
|
this.#promise = new Promise<T>((resolve, reject) => {
|
||||||
|
this.#resolve = resolve;
|
||||||
|
this.#reject = reject;
|
||||||
|
|
||||||
|
const onResolve = (value: T | PromiseLike<T>): void => {
|
||||||
|
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#isResolved = true;
|
||||||
|
this.#resolve?.(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onReject = (reason?: any): void => {
|
||||||
|
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#isRejected = true;
|
||||||
|
this.#reject?.(reason);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCancel = (cancelHandler: () => void): void => {
|
||||||
|
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#cancelHandlers.push(cancelHandler);
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.defineProperty(onCancel, 'isResolved', {
|
||||||
|
get: (): boolean => this.#isResolved,
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(onCancel, 'isRejected', {
|
||||||
|
get: (): boolean => this.#isRejected,
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(onCancel, 'isCancelled', {
|
||||||
|
get: (): boolean => this.#isCancelled,
|
||||||
|
});
|
||||||
|
|
||||||
|
return executor(onResolve, onReject, onCancel as OnCancel);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get [Symbol.toStringTag]() {
|
||||||
|
return "Cancellable Promise";
|
||||||
|
}
|
||||||
|
|
||||||
|
public then<TResult1 = T, TResult2 = never>(
|
||||||
|
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
|
||||||
|
onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
|
||||||
|
): Promise<TResult1 | TResult2> {
|
||||||
|
return this.#promise.then(onFulfilled, onRejected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public catch<TResult = never>(
|
||||||
|
onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
|
||||||
|
): Promise<T | TResult> {
|
||||||
|
return this.#promise.catch(onRejected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public finally(onFinally?: (() => void) | null): Promise<T> {
|
||||||
|
return this.#promise.finally(onFinally);
|
||||||
|
}
|
||||||
|
|
||||||
|
public cancel(): void {
|
||||||
|
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#isCancelled = true;
|
||||||
|
if (this.#cancelHandlers.length) {
|
||||||
|
try {
|
||||||
|
for (const cancelHandler of this.#cancelHandlers) {
|
||||||
|
cancelHandler();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Cancellation threw an error', error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.#cancelHandlers.length = 0;
|
||||||
|
this.#reject?.(new CancelError('Request aborted'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isCancelled(): boolean {
|
||||||
|
return this.#isCancelled;
|
||||||
|
}
|
||||||
|
}
|
||||||
26
client-libs/typescript/src/codegen/core/NodeHttpRequest.ts
Normal file
26
client-libs/typescript/src/codegen/core/NodeHttpRequest.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||||
|
import { BaseHttpRequest } from './BaseHttpRequest';
|
||||||
|
import type { CancelablePromise } from './CancelablePromise';
|
||||||
|
import type { OpenAPIConfig } from './OpenAPI';
|
||||||
|
import { request as __request } from './request';
|
||||||
|
|
||||||
|
export class NodeHttpRequest extends BaseHttpRequest {
|
||||||
|
|
||||||
|
constructor(config: OpenAPIConfig) {
|
||||||
|
super(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request method
|
||||||
|
* @param options The request options from the service
|
||||||
|
* @returns CancelablePromise<T>
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public override request<T>(options: ApiRequestOptions): CancelablePromise<T> {
|
||||||
|
return __request(this.config, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
client-libs/typescript/src/codegen/core/OpenAPI.ts
Normal file
32
client-libs/typescript/src/codegen/core/OpenAPI.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||||
|
|
||||||
|
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
|
||||||
|
type Headers = Record<string, string>;
|
||||||
|
|
||||||
|
export type OpenAPIConfig = {
|
||||||
|
BASE: string;
|
||||||
|
VERSION: string;
|
||||||
|
WITH_CREDENTIALS: boolean;
|
||||||
|
CREDENTIALS: 'include' | 'omit' | 'same-origin';
|
||||||
|
TOKEN?: string | Resolver<string> | undefined;
|
||||||
|
USERNAME?: string | Resolver<string> | undefined;
|
||||||
|
PASSWORD?: string | Resolver<string> | undefined;
|
||||||
|
HEADERS?: Headers | Resolver<Headers> | undefined;
|
||||||
|
ENCODE_PATH?: ((path: string) => string) | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OpenAPI: OpenAPIConfig = {
|
||||||
|
BASE: 'https://app.openpipe.ai/api/v1',
|
||||||
|
VERSION: '0.1.1',
|
||||||
|
WITH_CREDENTIALS: false,
|
||||||
|
CREDENTIALS: 'include',
|
||||||
|
TOKEN: undefined,
|
||||||
|
USERNAME: undefined,
|
||||||
|
PASSWORD: undefined,
|
||||||
|
HEADERS: undefined,
|
||||||
|
ENCODE_PATH: undefined,
|
||||||
|
};
|
||||||
341
client-libs/typescript/src/codegen/core/request.ts
Normal file
341
client-libs/typescript/src/codegen/core/request.ts
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import FormData from "form-data";
|
||||||
|
import fetch, { Headers } from "node-fetch";
|
||||||
|
import type { RequestInit, Response } from "node-fetch";
|
||||||
|
|
||||||
|
// @ts-expect-error TODO maybe I need an older node-fetch or something?
|
||||||
|
import type { AbortSignal } from "node-fetch/externals";
|
||||||
|
|
||||||
|
import { ApiError } from "./ApiError";
|
||||||
|
import type { ApiRequestOptions } from "./ApiRequestOptions";
|
||||||
|
import type { ApiResult } from "./ApiResult";
|
||||||
|
import { CancelablePromise } from "./CancelablePromise";
|
||||||
|
import type { OnCancel } from "./CancelablePromise";
|
||||||
|
import type { OpenAPIConfig } from "./OpenAPI";
|
||||||
|
|
||||||
|
export const isDefined = <T>(
|
||||||
|
value: T | null | undefined
|
||||||
|
): value is Exclude<T, null | undefined> => {
|
||||||
|
return value !== undefined && value !== null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isString = (value: any): value is string => {
|
||||||
|
return typeof value === "string";
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isStringWithValue = (value: any): value is string => {
|
||||||
|
return isString(value) && value !== "";
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isBlob = (value: any): value is Blob => {
|
||||||
|
return (
|
||||||
|
typeof value === "object" &&
|
||||||
|
typeof value.type === "string" &&
|
||||||
|
typeof value.stream === "function" &&
|
||||||
|
typeof value.arrayBuffer === "function" &&
|
||||||
|
typeof value.constructor === "function" &&
|
||||||
|
typeof value.constructor.name === "string" &&
|
||||||
|
/^(Blob|File)$/.test(value.constructor.name) &&
|
||||||
|
/^(Blob|File)$/.test(value[Symbol.toStringTag])
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isFormData = (value: any): value is FormData => {
|
||||||
|
return value instanceof FormData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const base64 = (str: string): string => {
|
||||||
|
try {
|
||||||
|
return btoa(str);
|
||||||
|
} catch (err) {
|
||||||
|
// @ts-ignore
|
||||||
|
return Buffer.from(str).toString("base64");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getQueryString = (params: Record<string, any>): string => {
|
||||||
|
const qs: string[] = [];
|
||||||
|
|
||||||
|
const append = (key: string, value: any) => {
|
||||||
|
qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const process = (key: string, value: any) => {
|
||||||
|
if (isDefined(value)) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value.forEach((v) => {
|
||||||
|
process(key, v);
|
||||||
|
});
|
||||||
|
} else if (typeof value === "object") {
|
||||||
|
Object.entries(value).forEach(([k, v]) => {
|
||||||
|
process(`${key}[${k}]`, v);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
append(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
|
process(key, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (qs.length > 0) {
|
||||||
|
return `?${qs.join("&")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
|
||||||
|
const encoder = config.ENCODE_PATH || encodeURI;
|
||||||
|
|
||||||
|
const path = options.url
|
||||||
|
.replace("{api-version}", config.VERSION)
|
||||||
|
.replace(/{(.*?)}/g, (substring: string, group: string) => {
|
||||||
|
if (options.path?.hasOwnProperty(group)) {
|
||||||
|
return encoder(String(options.path[group]));
|
||||||
|
}
|
||||||
|
return substring;
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = `${config.BASE}${path}`;
|
||||||
|
if (options.query) {
|
||||||
|
return `${url}${getQueryString(options.query)}`;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFormData = (options: ApiRequestOptions): FormData | undefined => {
|
||||||
|
if (options.formData) {
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
const process = (key: string, value: any) => {
|
||||||
|
if (isString(value) || isBlob(value)) {
|
||||||
|
formData.append(key, value);
|
||||||
|
} else {
|
||||||
|
formData.append(key, JSON.stringify(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.entries(options.formData)
|
||||||
|
.filter(([_, value]) => isDefined(value))
|
||||||
|
.forEach(([key, value]) => {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value.forEach((v) => process(key, v));
|
||||||
|
} else {
|
||||||
|
process(key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return formData;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
|
||||||
|
|
||||||
|
export const resolve = async <T>(
|
||||||
|
options: ApiRequestOptions,
|
||||||
|
resolver?: T | Resolver<T>
|
||||||
|
): Promise<T | undefined> => {
|
||||||
|
if (typeof resolver === "function") {
|
||||||
|
return (resolver as Resolver<T>)(options);
|
||||||
|
}
|
||||||
|
return resolver;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getHeaders = async (
|
||||||
|
config: OpenAPIConfig,
|
||||||
|
options: ApiRequestOptions
|
||||||
|
): Promise<Headers> => {
|
||||||
|
const token = await resolve(options, config.TOKEN);
|
||||||
|
const username = await resolve(options, config.USERNAME);
|
||||||
|
const password = await resolve(options, config.PASSWORD);
|
||||||
|
const additionalHeaders = await resolve(options, config.HEADERS);
|
||||||
|
|
||||||
|
const headers = Object.entries({
|
||||||
|
Accept: "application/json",
|
||||||
|
...additionalHeaders,
|
||||||
|
...options.headers,
|
||||||
|
})
|
||||||
|
.filter(([_, value]) => isDefined(value))
|
||||||
|
.reduce(
|
||||||
|
(headers, [key, value]) => ({
|
||||||
|
...headers,
|
||||||
|
[key]: String(value),
|
||||||
|
}),
|
||||||
|
{} as Record<string, string>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isStringWithValue(token)) {
|
||||||
|
headers["Authorization"] = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isStringWithValue(username) && isStringWithValue(password)) {
|
||||||
|
const credentials = base64(`${username}:${password}`);
|
||||||
|
headers["Authorization"] = `Basic ${credentials}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.body) {
|
||||||
|
if (options.mediaType) {
|
||||||
|
headers["Content-Type"] = options.mediaType;
|
||||||
|
} else if (isBlob(options.body)) {
|
||||||
|
headers["Content-Type"] = "application/octet-stream";
|
||||||
|
} else if (isString(options.body)) {
|
||||||
|
headers["Content-Type"] = "text/plain";
|
||||||
|
} else if (!isFormData(options.body)) {
|
||||||
|
headers["Content-Type"] = "application/json";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Headers(headers);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getRequestBody = (options: ApiRequestOptions): any => {
|
||||||
|
if (options.body !== undefined) {
|
||||||
|
if (options.mediaType?.includes("/json")) {
|
||||||
|
return JSON.stringify(options.body);
|
||||||
|
} else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) {
|
||||||
|
return options.body as any;
|
||||||
|
} else {
|
||||||
|
return JSON.stringify(options.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sendRequest = async (
|
||||||
|
options: ApiRequestOptions,
|
||||||
|
url: string,
|
||||||
|
body: any,
|
||||||
|
formData: FormData | undefined,
|
||||||
|
headers: Headers,
|
||||||
|
onCancel: OnCancel
|
||||||
|
): Promise<Response> => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
|
||||||
|
const request: RequestInit = {
|
||||||
|
headers,
|
||||||
|
method: options.method,
|
||||||
|
body: body ?? formData,
|
||||||
|
signal: controller.signal as AbortSignal,
|
||||||
|
};
|
||||||
|
|
||||||
|
onCancel(() => controller.abort());
|
||||||
|
|
||||||
|
return await fetch(url, request);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getResponseHeader = (
|
||||||
|
response: Response,
|
||||||
|
responseHeader?: string
|
||||||
|
): string | undefined => {
|
||||||
|
if (responseHeader) {
|
||||||
|
const content = response.headers.get(responseHeader);
|
||||||
|
if (isString(content)) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getResponseBody = async (response: Response): Promise<any> => {
|
||||||
|
if (response.status !== 204) {
|
||||||
|
try {
|
||||||
|
const contentType = response.headers.get("Content-Type");
|
||||||
|
if (contentType) {
|
||||||
|
const jsonTypes = ["application/json", "application/problem+json"];
|
||||||
|
const isJSON = jsonTypes.some((type) => contentType.toLowerCase().startsWith(type));
|
||||||
|
if (isJSON) {
|
||||||
|
return await response.json();
|
||||||
|
} else {
|
||||||
|
return await response.text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {
|
||||||
|
const errors: Record<number, string> = {
|
||||||
|
400: "Bad Request",
|
||||||
|
401: "Unauthorized",
|
||||||
|
403: "Forbidden",
|
||||||
|
404: "Not Found",
|
||||||
|
500: "Internal Server Error",
|
||||||
|
502: "Bad Gateway",
|
||||||
|
503: "Service Unavailable",
|
||||||
|
...options.errors,
|
||||||
|
};
|
||||||
|
|
||||||
|
const error = errors[result.status];
|
||||||
|
if (error) {
|
||||||
|
throw new ApiError(options, result, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.ok) {
|
||||||
|
const errorStatus = result.status ?? "unknown";
|
||||||
|
const errorStatusText = result.statusText ?? "unknown";
|
||||||
|
const errorBody = (() => {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(result.body, null, 2);
|
||||||
|
} catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
throw new ApiError(
|
||||||
|
options,
|
||||||
|
result,
|
||||||
|
`Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request method
|
||||||
|
* @param config The OpenAPI configuration object
|
||||||
|
* @param options The request options from the service
|
||||||
|
* @returns CancelablePromise<T>
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
export const request = <T>(
|
||||||
|
config: OpenAPIConfig,
|
||||||
|
options: ApiRequestOptions
|
||||||
|
): CancelablePromise<T> => {
|
||||||
|
return new CancelablePromise(async (resolve, reject, onCancel) => {
|
||||||
|
try {
|
||||||
|
const url = getUrl(config, options);
|
||||||
|
const formData = getFormData(options);
|
||||||
|
const body = getRequestBody(options);
|
||||||
|
const headers = await getHeaders(config, options);
|
||||||
|
|
||||||
|
if (!onCancel.isCancelled) {
|
||||||
|
const response = await sendRequest(options, url, body, formData, headers, onCancel);
|
||||||
|
const responseBody = await getResponseBody(response);
|
||||||
|
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||||
|
|
||||||
|
const result: ApiResult = {
|
||||||
|
url,
|
||||||
|
ok: response.ok,
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
body: responseHeader ?? responseBody,
|
||||||
|
};
|
||||||
|
|
||||||
|
catchErrorCodes(options, result);
|
||||||
|
|
||||||
|
resolve(result.body);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
|
|
||||||
#
|
|
||||||
# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com"
|
|
||||||
|
|
||||||
git_user_id=$1
|
|
||||||
git_repo_id=$2
|
|
||||||
release_note=$3
|
|
||||||
git_host=$4
|
|
||||||
|
|
||||||
if [ "$git_host" = "" ]; then
|
|
||||||
git_host="github.com"
|
|
||||||
echo "[INFO] No command line input provided. Set \$git_host to $git_host"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$git_user_id" = "" ]; then
|
|
||||||
git_user_id="GIT_USER_ID"
|
|
||||||
echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$git_repo_id" = "" ]; then
|
|
||||||
git_repo_id="GIT_REPO_ID"
|
|
||||||
echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$release_note" = "" ]; then
|
|
||||||
release_note="Minor update"
|
|
||||||
echo "[INFO] No command line input provided. Set \$release_note to $release_note"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Initialize the local directory as a Git repository
|
|
||||||
git init
|
|
||||||
|
|
||||||
# Adds the files in the local repository and stages them for commit.
|
|
||||||
git add .
|
|
||||||
|
|
||||||
# Commits the tracked changes and prepares them to be pushed to a remote repository.
|
|
||||||
git commit -m "$release_note"
|
|
||||||
|
|
||||||
# Sets the new remote
|
|
||||||
git_remote=$(git remote)
|
|
||||||
if [ "$git_remote" = "" ]; then # git remote not defined
|
|
||||||
|
|
||||||
if [ "$GIT_TOKEN" = "" ]; then
|
|
||||||
echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment."
|
|
||||||
git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git
|
|
||||||
else
|
|
||||||
git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git
|
|
||||||
fi
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
git pull origin master
|
|
||||||
|
|
||||||
# Pushes (Forces) the changes in the local repository up to the remote repository
|
|
||||||
echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git"
|
|
||||||
git push origin master 2>&1 | grep -v 'To https'
|
|
||||||
@@ -1,18 +1,13 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
/**
|
export { OPClient } from './OPClient';
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
export { ApiError } from './core/ApiError';
|
||||||
|
export { BaseHttpRequest } from './core/BaseHttpRequest';
|
||||||
|
export { CancelablePromise, CancelError } from './core/CancelablePromise';
|
||||||
|
export { OpenAPI } from './core/OpenAPI';
|
||||||
|
export type { OpenAPIConfig } from './core/OpenAPI';
|
||||||
|
|
||||||
export * from "./api";
|
export { DefaultService } from './services/DefaultService';
|
||||||
export * from "./configuration";
|
|
||||||
|
|
||||||
|
|||||||
118
client-libs/typescript/src/codegen/services/DefaultService.ts
Normal file
118
client-libs/typescript/src/codegen/services/DefaultService.ts
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do no edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||||
|
import type { BaseHttpRequest } from '../core/BaseHttpRequest';
|
||||||
|
|
||||||
|
export class DefaultService {
|
||||||
|
|
||||||
|
constructor(public readonly httpRequest: BaseHttpRequest) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a prompt is cached
|
||||||
|
* @param requestBody
|
||||||
|
* @returns any Successful response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public checkCache(
|
||||||
|
requestBody: {
|
||||||
|
/**
|
||||||
|
* Unix timestamp in milliseconds
|
||||||
|
*/
|
||||||
|
requestedAt: number;
|
||||||
|
/**
|
||||||
|
* JSON-encoded request payload
|
||||||
|
*/
|
||||||
|
reqPayload?: any;
|
||||||
|
/**
|
||||||
|
* Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }
|
||||||
|
*/
|
||||||
|
tags?: Record<string, string>;
|
||||||
|
},
|
||||||
|
): CancelablePromise<{
|
||||||
|
/**
|
||||||
|
* JSON-encoded response payload
|
||||||
|
*/
|
||||||
|
respPayload?: any;
|
||||||
|
}> {
|
||||||
|
return this.httpRequest.request({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/check-cache',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report an API call
|
||||||
|
* @param requestBody
|
||||||
|
* @returns any Successful response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public report(
|
||||||
|
requestBody: {
|
||||||
|
/**
|
||||||
|
* Unix timestamp in milliseconds
|
||||||
|
*/
|
||||||
|
requestedAt: number;
|
||||||
|
/**
|
||||||
|
* Unix timestamp in milliseconds
|
||||||
|
*/
|
||||||
|
receivedAt: number;
|
||||||
|
/**
|
||||||
|
* JSON-encoded request payload
|
||||||
|
*/
|
||||||
|
reqPayload?: any;
|
||||||
|
/**
|
||||||
|
* JSON-encoded response payload
|
||||||
|
*/
|
||||||
|
respPayload?: any;
|
||||||
|
/**
|
||||||
|
* HTTP status code of response
|
||||||
|
*/
|
||||||
|
statusCode?: number;
|
||||||
|
/**
|
||||||
|
* User-friendly error message
|
||||||
|
*/
|
||||||
|
errorMessage?: string;
|
||||||
|
/**
|
||||||
|
* Extra tags to attach to the call for filtering. Eg { "userId": "123", "promptId": "populate-title" }
|
||||||
|
*/
|
||||||
|
tags?: Record<string, string>;
|
||||||
|
},
|
||||||
|
): CancelablePromise<{
|
||||||
|
status: 'ok';
|
||||||
|
}> {
|
||||||
|
return this.httpRequest.request({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/report',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the latest logged call (only for local testing)
|
||||||
|
* @returns any Successful response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public localTestingOnlyGetLatestLoggedCall(): CancelablePromise<{
|
||||||
|
createdAt: string;
|
||||||
|
cacheHit: boolean;
|
||||||
|
tags: Record<string, string | null>;
|
||||||
|
modelResponse: {
|
||||||
|
id: string;
|
||||||
|
statusCode: number | null;
|
||||||
|
errorMessage: string | null;
|
||||||
|
reqPayload?: any;
|
||||||
|
respPayload?: any;
|
||||||
|
} | null;
|
||||||
|
} | null> {
|
||||||
|
return this.httpRequest.request({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/local-testing-only-get-latest-logged-call',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,90 +1,85 @@
|
|||||||
// import * as openPipeClient from "../codegen";
|
import * as openPipeClient from "../codegen";
|
||||||
// import * as openai from "openai-legacy";
|
import * as openai from "openai-legacy";
|
||||||
// import { version } from "../package.json";
|
import { version } from "../../package.json";
|
||||||
|
|
||||||
// // Anything we don't override we want to pass through to openai directly
|
// Anything we don't override we want to pass through to openai directly
|
||||||
// export * as openAILegacy from "openai-legacy";
|
export * as openAILegacy from "openai-legacy";
|
||||||
|
|
||||||
// type OPConfigurationParameters = {
|
type OPConfigurationParameters = {
|
||||||
// apiKey?: string;
|
apiKey?: string;
|
||||||
// basePath?: string;
|
basePath?: string;
|
||||||
// };
|
};
|
||||||
|
|
||||||
// export class Configuration extends openai.Configuration {
|
export class Configuration extends openai.Configuration {
|
||||||
// public qkConfig?: openPipeClient.Configuration;
|
public qkConfig?: openPipeClient.Configuration;
|
||||||
|
|
||||||
// constructor(
|
constructor(
|
||||||
// config: openai.ConfigurationParameters & {
|
config: openai.ConfigurationParameters & {
|
||||||
// opParameters?: OPConfigurationParameters;
|
opParameters?: OPConfigurationParameters;
|
||||||
// }
|
}
|
||||||
// ) {
|
) {
|
||||||
// super(config);
|
super(config);
|
||||||
// if (config.opParameters) {
|
if (config.opParameters) {
|
||||||
// this.qkConfig = new openPipeClient.Configuration(config.opParameters);
|
this.qkConfig = new openPipeClient.Configuration(config.opParameters);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// type CreateChatCompletion = InstanceType<
|
type CreateChatCompletion = InstanceType<typeof openai.OpenAIApi>["createChatCompletion"];
|
||||||
// typeof openai.OpenAIApi
|
|
||||||
// >["createChatCompletion"];
|
|
||||||
|
|
||||||
// export class OpenAIApi extends openai.OpenAIApi {
|
export class OpenAIApi extends openai.OpenAIApi {
|
||||||
// public openPipeApi?: openPipeClient.DefaultApi;
|
public openPipeApi?: openPipeClient.DefaultApi;
|
||||||
|
|
||||||
// constructor(config: Configuration) {
|
constructor(config: Configuration) {
|
||||||
// super(config);
|
super(config);
|
||||||
// if (config.qkConfig) {
|
if (config.qkConfig) {
|
||||||
// this.openPipeApi = new openPipeClient.DefaultApi(config.qkConfig);
|
this.openPipeApi = new openPipeClient.DefaultApi(config.qkConfig);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// public async createChatCompletion(
|
public async createChatCompletion(
|
||||||
// createChatCompletionRequest: Parameters<CreateChatCompletion>[0],
|
createChatCompletionRequest: Parameters<CreateChatCompletion>[0],
|
||||||
// options?: Parameters<CreateChatCompletion>[1]
|
options?: Parameters<CreateChatCompletion>[1]
|
||||||
// ): ReturnType<CreateChatCompletion> {
|
): ReturnType<CreateChatCompletion> {
|
||||||
// const requestedAt = Date.now();
|
const requestedAt = Date.now();
|
||||||
// let resp: Awaited<ReturnType<CreateChatCompletion>> | null = null;
|
let resp: Awaited<ReturnType<CreateChatCompletion>> | null = null;
|
||||||
// let respPayload: openai.CreateChatCompletionResponse | null = null;
|
let respPayload: openai.CreateChatCompletionResponse | null = null;
|
||||||
// let statusCode: number | undefined = undefined;
|
let statusCode: number | undefined = undefined;
|
||||||
// let errorMessage: string | undefined;
|
let errorMessage: string | undefined;
|
||||||
// try {
|
try {
|
||||||
// resp = await super.createChatCompletion(
|
resp = await super.createChatCompletion(createChatCompletionRequest, options);
|
||||||
// createChatCompletionRequest,
|
respPayload = resp.data;
|
||||||
// options
|
statusCode = resp.status;
|
||||||
// );
|
} catch (err) {
|
||||||
// respPayload = resp.data;
|
console.error("Error in createChatCompletion");
|
||||||
// statusCode = resp.status;
|
if ("isAxiosError" in err && err.isAxiosError) {
|
||||||
// } catch (err) {
|
errorMessage = err.response?.data?.error?.message;
|
||||||
// console.error("Error in createChatCompletion");
|
respPayload = err.response?.data;
|
||||||
// if ("isAxiosError" in err && err.isAxiosError) {
|
statusCode = err.response?.status;
|
||||||
// errorMessage = err.response?.data?.error?.message;
|
} else if ("message" in err) {
|
||||||
// respPayload = err.response?.data;
|
errorMessage = err.message.toString();
|
||||||
// statusCode = err.response?.status;
|
}
|
||||||
// } else if ("message" in err) {
|
throw err;
|
||||||
// errorMessage = err.message.toString();
|
} finally {
|
||||||
// }
|
this.openPipeApi
|
||||||
// throw err;
|
?.externalApiReport({
|
||||||
// } finally {
|
requestedAt,
|
||||||
// this.openPipeApi
|
receivedAt: Date.now(),
|
||||||
// ?.externalApiReport({
|
reqPayload: createChatCompletionRequest,
|
||||||
// requestedAt,
|
respPayload: respPayload,
|
||||||
// receivedAt: Date.now(),
|
statusCode: statusCode,
|
||||||
// reqPayload: createChatCompletionRequest,
|
errorMessage,
|
||||||
// respPayload: respPayload,
|
tags: {
|
||||||
// statusCode: statusCode,
|
client: "openai-js",
|
||||||
// errorMessage,
|
clientVersion: version,
|
||||||
// tags: {
|
},
|
||||||
// client: "openai-js",
|
})
|
||||||
// clientVersion: version,
|
.catch((err) => {
|
||||||
// },
|
console.error("Error reporting to OP", err);
|
||||||
// })
|
});
|
||||||
// .catch((err) => {
|
}
|
||||||
// console.error("Error reporting to OP", err);
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// console.log("done");
|
console.log("done");
|
||||||
// return resp;
|
return resp;
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|||||||
126
client-libs/typescript/src/openai/index.test.ts
Normal file
126
client-libs/typescript/src/openai/index.test.ts
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import dotenv from "dotenv";
|
||||||
|
import { expect, test } from "vitest";
|
||||||
|
import OpenAI from ".";
|
||||||
|
import {
|
||||||
|
CompletionCreateParams,
|
||||||
|
CreateChatCompletionRequestMessage,
|
||||||
|
} from "openai-beta/resources/chat/completions";
|
||||||
|
import { OPClient } from "../codegen";
|
||||||
|
|
||||||
|
dotenv.config({ path: "../.env" });
|
||||||
|
|
||||||
|
const oaiClient = new OpenAI({
|
||||||
|
apiKey: process.env.OPENAI_API_KEY,
|
||||||
|
openpipe: {
|
||||||
|
apiKey: process.env.OPENPIPE_API_KEY,
|
||||||
|
baseUrl: "http://localhost:3000/api/v1",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const opClient = new OPClient({
|
||||||
|
BASE: "http://localhost:3000/api/v1",
|
||||||
|
TOKEN: process.env.OPENPIPE_API_KEY,
|
||||||
|
});
|
||||||
|
|
||||||
|
const lastLoggedCall = async () => opClient.default.localTestingOnlyGetLatestLoggedCall();
|
||||||
|
|
||||||
|
test("basic call", async () => {
|
||||||
|
const payload: CompletionCreateParams = {
|
||||||
|
model: "gpt-3.5-turbo",
|
||||||
|
messages: [{ role: "system", content: "count to 3" }],
|
||||||
|
};
|
||||||
|
const completion = await oaiClient.chat.completions.create({
|
||||||
|
...payload,
|
||||||
|
openpipe: {
|
||||||
|
tags: { promptId: "test" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await completion.openpipe.reportingFinished;
|
||||||
|
const lastLogged = await lastLoggedCall();
|
||||||
|
expect(lastLogged?.modelResponse?.reqPayload).toMatchObject(payload);
|
||||||
|
expect(completion).toMatchObject(lastLogged?.modelResponse?.respPayload);
|
||||||
|
expect(lastLogged?.tags).toMatchObject({ promptId: "test" });
|
||||||
|
});
|
||||||
|
|
||||||
|
const randomString = (length: number) => {
|
||||||
|
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
return Array.from(
|
||||||
|
{ length },
|
||||||
|
() => characters[Math.floor(Math.random() * characters.length)]
|
||||||
|
).join("");
|
||||||
|
};
|
||||||
|
|
||||||
|
test.skip("streaming", async () => {
|
||||||
|
const completion = await oaiClient.chat.completions.create({
|
||||||
|
model: "gpt-3.5-turbo",
|
||||||
|
messages: [{ role: "system", content: "count to 4" }],
|
||||||
|
stream: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let merged = null;
|
||||||
|
for await (const chunk of completion) {
|
||||||
|
merged = merge_openai_chunks(merged, chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastLogged = await lastLoggedCall();
|
||||||
|
expect(lastLogged?.modelResponse?.respPayload.choices[0].message.content).toBe(
|
||||||
|
merged.choices[0].message.content
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.skip("bad call streaming", async () => {
|
||||||
|
try {
|
||||||
|
await oaiClient.chat.completions.create({
|
||||||
|
model: "gpt-3.5-turbo-blaster",
|
||||||
|
messages: [{ role: "system", content: "count to 10" }],
|
||||||
|
stream: true,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
const lastLogged = await lastLoggedCall();
|
||||||
|
expect(lastLogged?.modelResponse?.errorMessage).toBe(
|
||||||
|
"The model `gpt-3.5-turbo-blaster` does not exist"
|
||||||
|
);
|
||||||
|
expect(lastLogged?.modelResponse?.statusCode).toBe(404);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("bad call", async () => {
|
||||||
|
try {
|
||||||
|
await oaiClient.chat.completions.create({
|
||||||
|
model: "gpt-3.5-turbo-booster",
|
||||||
|
messages: [{ role: "system", content: "count to 10" }],
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
const lastLogged = await lastLoggedCall();
|
||||||
|
expect(lastLogged?.modelResponse?.errorMessage).toBe(
|
||||||
|
"The model `gpt-3.5-turbo-booster` does not exist"
|
||||||
|
);
|
||||||
|
expect(lastLogged?.modelResponse?.statusCode).toBe(404);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("caching", async () => {
|
||||||
|
const message: CreateChatCompletionRequestMessage = {
|
||||||
|
role: "system",
|
||||||
|
content: `repeat '${randomString(10)}'`,
|
||||||
|
};
|
||||||
|
const completion = await oaiClient.chat.completions.create({
|
||||||
|
model: "gpt-3.5-turbo",
|
||||||
|
messages: [message],
|
||||||
|
openpipe: { cache: true },
|
||||||
|
});
|
||||||
|
expect(completion.openpipe.cacheStatus).toBe("MISS");
|
||||||
|
|
||||||
|
await completion.openpipe.reportingFinished;
|
||||||
|
const firstLogged = await lastLoggedCall();
|
||||||
|
expect(completion.choices[0].message.content).toBe(
|
||||||
|
firstLogged?.modelResponse?.respPayload.choices[0].message.content
|
||||||
|
);
|
||||||
|
|
||||||
|
const completion2 = await oaiClient.chat.completions.create({
|
||||||
|
model: "gpt-3.5-turbo",
|
||||||
|
messages: [message],
|
||||||
|
openpipe: { cache: true },
|
||||||
|
});
|
||||||
|
expect(completion2.openpipe.cacheStatus).toBe("HIT");
|
||||||
|
});
|
||||||
@@ -1,109 +1,154 @@
|
|||||||
import * as openai from "openai-beta";
|
import * as openai from "openai-beta";
|
||||||
|
import * as Core from "openai-beta/core";
|
||||||
import { readEnv, type RequestOptions } from "openai-beta/core";
|
import { readEnv, type RequestOptions } from "openai-beta/core";
|
||||||
import { CompletionCreateParams } from "openai-beta/resources/chat/completions";
|
import {
|
||||||
import axios from "axios";
|
ChatCompletion,
|
||||||
|
ChatCompletionChunk,
|
||||||
|
CompletionCreateParams,
|
||||||
|
Completions,
|
||||||
|
} from "openai-beta/resources/chat/completions";
|
||||||
|
|
||||||
import * as openPipeClient from "../codegen";
|
import { DefaultService, OPClient } from "../codegen";
|
||||||
|
import { Stream } from "openai-beta/streaming";
|
||||||
interface ClientOptions extends openai.ClientOptions {
|
import { OpenPipeArgs, OpenPipeMeta, type OpenPipeConfig, getTags } from "../shared";
|
||||||
openPipeApiKey?: string;
|
|
||||||
openPipeBaseUrl?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
export type ClientOptions = openai.ClientOptions & { openpipe?: OpenPipeConfig };
|
||||||
export default class OpenAI extends openai.OpenAI {
|
export default class OpenAI extends openai.OpenAI {
|
||||||
public openPipeApi?: openPipeClient.DefaultApi;
|
public opClient?: OPClient;
|
||||||
|
|
||||||
constructor({
|
constructor({ openpipe, ...options }: ClientOptions = {}) {
|
||||||
openPipeApiKey = readEnv("OPENPIPE_API_KEY"),
|
super({ ...options });
|
||||||
openPipeBaseUrl = readEnv("OPENPIPE_BASE_URL") ?? `https://app.openpipe.ai/v1`,
|
|
||||||
...opts
|
const openPipeApiKey = openpipe?.apiKey ?? readEnv("OPENPIPE_API_KEY");
|
||||||
}: ClientOptions = {}) {
|
|
||||||
super({ ...opts });
|
|
||||||
|
|
||||||
if (openPipeApiKey) {
|
if (openPipeApiKey) {
|
||||||
const axiosInstance = axios.create({
|
this.chat.setClient(
|
||||||
baseURL: openPipeBaseUrl,
|
new OPClient({
|
||||||
headers: {
|
BASE:
|
||||||
Authorization: `Bearer ${openPipeApiKey}`,
|
openpipe?.baseUrl ?? readEnv("OPENPIPE_BASE_URL") ?? "https://app.openpipe.ai/api/v1",
|
||||||
},
|
TOKEN: openPipeApiKey,
|
||||||
});
|
})
|
||||||
this.openPipeApi = new openPipeClient.DefaultApi(
|
|
||||||
new openPipeClient.Configuration({
|
|
||||||
apiKey: openPipeApiKey,
|
|
||||||
basePath: openPipeBaseUrl,
|
|
||||||
}),
|
|
||||||
undefined,
|
|
||||||
axiosInstance
|
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
|
console.warn(
|
||||||
// Override the chat property
|
"You're using the OpenPipe client without an API key. No completion requests will be logged."
|
||||||
this.chat = new ExtendedChat(this);
|
|
||||||
|
|
||||||
if (openPipeApiKey === undefined) {
|
|
||||||
console.error(
|
|
||||||
"The OPENPIPE_API_KEY environment variable is missing or empty; either provide it, or instantiate the OpenPipe client with an openPipeApiKey option, like new OpenPipe({ openPipeApiKey: undefined })."
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
chat: WrappedChat = new WrappedChat(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExtendedChat extends openai.OpenAI.Chat {
|
class WrappedChat extends openai.OpenAI.Chat {
|
||||||
completions: ExtendedCompletions;
|
setClient(client: OPClient) {
|
||||||
|
this.completions.opClient = client;
|
||||||
constructor(openaiInstance: OpenAI) {
|
|
||||||
super(openaiInstance);
|
|
||||||
// Initialize the new completions instance
|
|
||||||
this.completions = new ExtendedCompletions(openaiInstance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
completions: InstrumentedCompletions = new InstrumentedCompletions(this.client);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExtendedCompletions extends openai.OpenAI.Chat.Completions {
|
class InstrumentedCompletions extends openai.OpenAI.Chat.Completions {
|
||||||
private openaiInstance: OpenAI;
|
opClient?: OPClient;
|
||||||
|
|
||||||
constructor(openaiInstance: OpenAI) {
|
constructor(client: openai.OpenAI, opClient?: OPClient) {
|
||||||
super(openaiInstance);
|
super(client);
|
||||||
this.openaiInstance = openaiInstance;
|
this.opClient = opClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_report(args: Parameters<DefaultService["report"]>[0]) {
|
||||||
|
try {
|
||||||
|
return this.opClient ? this.opClient.default.report(args) : Promise.resolve();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
create(
|
||||||
|
body: CompletionCreateParams.CreateChatCompletionRequestNonStreaming & OpenPipeArgs,
|
||||||
|
options?: Core.RequestOptions
|
||||||
|
): Promise<Core.APIResponse<ChatCompletion & { openpipe: OpenPipeMeta }>>;
|
||||||
|
create(
|
||||||
|
body: CompletionCreateParams.CreateChatCompletionRequestStreaming & OpenPipeArgs,
|
||||||
|
options?: Core.RequestOptions
|
||||||
|
): Promise<Core.APIResponse<Stream<ChatCompletionChunk>>>;
|
||||||
async create(
|
async create(
|
||||||
params:
|
{ openpipe, ...body }: CompletionCreateParams & OpenPipeArgs,
|
||||||
| CompletionCreateParams.CreateChatCompletionRequestNonStreaming
|
options?: Core.RequestOptions
|
||||||
| CompletionCreateParams.CreateChatCompletionRequestStreaming,
|
): Promise<
|
||||||
options?: RequestOptions,
|
Core.APIResponse<(ChatCompletion & { openpipe: OpenPipeMeta }) | Stream<ChatCompletionChunk>>
|
||||||
tags?: Record<string, string>
|
> {
|
||||||
): Promise<any> {
|
console.log("LALALA REPORT", this.opClient);
|
||||||
// // Your pre API call logic here
|
const requestedAt = Date.now();
|
||||||
// console.log("Doing pre API call...");
|
const cacheRequested = openpipe?.cache ?? false;
|
||||||
|
|
||||||
// // Determine the type of request
|
if (cacheRequested) {
|
||||||
// if (params.hasOwnProperty("stream") && params.stream === true) {
|
try {
|
||||||
// const result = await super.create(
|
const cached = await this.opClient?.default
|
||||||
// params as CompletionCreateParams.CreateChatCompletionRequestStreaming,
|
.checkCache({
|
||||||
// options
|
requestedAt,
|
||||||
// );
|
reqPayload: body,
|
||||||
// // Your post API call logic here
|
tags: getTags(openpipe),
|
||||||
// console.log("Doing post API call for Streaming...");
|
})
|
||||||
// return result;
|
.then((res) => res.respPayload);
|
||||||
// } else {
|
|
||||||
// const requestedAt = Date.now();
|
|
||||||
const result = await super.create(
|
|
||||||
params as CompletionCreateParams.CreateChatCompletionRequestNonStreaming,
|
|
||||||
options
|
|
||||||
);
|
|
||||||
return result;
|
|
||||||
// await this.openaiInstance.openPipeApi?.externalApiReport({
|
|
||||||
// requestedAt,
|
|
||||||
// receivedAt: Date.now(),
|
|
||||||
// reqPayload: params,
|
|
||||||
// respPayload: result,
|
|
||||||
// statusCode: 200,
|
|
||||||
// errorMessage: undefined,
|
|
||||||
// tags,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// console.log("GOT RESULT", result);
|
if (cached) {
|
||||||
// return result;
|
return {
|
||||||
// }
|
...cached,
|
||||||
|
openpipe: {
|
||||||
|
cacheStatus: "HIT",
|
||||||
|
reportingFinished: Promise.resolve(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let reportingFinished: OpenPipeMeta["reportingFinished"] = Promise.resolve();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (body.stream) {
|
||||||
|
const stream = await super.create(body, options);
|
||||||
|
|
||||||
|
// Do some logging of each chunk here
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
} else {
|
||||||
|
const response = await super.create(body, options);
|
||||||
|
|
||||||
|
reportingFinished = this._report({
|
||||||
|
requestedAt,
|
||||||
|
receivedAt: Date.now(),
|
||||||
|
reqPayload: body,
|
||||||
|
respPayload: response,
|
||||||
|
statusCode: 200,
|
||||||
|
tags: getTags(openpipe),
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
...response,
|
||||||
|
openpipe: {
|
||||||
|
cacheStatus: cacheRequested ? "MISS" : "SKIP",
|
||||||
|
reportingFinished,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (error instanceof openai.APIError) {
|
||||||
|
const rawMessage = error.message as string | string[];
|
||||||
|
const message = Array.isArray(rawMessage) ? rawMessage.join(", ") : rawMessage;
|
||||||
|
reportingFinished = this._report({
|
||||||
|
requestedAt,
|
||||||
|
receivedAt: Date.now(),
|
||||||
|
reqPayload: body,
|
||||||
|
respPayload: error.error,
|
||||||
|
statusCode: error.status,
|
||||||
|
errorMessage: message,
|
||||||
|
tags: getTags(openpipe),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
42
client-libs/typescript/src/openai/mergeChunks.ts
Normal file
42
client-libs/typescript/src/openai/mergeChunks.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { ChatCompletion, ChatCompletionChunk } from "openai-beta/resources/chat";
|
||||||
|
|
||||||
|
export default function mergeChunks(
|
||||||
|
base: ChatCompletion | null,
|
||||||
|
chunk: ChatCompletionChunk
|
||||||
|
): ChatCompletion {
|
||||||
|
if (base === null) {
|
||||||
|
return mergeChunks({ ...chunk, choices: [] }, chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
const choices = [...base.choices];
|
||||||
|
for (const choice of chunk.choices) {
|
||||||
|
const baseChoice = choices.find((c) => c.index === choice.index);
|
||||||
|
if (baseChoice) {
|
||||||
|
baseChoice.finish_reason = choice.finish_reason ?? baseChoice.finish_reason;
|
||||||
|
baseChoice.message = baseChoice.message ?? { role: "assistant" };
|
||||||
|
|
||||||
|
if (choice.delta?.content)
|
||||||
|
baseChoice.message.content =
|
||||||
|
((baseChoice.message.content as string) ?? "") + (choice.delta.content ?? "");
|
||||||
|
if (choice.delta?.function_call) {
|
||||||
|
const fnCall = baseChoice.message.function_call ?? {};
|
||||||
|
fnCall.name =
|
||||||
|
((fnCall.name as string) ?? "") + ((choice.delta.function_call.name as string) ?? "");
|
||||||
|
fnCall.arguments =
|
||||||
|
((fnCall.arguments as string) ?? "") +
|
||||||
|
((choice.delta.function_call.arguments as string) ?? "");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// @ts-expect-error the types are correctly telling us that finish_reason
|
||||||
|
// could be null, but don't want to fix it right now.
|
||||||
|
choices.push({ ...omit(choice, "delta"), message: { role: "assistant", ...choice.delta } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const merged: ChatCompletion = {
|
||||||
|
...base,
|
||||||
|
choices,
|
||||||
|
};
|
||||||
|
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
26
client-libs/typescript/src/shared.ts
Normal file
26
client-libs/typescript/src/shared.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import pkg from "../package.json";
|
||||||
|
|
||||||
|
export type OpenPipeConfig = {
|
||||||
|
apiKey?: string;
|
||||||
|
baseUrl?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OpenPipeArgs = {
|
||||||
|
openpipe?: { cache?: boolean; tags?: Record<string, string> };
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OpenPipeMeta = {
|
||||||
|
cacheStatus: "HIT" | "MISS" | "SKIP";
|
||||||
|
|
||||||
|
// We report your call to OpenPipe asynchronously in the background. If you
|
||||||
|
// need to wait until the report is sent to take further action, you can await
|
||||||
|
// this promise.
|
||||||
|
reportingFinished: Promise<void | { status: "ok" }>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTags = (args: OpenPipeArgs["openpipe"]): Record<string, string> => ({
|
||||||
|
...args?.tags,
|
||||||
|
...(args?.cache ? { $cache: args.cache?.toString() } : {}),
|
||||||
|
$sdk: "typescript",
|
||||||
|
"$sdk.version": pkg.version,
|
||||||
|
});
|
||||||
@@ -15,10 +15,7 @@
|
|||||||
"incremental": true,
|
"incremental": true,
|
||||||
"noUncheckedIndexedAccess": true,
|
"noUncheckedIndexedAccess": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"outDir": "dist",
|
"outDir": "dist"
|
||||||
"paths": {
|
|
||||||
"~/*": ["./src/*"]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts"],
|
"include": ["src/**/*.ts"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
|
|||||||
162
pnpm-lock.yaml
generated
162
pnpm-lock.yaml
generated
@@ -321,6 +321,9 @@ importers:
|
|||||||
openapi-typescript:
|
openapi-typescript:
|
||||||
specifier: ^6.3.4
|
specifier: ^6.3.4
|
||||||
version: 6.3.4
|
version: 6.3.4
|
||||||
|
openapi-typescript-codegen:
|
||||||
|
specifier: ^0.25.0
|
||||||
|
version: 0.25.0
|
||||||
prisma:
|
prisma:
|
||||||
specifier: ^4.14.0
|
specifier: ^4.14.0
|
||||||
version: 4.14.0
|
version: 4.14.0
|
||||||
@@ -339,9 +342,12 @@ importers:
|
|||||||
|
|
||||||
client-libs/typescript:
|
client-libs/typescript:
|
||||||
dependencies:
|
dependencies:
|
||||||
axios:
|
form-data:
|
||||||
specifier: ^0.26.0
|
specifier: ^4.0.0
|
||||||
version: 0.26.0
|
version: 4.0.0
|
||||||
|
node-fetch:
|
||||||
|
specifier: ^3.3.2
|
||||||
|
version: 3.3.2
|
||||||
openai-beta:
|
openai-beta:
|
||||||
specifier: npm:openai@4.0.0-beta.7
|
specifier: npm:openai@4.0.0-beta.7
|
||||||
version: /openai@4.0.0-beta.7
|
version: /openai@4.0.0-beta.7
|
||||||
@@ -361,6 +367,9 @@ importers:
|
|||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.0.4
|
specifier: ^5.0.4
|
||||||
version: 5.0.4
|
version: 5.0.4
|
||||||
|
vitest:
|
||||||
|
specifier: ^0.33.0
|
||||||
|
version: 0.33.0
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -402,6 +411,15 @@ packages:
|
|||||||
lodash.clonedeep: 4.5.0
|
lodash.clonedeep: 4.5.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@apidevtools/json-schema-ref-parser@9.0.9:
|
||||||
|
resolution: {integrity: sha512-GBD2Le9w2+lVFoc4vswGI/TjkNIZSVp7+9xPf+X3uidBfWnAeUWmquteSyt0+VCrhNMWj/FTABISQrD3Z/YA+w==}
|
||||||
|
dependencies:
|
||||||
|
'@jsdevtools/ono': 7.1.3
|
||||||
|
'@types/json-schema': 7.0.12
|
||||||
|
call-me-maybe: 1.0.2
|
||||||
|
js-yaml: 4.1.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@babel/code-frame@7.22.10:
|
/@babel/code-frame@7.22.10:
|
||||||
resolution: {integrity: sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==}
|
resolution: {integrity: sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -2398,7 +2416,6 @@ packages:
|
|||||||
|
|
||||||
/@jsdevtools/ono@7.1.3:
|
/@jsdevtools/ono@7.1.3:
|
||||||
resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
|
resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@monaco-editor/loader@1.3.3(monaco-editor@0.40.0):
|
/@monaco-editor/loader@1.3.3(monaco-editor@0.40.0):
|
||||||
resolution: {integrity: sha512-6KKF4CTzcJiS8BJwtxtfyYt9shBiEv32ateQ9T4UVogwn4HM/uPo9iJd2Dmbkpz8CM6Y0PDUpjnZzCwC+eYo2Q==}
|
resolution: {integrity: sha512-6KKF4CTzcJiS8BJwtxtfyYt9shBiEv32ateQ9T4UVogwn4HM/uPo9iJd2Dmbkpz8CM6Y0PDUpjnZzCwC+eYo2Q==}
|
||||||
@@ -2958,7 +2975,7 @@ packages:
|
|||||||
/@types/connect@3.4.35:
|
/@types/connect@3.4.35:
|
||||||
resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
|
resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.16.0
|
'@types/node': 20.4.10
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/cookie@0.4.1:
|
/@types/cookie@0.4.1:
|
||||||
@@ -3062,7 +3079,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
|
resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/minimatch': 5.1.2
|
'@types/minimatch': 5.1.2
|
||||||
'@types/node': 18.16.0
|
'@types/node': 20.4.10
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/hast@2.3.5:
|
/@types/hast@2.3.5:
|
||||||
@@ -3122,7 +3139,7 @@ packages:
|
|||||||
/@types/node-fetch@2.6.4:
|
/@types/node-fetch@2.6.4:
|
||||||
resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
|
resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.16.0
|
'@types/node': 20.4.10
|
||||||
form-data: 3.0.1
|
form-data: 3.0.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@@ -3206,7 +3223,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==}
|
resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mime': 1.3.2
|
'@types/mime': 1.3.2
|
||||||
'@types/node': 18.16.0
|
'@types/node': 20.4.10
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/serve-static@1.15.2:
|
/@types/serve-static@1.15.2:
|
||||||
@@ -3933,12 +3950,16 @@ packages:
|
|||||||
|
|
||||||
/call-me-maybe@1.0.2:
|
/call-me-maybe@1.0.2:
|
||||||
resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
|
resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/callsites@3.1.0:
|
/callsites@3.1.0:
|
||||||
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
/camelcase@6.3.0:
|
||||||
|
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/camelize@1.0.1:
|
/camelize@1.0.1:
|
||||||
resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
|
resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -4116,6 +4137,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==}
|
resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/commander@11.0.0:
|
||||||
|
resolution: {integrity: sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==}
|
||||||
|
engines: {node: '>=16'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/commander@2.20.3:
|
/commander@2.20.3:
|
||||||
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
|
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
|
||||||
|
|
||||||
@@ -4379,6 +4405,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
|
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/data-uri-to-buffer@4.0.1:
|
||||||
|
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
|
||||||
|
engines: {node: '>= 12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/date-fns@2.30.0:
|
/date-fns@2.30.0:
|
||||||
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
|
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
|
||||||
engines: {node: '>=0.11'}
|
engines: {node: '>=0.11'}
|
||||||
@@ -4614,7 +4645,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/cookie': 0.4.1
|
'@types/cookie': 0.4.1
|
||||||
'@types/cors': 2.8.13
|
'@types/cors': 2.8.13
|
||||||
'@types/node': 18.16.0
|
'@types/node': 20.4.10
|
||||||
accepts: 1.3.8
|
accepts: 1.3.8
|
||||||
base64id: 2.0.0
|
base64id: 2.0.0
|
||||||
cookie: 0.4.2
|
cookie: 0.4.2
|
||||||
@@ -5250,6 +5281,14 @@ packages:
|
|||||||
format: 0.2.2
|
format: 0.2.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/fetch-blob@3.2.0:
|
||||||
|
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
|
||||||
|
engines: {node: ^12.20 || >= 14.13}
|
||||||
|
dependencies:
|
||||||
|
node-domexception: 1.0.0
|
||||||
|
web-streams-polyfill: 3.2.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/fflate@0.4.8:
|
/fflate@0.4.8:
|
||||||
resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
|
resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -5367,6 +5406,13 @@ packages:
|
|||||||
web-streams-polyfill: 4.0.0-beta.3
|
web-streams-polyfill: 4.0.0-beta.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/formdata-polyfill@4.0.10:
|
||||||
|
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
||||||
|
engines: {node: '>=12.20.0'}
|
||||||
|
dependencies:
|
||||||
|
fetch-blob: 3.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/forwarded@0.2.0:
|
/forwarded@0.2.0:
|
||||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@@ -5401,6 +5447,15 @@ packages:
|
|||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/fs-extra@11.1.1:
|
||||||
|
resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
|
||||||
|
engines: {node: '>=14.14'}
|
||||||
|
dependencies:
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jsonfile: 6.1.0
|
||||||
|
universalify: 2.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/fs.realpath@1.0.0:
|
/fs.realpath@1.0.0:
|
||||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||||
|
|
||||||
@@ -5620,6 +5675,19 @@ packages:
|
|||||||
uncrypto: 0.1.3
|
uncrypto: 0.1.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/handlebars@4.7.8:
|
||||||
|
resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==}
|
||||||
|
engines: {node: '>=0.4.7'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
minimist: 1.2.8
|
||||||
|
neo-async: 2.6.2
|
||||||
|
source-map: 0.6.1
|
||||||
|
wordwrap: 1.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
uglify-js: 3.17.4
|
||||||
|
dev: true
|
||||||
|
|
||||||
/has-bigints@1.0.2:
|
/has-bigints@1.0.2:
|
||||||
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
|
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -6014,7 +6082,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
|
resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
|
||||||
engines: {node: '>= 10.13.0'}
|
engines: {node: '>= 10.13.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.4.10
|
'@types/node': 18.16.0
|
||||||
merge-stream: 2.0.0
|
merge-stream: 2.0.0
|
||||||
supports-color: 8.1.1
|
supports-color: 8.1.1
|
||||||
|
|
||||||
@@ -6049,6 +6117,14 @@ packages:
|
|||||||
/json-parse-even-better-errors@2.3.1:
|
/json-parse-even-better-errors@2.3.1:
|
||||||
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
|
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
|
||||||
|
|
||||||
|
/json-schema-ref-parser@9.0.9:
|
||||||
|
resolution: {integrity: sha512-qcP2lmGy+JUoQJ4DOQeLaZDqH9qSkeGCK3suKWxJXS82dg728Mn3j97azDMaOUmJAN4uCq91LdPx4K7E8F1a7Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
deprecated: Please switch to @apidevtools/json-schema-ref-parser
|
||||||
|
dependencies:
|
||||||
|
'@apidevtools/json-schema-ref-parser': 9.0.9
|
||||||
|
dev: true
|
||||||
|
|
||||||
/json-schema-to-typescript@13.0.2:
|
/json-schema-to-typescript@13.0.2:
|
||||||
resolution: {integrity: sha512-TCaEVW4aI2FmMQe7f98mvr3/oiVmXEC1xZjkTZ9L/BSoTXFlC7p64mD5AD2d8XWycNBQZUnHwXL5iVXt1HWwNQ==}
|
resolution: {integrity: sha512-TCaEVW4aI2FmMQe7f98mvr3/oiVmXEC1xZjkTZ9L/BSoTXFlC7p64mD5AD2d8XWycNBQZUnHwXL5iVXt1HWwNQ==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
@@ -6097,6 +6173,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
|
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/jsonfile@6.1.0:
|
||||||
|
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
|
||||||
|
dependencies:
|
||||||
|
universalify: 2.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
dev: true
|
||||||
|
|
||||||
/jsonschema@1.4.1:
|
/jsonschema@1.4.1:
|
||||||
resolution: {integrity: sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==}
|
resolution: {integrity: sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -6558,6 +6642,15 @@ packages:
|
|||||||
whatwg-url: 5.0.0
|
whatwg-url: 5.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/node-fetch@3.3.2:
|
||||||
|
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
|
||||||
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
dependencies:
|
||||||
|
data-uri-to-buffer: 4.0.1
|
||||||
|
fetch-blob: 3.2.0
|
||||||
|
formdata-polyfill: 4.0.10
|
||||||
|
dev: false
|
||||||
|
|
||||||
/node-mocks-http@1.12.2:
|
/node-mocks-http@1.12.2:
|
||||||
resolution: {integrity: sha512-xhWwC0dh35R9rf0j3bRZXuISXdHxxtMx0ywZQBwjrg3yl7KpRETzogfeCamUIjltpn0Fxvs/ZhGJul1vPLrdJQ==}
|
resolution: {integrity: sha512-xhWwC0dh35R9rf0j3bRZXuISXdHxxtMx0ywZQBwjrg3yl7KpRETzogfeCamUIjltpn0Fxvs/ZhGJul1vPLrdJQ==}
|
||||||
engines: {node: '>=0.6'}
|
engines: {node: '>=0.6'}
|
||||||
@@ -6715,6 +6808,17 @@ packages:
|
|||||||
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
|
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/openapi-typescript-codegen@0.25.0:
|
||||||
|
resolution: {integrity: sha512-nN/TnIcGbP58qYgwEEy5FrAAjePcYgfMaCe3tsmYyTgI3v4RR9v8os14L+LEWDvV50+CmqiyTzRkKKtJeb6Ybg==}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
camelcase: 6.3.0
|
||||||
|
commander: 11.0.0
|
||||||
|
fs-extra: 11.1.1
|
||||||
|
handlebars: 4.7.8
|
||||||
|
json-schema-ref-parser: 9.0.9
|
||||||
|
dev: true
|
||||||
|
|
||||||
/openapi-typescript@5.4.1:
|
/openapi-typescript@5.4.1:
|
||||||
resolution: {integrity: sha512-AGB2QiZPz4rE7zIwV3dRHtoUC/CWHhUjuzGXvtmMQN2AFV8xCTLKcZUHLcdPQmt/83i22nRE7+TxXOXkK+gf4Q==}
|
resolution: {integrity: sha512-AGB2QiZPz4rE7zIwV3dRHtoUC/CWHhUjuzGXvtmMQN2AFV8xCTLKcZUHLcdPQmt/83i22nRE7+TxXOXkK+gf4Q==}
|
||||||
engines: {node: '>= 14.0.0'}
|
engines: {node: '>= 14.0.0'}
|
||||||
@@ -8355,6 +8459,14 @@ packages:
|
|||||||
/ufo@1.2.0:
|
/ufo@1.2.0:
|
||||||
resolution: {integrity: sha512-RsPyTbqORDNDxqAdQPQBpgqhWle1VcTSou/FraClYlHf6TZnQcGslpLcAphNR+sQW4q5lLWLbOsRlh9j24baQg==}
|
resolution: {integrity: sha512-RsPyTbqORDNDxqAdQPQBpgqhWle1VcTSou/FraClYlHf6TZnQcGslpLcAphNR+sQW4q5lLWLbOsRlh9j24baQg==}
|
||||||
|
|
||||||
|
/uglify-js@3.17.4:
|
||||||
|
resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==}
|
||||||
|
engines: {node: '>=0.8.0'}
|
||||||
|
hasBin: true
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/unbox-primitive@1.0.2:
|
/unbox-primitive@1.0.2:
|
||||||
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
|
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -8382,6 +8494,11 @@ packages:
|
|||||||
tiny-inflate: 1.0.3
|
tiny-inflate: 1.0.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/universalify@2.0.0:
|
||||||
|
resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
|
||||||
|
engines: {node: '>= 10.0.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/unpipe@1.0.0:
|
/unpipe@1.0.0:
|
||||||
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
|
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
@@ -8547,7 +8664,7 @@ packages:
|
|||||||
d3-timer: 3.0.1
|
d3-timer: 3.0.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/vite-node@0.33.0(@types/node@18.16.0):
|
/vite-node@0.33.0(@types/node@20.4.10):
|
||||||
resolution: {integrity: sha512-19FpHYbwWWxDr73ruNahC+vtEdza52kA90Qb3La98yZ0xULqV8A5JLNPUff0f5zID4984tW7l3DH2przTJUZSw==}
|
resolution: {integrity: sha512-19FpHYbwWWxDr73ruNahC+vtEdza52kA90Qb3La98yZ0xULqV8A5JLNPUff0f5zID4984tW7l3DH2przTJUZSw==}
|
||||||
engines: {node: '>=v14.18.0'}
|
engines: {node: '>=v14.18.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -8557,7 +8674,7 @@ packages:
|
|||||||
mlly: 1.4.0
|
mlly: 1.4.0
|
||||||
pathe: 1.1.1
|
pathe: 1.1.1
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
vite: 4.4.9(@types/node@18.16.0)
|
vite: 4.4.9(@types/node@20.4.10)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
- less
|
- less
|
||||||
@@ -8585,7 +8702,7 @@ packages:
|
|||||||
- typescript
|
- typescript
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/vite@4.4.9(@types/node@18.16.0):
|
/vite@4.4.9(@types/node@20.4.10):
|
||||||
resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==}
|
resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==}
|
||||||
engines: {node: ^14.18.0 || >=16.0.0}
|
engines: {node: ^14.18.0 || >=16.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -8613,7 +8730,7 @@ packages:
|
|||||||
terser:
|
terser:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.16.0
|
'@types/node': 20.4.10
|
||||||
esbuild: 0.18.20
|
esbuild: 0.18.20
|
||||||
postcss: 8.4.27
|
postcss: 8.4.27
|
||||||
rollup: 3.28.0
|
rollup: 3.28.0
|
||||||
@@ -8654,7 +8771,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/chai': 4.3.5
|
'@types/chai': 4.3.5
|
||||||
'@types/chai-subset': 1.3.3
|
'@types/chai-subset': 1.3.3
|
||||||
'@types/node': 18.16.0
|
'@types/node': 20.4.10
|
||||||
'@vitest/expect': 0.33.0
|
'@vitest/expect': 0.33.0
|
||||||
'@vitest/runner': 0.33.0
|
'@vitest/runner': 0.33.0
|
||||||
'@vitest/snapshot': 0.33.0
|
'@vitest/snapshot': 0.33.0
|
||||||
@@ -8673,8 +8790,8 @@ packages:
|
|||||||
strip-literal: 1.3.0
|
strip-literal: 1.3.0
|
||||||
tinybench: 2.5.0
|
tinybench: 2.5.0
|
||||||
tinypool: 0.6.0
|
tinypool: 0.6.0
|
||||||
vite: 4.4.9(@types/node@18.16.0)
|
vite: 4.4.9(@types/node@20.4.10)
|
||||||
vite-node: 0.33.0(@types/node@18.16.0)
|
vite-node: 0.33.0(@types/node@20.4.10)
|
||||||
why-is-node-running: 2.2.2
|
why-is-node-running: 2.2.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- less
|
- less
|
||||||
@@ -8693,6 +8810,11 @@ packages:
|
|||||||
glob-to-regexp: 0.4.1
|
glob-to-regexp: 0.4.1
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
|
|
||||||
|
/web-streams-polyfill@3.2.1:
|
||||||
|
resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/web-streams-polyfill@4.0.0-beta.3:
|
/web-streams-polyfill@4.0.0-beta.3:
|
||||||
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
|
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
@@ -8788,6 +8910,10 @@ packages:
|
|||||||
stackback: 0.0.2
|
stackback: 0.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/wordwrap@1.0.0:
|
||||||
|
resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/wrap-ansi@7.0.0:
|
/wrap-ansi@7.0.0:
|
||||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ databases:
|
|||||||
services:
|
services:
|
||||||
- type: web
|
- type: web
|
||||||
name: querykey-prod-web
|
name: querykey-prod-web
|
||||||
rootDir: app
|
|
||||||
env: docker
|
env: docker
|
||||||
dockerfilePath: Dockerfile
|
dockerfilePath: ./app/Dockerfile
|
||||||
dockerContext: .
|
dockerContext: .
|
||||||
plan: standard
|
plan: standard
|
||||||
domains:
|
domains:
|
||||||
@@ -32,9 +31,8 @@ services:
|
|||||||
|
|
||||||
- type: web
|
- type: web
|
||||||
name: querykey-prod-wss
|
name: querykey-prod-wss
|
||||||
rootDir: app
|
|
||||||
env: docker
|
env: docker
|
||||||
dockerfilePath: Dockerfile
|
dockerfilePath: ./app/Dockerfile
|
||||||
dockerContext: .
|
dockerContext: .
|
||||||
plan: free
|
plan: free
|
||||||
dockerCommand: pnpm tsx src/wss-server.ts
|
dockerCommand: pnpm tsx src/wss-server.ts
|
||||||
|
|||||||
Reference in New Issue
Block a user