This is an automated email from the ASF dual-hosted git repository.
skrawcz pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/burr.git
The following commit(s) were added to refs/heads/main by this push:
new a7caa128 Various UI updates: remove elkjs add dagre, cost updates,
executable script (#586)
a7caa128 is described below
commit a7caa12871f6043140ce27dbd4d29286d2229370
Author: Stefan Krawczyk <[email protected]>
AuthorDate: Sun Oct 26 23:42:03 2025 -0700
Various UI updates: remove elkjs add dagre, cost updates, executable script
(#586)
* Removes ELKJS and adds DAGRE
DAGRE is MIT licensed and compatible with apache 2.0
Validated that the UI works.
* Makes run.sh executable
This wasn't before.
* Updates model costs
So that UI costs are closer to reality today.
* Updates UI links to apache domains
---
burr/tracking/server/run.sh | 0
telemetry/ui/package-lock.json | 195 +++------------------
telemetry/ui/package.json | 3 +-
telemetry/ui/scripts/model_costs.json | 2 +-
telemetry/ui/src/components/nav/appcontainer.tsx | 4 +-
.../ui/src/components/routes/app/GraphView.tsx | 126 ++++++-------
.../ui/src/components/routes/app/InsightsView.tsx | 2 +-
.../ui/src/components/routes/app/ReproduceView.tsx | 2 +-
8 files changed, 91 insertions(+), 243 deletions(-)
diff --git a/burr/tracking/server/run.sh b/burr/tracking/server/run.sh
old mode 100644
new mode 100755
diff --git a/telemetry/ui/package-lock.json b/telemetry/ui/package-lock.json
index d830dce4..b71fbefc 100644
--- a/telemetry/ui/package-lock.json
+++ b/telemetry/ui/package-lock.json
@@ -24,7 +24,7 @@
"@types/react-syntax-highlighter": "^15.5.11",
"@uiw/react-json-view": "^2.0.0-alpha.12",
"clsx": "^2.1.0",
- "elkjs": "^0.9.1",
+ "dagre": "^0.8.5",
"fuse.js": "^7.0.0",
"heroicons": "^2.1.1",
"react": "^18.2.0",
@@ -43,6 +43,7 @@
"web-vitals": "^2.1.4"
},
"devDependencies": {
+ "@types/dagre": "^0.7.52",
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
"eslint": "^8.56.0",
@@ -5563,150 +5564,6 @@
"integrity":
"sha512-/0hWQfiaD5//LvGNgc8PjvyqV50vGK0cADYzaoOOGN8fxzBn3iAiaq3S0tCRnFBldq0LVveLcxCTi41ZoYgAgg==",
"peer": true
},
- "node_modules/@next/swc-darwin-arm64": {
- "version": "14.2.14",
- "resolved":
"https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.14.tgz",
- "integrity":
"sha512-bsxbSAUodM1cjYeA4o6y7sp9wslvwjSkWw57t8DtC8Zig8aG8V6r+Yc05/9mDzLKcybb6EN85k1rJDnMKBd9Gw==",
- "cpu": [
- "arm64"
- ],
- "optional": true,
- "os": [
- "darwin"
- ],
- "peer": true,
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-darwin-x64": {
- "version": "14.2.14",
- "resolved":
"https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.14.tgz",
- "integrity":
"sha512-cC9/I+0+SK5L1k9J8CInahduTVWGMXhQoXFeNvF0uNs3Bt1Ub0Azb8JzTU9vNCr0hnaMqiWu/Z0S1hfKc3+dww==",
- "cpu": [
- "x64"
- ],
- "optional": true,
- "os": [
- "darwin"
- ],
- "peer": true,
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-linux-arm64-gnu": {
- "version": "14.2.14",
- "resolved":
"https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.14.tgz",
- "integrity":
"sha512-RMLOdA2NU4O7w1PQ3Z9ft3PxD6Htl4uB2TJpocm+4jcllHySPkFaUIFacQ3Jekcg6w+LBaFvjSPthZHiPmiAUg==",
- "cpu": [
- "arm64"
- ],
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-linux-arm64-musl": {
- "version": "14.2.14",
- "resolved":
"https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.14.tgz",
- "integrity":
"sha512-WgLOA4hT9EIP7jhlkPnvz49iSOMdZgDJVvbpb8WWzJv5wBD07M2wdJXLkDYIpZmCFfo/wPqFsFR4JS4V9KkQ2A==",
- "cpu": [
- "arm64"
- ],
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-linux-x64-gnu": {
- "version": "14.2.14",
- "resolved":
"https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.14.tgz",
- "integrity":
"sha512-lbn7svjUps1kmCettV/R9oAvEW+eUI0lo0LJNFOXoQM5NGNxloAyFRNByYeZKL3+1bF5YE0h0irIJfzXBq9Y6w==",
- "cpu": [
- "x64"
- ],
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-linux-x64-musl": {
- "version": "14.2.14",
- "resolved":
"https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.14.tgz",
- "integrity":
"sha512-7TcQCvLQ/hKfQRgjxMN4TZ2BRB0P7HwrGAYL+p+m3u3XcKTraUFerVbV3jkNZNwDeQDa8zdxkKkw2els/S5onQ==",
- "cpu": [
- "x64"
- ],
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-win32-arm64-msvc": {
- "version": "14.2.14",
- "resolved":
"https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.14.tgz",
- "integrity":
"sha512-8i0Ou5XjTLEje0oj0JiI0Xo9L/93ghFtAUYZ24jARSeTMXLUx8yFIdhS55mTExq5Tj4/dC2fJuaT4e3ySvXU1A==",
- "cpu": [
- "arm64"
- ],
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-win32-ia32-msvc": {
- "version": "14.2.14",
- "resolved":
"https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.14.tgz",
- "integrity":
"sha512-2u2XcSaDEOj+96eXpyjHjtVPLhkAFw2nlaz83EPeuK4obF+HmtDJHqgR1dZB7Gb6V/d55FL26/lYVd0TwMgcOQ==",
- "cpu": [
- "ia32"
- ],
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-win32-x64-msvc": {
- "version": "14.2.14",
- "resolved":
"https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.14.tgz",
- "integrity":
"sha512-MZom+OvZ1NZxuRovKt1ApevjiUJTcU2PmdJKL66xUPaJeRywnbGGRWUlaAOwunD6dX+pm83vj979NTC8QXjGWg==",
- "cpu": [
- "x64"
- ],
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
- "engines": {
- "node": ">= 10"
- }
- },
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1",
"resolved":
"https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@@ -7641,6 +7498,13 @@
"@types/d3-selection": "*"
}
},
+ "node_modules/@types/dagre": {
+ "version": "0.7.53",
+ "resolved": "https://registry.npmjs.org/@types/dagre/-/dagre-0.7.53.tgz",
+ "integrity":
"sha512-f4gkWqzPZvYmKhOsDnhq/R8mO4UMcKdxZo+i5SCkOU1wvGeHJeUXGIHeE9pnwGyPMDof1Vx5ZQo4nxpeg2TTVQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/debug": {
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
@@ -11391,6 +11255,16 @@
"node": ">=12"
}
},
+ "node_modules/dagre": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz",
+ "integrity":
"sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==",
+ "license": "MIT",
+ "dependencies": {
+ "graphlib": "^2.1.8",
+ "lodash": "^4.17.15"
+ }
+ },
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
"resolved":
"https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
@@ -11924,11 +11798,6 @@
"resolved":
"https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.673.tgz",
"integrity":
"sha512-zjqzx4N7xGdl5468G+vcgzDhaHkaYgVcf9MqgexcTqsl2UHSCmOj/Bi3HAprg4BZCpC7HyD8a6nZl6QAZf72gw=="
},
- "node_modules/elkjs": {
- "version": "0.9.1",
- "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.1.tgz",
- "integrity":
"sha512-JWKDyqAdltuUcyxaECtYG6H4sqysXSLeoXuGUBfRNESMTkj+w+qdb0jya8Z/WI0jVd03WQtCGhS6FOFtlhD5FQ=="
- },
"node_modules/emittery": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz",
@@ -14246,16 +14115,6 @@
"node": ">= 8.0.0"
}
},
- "node_modules/fuse/node_modules/@types/node": {
- "version": "22.7.4",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz",
- "integrity":
"sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==",
- "optional": true,
- "peer": true,
- "dependencies": {
- "undici-types": "~6.19.2"
- }
- },
"node_modules/fuse/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved":
"https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -14712,6 +14571,15 @@
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
"integrity":
"sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
},
+ "node_modules/graphlib": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz",
+ "integrity":
"sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==",
+ "license": "MIT",
+ "dependencies": {
+ "lodash": "^4.17.15"
+ }
+ },
"node_modules/graphql": {
"version": "16.9.0",
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz",
@@ -26391,13 +26259,6 @@
"resolved":
"https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz",
"integrity":
"sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw=="
},
- "node_modules/undici-types": {
- "version": "6.19.8",
- "resolved":
"https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
- "integrity":
"sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
- "optional": true,
- "peer": true
- },
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",
"resolved":
"https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
diff --git a/telemetry/ui/package.json b/telemetry/ui/package.json
index aaf64e6d..1c492bf3 100644
--- a/telemetry/ui/package.json
+++ b/telemetry/ui/package.json
@@ -19,7 +19,7 @@
"@types/react-syntax-highlighter": "^15.5.11",
"@uiw/react-json-view": "^2.0.0-alpha.12",
"clsx": "^2.1.0",
- "elkjs": "^0.9.1",
+ "dagre": "^0.8.5",
"fuse.js": "^7.0.0",
"heroicons": "^2.1.1",
"react": "^18.2.0",
@@ -67,6 +67,7 @@
]
},
"devDependencies": {
+ "@types/dagre": "^0.7.52",
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
"eslint": "^8.56.0",
diff --git a/telemetry/ui/scripts/model_costs.json
b/telemetry/ui/scripts/model_costs.json
index a6910276..ae1d4287 100644
--- a/telemetry/ui/scripts/model_costs.json
+++ b/telemetry/ui/scripts/model_costs.json
@@ -1 +1 @@
-{"sambanova/Meta-Llama-3.1-8B-Instruct": {"max_tokens": 16000,
"max_input_tokens": 16000, "max_output_tokens": 16000, "input_cost_per_token":
1e-07, "output_cost_per_token": 2e-07},
"sambanova/Meta-Llama-3.1-70B-Instruct": {"max_tokens": 128000,
"max_input_tokens": 128000, "max_output_tokens": 128000,
"input_cost_per_token": 6e-07, "output_cost_per_token": 1.2e-06},
"sambanova/Meta-Llama-3.1-405B-Instruct": {"max_tokens": 16000,
"max_input_tokens": 16000, "max_output_tokens": 16000, "inp [...]
\ No newline at end of file
+{"1024-x-1024/50-steps/bedrock/amazon.nova-canvas-v1:0": {"max_input_tokens":
2600}, "1024-x-1024/50-steps/stability.stable-diffusion-xl-v1":
{"max_input_tokens": 77, "max_tokens": 77}, "1024-x-1024/dall-e-2": {},
"1024-x-1024/max-steps/stability.stable-diffusion-xl-v1": {"max_input_tokens":
77, "max_tokens": 77}, "256-x-256/dall-e-2": {},
"512-x-512/50-steps/stability.stable-diffusion-xl-v0": {"max_input_tokens": 77,
"max_tokens": 77}, "512-x-512/dall-e-2": {}, "512-x-512/max-steps/stab [...]
\ No newline at end of file
diff --git a/telemetry/ui/src/components/nav/appcontainer.tsx
b/telemetry/ui/src/components/nav/appcontainer.tsx
index 42a4fb66..edcce3f2 100644
--- a/telemetry/ui/src/components/nav/appcontainer.tsx
+++ b/telemetry/ui/src/components/nav/appcontainer.tsx
@@ -146,13 +146,13 @@ export const AppContainer = (props: { children:
React.ReactNode }) => {
},
{
name: 'Documentation',
- href: 'https://burr.dagworks.io',
+ href: 'https://burr.apache.org',
icon: QuestionMarkCircleIcon,
linkType: 'external'
},
{
name: 'GitHub Discussions',
- href: 'https://github.com/DAGWorks-Inc/burr/discussions',
+ href: 'https://github.com/apache/burr/discussions',
icon: GithubLogo,
linkType: 'external'
},
diff --git a/telemetry/ui/src/components/routes/app/GraphView.tsx
b/telemetry/ui/src/components/routes/app/GraphView.tsx
index 0de181d2..ce85b823 100644
--- a/telemetry/ui/src/components/routes/app/GraphView.tsx
+++ b/telemetry/ui/src/components/routes/app/GraphView.tsx
@@ -19,7 +19,7 @@
import { ActionModel, ApplicationModel, Step } from '../../../api';
-import ELK from 'elkjs/lib/elk.bundled.js';
+import dagre from 'dagre';
import React, { createContext, useCallback, useLayoutEffect, useRef, useState
} from 'react';
import ReactFlow, {
BaseEdge,
@@ -39,16 +39,14 @@ import { backgroundColorsForIndex } from './AppView';
import { getActionStatus } from '../../../utils';
import { getSmartEdge } from '@tisoap/react-flow-smart-edge';
-const elk = new ELK();
+const dagreGraph = new dagre.graphlib.Graph();
-const elkOptions = {
- 'elk.algorithm': 'layered',
- 'elk.layered.spacing.nodeNodeBetweenLayers': '100',
- 'elk.spacing.nodeNode': '80',
- 'org.eclipse.elk.alg.layered.options.CycleBreakingStrategy': 'GREEDY',
- 'org.eclipse.elk.layered.nodePlacement.strategy': 'BRANDES_KOEPF',
- // 'org.eclipse.elk.layered.feedbackEdges': 'true',
- 'org.eclipse.elk.layered.crossingMinimization.strategy': 'LAYER_SWEEP'
+const dagreOptions = {
+ rankdir: 'TB', // Top to bottom layout (equivalent to ELK's UP direction)
+ nodesep: 80, // Node separation (equivalent to elk.spacing.nodeNode)
+ ranksep: 100, // Rank separation (equivalent to
elk.layered.spacing.nodeNodeBetweenLayers)
+ marginx: 20,
+ marginy: 20
};
type ActionNodeData = {
@@ -200,68 +198,56 @@ const getLayoutedElements = (
edges: EdgeType[],
options: { [key: string]: string } = {}
) => {
- const isHorizontal = options?.['elk.direction'] === 'RIGHT';
- const nodeNameMap = nodes.reduce(
- (acc, node) => {
- acc[node.id] = node;
- return acc;
- },
- {} as { [key: string]: NodeType }
- );
- const edgeNameMap = edges.reduce(
- (acc, edge) => {
- acc[edge.id] = edge;
- return acc;
- },
- {} as { [key: string]: EdgeType }
- );
- const graph = {
- id: 'root',
- layoutOptions: options,
- children: nodes.map((node) => ({
+ const isHorizontal = options?.['direction'] === 'LR';
+ const direction = isHorizontal ? 'LR' : 'TB';
+
+ // Configure dagre graph
+ dagreGraph.setDefaultEdgeLabel(() => ({}));
+ dagreGraph.setGraph({
+ ...dagreOptions,
+ rankdir: direction
+ });
+
+ // Add nodes to dagre graph
+ nodes.forEach((node) => {
+ dagreGraph.setNode(node.id, {
+ width: 150,
+ height: 100
+ });
+ });
+
+ // Add edges to dagre graph
+ edges.forEach((edge) => {
+ dagreGraph.setEdge(edge.source, edge.target);
+ });
+
+ // Calculate layout
+ dagre.layout(dagreGraph);
+
+ // Apply layout to nodes
+ const layoutedNodes = nodes.map((node) => {
+ const nodeWithPosition = dagreGraph.node(node.id);
+ return {
...node,
- // Adjust the target and source handle positions based on the layout
- // direction.
targetPosition: isHorizontal ? 'left' : 'top',
sourcePosition: isHorizontal ? 'right' : 'bottom',
+ position: {
+ x: nodeWithPosition.x - 75, // Center the node (width/2)
+ y: nodeWithPosition.y - 50 // Center the node (height/2)
+ }
+ };
+ });
- // Hardcode a width and height for elk to use when layouting.
- width: 150,
- height: 100
- })),
- edges: edges.map((edge) => {
- return {
- ...edge,
- sources: [edge.source],
- targets: [edge.target]
- };
- })
- };
- return elk.layout(graph).then((layoutedGraph) => ({
- nodes: (layoutedGraph.children || []).map((node) => {
- const originalNode = nodeNameMap[node.id];
- return {
- ...originalNode,
- position: {
- x: node.x as number,
- y: node.y as number
- }
- };
- }),
- edges: (layoutedGraph?.edges || []).map((edge) => {
- return {
- ...edge,
- markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 },
- source: edge.sources[0],
- target: edge.targets[0],
- data: {
- from: edge.sources[0],
- to: edge.targets[0],
- condition: edgeNameMap[edge.id].data.condition
- }
- };
- })
+ // Apply layout to edges
+ const layoutedEdges = edges.map((edge) => ({
+ ...edge,
+ markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }
}));
+
+ return Promise.resolve({
+ nodes: layoutedNodes,
+ edges: layoutedEdges
+ });
};
const convertApplicationToGraph = (stateMachine: ApplicationModel):
[NodeType[], EdgeType[]] => {
@@ -341,8 +327,8 @@ export const _Graph = (props: {
const { fitView } = useReactFlow();
const onLayout = useCallback(
- ({ direction = 'UP', useInitialNodes = false }): void => {
- const opts = { 'elk.direction': direction, ...elkOptions };
+ ({ direction = 'TB', useInitialNodes = false }): void => {
+ const opts = { direction };
const ns = useInitialNodes ? initialNodes : nodes;
const es = useInitialNodes ? initialEdges : edges;
@@ -357,7 +343,7 @@ export const _Graph = (props: {
);
useLayoutEffect(() => {
- onLayout({ direction: 'DOWN', useInitialNodes: true });
+ onLayout({ direction: 'TB', useInitialNodes: true });
}, []);
return (
diff --git a/telemetry/ui/src/components/routes/app/InsightsView.tsx
b/telemetry/ui/src/components/routes/app/InsightsView.tsx
index 7c5ce8fd..48da7ba2 100644
--- a/telemetry/ui/src/components/routes/app/InsightsView.tsx
+++ b/telemetry/ui/src/components/routes/app/InsightsView.tsx
@@ -518,7 +518,7 @@ export const InsightsView = (props: {
-- E.G. LLM call data. To instrument, and start collecting, see{' '}
<a
className="text-dwlightblue"
-
href={'https://burr.dagworks.io/concepts/additional-visibility/#quickstart'}
+
href={'https://burr.apache.org/concepts/additional-visibility/#quickstart'}
>
see docs.
</a>
diff --git a/telemetry/ui/src/components/routes/app/ReproduceView.tsx
b/telemetry/ui/src/components/routes/app/ReproduceView.tsx
index 6eee510c..702546b9 100644
--- a/telemetry/ui/src/components/routes/app/ReproduceView.tsx
+++ b/telemetry/ui/src/components/routes/app/ReproduceView.tsx
@@ -71,7 +71,7 @@ export const ReproduceView = (props: {
<p>
To generate a test case for this step, run the following command.
<a
- href="https://burr.dagworks.io/examples/guardrails/creating_tests/"
+ href="https://burr.apache.org/examples/guardrails/creating_tests/"
target="_blank"
rel="noreferrer"
className="hover:underline text-dwlightblue"