diff --git a/src/apps/chat/components/message/fragments-content/BlockPartToolInvocation.tsx b/src/apps/chat/components/message/fragments-content/BlockPartToolInvocation.tsx
new file mode 100644
index 000000000..1ab7f279d
--- /dev/null
+++ b/src/apps/chat/components/message/fragments-content/BlockPartToolInvocation.tsx
@@ -0,0 +1,104 @@
+import * as React from 'react';
+
+import type { ColorPaletteProp, SxProps, VariantProp } from '@mui/joy/styles/types';
+import { Sheet } from '@mui/joy';
+
+import { BlocksContainer } from '~/modules/blocks/BlocksContainers';
+import { useScaledTypographySx } from '~/modules/blocks/blocks.styles';
+
+import type { ContentScaling } from '~/common/app.theme';
+import type { DMessageToolInvocationPart } from '~/common/stores/chat/chat.fragments';
+
+
+const keyValueGridSx = {
+ border: '1px solid',
+ borderRadius: 'sm',
+ boxShadow: 'inset 2px 0 4px -2px rgba(0, 0, 0, 0.2)',
+ p: 1.5,
+
+ // Grid layout with 2 columns
+ display: 'grid',
+ gridTemplateColumns: 'auto 1fr',
+ // alignItems: 'baseline',
+ columnGap: 2,
+ rowGap: 0.5,
+
+ // fade the text of the first column
+ // '& > :nth-of-type(odd)': {
+ // opacity: 0.67,
+ // // fontSize: '90%',
+ // },
+} as const;
+
+
+export type KeyValueData = { label: string, value: React.ReactNode, asCode?: boolean }[];
+
+export function KeyValueGrid(props: {
+ data: KeyValueData,
+ contentScaling: ContentScaling,
+ color?: ColorPaletteProp,
+ variant?: VariantProp,
+ stableSx?: SxProps,
+}) {
+
+ const { fontSize, lineHeight } = useScaledTypographySx(props.contentScaling, false, false);
+
+ const gridSx = React.useMemo(() => ({
+ ...keyValueGridSx,
+ // fontWeight,
+ fontSize,
+ lineHeight,
+ ...props.stableSx,
+ }), [fontSize, lineHeight, props.stableSx]);
+
+ return (
+
+ {props.data.map(({ label, value }, index) => (
+
+ {label}
+ {value}
+
+ ))}
+
+ );
+}
+
+
+export function BlockPartToolInvocation(props: {
+ toolInvocationPart: DMessageToolInvocationPart,
+ contentScaling: ContentScaling,
+ onDoubleClick?: (event: React.MouseEvent) => void;
+}) {
+
+ const part = props.toolInvocationPart;
+
+ const kvData: KeyValueData = React.useMemo(() => {
+ switch (part.invocation.type) {
+ case 'function_call':
+ return [
+ { label: 'Name', value: {part.invocation.name} },
+ { label: 'Args', value: part.invocation.args || 'None', asCode: true },
+ { label: 'Id', value: part.id },
+ ];
+ case 'code_execution':
+ return [
+ { label: 'Language', value: part.invocation.language },
+ { label: 'Author', value: part.invocation.author },
+ {
+ label: 'Code',
+ value:
{part.invocation.code.trim()}
,
+ },
+ { label: 'Id', value: part.id },
+ ];
+ }
+ }, [part]);
+
+ return (
+
+
+
+ );
+}
diff --git a/src/apps/chat/components/message/fragments-content/BlockPartToolResponse.tsx b/src/apps/chat/components/message/fragments-content/BlockPartToolResponse.tsx
new file mode 100644
index 000000000..7d230c87f
--- /dev/null
+++ b/src/apps/chat/components/message/fragments-content/BlockPartToolResponse.tsx
@@ -0,0 +1,49 @@
+import * as React from 'react';
+
+import { BlocksContainer } from '~/modules/blocks/BlocksContainers';
+
+import type { ContentScaling } from '~/common/app.theme';
+import type { DMessageToolResponsePart } from '~/common/stores/chat/chat.fragments';
+
+import { KeyValueData, KeyValueGrid } from './BlockPartToolInvocation';
+
+
+export function BlockPartToolResponse(props: {
+ toolResponsePart: DMessageToolResponsePart,
+ contentScaling: ContentScaling,
+ onDoubleClick?: (event: React.MouseEvent) => void;
+}) {
+
+ const part = props.toolResponsePart;
+
+ const kvData: KeyValueData = React.useMemo(() => {
+ switch (part.response.type) {
+ case 'function_call':
+ return [
+ { label: 'Id', value: part.id },
+ { label: 'Name', value: {part.response.name} },
+ { label: 'Response', value: part.response.result, asCode: true },
+ ...(!part.error ? [] : [{ label: 'Error', value: part.error }]),
+ { label: 'Environment', value: part.environment },
+ ];
+ case 'code_execution':
+ return [
+ { label: 'Id', value: part.id },
+ { label: 'Response', value: part.response.result, asCode: true },
+ ...(!part.error ? [] : [{ label: 'Error', value: part.error }]),
+ { label: 'Executor', value: part.response.executor },
+ { label: 'Environment', value: part.environment },
+ ];
+ }
+ }, [part]);
+
+ return (
+
+
+
+ );
+}
diff --git a/src/apps/chat/components/message/fragments-content/ContentFragments.tsx b/src/apps/chat/components/message/fragments-content/ContentFragments.tsx
index aea6281c2..1592000ab 100644
--- a/src/apps/chat/components/message/fragments-content/ContentFragments.tsx
+++ b/src/apps/chat/components/message/fragments-content/ContentFragments.tsx
@@ -1,9 +1,7 @@
import * as React from 'react';
import type { SxProps } from '@mui/joy/styles/types';
-import { Box, Button, Sheet } from '@mui/joy';
-
-import { BlocksContainer } from '~/modules/blocks/BlocksContainers';
+import { Box, Button } from '@mui/joy';
import { ScaledTextBlockRenderer } from '~/modules/blocks/ScaledTextBlockRenderer';
import type { ContentScaling, UIComplexityMode } from '~/common/app.theme';
@@ -17,6 +15,8 @@ import { BlockPartError } from './BlockPartError';
import { BlockPartImageRef } from './BlockPartImageRef';
import { BlockPartPlaceholder } from './BlockPartPlaceholder';
import { BlockPartText_AutoBlocks } from './BlockPartText_AutoBlocks';
+import { BlockPartToolInvocation } from './BlockPartToolInvocation';
+import { BlockPartToolResponse } from './BlockPartToolResponse';
const editLayoutSx: SxProps = {
@@ -234,122 +234,22 @@ export function ContentFragments(props: {
case 'tool_invocation':
return (
-
- {part.invocation.type === 'function_call' ? (
-
- Id
- {part.id}
- Name
- {part.invocation.name}
- Args
- {part.invocation.args/*?.replaceAll('{', '').replaceAll('}', '').replaceAll('","', '", "')*/}
-
- ) : (
-
- Id
- {part.id}
- Language
- {part.invocation.language}
- Code
- {part.invocation.code?.trim()}
- Author
- {part.invocation.author}
-
- )}
-
+
);
case 'tool_response':
return (
-
- {part.response.type === 'function_call' ? (
-
- Type
- Function Call Response
- Id
- {part.id}
- Error
- {part.error === null ? 'null' : part.error === 'false' ? '' : part.error}
- Name
- {part.response.name}
- Result
- {part.response.result}
- Environment
- {part.environment}
-
- ) : (
-
- Type
- Code Execution Response
- Id
- {part.id}
- Error
- {part.error === null ? 'null' : part.error === 'false' ? '' : part.error}
- Result
- {part.response.result}
- Executor
- {part.response.executor}
- Environment
- {part.environment}
-
- )}
-
+
);
case '_pt_sentinel':
diff --git a/src/modules/blocks/blocks.styles.ts b/src/modules/blocks/blocks.styles.ts
index 3cfa20176..e5d097876 100644
--- a/src/modules/blocks/blocks.styles.ts
+++ b/src/modules/blocks/blocks.styles.ts
@@ -41,7 +41,7 @@ export function useScaledImageSx(contentScaling: ContentScaling): SxProps {
}), [contentScaling]);
}
-export function useScaledTypographySx(contentScaling: ContentScaling, showAsDanger: boolean, showAsItalic: boolean): SxProps {
+export function useScaledTypographySx(contentScaling: ContentScaling, showAsDanger: boolean, showAsItalic: boolean) {
return React.useMemo(() => ({
fontSize: themeScalingMap[contentScaling]?.blockFontSize ?? undefined,
lineHeight: themeScalingMap[contentScaling]?.blockLineHeight ?? 1.75,