/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import "./logger.scss";
import { Part } from "@google/generative-ai";
import cn from "classnames";
import { ReactNode } from "react";
import { useLoggerStore } from "../../lib/store-logger";
import SyntaxHighlighter from "react-syntax-highlighter";
import { vs2015 as dark } from "react-syntax-highlighter/dist/esm/styles/hljs";
import {
ClientContentMessage,
isClientContentMessage,
isInterrupted,
isModelTurn,
isServerContentMessage,
isToolCallCancellationMessage,
isToolCallMessage,
isToolResponseMessage,
isTurnComplete,
ModelTurn,
ServerContentMessage,
StreamingLog,
ToolCallCancellationMessage,
ToolCallMessage,
ToolResponseMessage,
} from "../../multimodal-live-types";
const formatTime = (d: Date) => d.toLocaleTimeString().slice(0, -3);
const LogEntry = ({
log,
MessageComponent,
}: {
log: StreamingLog;
MessageComponent: ({
message,
}: {
message: StreamingLog["message"];
}) => ReactNode;
}): JSX.Element => (
{formatTime(log.date)}
{log.type}
{log.count && {log.count}}
);
const PlainTextMessage = ({
message,
}: {
message: StreamingLog["message"];
}) => {message as string};
type Message = { message: StreamingLog["message"] };
const AnyMessage = ({ message }: Message) => (
{JSON.stringify(message, null, " ")}
);
function tryParseCodeExecutionResult(output: string) {
try {
const json = JSON.parse(output);
return JSON.stringify(json, null, " ");
} catch (e) {
return output;
}
}
const RenderPart = ({ part }: { part: Part }) =>
part.text && part.text.length ? (
{part.text}
) : part.executableCode ? (
executableCode: {part.executableCode.language}
{part.executableCode.code}
) : part.codeExecutionResult ? (
codeExecutionResult: {part.codeExecutionResult.outcome}
{tryParseCodeExecutionResult(part.codeExecutionResult.output)}
) : (
Inline Data: {part.inlineData?.mimeType}
{part.inlineData?.data || ""}
);
const ClientContentLog = ({ message }: Message) => {
const { turns, turnComplete } = (message as ClientContentMessage)
.clientContent;
return (
User
{turns.map((turn, i) => (
{turn.parts
.filter((part) => !(part.text && part.text === "\n"))
.map((part, j) => (
))}
))}
{!turnComplete ?
turnComplete: false : ""}
);
};
const ToolCallLog = ({ message }: Message) => {
const { toolCall } = message as ToolCallMessage;
return (
{toolCall.functionCalls.map((fc, i) => (
Function call: {fc.name}
{JSON.stringify(fc, null, " ")}
))}
);
};
const ToolCallCancellationLog = ({ message }: Message): JSX.Element => (
{" "}
ids:{" "}
{(message as ToolCallCancellationMessage).toolCallCancellation.ids.map(
(id) => (
"{id}"
),
)}
);
const ToolResponseLog = ({ message }: Message): JSX.Element => (
{(message as ToolResponseMessage).toolResponse.functionResponses.map(
(fc) => (
Function Response: {fc.id}
{JSON.stringify(fc.response, null, " ")}
),
)}
);
const ModelTurnLog = ({ message }: Message): JSX.Element => {
const serverContent = (message as ServerContentMessage).serverContent;
const { modelTurn } = serverContent as ModelTurn;
const { parts } = modelTurn;
return (
Model
{parts
.filter((part) => !(part.text && part.text === "\n"))
.map((part, j) => (
))}
);
};
const CustomPlainTextLog = (msg: string) => () => (
);
export type LoggerFilterType = "conversations" | "tools" | "none";
export type LoggerProps = {
filter: LoggerFilterType;
};
const filters: Record boolean> = {
tools: (log: StreamingLog) =>
isToolCallMessage(log.message) ||
isToolResponseMessage(log.message) ||
isToolCallCancellationMessage(log.message),
conversations: (log: StreamingLog) =>
isClientContentMessage(log.message) || isServerContentMessage(log.message),
none: () => true,
};
const component = (log: StreamingLog) => {
if (typeof log.message === "string") {
return PlainTextMessage;
}
if (isClientContentMessage(log.message)) {
return ClientContentLog;
}
if (isToolCallMessage(log.message)) {
return ToolCallLog;
}
if (isToolCallCancellationMessage(log.message)) {
return ToolCallCancellationLog;
}
if (isToolResponseMessage(log.message)) {
return ToolResponseLog;
}
if (isServerContentMessage(log.message)) {
const { serverContent } = log.message;
if (isInterrupted(serverContent)) {
return CustomPlainTextLog("interrupted");
}
if (isTurnComplete(serverContent)) {
return CustomPlainTextLog("turnComplete");
}
if (isModelTurn(serverContent)) {
return ModelTurnLog;
}
}
return AnyMessage;
};
export default function Logger({ filter = "none" }: LoggerProps) {
const { logs } = useLoggerStore();
const filterFn = filters[filter];
return (
{logs.filter(filterFn).map((log, key) => {
return (
);
})}
);
}