feat: logs on component output (#2740)
* Added tabs and passed the type of the table to the SwitchOutputView component * Added type to SwitchOutputViewProps and changed results based on type * Removed unused import * Changed type of VertexDataTypeAPI to include logs * Added condition where type is Logs to display only table * Added handling when resultMessage is empty to SwitchOutputView * Removed return that made Logs not appear when its empty * Fixed logs not appearing * feat: Add _output_logs attribute to CustomComponent The code changes include adding a new attribute `_output_logs` to the `CustomComponent` class. This attribute is a dictionary that stores logs related to output values. This change is necessary to enhance the logging functionality of the component. Note: Please remove any meta information such as issue references, tags, or author names from the commit message. * feat: Serialize messages with to_json() in BaseCrewComponent This commit modifies the `BaseCrewComponent` class in the `crew.py` file. It adds serialization of messages using the `to_json()` method to avoid circular reference issues. The `_messages_dict` dictionary is serialized by converting each message object to JSON format. This change enhances the functionality of the component and improves the logging process. Note: Please remove any meta information such as issue references, tags, or author names from the commit message. --------- Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
This commit is contained in:
parent
b9ef29ecd2
commit
0897ee542a
6 changed files with 58 additions and 13 deletions
|
|
@ -67,7 +67,9 @@ class BaseCrewComponent(Component):
|
|||
self.log(cast(dict, messages[0].to_json()), name=f"Finish (Agent: {_id})")
|
||||
elif isinstance(agent_output, list):
|
||||
_messages_dict = {f"Action {i}": action.messages for i, (action, _) in enumerate(agent_output)}
|
||||
messages_dict = {k: v[0] if len(v) == 1 else v for k, v in _messages_dict.items()}
|
||||
# Serialize the messages with to_json() to avoid issues with circular references
|
||||
serializable_dict = {k: [m.to_json() for m in v] for k, v in _messages_dict.items()}
|
||||
messages_dict = {k: v[0] if len(v) == 1 else v for k, v in serializable_dict.items()}
|
||||
self.log(messages_dict, name=f"Step (Agent: {_id})")
|
||||
|
||||
return step_callback
|
||||
|
|
@ -76,5 +78,5 @@ class BaseCrewComponent(Component):
|
|||
crew = self.build_crew()
|
||||
result = await crew.kickoff_async()
|
||||
message = Message(text=result, sender="Machine")
|
||||
self.status = "\n\n".join([result] + [str(message) for message in self._logs])
|
||||
self.status = message
|
||||
return message
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ from langflow.inputs.inputs import InputTypes
|
|||
from langflow.schema.artifact import get_artifact_type, post_process_raw
|
||||
from langflow.schema.data import Data
|
||||
from langflow.schema.message import Message
|
||||
from langflow.services.tracing.schema import Log
|
||||
from langflow.template.field.base import UNDEFINED, Output
|
||||
|
||||
from .custom_component import CustomComponent
|
||||
|
|
@ -39,7 +38,6 @@ class Component(CustomComponent):
|
|||
inputs: List[InputTypes] = []
|
||||
outputs: List[Output] = []
|
||||
code_class_base_inheritance: ClassVar[str] = "Component"
|
||||
_output_logs: dict[str, Log] = {}
|
||||
|
||||
def __init__(self, **data):
|
||||
self._inputs: dict[str, InputTypes] = {}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ class CustomComponent(BaseComponent):
|
|||
_flows_data: Optional[List[Data]] = None
|
||||
_outputs: List[OutputValue] = []
|
||||
_logs: List[Log] = []
|
||||
_output_logs: dict[str, Log] = {}
|
||||
tracing_service: Optional["TracingService"] = None
|
||||
|
||||
def set_attributes(self, parameters: dict):
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { LogsLogType, OutputLogType } from "@/types/api";
|
||||
import DataOutputComponent from "../../../../../../components/dataOutputComponent";
|
||||
import ForwardedIconComponent from "../../../../../../components/genericIconComponent";
|
||||
import {
|
||||
|
|
@ -13,26 +14,28 @@ import ErrorOutput from "./components";
|
|||
interface SwitchOutputViewProps {
|
||||
nodeId: string;
|
||||
outputName: string;
|
||||
type: "Outputs" | "Logs";
|
||||
}
|
||||
const SwitchOutputView: React.FC<SwitchOutputViewProps> = ({
|
||||
nodeId,
|
||||
outputName,
|
||||
type,
|
||||
}) => {
|
||||
const flowPool = useFlowStore((state) => state.flowPool);
|
||||
const flowPoolNode = (flowPool[nodeId] ?? [])[
|
||||
(flowPool[nodeId]?.length ?? 1) - 1
|
||||
];
|
||||
let results = flowPoolNode?.data?.outputs[outputName] ?? "";
|
||||
if (Array.isArray(results)) {
|
||||
return;
|
||||
}
|
||||
let results: OutputLogType | LogsLogType =
|
||||
(type === "Outputs"
|
||||
? flowPoolNode?.data?.outputs[outputName]
|
||||
: flowPoolNode?.data?.logs[outputName]) ?? {};
|
||||
const resultType = results?.type;
|
||||
let resultMessage = results?.message;
|
||||
let resultMessage = results?.message ?? {};
|
||||
const RECORD_TYPES = ["data", "object", "array", "message"];
|
||||
if (resultMessage?.raw) {
|
||||
resultMessage = resultMessage.raw;
|
||||
}
|
||||
return (
|
||||
return type === "Outputs" ? (
|
||||
<>
|
||||
<Case condition={!resultType || resultType === "unknown"}>
|
||||
<div>NO OUTPUT</div>
|
||||
|
|
@ -54,7 +57,9 @@ const SwitchOutputView: React.FC<SwitchOutputViewProps> = ({
|
|||
? (resultMessage as Array<any>).every((item) => item.data)
|
||||
? (resultMessage as Array<any>).map((item) => item.data)
|
||||
: resultMessage
|
||||
: [resultMessage]
|
||||
: Object.keys(resultMessage).length > 0
|
||||
? [resultMessage]
|
||||
: []
|
||||
}
|
||||
pagination={true}
|
||||
columnMode="union"
|
||||
|
|
@ -78,6 +83,20 @@ const SwitchOutputView: React.FC<SwitchOutputViewProps> = ({
|
|||
</div>
|
||||
</Case>
|
||||
</>
|
||||
) : (
|
||||
<DataOutputComponent
|
||||
rows={
|
||||
Array.isArray(results)
|
||||
? (results as Array<any>).every((item) => item.data)
|
||||
? (results as Array<any>).map((item) => item.data)
|
||||
: results
|
||||
: Object.keys(results).length > 0
|
||||
? [results]
|
||||
: []
|
||||
}
|
||||
pagination={true}
|
||||
columnMode="union"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { useState } from "react";
|
||||
import { Button } from "../../../../components/ui/button";
|
||||
import BaseModal from "../../../../modals/baseModal";
|
||||
import SwitchOutputView from "./components/switchOutputView";
|
||||
|
|
@ -8,15 +10,32 @@ export default function OutputModal({
|
|||
nodeId,
|
||||
outputName,
|
||||
}): JSX.Element {
|
||||
const [activeTab, setActiveTab] = useState<"Outputs" | "Logs">("Outputs");
|
||||
return (
|
||||
<BaseModal open={open} setOpen={setOpen} size="medium-tall">
|
||||
<BaseModal open={open} setOpen={setOpen} size="large">
|
||||
<BaseModal.Header description="Inspect the output of the component below.">
|
||||
<div className="flex items-center">
|
||||
<span className="pr-2">Component Output</span>
|
||||
</div>
|
||||
</BaseModal.Header>
|
||||
<BaseModal.Content>
|
||||
<SwitchOutputView nodeId={nodeId} outputName={outputName} />
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onValueChange={(value) => setActiveTab(value as "Outputs" | "Logs")}
|
||||
className={
|
||||
"absolute top-6 flex flex-col self-center overflow-hidden rounded-md border bg-muted text-center"
|
||||
}
|
||||
>
|
||||
<TabsList>
|
||||
<TabsTrigger value="Outputs">Outputs</TabsTrigger>
|
||||
<TabsTrigger value="Logs">Logs</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
<SwitchOutputView
|
||||
nodeId={nodeId}
|
||||
outputName={outputName}
|
||||
type={activeTab}
|
||||
/>
|
||||
</BaseModal.Content>
|
||||
<BaseModal.Footer>
|
||||
<div className="flex w-full justify-end pt-2">
|
||||
|
|
|
|||
|
|
@ -201,12 +201,18 @@ export type OutputLogType = {
|
|||
message: any | ErrorLogType;
|
||||
type: string;
|
||||
};
|
||||
export type LogsLogType = {
|
||||
name: string;
|
||||
message: any | ErrorLogType;
|
||||
type: string;
|
||||
};
|
||||
|
||||
// data is the object received by the API
|
||||
// it has results, artifacts, timedelta, duration
|
||||
export type VertexDataTypeAPI = {
|
||||
results: { [key: string]: string };
|
||||
outputs: { [key: string]: OutputLogType };
|
||||
logs: { [key: string]: LogsLogType };
|
||||
messages: ChatOutputType[] | ChatInputType[];
|
||||
inactive?: boolean;
|
||||
timedelta?: number;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue