Skip to content

Commit

Permalink
feat(ui): Consolidate action IO structure + some fixes (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
daryllimyt committed Mar 24, 2024
1 parent fbf7874 commit ba49f14
Show file tree
Hide file tree
Showing 17 changed files with 381 additions and 130 deletions.
15 changes: 13 additions & 2 deletions frontend/src/app/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

// Error components must be Client Components
import { useEffect } from "react"
import Image from "next/image"
import Link from "next/link"
import TracecatIcon from "public/icon.png"

import { Button } from "@/components/ui/button"
import { AlertNotification } from "@/components/notifications"

export default function Error({
Expand All @@ -18,8 +22,15 @@ export default function Error({
}, [error])

return (
<main className="container flex h-full w-full max-w-[400px] items-center justify-center">
<AlertNotification level="error" message={error.message} reset={reset} />
<main className="container flex h-full w-full max-w-[400px] flex-col items-center justify-center space-y-4">
<Image src={TracecatIcon} alt="Tracecat" className="mb-8 h-16 w-16" />
<h1 className="text-2xl font-medium">Oh no! An error occurred :(</h1>

<Link href="/" className="">
<Button variant="outline">Return to the home page</Button>
</Link>

<AlertNotification level="error" message={error.message} />
</main>
)
}
15 changes: 13 additions & 2 deletions frontend/src/app/workflows/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

// Error components must be Client Components
import { useEffect } from "react"
import Image from "next/image"
import Link from "next/link"
import TracecatIcon from "public/icon.png"

import { Button } from "@/components/ui/button"
import { AlertNotification } from "@/components/notifications"

export default function Error({
Expand All @@ -18,8 +22,15 @@ export default function Error({
}, [error])

return (
<main className="container flex h-full w-full max-w-[400px] items-center justify-center">
<AlertNotification level="error" message={error.message} reset={reset} />
<main className="container flex h-full w-full max-w-[400px] flex-col items-center justify-center space-y-4">
<Image src={TracecatIcon} alt="Tracecat" className="mb-8 h-16 w-16" />
<h1 className="text-2xl font-medium">Oh no! An error occurred :(</h1>

<Link href="/" className="">
<Button variant="outline">Return to the home page</Button>
</Link>

<AlertNotification level="error" message={error.message} />
</main>
)
}
10 changes: 10 additions & 0 deletions frontend/src/components/cases/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ export const columns: ColumnDef<Case>[] = [
return value.includes(row.getValue<Case["id"]>(id))
},
},
{
accessorKey: "created_at",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Created At" />
),
cell: ({ row }) => row.getValue<Case["created_at"]>("created_at"),
filterFn: (row, id, value) => {
return value.includes(row.getValue<Case["id"]>(id))
},
},
{
accessorKey: "title",
header: ({ column }) => (
Expand Down
139 changes: 95 additions & 44 deletions frontend/src/components/forms/action-schemas.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,7 @@
import { z } from "zod"

import { ActionType } from "@/types/schemas"

const jsonPayload = z
.string()
.optional()
.transform((val) => {
try {
return val ? JSON.parse(val) : {}
} catch (error) {
// TODO: Handle error on SAVE only
console.error("Error parsing payload:", error)
return {}
}
})

const stringArray = z
.array(z.string().min(1, { message: "Strings cannot be empty" }))
.min(1, { message: "List cannot be empty" })
import { stringArray, stringToJSONSchema } from "@/types/validators"

const WebhookActionSchema = z.object({
path: z.string(), // The webhook ID
Expand All @@ -29,8 +13,8 @@ const WebhookActionSchema = z.object({
const HTTPRequestActionSchema = z.object({
url: z.string(),
method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
headers: jsonPayload.optional(),
payload: jsonPayload,
headers: stringToJSONSchema.optional(),
payload: stringToJSONSchema,
})

const SendEmailActionSchema = z.object({
Expand Down Expand Up @@ -74,41 +58,41 @@ const LLMTranslateActionSchema = z.object({
message: z.string(),
from_language: z.string(),
to_language: z.string(),
response_schema: jsonPayload.optional(),
response_schema: stringToJSONSchema.optional(),
})

const LLMExtractActionSchema = z.object({
message: z.string(),
groups: stringArray,
response_schema: jsonPayload.optional(),
response_schema: stringToJSONSchema.optional(),
})

const LLMLabelTaskActionSchema = z.object({
message: z.string(),
labels: stringArray,
response_schema: jsonPayload.optional(),
response_schema: stringToJSONSchema.optional(),
})

const LLMChoiceTaskActionSchema = z.object({
message: z.string(),
choices: stringArray,
response_schema: jsonPayload.optional(),
response_schema: stringToJSONSchema.optional(),
})

const LLMSummarizeTaskActionSchema = z.object({
message: z.string(),
response_schema: jsonPayload.optional(),
response_schema: stringToJSONSchema.optional(),
})

const OpenCaseActionSchema = z.object({
title: z.string(),
payload: jsonPayload,
payload: stringToJSONSchema,
malice: z.enum(["malicious", "benign"]),
status: z.enum(["open", "closed", "in_progress", "reported", "escalated"]),
priority: z.enum(["low", "medium", "high", "critical"]),
context: jsonPayload.optional(),
context: stringToJSONSchema.optional(),
action: z.string().optional(),
suppression: jsonPayload.optional(),
suppression: stringToJSONSchema.optional(),
})
export const baseActionSchema = z.object({
title: z.string(),
Expand All @@ -120,6 +104,9 @@ export type ActionFieldType = "input" | "select" | "textarea" | "json" | "array"
export interface ActionFieldOption {
type: ActionFieldType
options?: readonly string[]
placeholder?: string
disabled?: boolean
optional?: boolean
}

export interface ActionFieldSchema {
Expand Down Expand Up @@ -152,15 +139,27 @@ export const getSubActionSchema = (actionType: ActionType) => {
fieldSchema: actionFieldSchemas[actionType] || {},
}
}

const LLM_MESSAGE_PLACEHOLDER =
"The input message for the AI to extract information. You may use templated expressions here."

const LLM_RESPONSE_SCHEMA_PLACEHOLDER = `An optional JSON object to control the format of the LLM output. This is mapping of field name to data type (Python data types). You may also add comments. If left blank, the LLM will output freeform text.
For example:
{
\t"website_name": "str",
\t"created_year": "int",
\t"url": "str # This must be a valid url!"
}`

const actionFieldSchemas: Partial<AllActionFieldSchemas> = {
webhook: {
url: { type: "input" },
url: { type: "input", placeholder: "The allowed domain." },
method: {
type: "select",
options: ["GET", "POST"],
},
path: { type: "input" },
secret: { type: "input" },
path: { type: "input", disabled: true },
secret: { type: "input", disabled: true },
},
http_request: {
url: { type: "input" },
Expand Down Expand Up @@ -202,35 +201,74 @@ const actionFieldSchemas: Partial<AllActionFieldSchemas> = {
},
"llm.translate": {
// TODO: Replace with supported languages and Command input
message: { type: "textarea" },
message: {
type: "textarea",
placeholder: LLM_MESSAGE_PLACEHOLDER,
},
from_language: { type: "input" },
to_language: { type: "input" },
response_schema: { type: "textarea" },
response_schema: {
type: "json",
placeholder: LLM_RESPONSE_SCHEMA_PLACEHOLDER,
optional: true,
},
},
"llm.extract": {
message: { type: "textarea" },
message: {
type: "textarea",
placeholder: LLM_MESSAGE_PLACEHOLDER,
},
// TODO: Replace with Command input and ability to add to list
groups: { type: "array" }, // Assuming a comma-separated string to be transformed into an array
response_schema: { type: "json" },
response_schema: {
type: "json",
placeholder: LLM_RESPONSE_SCHEMA_PLACEHOLDER,
optional: true,
},
},
"llm.label": {
// TODO: Replace with Command input and ability to add to list
message: { type: "textarea" },
message: {
type: "textarea",
placeholder: LLM_MESSAGE_PLACEHOLDER,
},
labels: { type: "array" }, // Assuming a comma-separated string to be transformed into an array
response_schema: { type: "json" },
response_schema: {
type: "json",
placeholder: LLM_RESPONSE_SCHEMA_PLACEHOLDER,
optional: true,
},
},
"llm.choice": {
message: { type: "textarea" },
message: {
type: "textarea",
placeholder: LLM_MESSAGE_PLACEHOLDER,
},
choices: { type: "array" },
response_schema: { type: "json" },
response_schema: {
type: "json",
placeholder: LLM_RESPONSE_SCHEMA_PLACEHOLDER,
optional: true,
},
},
"llm.summarize": {
message: { type: "textarea" },
response_schema: { type: "json" },
message: {
type: "textarea",
placeholder: LLM_MESSAGE_PLACEHOLDER,
},
response_schema: {
type: "json",
placeholder: LLM_RESPONSE_SCHEMA_PLACEHOLDER,
optional: true,
},
},
open_case: {
title: { type: "input" },
payload: { type: "json" },
payload: {
type: "json",
placeholder:
"A JSON payload to be included in the case. You may use templated expressions here.",
},
malice: {
type: "select",
options: ["malicious", "benign"],
Expand All @@ -242,9 +280,22 @@ const actionFieldSchemas: Partial<AllActionFieldSchemas> = {
priority: {
type: "select",
options: ["low", "medium", "high", "critical"],
optional: true,
},
context: {
type: "json",
optional: true,
placeholder: "An optional JSON object containing additional context.",
},
action: {
type: "textarea",
optional: true,
placeholder: "Action to be taken.",
},
suppression: {
type: "json",
optional: true,
placeholder: "An optional JSON object containing suppression rules.",
},
context: { type: "json" },
action: { type: "textarea" },
suppression: { type: "json" },
},
}
Loading

0 comments on commit ba49f14

Please sign in to comment.