Use code editor in diagnostics tab (#1024)
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { artifactApiClient } from "@/api/AxiosClient";
|
import { artifactApiClient } from "@/api/AxiosClient";
|
||||||
import { ArtifactApiResponse } from "@/api/types";
|
import { ArtifactApiResponse } from "@/api/types";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { CodeEditor } from "@/routes/workflows/components/CodeEditor";
|
||||||
import { useQueries } from "@tanstack/react-query";
|
import { useQueries } from "@tanstack/react-query";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
@@ -26,15 +26,38 @@ function format(html: string) {
|
|||||||
return result.substring(1, result.length - 3);
|
return result.substring(1, result.length - 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFormattedResult(type: "json" | "html" | "text", result: unknown) {
|
||||||
|
switch (type) {
|
||||||
|
case "json":
|
||||||
|
return JSON.stringify(result, null, 2);
|
||||||
|
case "html":
|
||||||
|
return format(result as string);
|
||||||
|
case "text":
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEndpoint(type: "json" | "html" | "text") {
|
||||||
|
switch (type) {
|
||||||
|
case "json":
|
||||||
|
return "/artifact/json";
|
||||||
|
case "html":
|
||||||
|
case "text":
|
||||||
|
return "/artifact/text";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
type: "json" | "html" | "text";
|
||||||
artifacts: Array<ArtifactApiResponse>;
|
artifacts: Array<ArtifactApiResponse>;
|
||||||
};
|
};
|
||||||
|
|
||||||
function HTMLArtifact({ artifacts }: Props) {
|
function Artifact({ type, artifacts }: Props) {
|
||||||
function fetchArtifact(artifact: ArtifactApiResponse) {
|
function fetchArtifact(artifact: ArtifactApiResponse) {
|
||||||
if (artifact.uri.startsWith("file://")) {
|
if (artifact.uri.startsWith("file://")) {
|
||||||
|
const endpoint = getEndpoint(type);
|
||||||
return artifactApiClient
|
return artifactApiClient
|
||||||
.get(`/artifact/text`, {
|
.get(endpoint, {
|
||||||
params: {
|
params: {
|
||||||
path: artifact.uri.slice(7),
|
path: artifact.uri.slice(7),
|
||||||
},
|
},
|
||||||
@@ -61,17 +84,21 @@ function HTMLArtifact({ artifacts }: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Textarea
|
<CodeEditor
|
||||||
|
language={type === "text" ? undefined : type}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
rows={15}
|
|
||||||
value={
|
value={
|
||||||
results.some((result) => result.isError)
|
results.some((result) => result.isError)
|
||||||
? JSON.stringify(results.find((result) => result.isError)?.error)
|
? JSON.stringify(results.find((result) => result.isError)?.error)
|
||||||
: results.map((result) => format(result.data ?? "")).join(",\n")
|
: results
|
||||||
|
.map((result) => getFormattedResult(type, result.data))
|
||||||
|
.join(",\n")
|
||||||
}
|
}
|
||||||
|
minHeight="96px"
|
||||||
|
maxHeight="500px"
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { HTMLArtifact };
|
export { Artifact };
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
import { artifactApiClient } from "@/api/AxiosClient";
|
|
||||||
import { ArtifactApiResponse } from "@/api/types";
|
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
|
||||||
import { useQueries } from "@tanstack/react-query";
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
artifacts: Array<ArtifactApiResponse>;
|
|
||||||
};
|
|
||||||
|
|
||||||
function JSONArtifact({ artifacts }: Props) {
|
|
||||||
function fetchArtifact(artifact: ArtifactApiResponse) {
|
|
||||||
if (artifact.uri.startsWith("file://")) {
|
|
||||||
return artifactApiClient
|
|
||||||
.get(`/artifact/json`, {
|
|
||||||
params: {
|
|
||||||
path: artifact.uri.slice(7),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((response) => response.data);
|
|
||||||
}
|
|
||||||
if (artifact.uri.startsWith("s3://") && artifact.signed_url) {
|
|
||||||
return axios.get(artifact.signed_url).then((response) => response.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const results = useQueries({
|
|
||||||
queries:
|
|
||||||
artifacts?.map((artifact) => {
|
|
||||||
return {
|
|
||||||
queryKey: ["artifact", artifact.artifact_id],
|
|
||||||
queryFn: () => fetchArtifact(artifact),
|
|
||||||
};
|
|
||||||
}) ?? [],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (results.some((result) => result.isLoading)) {
|
|
||||||
return <Skeleton className="h-48 w-full" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Textarea
|
|
||||||
className="w-full"
|
|
||||||
rows={15}
|
|
||||||
value={
|
|
||||||
results.some((result) => result.isError)
|
|
||||||
? JSON.stringify(results.find((result) => result.isError)?.error)
|
|
||||||
: results
|
|
||||||
.map((result) => JSON.stringify(result.data, null, 2))
|
|
||||||
.join(",\n")
|
|
||||||
}
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export { JSONArtifact };
|
|
||||||
@@ -11,13 +11,12 @@ import { useParams } from "react-router-dom";
|
|||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { ZoomableImage } from "@/components/ZoomableImage";
|
import { ZoomableImage } from "@/components/ZoomableImage";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { JSONArtifact } from "./JSONArtifact";
|
|
||||||
import { TextArtifact } from "./TextArtifact";
|
|
||||||
import { getImageURL } from "./artifactUtils";
|
import { getImageURL } from "./artifactUtils";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { basicTimeFormat } from "@/util/timeFormat";
|
import { basicTimeFormat } from "@/util/timeFormat";
|
||||||
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
||||||
import { HTMLArtifact } from "./HTMLArtifact";
|
import { Artifact } from "./Artifact";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
id: string;
|
id: string;
|
||||||
stepProps: StepApiResponse;
|
stepProps: StepApiResponse;
|
||||||
@@ -167,27 +166,27 @@ function StepArtifacts({ id, stepProps }: Props) {
|
|||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="element_tree_trimmed">
|
<TabsContent value="element_tree_trimmed">
|
||||||
{visibleElementsTreeInPrompt ? (
|
{visibleElementsTreeInPrompt ? (
|
||||||
<HTMLArtifact artifacts={visibleElementsTreeInPrompt} />
|
<Artifact type="html" artifacts={visibleElementsTreeInPrompt} />
|
||||||
) : null}
|
) : null}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="element_tree">
|
<TabsContent value="element_tree">
|
||||||
{visibleElementsTree ? (
|
{visibleElementsTree ? (
|
||||||
<JSONArtifact artifacts={visibleElementsTree} />
|
<Artifact type="json" artifacts={visibleElementsTree} />
|
||||||
) : null}
|
) : null}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="llm_prompt">
|
<TabsContent value="llm_prompt">
|
||||||
{llmPrompt ? <TextArtifact artifacts={llmPrompt} /> : null}
|
{llmPrompt ? <Artifact type="text" artifacts={llmPrompt} /> : null}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="llm_response_parsed">
|
<TabsContent value="llm_response_parsed">
|
||||||
{llmResponseParsed ? (
|
{llmResponseParsed ? (
|
||||||
<JSONArtifact artifacts={llmResponseParsed} />
|
<Artifact type="json" artifacts={llmResponseParsed} />
|
||||||
) : null}
|
) : null}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="html_raw">
|
<TabsContent value="html_raw">
|
||||||
{htmlRaw ? <TextArtifact artifacts={htmlRaw} /> : null}
|
{htmlRaw ? <Artifact type="html" artifacts={htmlRaw} /> : null}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="llm_request">
|
<TabsContent value="llm_request">
|
||||||
{llmRequest ? <JSONArtifact artifacts={llmRequest} /> : null}
|
{llmRequest ? <Artifact type="json" artifacts={llmRequest} /> : null}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
import { artifactApiClient } from "@/api/AxiosClient";
|
|
||||||
import { ArtifactApiResponse } from "@/api/types";
|
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
|
||||||
import { useQueries } from "@tanstack/react-query";
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
artifacts: Array<ArtifactApiResponse>;
|
|
||||||
};
|
|
||||||
|
|
||||||
function TextArtifact({ artifacts }: Props) {
|
|
||||||
function fetchArtifact(artifact: ArtifactApiResponse) {
|
|
||||||
if (artifact.uri.startsWith("file://")) {
|
|
||||||
return artifactApiClient
|
|
||||||
.get(`/artifact/text`, {
|
|
||||||
params: {
|
|
||||||
path: artifact.uri.slice(7),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((response) => response.data);
|
|
||||||
}
|
|
||||||
if (artifact.uri.startsWith("s3://") && artifact.signed_url) {
|
|
||||||
return axios.get(artifact.signed_url).then((response) => response.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const results = useQueries({
|
|
||||||
queries:
|
|
||||||
artifacts?.map((artifact) => {
|
|
||||||
return {
|
|
||||||
queryKey: ["artifact", artifact.artifact_id],
|
|
||||||
queryFn: () => fetchArtifact(artifact),
|
|
||||||
};
|
|
||||||
}) ?? [],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (results.some((result) => result.isLoading)) {
|
|
||||||
return <Skeleton className="h-48 w-full" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Textarea
|
|
||||||
className="w-full"
|
|
||||||
rows={15}
|
|
||||||
value={
|
|
||||||
results.some((result) => result.isError)
|
|
||||||
? JSON.stringify(results.find((result) => result.isError)?.error)
|
|
||||||
: results.map((result) => result.data).join(",\n")
|
|
||||||
}
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export { TextArtifact };
|
|
||||||
@@ -1,13 +1,25 @@
|
|||||||
import CodeMirror, { EditorView } from "@uiw/react-codemirror";
|
import CodeMirror, { EditorView } from "@uiw/react-codemirror";
|
||||||
import { json } from "@codemirror/lang-json";
|
import { json } from "@codemirror/lang-json";
|
||||||
import { python } from "@codemirror/lang-python";
|
import { python } from "@codemirror/lang-python";
|
||||||
|
import { html } from "@codemirror/lang-html";
|
||||||
import { tokyoNightStorm } from "@uiw/codemirror-theme-tokyo-night-storm";
|
import { tokyoNightStorm } from "@uiw/codemirror-theme-tokyo-night-storm";
|
||||||
import { cn } from "@/util/utils";
|
import { cn } from "@/util/utils";
|
||||||
|
|
||||||
|
function getLanguageExtension(language: "python" | "json" | "html") {
|
||||||
|
switch (language) {
|
||||||
|
case "python":
|
||||||
|
return python();
|
||||||
|
case "json":
|
||||||
|
return json();
|
||||||
|
case "html":
|
||||||
|
return html();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
value: string;
|
value: string;
|
||||||
onChange?: (value: string) => void;
|
onChange?: (value: string) => void;
|
||||||
language: "python" | "json";
|
language?: "python" | "json" | "html";
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
minHeight?: string;
|
minHeight?: string;
|
||||||
maxHeight?: string;
|
maxHeight?: string;
|
||||||
@@ -25,10 +37,10 @@ function CodeEditor({
|
|||||||
readOnly = false,
|
readOnly = false,
|
||||||
fontSize = 12,
|
fontSize = 12,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const extensions =
|
const extensions = language
|
||||||
language === "json"
|
? [getLanguageExtension(language), EditorView.lineWrapping]
|
||||||
? [json(), EditorView.lineWrapping]
|
: [EditorView.lineWrapping];
|
||||||
: [python(), EditorView.lineWrapping];
|
|
||||||
return (
|
return (
|
||||||
<CodeMirror
|
<CodeMirror
|
||||||
value={value}
|
value={value}
|
||||||
|
|||||||
Reference in New Issue
Block a user