wuyiqunLu commited on
Commit
f0f12a9
β€’
1 Parent(s): 84c9f51

feat: format the responses (#15)

Browse files

<img width="1054" alt="image"
src="https://github.com/landing-ai/vision-agent-ui/assets/132986242/b1ecf15d-1262-4cb3-be8e-cc39d9d01773">

Files changed (2) hide show
  1. app/globals.css +52 -0
  2. components/chat/ChatMessage.tsx +90 -3
app/globals.css CHANGED
@@ -110,3 +110,55 @@
110
  pointer-events: none;
111
  }
112
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  pointer-events: none;
111
  }
112
  }
113
+
114
+ /* Light theme. */
115
+ :root {
116
+ --color-canvas-default: #ffffff;
117
+ --color-canvas-subtle: #f6f8fa;
118
+ --color-border-default: #d0d7de;
119
+ --color-border-muted: hsla(210, 18%, 87%, 1);
120
+ }
121
+
122
+ /* Dark theme. */
123
+ @media (prefers-color-scheme: dark) {
124
+ :root {
125
+ --color-canvas-default: #0d1117;
126
+ --color-canvas-subtle: #161b22;
127
+ --color-border-default: #30363d;
128
+ --color-border-muted: #21262d;
129
+ }
130
+ }
131
+
132
+ table {
133
+ border-spacing: 0;
134
+ border-collapse: collapse;
135
+ display: block;
136
+ margin-top: 0;
137
+ margin-bottom: 16px;
138
+ width: max-content;
139
+ max-width: 100%;
140
+ overflow: auto;
141
+ }
142
+
143
+ tr {
144
+ background-color: var(--color-canvas-default);
145
+ border-top: 1px solid var(--color-border-muted);
146
+ }
147
+
148
+ tr:nth-child(2n) {
149
+ background-color: var(--color-canvas-subtle);
150
+ }
151
+
152
+ td,
153
+ th {
154
+ padding: 6px 13px;
155
+ border: 1px solid var(--color-border-default);
156
+ }
157
+
158
+ th {
159
+ font-weight: 600;
160
+ }
161
+
162
+ table img {
163
+ background-color: transparent;
164
+ }
components/chat/ChatMessage.tsx CHANGED
@@ -15,7 +15,68 @@ export interface ChatMessageProps {
15
  message: MessageBase;
16
  }
17
 
 
 
 
 
 
 
 
 
 
 
 
18
  export function ChatMessage({ message, ...props }: ChatMessageProps) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  return (
20
  <div className={cn('group relative mb-4 flex items-start')} {...props}>
21
  <div
@@ -29,12 +90,39 @@ export function ChatMessage({ message, ...props }: ChatMessageProps) {
29
  {message.role === 'user' ? <IconUser /> : <IconOpenAI />}
30
  </div>
31
  <div className="flex-1 px-1 ml-4 space-y-2 overflow-hidden">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  <MemoizedReactMarkdown
33
  className="break-words"
34
  remarkPlugins={[remarkGfm, remarkMath]}
35
  components={{
36
  p({ children }) {
37
- return <p className="mb-2 last:mb-0">{children}</p>;
 
 
38
  },
39
  code({ node, inline, className, children, ...props }) {
40
  if (children.length) {
@@ -48,7 +136,6 @@ export function ChatMessage({ message, ...props }: ChatMessageProps) {
48
  }
49
 
50
  const match = /language-(\w+)/.exec(className || '');
51
-
52
  if (inline) {
53
  return (
54
  <code className={className} {...props}>
@@ -68,7 +155,7 @@ export function ChatMessage({ message, ...props }: ChatMessageProps) {
68
  },
69
  }}
70
  >
71
- {message.content}
72
  </MemoizedReactMarkdown>
73
  <ChatMessageActions message={message} />
74
  </div>
 
15
  message: MessageBase;
16
  }
17
 
18
+
19
+ const PAIRS: Record<string, string> = {
20
+ '┍': 'β”‘',
21
+ '┝': 'β”₯',
22
+ 'β”œ': '─',
23
+ 'β”•': 'β”™',
24
+ };
25
+
26
+ const MIDDLE_STARTER = '┝';
27
+ const MIDDLE_SEPARATOR = 'β”Ώ';
28
+
29
  export function ChatMessage({ message, ...props }: ChatMessageProps) {
30
+ const cleanupMessage = ({ content, role }: MessageBase) => {
31
+ if (role === 'user') {
32
+ return {
33
+ content,
34
+ };
35
+ }
36
+ const [logs = '', answer = ''] = content.split('<ANSWER>');
37
+ const cleanedLogs = [];
38
+ let left = 0;
39
+ let right = 0;
40
+ while (right < logs.length) {
41
+ if (Object.keys(PAIRS).includes(content[right])) {
42
+ cleanedLogs.push(content.substring(left, right));
43
+ left = right++;
44
+ while (
45
+ right < content.length &&
46
+ PAIRS[content[left]] !== content[right]
47
+ ) {
48
+ right++;
49
+ }
50
+ if (content[left] === MIDDLE_STARTER) {
51
+ // add the text alignment so it can be shown as a table
52
+ const separators = logs
53
+ .substring(left, right)
54
+ .split(MIDDLE_SEPARATOR).length;
55
+ if (separators > 0) {
56
+ cleanedLogs.push(
57
+ Array(separators + 1)
58
+ .fill('|')
59
+ .join(' :- '),
60
+ );
61
+ }
62
+ }
63
+ left = ++right;
64
+ } else {
65
+ right++;
66
+ }
67
+ }
68
+ cleanedLogs.push(content.substring(left, right));
69
+ return {
70
+ logs: cleanedLogs
71
+ .join('')
72
+ .replace(/β”‚/g, '|')
73
+ .split('|\n\n|')
74
+ .join('|\n|'),
75
+ content: answer.replace('</</ANSWER>', '').replace('</ANSWER>', ''),
76
+ };
77
+ };
78
+
79
+ const { logs, content } = cleanupMessage(message);
80
  return (
81
  <div className={cn('group relative mb-4 flex items-start')} {...props}>
82
  <div
 
90
  {message.role === 'user' ? <IconUser /> : <IconOpenAI />}
91
  </div>
92
  <div className="flex-1 px-1 ml-4 space-y-2 overflow-hidden">
93
+ {logs && message.role !== 'user' && (
94
+ <div className="bg-slate-100 mb-4 p-4 max-h-60 overflow-auto">
95
+ <div className="text-xl font-bold">Thinking Process</div>
96
+ <MemoizedReactMarkdown
97
+ className="break-words text-sm"
98
+ remarkPlugins={[remarkGfm, remarkMath]}
99
+ components={{
100
+ p({ children }) {
101
+ return (
102
+ <p className="my-2 last:mb-0 whitespace-pre-line">
103
+ {children}
104
+ </p>
105
+ );
106
+ },
107
+ code({ children, ...props }) {
108
+ return (
109
+ <code className="whitespace-pre-line">{children}</code>
110
+ );
111
+ },
112
+ }}
113
+ >
114
+ {logs}
115
+ </MemoizedReactMarkdown>
116
+ </div>
117
+ )}
118
  <MemoizedReactMarkdown
119
  className="break-words"
120
  remarkPlugins={[remarkGfm, remarkMath]}
121
  components={{
122
  p({ children }) {
123
+ return (
124
+ <p className="my-2 last:mb-0 whitespace-pre-line">{children}</p>
125
+ );
126
  },
127
  code({ node, inline, className, children, ...props }) {
128
  if (children.length) {
 
136
  }
137
 
138
  const match = /language-(\w+)/.exec(className || '');
 
139
  if (inline) {
140
  return (
141
  <code className={className} {...props}>
 
155
  },
156
  }}
157
  >
158
+ {content}
159
  </MemoizedReactMarkdown>
160
  <ChatMessageActions message={message} />
161
  </div>