From 8fed9730da45b725d9b28ee556dc196bc89076a1 Mon Sep 17 00:00:00 2001 From: David Corbitt Date: Mon, 7 Aug 2023 21:04:38 -0700 Subject: [PATCH] Send api token properly --- .../migration.sql | 2 ++ app/prisma/schema.prisma | 8 ++--- .../server/api/routers/externalApi.router.ts | 34 ++++++++++++------- app/src/server/api/trpc.ts | 4 +-- client-libs/typescript/openai/index.ts | 31 ++++++++++------- 5 files changed, 46 insertions(+), 33 deletions(-) create mode 100644 app/prisma/migrations/20230808034313_make_model_response_optional/migration.sql diff --git a/app/prisma/migrations/20230808034313_make_model_response_optional/migration.sql b/app/prisma/migrations/20230808034313_make_model_response_optional/migration.sql new file mode 100644 index 0000000..f9c991c --- /dev/null +++ b/app/prisma/migrations/20230808034313_make_model_response_optional/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "LoggedCall" ALTER COLUMN "modelResponseId" DROP NOT NULL; diff --git a/app/prisma/schema.prisma b/app/prisma/schema.prisma index 387084a..06797f6 100644 --- a/app/prisma/schema.prisma +++ b/app/prisma/schema.prisma @@ -265,11 +265,11 @@ model LoggedCall { // A LoggedCall is always associated with a LoggedCallModelResponse. If this // is a cache miss, we create a new LoggedCallModelResponse. // If it's a cache hit, it's a pre-existing LoggedCallModelResponse. - modelResponseId String @db.Uuid - modelResponse LoggedCallModelResponse @relation(fields: [modelResponseId], references: [id], onDelete: Cascade) + modelResponseId String? @db.Uuid + modelResponse LoggedCallModelResponse? @relation(fields: [modelResponseId], references: [id], onDelete: Cascade) - // The response created by this LoggedCall. Will be null if this LoggedCall is a cache hit. - createdResponse LoggedCallModelResponse[] @relation(name: "ModelResponseOriginalCall") + // The responses created by this LoggedCall. Will be empty if this LoggedCall was a cache hit. + createdResponses LoggedCallModelResponse[] @relation(name: "ModelResponseOriginalCall") organizationId String @db.Uuid organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade) diff --git a/app/src/server/api/routers/externalApi.router.ts b/app/src/server/api/routers/externalApi.router.ts index c654c53..08619bf 100644 --- a/app/src/server/api/routers/externalApi.router.ts +++ b/app/src/server/api/routers/externalApi.router.ts @@ -2,6 +2,7 @@ import { type Prisma } from "@prisma/client"; import { type JsonValue } from "type-fest"; import { z } from "zod"; import { v4 as uuidv4 } from "uuid"; +import { TRPCError } from "@trpc/server"; import { createTRPCRouter, publicProcedure } from "~/server/api/trpc"; import { prisma } from "~/server/db"; @@ -56,13 +57,13 @@ export const externalApiRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const apiKey = ctx.apiKey; if (!apiKey) { - throw new Error("Missing API key"); + throw new TRPCError({ code: "UNAUTHORIZED" }); } const key = await prisma.apiKey.findUnique({ where: { apiKey }, }); if (!key) { - throw new Error("Invalid API key"); + throw new TRPCError({ code: "UNAUTHORIZED" }); } const reqPayload = await reqValidator.spa(input.reqPayload); const cacheKey = hashRequest(key.organizationId, reqPayload as JsonValue); @@ -76,19 +77,19 @@ export const externalApiRouter = createTRPCRouter({ }, orderBy: { startTime: "desc", - } + }, }); if (!existingResponse) return { respPayload: null }; await prisma.loggedCall.create({ data: { - organizationId: key.organizationId, - startTime: new Date(input.startTime), - cacheHit: true, - modelResponseId: existingResponse.id, - } - }) + organizationId: key.organizationId, + startTime: new Date(input.startTime), + cacheHit: true, + modelResponseId: existingResponse.id, + }, + }); return { respPayload: existingResponse.respPayload, @@ -123,13 +124,13 @@ export const externalApiRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const apiKey = ctx.apiKey; if (!apiKey) { - throw new Error("Missing API key"); + throw new TRPCError({ code: "UNAUTHORIZED" }); } const key = await prisma.apiKey.findUnique({ where: { apiKey }, }); if (!key) { - throw new Error("Invalid API key"); + throw new TRPCError({ code: "UNAUTHORIZED" }); } const reqPayload = await reqValidator.spa(input.reqPayload); const respPayload = await respValidator.spa(input.respPayload); @@ -148,7 +149,6 @@ export const externalApiRouter = createTRPCRouter({ organizationId: key.organizationId, startTime: new Date(input.startTime), cacheHit: false, - modelResponseId: newModelResponseId, }, }), prisma.loggedCallModelResponse.create({ @@ -167,11 +167,19 @@ export const externalApiRouter = createTRPCRouter({ cacheKey: requestHash, inputTokens: usage ? usage.prompt_tokens : undefined, outputTokens: usage ? usage.completion_tokens : undefined, - model: respPayload.data.model, } : null), }, }), + // Avoid foreign key constraint error by updating the logged call after the model response is created + prisma.loggedCall.update({ + where: { + id: newLoggedCallId, + }, + data: { + modelResponseId: newModelResponseId, + }, + }), ]); if (input.tags) { diff --git a/app/src/server/api/trpc.ts b/app/src/server/api/trpc.ts index eab697e..fc1f1e3 100644 --- a/app/src/server/api/trpc.ts +++ b/app/src/server/api/trpc.ts @@ -64,9 +64,7 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => { // Get the session from the server using the getServerSession wrapper function const session = await getServerAuthSession({ req, res }); - const apiKey = req.headers["X-Openpipe-Api-Key"] as string | null; - - console.log('api key is', apiKey) + const apiKey = req.headers["x-openpipe-api-key"] as string | null; return createInnerTRPCContext({ session, diff --git a/client-libs/typescript/openai/index.ts b/client-libs/typescript/openai/index.ts index 8490362..5676f4b 100644 --- a/client-libs/typescript/openai/index.ts +++ b/client-libs/typescript/openai/index.ts @@ -1,11 +1,7 @@ import * as openai from "openai-beta"; -import { - readEnv, - type RequestOptions, -} from "openai-beta/core"; -import { - CompletionCreateParams, -} from "openai-beta/resources/chat/completions"; +import { readEnv, type RequestOptions } from "openai-beta/core"; +import { CompletionCreateParams } from "openai-beta/resources/chat/completions"; +import axios from "axios"; export * as openai from "openai-beta"; import * as openPipeClient from "../codegen"; @@ -27,10 +23,20 @@ export class OpenAI extends openai.OpenAI { super({ ...opts }); if (openPipeApiKey) { - this.openPipeApi = new openPipeClient.DefaultApi(new openPipeClient.Configuration({ - apiKey: openPipeApiKey, - basePath: openPipeBaseUrl, - })); + const axiosInstance = axios.create({ + baseURL: openPipeBaseUrl, + headers: { + 'x-openpipe-api-key': openPipeApiKey, + }, + }); + this.openPipeApi = new openPipeClient.DefaultApi( + new openPipeClient.Configuration({ + apiKey: openPipeApiKey, + basePath: openPipeBaseUrl, + }), + undefined, + axiosInstance + ); } // Override the chat property @@ -87,8 +93,7 @@ class ExtendedCompletions extends openai.OpenAI.Chat.Completions { params as CompletionCreateParams.CreateChatCompletionRequestNonStreaming, options ); - console.log('result is this', result) - this.openaiInstance.openPipeApi?.externalApiReport({ + await this.openaiInstance.openPipeApi?.externalApiReport({ startTime, endTime: Date.now(), reqPayload: params,