Skip to content

Commit d3d7d40

Browse files
committed
feat: update masonry ui to display structured model response meta
Signed-off-by: Nick Mitchell <[email protected]>
1 parent c6b95ca commit d3d7d40

File tree

6 files changed

+136
-86
lines changed

6 files changed

+136
-86
lines changed

pdl-live-react/src/helpers.ts

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { type ReactElement } from "react"
2-
1+
import { stringify } from "yaml"
32
import type { LitellmModelBlock, PdlBlock, TextBlock } from "./pdl_ast"
43

54
/** Re-export for convenience */
@@ -36,7 +35,7 @@ export function hasResult(block: unknown): block is PdlBlockWithResult {
3635
}
3736

3837
export function isPdlBlock(
39-
o: unknown | ReactElement | PdlBlock,
38+
o: unknown | import("react").ReactElement | PdlBlock,
4039
): o is PdlBlock {
4140
const obj = o as PdlBlock
4241
return (
@@ -180,3 +179,55 @@ type MessageBearing = Omit<import("./pdl_ast").ReadBlock, "message"> & {
180179
export function hasMessage(block: PdlBlock): block is MessageBearing {
181180
return typeof (block as MessageBearing).message === "string"
182181
}
182+
183+
function tryJson(s: unknown) {
184+
if (typeof s === "string") {
185+
try {
186+
return JSON.parse(s)
187+
} catch (_err) {
188+
// intentional fall-through
189+
}
190+
}
191+
return s
192+
}
193+
194+
export function extractStructuredModelResponse({
195+
result,
196+
parser,
197+
}: LitellmModelBlock) {
198+
const json = tryJson(result)
199+
const resultForDisplay: string = Array.isArray(json)
200+
? json.map(({ sentence }) => String(sentence)).join("\n")
201+
: typeof result === "object"
202+
? stringify(result)
203+
: String(result)
204+
205+
const lang: import("./view/code/Code").SupportedLanguage | undefined =
206+
Array.isArray(json)
207+
? undefined
208+
: parser === "jsonl" || parser === "json"
209+
? "json"
210+
: parser === "yaml"
211+
? "yaml"
212+
: "plaintext"
213+
214+
// Ugh, some of this logic may be specific to Granite LLM
215+
const meta = !Array.isArray(json)
216+
? undefined
217+
: json.flatMap(({ meta }) =>
218+
Object.entries(meta).map(([k, v]) => {
219+
if (
220+
k === "citation" &&
221+
v &&
222+
typeof v === "object" &&
223+
"snippet" in v
224+
) {
225+
return [k, v.snippet]
226+
} else {
227+
return [k, v]
228+
}
229+
}),
230+
)
231+
232+
return { resultForDisplay, lang, meta }
233+
}

pdl-live-react/src/view/detail/kind/Model.tsx

Lines changed: 16 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,27 @@ import { stringify } from "yaml"
22

33
import Group from "../Group"
44
import Result from "../../Result"
5-
import { capitalizeAndUnSnakeCase } from "../../../helpers"
6-
7-
function tryJson(s: unknown) {
8-
if (typeof s === "string") {
9-
try {
10-
return JSON.parse(s)
11-
} catch (_err) {
12-
// intentional fall-through
13-
}
14-
}
15-
return s
16-
}
5+
import {
6+
capitalizeAndUnSnakeCase,
7+
extractStructuredModelResponse,
8+
} from "../../../helpers"
179

1810
export default function ModelItems({
19-
block: { platform, model, input, result, parser },
11+
block,
2012
}: {
2113
block: import("../../../pdl_ast").LitellmModelBlock
2214
}) {
23-
// All of this JSON stuff is to handle structured responses from the
24-
// model
25-
const json = tryJson(result)
26-
const resultForDisplay = Array.isArray(json)
27-
? json.map(({ sentence }) => sentence).join("\n")
28-
: result
29-
30-
const lang = Array.isArray(json)
31-
? undefined
32-
: parser === "jsonl" || parser === "json"
33-
? "json"
34-
: parser === "yaml"
35-
? "yaml"
36-
: "plaintext"
15+
const { platform, model, input } = block
16+
const { resultForDisplay, lang, meta } = extractStructuredModelResponse(block)
3717

38-
// Ugh, some of this logic may be specific to Granite LLM
39-
const meta = Array.isArray(json)
40-
? json.flatMap(({ meta }, idx) =>
41-
Object.entries(meta)
42-
.map(([k, v]) => {
43-
if (
44-
k === "citation" &&
45-
v &&
46-
typeof v === "object" &&
47-
"snippet" in v
48-
) {
49-
return [k, v.snippet]
50-
} else {
51-
return [k, v]
52-
}
53-
})
54-
.map(([k, v]) => (
55-
<Result
56-
key={k + "." + idx}
57-
term={capitalizeAndUnSnakeCase(String(k))}
58-
result={typeof v === "object" ? stringify(v) : v}
59-
lang={typeof v === "object" ? "yaml" : undefined}
60-
/>
61-
)),
62-
)
63-
: undefined
18+
const metaForDisplay = meta?.map(([k, v], idx) => (
19+
<Result
20+
key={k + "." + idx}
21+
term={capitalizeAndUnSnakeCase(String(k))}
22+
result={typeof v === "object" ? stringify(v) : v}
23+
lang={typeof v === "object" ? "yaml" : undefined}
24+
/>
25+
))
6426

6527
return (
6628
<>
@@ -70,7 +32,7 @@ export default function ModelItems({
7032
{typeof model === "string" && <Group term="Model" description={model} />}
7133
{typeof input === "string" && <Group term="Input" description={input} />}
7234
<Result result={resultForDisplay} lang={lang} />
73-
{meta}
35+
{metaForDisplay}
7436
</>
7537
)
7638
}

pdl-live-react/src/view/masonry/MasonryTile.tsx

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { useMemo } from "react"
33
import {
44
CardHeader,
55
CardTitle,
6+
DescriptionList,
7+
DescriptionListGroup,
8+
DescriptionListTerm,
9+
DescriptionListDescription,
610
Flex,
711
Panel,
812
PanelMain,
@@ -32,9 +36,10 @@ export default function MasonryTile({
3236
message,
3337
content,
3438
lang,
35-
crumb,
3639
kind,
3740
idx,
41+
footer1Key,
42+
footer1Value,
3843
}: Props) {
3944
const actions = useMemo(
4045
() =>
@@ -72,25 +77,33 @@ export default function MasonryTile({
7277
className="pdl-masonry-tile-header"
7378
>
7479
<div className="pdl-masonry-index">{idx}</div>
75-
{crumb && (
76-
<BreadcrumbBarForBlockId
77-
id={id}
78-
def={def}
79-
value={content}
80-
isCompact
81-
maxCrumbs={sml === "xl" ? 4 : sml === "l" ? 3 : 2}
82-
/>
83-
)}
80+
<BreadcrumbBarForBlockId
81+
id={id}
82+
def={def}
83+
value={content}
84+
isCompact
85+
maxCrumbs={sml === "xl" ? 4 : sml === "l" ? 3 : 2}
86+
/>
8487
</Flex>
8588
</CardTitle>
8689
</CardHeader>
8790
)
8891

92+
const footer = footer1Key && footer1Value && (
93+
<DescriptionList isCompact isHorizontal isFluid>
94+
<DescriptionListGroup>
95+
<DescriptionListTerm>{footer1Key}</DescriptionListTerm>
96+
<DescriptionListDescription>{footer1Value}</DescriptionListDescription>
97+
</DescriptionListGroup>
98+
</DescriptionList>
99+
)
100+
89101
return (
90102
<MasonryTileWrapper
91103
sml={sml}
92104
kind={/^[^.]+$/.test(id) ? "output-of-program" : kind}
93105
header={header}
106+
footer={footer}
94107
>
95108
<Panel isScrollable={sml !== "xl"} className="pdl-masonry-tile-panel">
96109
<PanelMain maxHeight={maxHeight}>

pdl-live-react/src/view/masonry/MasonryTileWrapper.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
import { Card, CardBody } from "@patternfly/react-core"
1+
import { Card, CardBody, CardFooter, Divider } from "@patternfly/react-core"
22

33
type Props = import("react").PropsWithChildren<{
44
kind?: string
55
variant?: "plain" | "default"
66
sml: import("./Toolbar").SML
77
header?: import("react").ReactNode
8+
footer?: import("react").ReactNode
89
}>
910

1011
export default function MasonryTileWrapper({
1112
sml,
1213
header,
14+
footer,
1315
kind,
1416
variant = "default",
1517
children,
@@ -25,6 +27,12 @@ export default function MasonryTileWrapper({
2527
>
2628
{header}
2729
<CardBody className="pdl-masonry-tile-body">{children}</CardBody>
30+
{footer && (
31+
<>
32+
<Divider />
33+
<CardFooter>{footer}</CardFooter>
34+
</>
35+
)}
2836
</Card>
2937
)
3038
}
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
type Tile = {
22
id: string
33
def?: string | null
4+
kind?: string
45
message?: string
6+
57
start_nanos?: number
68
end_nanos?: number
79
timezone?: string
8-
content: string
10+
911
lang?: import("../code/Code").SupportedLanguage
10-
crumb?: boolean
11-
kind?: string
12-
boundedHeight?: boolean
12+
content: string
13+
14+
footer1Key?: string
15+
footer1Value?: string | number | boolean
1316
}
1417

1518
export default Tile

pdl-live-react/src/view/masonry/model.ts

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ import { stringify } from "yaml"
22
import { computeModel as computeBaseModel } from "../timeline/model"
33

44
import {
5+
isLLMBlock,
56
hasMessage,
67
hasParser,
78
hasResult,
89
hasScalarResult,
910
hasTimingInformation,
11+
capitalizeAndUnSnakeCase,
12+
extractStructuredModelResponse,
1013
type NonScalarPdlBlock,
1114
} from "../../helpers"
1215

@@ -43,28 +46,39 @@ export default function computeModel(block: import("../../pdl_ast").PdlBlock) {
4346
.concat(result(block))
4447
.flatMap(({ id, block, children }) => {
4548
if (children.length === 0 && hasTimingInformation(block)) {
49+
const { resultForDisplay, meta, lang } = isLLMBlock(block)
50+
? extractStructuredModelResponse(block)
51+
: {
52+
resultForDisplay:
53+
typeof block.result === "object"
54+
? stringify(block.result)
55+
: String(block.result),
56+
meta: undefined,
57+
lang:
58+
typeof block.result === "object"
59+
? "yaml"
60+
: hasParser(block)
61+
? block.parser === "jsonl"
62+
? "json"
63+
: block.parser
64+
: undefined,
65+
}
66+
4667
return withDefs(block, [
4768
{
4869
id,
4970
def: block.def,
5071
kind: block.kind,
72+
lang,
5173
message: hasMessage(block) ? block.message : undefined,
52-
content:
53-
typeof block.result === "object"
54-
? stringify(block.result)
55-
: String(block.result),
74+
footer1Key: meta?.[0]?.[0]
75+
? capitalizeAndUnSnakeCase(String(meta[0][0]))
76+
: undefined,
77+
footer1Value: meta?.[0]?.[1] ? String(meta[0][1]) : undefined,
78+
content: resultForDisplay,
5679
start_nanos: block.pdl__timing.start_nanos,
5780
end_nanos: block.pdl__timing.end_nanos,
5881
timezone: block.pdl__timing.timezone,
59-
lang:
60-
typeof block.result === "object"
61-
? "yaml"
62-
: hasParser(block)
63-
? block.parser === "jsonl"
64-
? "json"
65-
: block.parser
66-
: undefined,
67-
crumb: true,
6882
},
6983
])
7084
}
@@ -95,7 +109,6 @@ function withDefs(block: NonScalarPdlBlock, tiles: Tile[]) {
95109
!v
96110
? []
97111
: {
98-
crumb: true,
99112
id: (block.id ?? "").replace(/\.?empty/g, ""),
100113
kind: "",
101114
def,

0 commit comments

Comments
 (0)