-
Notifications
You must be signed in to change notification settings - Fork 272
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* [OPIK-536]: fix merge conflicts; * [OPIK-536]: playground ui improvements; * [OPIK-536]: fix the comments from the product [2]; * [OPIK-536]: update the color of a placeholder in select; * [OPIK-536]: update the color of a placeholder in select; * [OPIK-536]: update the openai api key; * [OPIK-536]: finish the functionality of playground; * [OPIK-536]: eslint fixes; * [OPIK-536]: eslint fixes; * [OPIK-536]: update package-lock.json; * [OPIK-536]: merge problems fixes; * [OPIK-536]: fix a type; * [OPIK-536]: remove an old file and comments; * [OPIK-536]: improve code readability; * [OPIK-536]: add more models; * [OPIK-536]: remove a useless comment; * [OPIK-536]: hide playground if not ready for users; * [OPIK-536]: remove a buggy tooltip; * [OPIK-536]: fix the min width; * [OPIK-536]: support tracing for stop button; * [OPIK-536]: improve code readability; * [OPIK-536]: pr comment fixes; * [OPIK-536]: remove use memo; * [OPIK-536]: fix the package lock json; --------- Co-authored-by: Sasha <aliaksandr@comet.com>
- Loading branch information
Showing
33 changed files
with
2,832 additions
and
92 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
97 changes: 97 additions & 0 deletions
97
apps/opik-frontend/src/api/playground/useCreateOutputTraceAndSpan.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { useCallback } from "react"; | ||
import { v7 } from "uuid"; | ||
import pick from "lodash/pick"; | ||
|
||
import useSpanCreateMutation from "@/api/traces/useSpanCreateMutation"; | ||
import useTraceCreateMutation from "@/api/traces/useTraceCreateMutation"; | ||
import { RunStreamingReturn } from "@/api/playground/useOpenApiRunStreaming"; | ||
|
||
import { | ||
PLAYGROUND_MODEL, | ||
PlaygroundPromptConfigsType, | ||
ProviderMessageType, | ||
} from "@/types/playground"; | ||
import { useToast } from "@/components/ui/use-toast"; | ||
import { SPAN_TYPE } from "@/types/traces"; | ||
|
||
const PLAYGROUND_TRACE_SPAN_NAME = "chat_completion_create"; | ||
|
||
const USAGE_FIELDS_TO_SEND = [ | ||
"completion_tokens", | ||
"prompt_tokens", | ||
"total_tokens", | ||
]; | ||
|
||
const PLAYGROUND_PROJECT_NAME = "playground"; | ||
|
||
interface CreateTraceSpanParams extends RunStreamingReturn { | ||
model: PLAYGROUND_MODEL | ""; | ||
providerMessages: ProviderMessageType[]; | ||
configs: PlaygroundPromptConfigsType; | ||
} | ||
|
||
const useCreateOutputTraceAndSpan = () => { | ||
const { toast } = useToast(); | ||
|
||
const { mutateAsync: createSpanMutateAsync } = useSpanCreateMutation(); | ||
const { mutateAsync: createTraceMutateAsync } = useTraceCreateMutation(); | ||
|
||
const createTraceSpan = useCallback( | ||
async ({ | ||
startTime, | ||
endTime, | ||
result, | ||
usage, | ||
error, | ||
choices, | ||
model, | ||
providerMessages, | ||
configs, | ||
}: CreateTraceSpanParams) => { | ||
const traceId = v7(); | ||
const spanId = v7(); | ||
|
||
try { | ||
await createTraceMutateAsync({ | ||
id: traceId, | ||
projectName: PLAYGROUND_PROJECT_NAME, | ||
name: PLAYGROUND_TRACE_SPAN_NAME, | ||
startTime, | ||
endTime, | ||
input: { messages: providerMessages }, | ||
output: { output: result || error }, | ||
}); | ||
|
||
await createSpanMutateAsync({ | ||
id: spanId, | ||
traceId, | ||
projectName: PLAYGROUND_PROJECT_NAME, | ||
type: SPAN_TYPE.llm, | ||
name: PLAYGROUND_TRACE_SPAN_NAME, | ||
startTime, | ||
endTime, | ||
input: { messages: providerMessages }, | ||
output: { choices }, | ||
usage: !usage ? undefined : pick(usage, USAGE_FIELDS_TO_SEND), | ||
metadata: { | ||
created_from: "openai", | ||
usage, | ||
model, | ||
parameters: configs, | ||
}, | ||
}); | ||
} catch { | ||
toast({ | ||
title: "Error", | ||
description: "There was an error while logging data", | ||
variant: "destructive", | ||
}); | ||
} | ||
}, | ||
[createTraceMutateAsync, createSpanMutateAsync, toast], | ||
); | ||
|
||
return createTraceSpan; | ||
}; | ||
|
||
export default useCreateOutputTraceAndSpan; |
197 changes: 197 additions & 0 deletions
197
apps/opik-frontend/src/api/playground/useOpenApiRunStreaming.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
import { useCallback, useRef } from "react"; | ||
|
||
import dayjs from "dayjs"; | ||
import { UsageType } from "@/types/shared"; | ||
import { | ||
PLAYGROUND_MODEL, | ||
PlaygroundPromptConfigsType, | ||
ProviderMessageType, | ||
ProviderStreamingMessageChoiceType, | ||
ProviderStreamingMessageType, | ||
} from "@/types/playground"; | ||
import { safelyParseJSON, snakeCaseObj } from "@/lib/utils"; | ||
import { OPENAI_API_KEY } from "@/constants/playground"; | ||
|
||
interface GetOpenAIStreamParams { | ||
model: PLAYGROUND_MODEL | ""; | ||
messages: ProviderMessageType[]; | ||
signal: AbortSignal; | ||
configs: PlaygroundPromptConfigsType; | ||
} | ||
|
||
const getOpenAIStream = async ({ | ||
model, | ||
messages, | ||
signal, | ||
configs, | ||
}: GetOpenAIStreamParams) => { | ||
const apiKey = window.localStorage.getItem(OPENAI_API_KEY) || ""; | ||
|
||
return fetch("https://api.openai.com/v1/chat/completions", { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
Authorization: `Bearer ${apiKey}`, | ||
}, | ||
body: JSON.stringify({ | ||
model, | ||
messages, | ||
stream: true, | ||
stream_options: { include_usage: true }, | ||
...snakeCaseObj(configs), | ||
}), | ||
signal: signal, | ||
}); | ||
}; | ||
|
||
const getResponseError = async (response: Response) => { | ||
let error; | ||
|
||
try { | ||
error = (await response?.json())?.error?.message; | ||
} catch { | ||
error = "Unexpected error occurred."; | ||
} | ||
|
||
return error; | ||
}; | ||
|
||
export interface RunStreamingReturn { | ||
error: null | string; | ||
result: null | string; | ||
startTime: string; | ||
endTime: string; | ||
usage: UsageType | null; | ||
choices: ProviderStreamingMessageChoiceType[] | null; | ||
} | ||
|
||
interface UseOpenApiRunStreamingParameters { | ||
model: PLAYGROUND_MODEL | ""; | ||
messages: ProviderMessageType[]; | ||
onAddChunk: (accumulatedValue: string) => void; | ||
onLoading: (v: boolean) => void; | ||
onError: (errMsg: string | null) => void; | ||
configs: PlaygroundPromptConfigsType; | ||
} | ||
|
||
const useOpenApiRunStreaming = ({ | ||
model, | ||
messages, | ||
configs, | ||
onAddChunk, | ||
onLoading, | ||
onError, | ||
}: UseOpenApiRunStreamingParameters) => { | ||
const abortControllerRef = useRef<AbortController | null>(null); | ||
|
||
const stop = useCallback(() => { | ||
abortControllerRef.current?.abort(); | ||
abortControllerRef.current = null; | ||
}, []); | ||
|
||
const runStreaming = useCallback(async (): Promise<RunStreamingReturn> => { | ||
const startTime = dayjs().utc().toISOString(); | ||
let accumulatedValue = ""; | ||
let usage = null; | ||
let choices: ProviderStreamingMessageChoiceType[] = []; | ||
|
||
onLoading(true); | ||
onError(null); | ||
|
||
try { | ||
abortControllerRef.current = new AbortController(); | ||
|
||
const response = await getOpenAIStream({ | ||
model, | ||
messages, | ||
configs, | ||
signal: abortControllerRef.current?.signal, | ||
}); | ||
|
||
if (!response.ok || !response) { | ||
const error = await getResponseError(response); | ||
onError(error); | ||
onLoading(false); | ||
|
||
const endTime = dayjs().utc().toISOString(); | ||
|
||
return { | ||
error, | ||
result: null, | ||
startTime, | ||
endTime, | ||
usage: null, | ||
choices: null, | ||
}; | ||
} | ||
|
||
const reader = response?.body?.getReader(); | ||
const decoder = new TextDecoder("utf-8"); | ||
|
||
let done = false; | ||
|
||
while (!done && reader) { | ||
const { value, done: doneReading } = await reader.read(); | ||
|
||
done = doneReading; | ||
|
||
if (value) { | ||
const chunk = decoder.decode(value, { stream: true }); | ||
const lines = chunk.split("\n").filter((line) => line.trim() !== ""); | ||
|
||
for (const line of lines) { | ||
if (line.startsWith("data:")) { | ||
const data = line.replace(/^data:\s*/, ""); | ||
|
||
// stream finished | ||
if (data === "[DONE]") { | ||
break; | ||
} | ||
|
||
const parsed = safelyParseJSON( | ||
data, | ||
) as ProviderStreamingMessageType; | ||
|
||
choices = parsed?.choices; | ||
const deltaContent = choices?.[0]?.delta?.content; | ||
|
||
if (parsed?.usage) { | ||
usage = parsed.usage as UsageType; | ||
} | ||
|
||
if (deltaContent) { | ||
accumulatedValue += deltaContent; | ||
onAddChunk(accumulatedValue); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
return { | ||
startTime, | ||
endTime: dayjs().utc().toISOString(), | ||
result: accumulatedValue, | ||
error: null, | ||
usage, | ||
choices, | ||
}; | ||
// abort signal also jumps into here | ||
} catch (error) { | ||
return { | ||
startTime, | ||
endTime: dayjs().utc().toISOString(), | ||
result: accumulatedValue, | ||
error: null, | ||
usage: null, | ||
choices, | ||
}; | ||
} finally { | ||
onLoading(false); | ||
} | ||
}, [messages, model, onAddChunk, onError, onLoading, configs]); | ||
|
||
return { runStreaming, stop }; | ||
}; | ||
|
||
export default useOpenApiRunStreaming; |
67 changes: 67 additions & 0 deletions
67
apps/opik-frontend/src/api/traces/useSpanCreateMutation.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { useMutation, useQueryClient } from "@tanstack/react-query"; | ||
import { AxiosError } from "axios"; | ||
import get from "lodash/get"; | ||
|
||
import api, { SPANS_KEY, SPANS_REST_ENDPOINT } from "@/api/api"; | ||
import { useToast } from "@/components/ui/use-toast"; | ||
import { SPAN_TYPE } from "@/types/traces"; | ||
|
||
import { JsonNode, UsageType } from "@/types/shared"; | ||
import { snakeCaseObj } from "@/lib/utils"; | ||
|
||
type UseSpanCreateMutationParams = { | ||
id: string; | ||
projectName?: string; | ||
traceId: string; | ||
parentSpanId?: string; | ||
name: string; | ||
type: SPAN_TYPE; | ||
startTime: string; | ||
endTime?: string; | ||
input?: JsonNode; | ||
output?: JsonNode; | ||
model?: string; | ||
provider?: string; | ||
tags?: string[]; | ||
usage?: UsageType; | ||
metadata?: object; | ||
}; | ||
|
||
const useSpanCreateMutation = () => { | ||
const queryClient = useQueryClient(); | ||
const { toast } = useToast(); | ||
|
||
return useMutation({ | ||
mutationFn: async (span: UseSpanCreateMutationParams) => { | ||
const { data } = await api.post(SPANS_REST_ENDPOINT, snakeCaseObj(span)); | ||
|
||
return data; | ||
}, | ||
onError: (error: AxiosError) => { | ||
const message = get( | ||
error, | ||
["response", "data", "message"], | ||
error.message, | ||
); | ||
|
||
toast({ | ||
title: "Error", | ||
description: message, | ||
variant: "destructive", | ||
}); | ||
}, | ||
onSettled: (data, error, variables) => { | ||
if (variables.projectName) { | ||
queryClient.invalidateQueries({ | ||
queryKey: ["projects"], | ||
}); | ||
} | ||
|
||
queryClient.invalidateQueries({ | ||
queryKey: [SPANS_KEY], | ||
}); | ||
}, | ||
}); | ||
}; | ||
|
||
export default useSpanCreateMutation; |
Oops, something went wrong.