Use code editor in diagnostics tab (#1024)

This commit is contained in:
Shuchang Zheng
2024-10-22 06:45:46 -07:00
committed by GitHub
parent 439815c33a
commit d571519a67
5 changed files with 59 additions and 137 deletions

View File

@@ -1,7 +1,7 @@
import { artifactApiClient } from "@/api/AxiosClient";
import { ArtifactApiResponse } from "@/api/types";
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 axios from "axios";
@@ -26,15 +26,38 @@ function format(html: string) {
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: "json" | "html" | "text";
artifacts: Array<ArtifactApiResponse>;
};
function HTMLArtifact({ artifacts }: Props) {
function Artifact({ type, artifacts }: Props) {
function fetchArtifact(artifact: ArtifactApiResponse) {
if (artifact.uri.startsWith("file://")) {
const endpoint = getEndpoint(type);
return artifactApiClient
.get(`/artifact/text`, {
.get(endpoint, {
params: {
path: artifact.uri.slice(7),
},
@@ -61,17 +84,21 @@ function HTMLArtifact({ artifacts }: Props) {
}
return (
<Textarea
<CodeEditor
language={type === "text" ? undefined : type}
className="w-full"
rows={15}
value={
results.some((result) => result.isError)
? 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
/>
);
}
export { HTMLArtifact };
export { Artifact };

View File

@@ -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 };

View File

@@ -11,13 +11,12 @@ import { useParams } from "react-router-dom";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { ZoomableImage } from "@/components/ZoomableImage";
import { Skeleton } from "@/components/ui/skeleton";
import { JSONArtifact } from "./JSONArtifact";
import { TextArtifact } from "./TextArtifact";
import { getImageURL } from "./artifactUtils";
import { Input } from "@/components/ui/input";
import { basicTimeFormat } from "@/util/timeFormat";
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
import { HTMLArtifact } from "./HTMLArtifact";
import { Artifact } from "./Artifact";
type Props = {
id: string;
stepProps: StepApiResponse;
@@ -167,27 +166,27 @@ function StepArtifacts({ id, stepProps }: Props) {
</TabsContent>
<TabsContent value="element_tree_trimmed">
{visibleElementsTreeInPrompt ? (
<HTMLArtifact artifacts={visibleElementsTreeInPrompt} />
<Artifact type="html" artifacts={visibleElementsTreeInPrompt} />
) : null}
</TabsContent>
<TabsContent value="element_tree">
{visibleElementsTree ? (
<JSONArtifact artifacts={visibleElementsTree} />
<Artifact type="json" artifacts={visibleElementsTree} />
) : null}
</TabsContent>
<TabsContent value="llm_prompt">
{llmPrompt ? <TextArtifact artifacts={llmPrompt} /> : null}
{llmPrompt ? <Artifact type="text" artifacts={llmPrompt} /> : null}
</TabsContent>
<TabsContent value="llm_response_parsed">
{llmResponseParsed ? (
<JSONArtifact artifacts={llmResponseParsed} />
<Artifact type="json" artifacts={llmResponseParsed} />
) : null}
</TabsContent>
<TabsContent value="html_raw">
{htmlRaw ? <TextArtifact artifacts={htmlRaw} /> : null}
{htmlRaw ? <Artifact type="html" artifacts={htmlRaw} /> : null}
</TabsContent>
<TabsContent value="llm_request">
{llmRequest ? <JSONArtifact artifacts={llmRequest} /> : null}
{llmRequest ? <Artifact type="json" artifacts={llmRequest} /> : null}
</TabsContent>
</Tabs>
);

View File

@@ -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 };

View File

@@ -1,13 +1,25 @@
import CodeMirror, { EditorView } from "@uiw/react-codemirror";
import { json } from "@codemirror/lang-json";
import { python } from "@codemirror/lang-python";
import { html } from "@codemirror/lang-html";
import { tokyoNightStorm } from "@uiw/codemirror-theme-tokyo-night-storm";
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 = {
value: string;
onChange?: (value: string) => void;
language: "python" | "json";
language?: "python" | "json" | "html";
readOnly?: boolean;
minHeight?: string;
maxHeight?: string;
@@ -25,10 +37,10 @@ function CodeEditor({
readOnly = false,
fontSize = 12,
}: Props) {
const extensions =
language === "json"
? [json(), EditorView.lineWrapping]
: [python(), EditorView.lineWrapping];
const extensions = language
? [getLanguageExtension(language), EditorView.lineWrapping]
: [EditorView.lineWrapping];
return (
<CodeMirror
value={value}