Save and display timeToComplete on model outputs (#6)
* Calculate and save timeToComplete on model outputs * Add output stats to function call as well * Record timeToComplete before parsing response json * Add default value for timeToComplete
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "ModelOutput" ADD COLUMN "timeToComplete" INTEGER NOT NULL DEFAULT 0;
|
||||||
@@ -76,10 +76,11 @@ model TemplateVariable {
|
|||||||
model ModelOutput {
|
model ModelOutput {
|
||||||
id String @id @default(uuid()) @db.Uuid
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
|
||||||
inputHash String
|
inputHash String
|
||||||
output Json
|
output Json
|
||||||
statusCode Int
|
statusCode Int
|
||||||
errorMessage String?
|
errorMessage String?
|
||||||
|
timeToComplete Int @default(0)
|
||||||
|
|
||||||
promptVariantId String @db.Uuid
|
promptVariantId String @db.Uuid
|
||||||
promptVariant PromptVariant @relation(fields: [promptVariantId], references: [id], onDelete: Cascade)
|
promptVariant PromptVariant @relation(fields: [promptVariantId], references: [id], onDelete: Cascade)
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { type PromptVariant, type Scenario } from "./types";
|
import { type PromptVariant, type Scenario } from "./types";
|
||||||
import { Spinner, Text, Box, Center } from "@chakra-ui/react";
|
import { Spinner, Text, Box, Center, Flex, Icon } from "@chakra-ui/react";
|
||||||
import { useExperiment } from "~/utils/hooks";
|
import { useExperiment } from "~/utils/hooks";
|
||||||
import { type CreateChatCompletionResponse } from "openai";
|
import { type CreateChatCompletionResponse } from "openai";
|
||||||
import SyntaxHighlighter from "react-syntax-highlighter";
|
import SyntaxHighlighter from "react-syntax-highlighter";
|
||||||
import { docco } from "react-syntax-highlighter/dist/cjs/styles/hljs";
|
import { docco } from "react-syntax-highlighter/dist/cjs/styles/hljs";
|
||||||
import stringify from "json-stringify-pretty-compact";
|
import stringify from "json-stringify-pretty-compact";
|
||||||
import { type ReactElement } from "react";
|
import { type ReactElement } from "react";
|
||||||
|
import { BsClock } from "react-icons/bs";
|
||||||
|
import { type ModelOutput } from "@prisma/client";
|
||||||
|
|
||||||
export default function OutputCell({
|
export default function OutputCell({
|
||||||
scenario,
|
scenario,
|
||||||
@@ -22,13 +24,11 @@ export default function OutputCell({
|
|||||||
|
|
||||||
const scenarioVariables = scenario.variableValues as Record<string, string>;
|
const scenarioVariables = scenario.variableValues as Record<string, string>;
|
||||||
const templateHasVariables =
|
const templateHasVariables =
|
||||||
vars?.length === 0 ||
|
vars?.length === 0 || vars?.some((v) => scenarioVariables[v.label] !== undefined);
|
||||||
vars?.some((v) => scenarioVariables[v.label] !== undefined);
|
|
||||||
|
|
||||||
let disabledReason: string | null = null;
|
let disabledReason: string | null = null;
|
||||||
|
|
||||||
if (!templateHasVariables)
|
if (!templateHasVariables) disabledReason = "Add a value to the scenario variables to see output";
|
||||||
disabledReason = "Add a value to the scenario variables to see output";
|
|
||||||
|
|
||||||
if (variant.config === null || Object.keys(variant.config).length === 0)
|
if (variant.config === null || Object.keys(variant.config).length === 0)
|
||||||
disabledReason = "Save your prompt variant to see output";
|
disabledReason = "Save your prompt variant to see output";
|
||||||
@@ -45,17 +45,20 @@ export default function OutputCell({
|
|||||||
|
|
||||||
if (disabledReason) return <Text color="gray.500">{disabledReason}</Text>;
|
if (disabledReason) return <Text color="gray.500">{disabledReason}</Text>;
|
||||||
|
|
||||||
if (output.isLoading) return <Center h="100%" w="100%"><Spinner /></Center>;
|
if (output.isLoading)
|
||||||
|
return (
|
||||||
|
<Center h="100%" w="100%">
|
||||||
|
<Spinner />
|
||||||
|
</Center>
|
||||||
|
);
|
||||||
|
|
||||||
if (!output.data)
|
if (!output.data) return <Text color="gray.500">Error retrieving output</Text>;
|
||||||
return <Text color="gray.500">Error retrieving output</Text>;
|
|
||||||
|
|
||||||
if (output.data.errorMessage) {
|
if (output.data.errorMessage) {
|
||||||
return <Text color="red.600">Error: {output.data.errorMessage}</Text>;
|
return <Text color="red.600">Error: {output.data.errorMessage}</Text>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = output.data
|
const response = output.data?.output as unknown as CreateChatCompletionResponse;
|
||||||
?.output as unknown as CreateChatCompletionResponse;
|
|
||||||
const message = response?.choices?.[0]?.message;
|
const message = response?.choices?.[0]?.message;
|
||||||
|
|
||||||
if (message?.function_call) {
|
if (message?.function_call) {
|
||||||
@@ -64,9 +67,7 @@ export default function OutputCell({
|
|||||||
try {
|
try {
|
||||||
parsedArgs = JSON.parse(rawArgs);
|
parsedArgs = JSON.parse(rawArgs);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
parsedArgs = `Failed to parse arguments as JSON: '${rawArgs}' ERROR: ${
|
parsedArgs = `Failed to parse arguments as JSON: '${rawArgs}' ERROR: ${e.message as string}`;
|
||||||
e.message as string
|
|
||||||
}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -88,9 +89,26 @@ export default function OutputCell({
|
|||||||
{ maxLength: 40 }
|
{ maxLength: 40 }
|
||||||
)}
|
)}
|
||||||
</SyntaxHighlighter>
|
</SyntaxHighlighter>
|
||||||
|
<OutputStats modelOutput={output.data} />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Box whiteSpace="pre-wrap">{message?.content ?? JSON.stringify(output.data.output)}</Box>;
|
return (
|
||||||
|
<Flex w="100%" h="100%" direction="column" justifyContent="space-between" whiteSpace="pre-wrap">
|
||||||
|
{message?.content ?? JSON.stringify(output.data.output)}
|
||||||
|
<OutputStats modelOutput={output.data} />
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const OutputStats = ({ modelOutput }: { modelOutput: ModelOutput }) => {
|
||||||
|
const timeToComplete = modelOutput.timeToComplete;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex justifyContent="flex-end" alignItems="center" color="gray.500" fontSize="xs" mt={2}>
|
||||||
|
<Icon as={BsClock} mr={0.5} />
|
||||||
|
<Text>{(timeToComplete / 1000).toFixed(2)}s</Text>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ export const modelOutputsRouter = createTRPCRouter({
|
|||||||
output: existingResponse.output as Prisma.InputJsonValue,
|
output: existingResponse.output as Prisma.InputJsonValue,
|
||||||
statusCode: existingResponse.statusCode,
|
statusCode: existingResponse.statusCode,
|
||||||
errorMessage: existingResponse.errorMessage,
|
errorMessage: existingResponse.errorMessage,
|
||||||
|
timeToComplete: existingResponse.timeToComplete,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
modelResponse = await getChatCompletion(filledTemplate, env.OPENAI_API_KEY);
|
modelResponse = await getChatCompletion(filledTemplate, env.OPENAI_API_KEY);
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ type CompletionResponse = {
|
|||||||
output: Prisma.InputJsonValue | typeof Prisma.JsonNull;
|
output: Prisma.InputJsonValue | typeof Prisma.JsonNull;
|
||||||
statusCode: number;
|
statusCode: number;
|
||||||
errorMessage: string | null;
|
errorMessage: string | null;
|
||||||
|
timeToComplete: number
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getChatCompletion(
|
export async function getChatCompletion(
|
||||||
payload: JSONSerializable,
|
payload: JSONSerializable,
|
||||||
apiKey: string
|
apiKey: string
|
||||||
): Promise<CompletionResponse> {
|
): Promise<CompletionResponse> {
|
||||||
|
const start = Date.now();
|
||||||
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -25,9 +27,11 @@ export async function getChatCompletion(
|
|||||||
output: Prisma.JsonNull,
|
output: Prisma.JsonNull,
|
||||||
errorMessage: null,
|
errorMessage: null,
|
||||||
statusCode: response.status,
|
statusCode: response.status,
|
||||||
|
timeToComplete: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
resp.timeToComplete = Date.now() - start;
|
||||||
resp.output = await response.json();
|
resp.output = await response.json();
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
|||||||
Reference in New Issue
Block a user