rippletide 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,45 @@
1
+ import React, { useState } from 'react';
2
+ import { Box, Text, useInput } from 'ink';
3
+ export const SelectMenu = ({ title, options, onSelect }) => {
4
+ const [selectedIndex, setSelectedIndex] = useState(0);
5
+ const getNextEnabledIndex = (start, direction) => {
6
+ for (let i = 1; i <= options.length; i++) {
7
+ const idx = (start + direction * i + options.length) % options.length;
8
+ if (!options[idx].disabled) {
9
+ return idx;
10
+ }
11
+ }
12
+ return start;
13
+ };
14
+ useInput((input, key) => {
15
+ if (key.upArrow) {
16
+ setSelectedIndex((prev) => getNextEnabledIndex(prev, -1));
17
+ }
18
+ else if (key.downArrow) {
19
+ setSelectedIndex((prev) => getNextEnabledIndex(prev, 1));
20
+ }
21
+ else if (key.return) {
22
+ const option = options[selectedIndex];
23
+ if (!option.disabled) {
24
+ onSelect(option.value);
25
+ }
26
+ }
27
+ });
28
+ return (React.createElement(Box, { flexDirection: "column" },
29
+ React.createElement(Box, { marginBottom: 1 },
30
+ React.createElement(Text, { color: "white", dimColor: true }, title)),
31
+ options.map((option, index) => (React.createElement(Box, { key: option.value, flexDirection: "column", marginBottom: 0 },
32
+ React.createElement(Box, null,
33
+ React.createElement(Text, { color: option.disabled ? '#d0c0cf' : 'white' }, index === selectedIndex ? '> ' : ' '),
34
+ React.createElement(Text, { color: option.disabled
35
+ ? '#d0c0cf'
36
+ : index === selectedIndex
37
+ ? '#eba1b5'
38
+ : 'white', bold: index === selectedIndex && !option.disabled }, option.label)),
39
+ option.description && (React.createElement(Box, { paddingLeft: 2 },
40
+ React.createElement(Text, { color: option.disabled ? '#d0c0cf' : 'white', dimColor: option.disabled },
41
+ " ",
42
+ option.description)))))),
43
+ React.createElement(Box, { marginTop: 1 },
44
+ React.createElement(Text, { color: "white", dimColor: true }, "Up/Down to navigate - Enter to select (gray = coming soon)"))));
45
+ };
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ interface SpinnerProps {
3
+ label: string;
4
+ }
5
+ export declare const Spinner: React.FC<SpinnerProps>;
6
+ export {};
@@ -0,0 +1,19 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Box, Text } from 'ink';
3
+ const spinnerFrames = ['|', '/', '-', '\\'];
4
+ export const Spinner = ({ label }) => {
5
+ const [frame, setFrame] = useState(0);
6
+ useEffect(() => {
7
+ const timer = setInterval(() => {
8
+ setFrame((prevFrame) => (prevFrame + 1) % spinnerFrames.length);
9
+ }, 100);
10
+ return () => {
11
+ clearInterval(timer);
12
+ };
13
+ }, []);
14
+ return (React.createElement(Box, null,
15
+ React.createElement(Text, { color: "#eba1b5" },
16
+ spinnerFrames[frame],
17
+ " "),
18
+ React.createElement(Text, { dimColor: true }, label)));
19
+ };
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ interface SummaryProps {
3
+ totalTests: number;
4
+ passed: number;
5
+ failed: number;
6
+ duration: string;
7
+ evaluationUrl: string;
8
+ }
9
+ export declare const Summary: React.FC<SummaryProps>;
10
+ export {};
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ import { Box, Text } from 'ink';
3
+ export const Summary = ({ totalTests, passed, failed, duration, evaluationUrl }) => {
4
+ const passRate = ((passed / totalTests) * 100).toFixed(1);
5
+ return (React.createElement(Box, { flexDirection: "column" },
6
+ React.createElement(Box, { marginBottom: 2 },
7
+ React.createElement(Text, { bold: true, color: "#eba1b5" }, "Evaluation Complete")),
8
+ React.createElement(Box, { flexDirection: "column", marginBottom: 2 },
9
+ React.createElement(Box, null,
10
+ React.createElement(Box, { width: 12 },
11
+ React.createElement(Text, { dimColor: true }, "Tests:")),
12
+ React.createElement(Text, null, totalTests)),
13
+ React.createElement(Box, null,
14
+ React.createElement(Box, { width: 12 },
15
+ React.createElement(Text, { dimColor: true }, "Passed:")),
16
+ React.createElement(Text, null, passed)),
17
+ React.createElement(Box, null,
18
+ React.createElement(Box, { width: 12 },
19
+ React.createElement(Text, { dimColor: true }, "Failed:")),
20
+ React.createElement(Text, null, failed)),
21
+ React.createElement(Box, null,
22
+ React.createElement(Box, { width: 12 },
23
+ React.createElement(Text, { dimColor: true }, "Success:")),
24
+ React.createElement(Text, null,
25
+ passRate,
26
+ "%")),
27
+ React.createElement(Box, null,
28
+ React.createElement(Box, { width: 12 },
29
+ React.createElement(Text, { dimColor: true }, "Duration:")),
30
+ React.createElement(Text, null, duration))),
31
+ React.createElement(Box, { flexDirection: "column" },
32
+ React.createElement(Text, { dimColor: true }, "Full results:"),
33
+ React.createElement(Text, { color: "#eba1b5" }, evaluationUrl))));
34
+ };
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ interface TextInputProps {
3
+ label: string;
4
+ onSubmit: (value: string) => void;
5
+ placeholder?: string;
6
+ }
7
+ export declare const TextInput: React.FC<TextInputProps>;
8
+ export {};
@@ -0,0 +1,12 @@
1
+ import React, { useState } from 'react';
2
+ import { Box, Text } from 'ink';
3
+ import InkTextInput from 'ink-text-input';
4
+ export const TextInput = ({ label, onSubmit, placeholder }) => {
5
+ const [value, setValue] = useState('');
6
+ return (React.createElement(Box, { flexDirection: "column" },
7
+ React.createElement(Box, { marginBottom: 1 },
8
+ React.createElement(Text, { dimColor: true }, label)),
9
+ React.createElement(Box, null,
10
+ React.createElement(Text, { color: "#eba1b5" }, "> "),
11
+ React.createElement(InkTextInput, { value: value, onChange: setValue, onSubmit: () => onSubmit(value), placeholder: placeholder }))));
12
+ };
package/dist/demo.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/demo.js ADDED
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ import React, { useState, useEffect } from 'react';
3
+ import { render, Box, Text } from 'ink';
4
+ import { Header } from './components/Header.js';
5
+ import { Spinner } from './components/Spinner.js';
6
+ import { ProgressBar } from './components/ProgressBar.js';
7
+ import { Summary } from './components/Summary.js';
8
+ const knowledgeSources = [
9
+ { label: 'Local Files', value: 'files', description: 'Use files from current directory' },
10
+ { label: 'Database', value: 'database', description: 'Connect to a database' },
11
+ { label: 'API Endpoint', value: 'api', description: 'Fetch from REST API' },
12
+ { label: 'Manual Input', value: 'manual', description: 'Enter knowledge manually' },
13
+ ];
14
+ const Demo = () => {
15
+ const [step, setStep] = useState('agent-endpoint');
16
+ const [agentEndpoint] = useState('http://localhost:8000');
17
+ const [knowledgeSource] = useState('files');
18
+ const [knowledgeFound, setKnowledgeFound] = useState(false);
19
+ const [evaluationProgress, setEvaluationProgress] = useState(0);
20
+ // Auto-progress through steps for demo
21
+ useEffect(() => {
22
+ const timers = [];
23
+ if (step === 'agent-endpoint') {
24
+ timers.push(setTimeout(() => setStep('checking-knowledge'), 3000));
25
+ }
26
+ else if (step === 'checking-knowledge') {
27
+ timers.push(setTimeout(() => {
28
+ const found = true; // Always find knowledge in demo
29
+ setKnowledgeFound(found);
30
+ if (found) {
31
+ setStep('running-evaluation');
32
+ }
33
+ else {
34
+ setStep('select-source');
35
+ }
36
+ }, 2000));
37
+ }
38
+ else if (step === 'select-source') {
39
+ timers.push(setTimeout(() => setStep('running-evaluation'), 3000));
40
+ }
41
+ else if (step === 'running-evaluation') {
42
+ const progressTimer = setInterval(() => {
43
+ setEvaluationProgress(prev => {
44
+ const next = prev + Math.random() * 8 + 2;
45
+ if (next >= 100) {
46
+ setStep('complete');
47
+ return 100;
48
+ }
49
+ return next;
50
+ });
51
+ }, 300);
52
+ timers.push(progressTimer);
53
+ }
54
+ return () => timers.forEach(timer => clearTimeout(timer));
55
+ }, [step]);
56
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
57
+ React.createElement(Header, null),
58
+ step === 'agent-endpoint' && (React.createElement(Box, { flexDirection: "column" },
59
+ React.createElement(Box, { marginBottom: 1 },
60
+ React.createElement(Text, { bold: true, color: "cyan" }, "Enter your agent endpoint:")),
61
+ React.createElement(Box, { marginBottom: 1 },
62
+ React.createElement(Text, { color: "green" }, "\u2713 http://localhost:8000")),
63
+ React.createElement(Box, { marginTop: 1 },
64
+ React.createElement(Text, { dimColor: true }, "Continuing to next step...")))),
65
+ step === 'checking-knowledge' && (React.createElement(Box, { flexDirection: "column" },
66
+ React.createElement(Spinner, { label: "Checking for knowledge base in current folder..." }))),
67
+ step === 'select-source' && (React.createElement(Box, { flexDirection: "column" },
68
+ React.createElement(Box, { marginBottom: 1 },
69
+ React.createElement(Text, { color: "yellow" }, "\u26A0 No knowledge base found in current folder")),
70
+ React.createElement(Box, { marginBottom: 1 },
71
+ React.createElement(Text, { bold: true, color: "cyan" }, "Select knowledge source:")),
72
+ knowledgeSources.map((option, index) => (React.createElement(Box, { key: option.value, marginLeft: 2 },
73
+ React.createElement(Text, { color: index === 0 ? 'green' : 'white' },
74
+ index === 0 ? '❯ ' : ' ',
75
+ option.label,
76
+ React.createElement(Text, { color: "gray" },
77
+ " - ",
78
+ option.description))))),
79
+ React.createElement(Box, { marginTop: 1 },
80
+ React.createElement(Text, { dimColor: true }, "Auto-selecting for demo...")))),
81
+ step === 'running-evaluation' && (React.createElement(Box, { flexDirection: "column" },
82
+ React.createElement(Box, { marginBottom: 2 },
83
+ React.createElement(Text, { bold: true }, "Running evaluation...")),
84
+ React.createElement(Box, { marginBottom: 1 },
85
+ React.createElement(Text, null,
86
+ "Agent Endpoint: ",
87
+ React.createElement(Text, { color: "cyan" }, agentEndpoint))),
88
+ React.createElement(Box, { marginBottom: 2 },
89
+ React.createElement(Text, null,
90
+ "Knowledge Source: ",
91
+ React.createElement(Text, { color: "green" }, "\u2713 Found in folder"))),
92
+ React.createElement(ProgressBar, { progress: evaluationProgress, label: "Evaluation Progress" }),
93
+ React.createElement(Box, { marginTop: 1 },
94
+ React.createElement(Spinner, { label: "Running test cases..." })))),
95
+ step === 'complete' && (React.createElement(Summary, { totalTests: 50, passed: 42, failed: 8, duration: "2m 34s", evaluationUrl: "https://app.rippletide.com/evaluations/abc123def456" }))));
96
+ };
97
+ render(React.createElement(Demo, null));
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import React from 'react';
3
+ import { render } from 'ink';
4
+ import { App } from './App.js';
5
+ process.stdout.write('\x1Bc');
6
+ render(React.createElement(App, null));
@@ -0,0 +1,5 @@
1
+ export declare const logger: {
2
+ debug: (...args: any[]) => void;
3
+ error: (...args: any[]) => void;
4
+ info: (...args: any[]) => void;
5
+ };
@@ -0,0 +1,18 @@
1
+ const isDebugMode = process.argv.includes('--debug');
2
+ export const logger = {
3
+ debug: (...args) => {
4
+ if (isDebugMode) {
5
+ console.log('[DEBUG]', ...args);
6
+ }
7
+ },
8
+ error: (...args) => {
9
+ if (isDebugMode) {
10
+ console.error('[ERROR]', ...args);
11
+ }
12
+ },
13
+ info: (...args) => {
14
+ if (isDebugMode) {
15
+ console.log(...args);
16
+ }
17
+ },
18
+ };
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "rippletide",
3
+ "version": "1.0.0",
4
+ "description": "Rippletide Evaluation CLI",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "rippletide": "./bin/rippletide"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "eval": "tsx src/index.tsx",
12
+ "start": "node dist/index.js"
13
+ },
14
+ "keywords": [
15
+ "rippletide",
16
+ "cli",
17
+ "evaluation"
18
+ ],
19
+ "author": "",
20
+ "license": "ISC",
21
+ "type": "module",
22
+ "dependencies": {
23
+ "axios": "^1.13.2",
24
+ "chalk": "^5.3.0",
25
+ "ink": "^5.0.1",
26
+ "ink-text-input": "^6.0.0",
27
+ "react": "^18.3.1"
28
+ },
29
+ "devDependencies": {
30
+ "@babel/core": "^7.26.0",
31
+ "@babel/preset-react": "^7.25.9",
32
+ "@babel/preset-typescript": "^7.26.0",
33
+ "@types/node": "^22.10.2",
34
+ "@types/react": "^18.3.18",
35
+ "tsx": "^4.19.2",
36
+ "typescript": "^5.7.2"
37
+ }
38
+ }
package/qanda.json ADDED
@@ -0,0 +1,22 @@
1
+ [
2
+ {
3
+ "question": "What can you help me with?",
4
+ "answer": "I am a test AI agent designed to help you with various tasks including answering questions, providing information, and assisting with problem-solving."
5
+ },
6
+ {
7
+ "question": "Tell me about your capabilities",
8
+ "answer": "I can provide information, answer questions, understand natural language, and assist with a wide range of topics through conversation."
9
+ },
10
+ {
11
+ "question": "How do I get started?",
12
+ "answer": "Getting started is easy! Just ask me any question or tell me what you need help with, and I'll do my best to assist you."
13
+ },
14
+ {
15
+ "question": "What features do you support?",
16
+ "answer": "I support natural language understanding, contextual responses, information retrieval, and can help with various tasks across different domains."
17
+ },
18
+ {
19
+ "question": "Can you explain your main functionality?",
20
+ "answer": "My main functionality includes answering questions, providing explanations, offering assistance with tasks, and engaging in meaningful conversations to help users achieve their goals."
21
+ }
22
+ ]
package/src/App.tsx ADDED
@@ -0,0 +1,266 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Box, Text, useApp } from 'ink';
3
+ import { Header } from './components/Header.js';
4
+ import { TextInput } from './components/TextInput.js';
5
+ import { SelectMenu } from './components/SelectMenu.js';
6
+ import { Spinner } from './components/Spinner.js';
7
+ import { ProgressBar } from './components/ProgressBar.js';
8
+ import { Summary } from './components/Summary.js';
9
+ import { api } from './api/client.js';
10
+
11
+ type Step =
12
+ | 'agent-endpoint'
13
+ | 'checking-knowledge'
14
+ | 'select-source'
15
+ | 'running-evaluation'
16
+ | 'complete';
17
+
18
+ const knowledgeSources = [
19
+ { label: 'Local Files (qanda.json)', value: 'files', description: 'Use qanda.json from current directory' },
20
+ { label: 'Current Repository', value: 'repo', description: 'Scan current git repository', disabled: true },
21
+ { label: 'Database', value: 'database', description: 'Connect to a database', disabled: true },
22
+ { label: 'API Endpoint', value: 'api', description: 'Fetch from REST API', disabled: true },
23
+ { label: 'GitHub Repository', value: 'github', description: 'Import from GitHub repo', disabled: true },
24
+ { label: 'Skip (No Knowledge)', value: 'skip', description: 'Run tests without knowledge base', disabled: true },
25
+ ];
26
+
27
+ export const App: React.FC = () => {
28
+ const [step, setStep] = useState<Step>('agent-endpoint');
29
+ const [agentEndpoint, setAgentEndpoint] = useState('');
30
+ const [knowledgeSource, setKnowledgeSource] = useState('');
31
+ const [knowledgeFound, setKnowledgeFound] = useState(false);
32
+ const [evaluationProgress, setEvaluationProgress] = useState(0);
33
+ const [evaluationResult, setEvaluationResult] = useState<any>(null);
34
+ const [currentQuestion, setCurrentQuestion] = useState<string>('');
35
+ const [currentLLMResponse, setCurrentLLMResponse] = useState<string>('');
36
+ const [evaluationLogs, setEvaluationLogs] = useState<Array<{question: string, response: string}>>([]);
37
+
38
+ useEffect(() => {
39
+ if (step === 'checking-knowledge') {
40
+ (async () => {
41
+ try {
42
+ const result = await api.checkKnowledge();
43
+ setKnowledgeFound(result.found);
44
+ } catch (error) {
45
+ console.error('Error checking knowledge:', error);
46
+ setKnowledgeFound(false);
47
+ }
48
+ setStep('select-source');
49
+ })();
50
+ }
51
+ }, [step]);
52
+
53
+ useEffect(() => {
54
+ if (step === 'running-evaluation') {
55
+ (async () => {
56
+ try {
57
+ const startTime = Date.now();
58
+ const logs: Array<{question: string, response: string}> = [];
59
+
60
+ setEvaluationProgress(5);
61
+ await api.generateApiKey('CLI Evaluation');
62
+
63
+ setEvaluationProgress(10);
64
+ const agent = await api.createAgent(agentEndpoint);
65
+ const agentId = agent.id;
66
+
67
+ setEvaluationProgress(30);
68
+
69
+ setEvaluationProgress(40);
70
+ let testPrompts: Array<{question: string, answer?: string}> | string[] = [];
71
+ if (knowledgeSource === 'files') {
72
+ const knowledgeResult = await api.checkKnowledge();
73
+ if (knowledgeResult.found && knowledgeResult.path) {
74
+ try {
75
+ const fs = await import('fs');
76
+ const knowledgeData = JSON.parse(fs.readFileSync(knowledgeResult.path, 'utf-8'));
77
+ if (Array.isArray(knowledgeData)) {
78
+ testPrompts = knowledgeData.slice(0, 5).map((item: any) => ({
79
+ question: item.question || item.prompt || item.input || 'Test question',
80
+ answer: item.answer || item.response || item.expectedAnswer
81
+ }));
82
+ }
83
+ } catch (error) {
84
+ testPrompts = [];
85
+ }
86
+ }
87
+ }
88
+
89
+ const createdPrompts = await api.addTestPrompts(agentId, testPrompts);
90
+
91
+ setEvaluationProgress(50);
92
+ const evaluationResults = await api.runAllPromptEvaluations(
93
+ agentId,
94
+ createdPrompts,
95
+ agentEndpoint,
96
+ (current, total, question, llmResponse) => {
97
+ const progress = 50 + Math.round((current / total) * 40);
98
+ setEvaluationProgress(progress);
99
+
100
+ if (question) {
101
+ setCurrentQuestion(question);
102
+ }
103
+ if (llmResponse) {
104
+ setCurrentLLMResponse(llmResponse);
105
+ logs.push({ question: question || '', response: llmResponse });
106
+ setEvaluationLogs([...logs]);
107
+ }
108
+ }
109
+ );
110
+
111
+ setEvaluationProgress(100);
112
+
113
+ let passed = 0;
114
+ let failed = 0;
115
+
116
+ evaluationResults.forEach((result: any) => {
117
+ if (result.success) {
118
+ passed++;
119
+ } else {
120
+ failed++;
121
+ }
122
+ });
123
+
124
+ const duration = Math.round((Date.now() - startTime) / 1000);
125
+ const durationStr = duration > 60
126
+ ? `${Math.floor(duration / 60)}m ${duration % 60}s`
127
+ : `${duration}s`;
128
+
129
+ const result = {
130
+ totalTests: createdPrompts.length,
131
+ passed,
132
+ failed,
133
+ duration: durationStr,
134
+ evaluationUrl: `http://localhost:5173/eval/${agentId}`,
135
+ agentId,
136
+ };
137
+
138
+ setEvaluationResult(result);
139
+ setStep('complete');
140
+ } catch (error) {
141
+ console.error('Error running evaluation:', error);
142
+ setEvaluationResult({
143
+ totalTests: 0,
144
+ passed: 0,
145
+ failed: 0,
146
+ duration: 'Failed',
147
+ evaluationUrl: 'http://localhost:5173',
148
+ });
149
+ setStep('complete');
150
+ }
151
+ })();
152
+ }
153
+ }, [step, agentEndpoint, knowledgeSource]);
154
+
155
+ const handleAgentEndpointSubmit = (value: string) => {
156
+ setAgentEndpoint(value);
157
+ setStep('checking-knowledge');
158
+ };
159
+
160
+ const handleSourceSelect = (value: string) => {
161
+ setKnowledgeSource(value);
162
+ setStep('running-evaluation');
163
+ };
164
+
165
+ return (
166
+ <Box flexDirection="column" padding={1}>
167
+ <Header />
168
+
169
+ {step === 'agent-endpoint' && (
170
+ <Box flexDirection="column">
171
+ <TextInput
172
+ label="Agent endpoint"
173
+ placeholder="http://localhost:8000"
174
+ onSubmit={handleAgentEndpointSubmit}
175
+ />
176
+ </Box>
177
+ )}
178
+
179
+ {step === 'checking-knowledge' && (
180
+ <Box flexDirection="column">
181
+ <Spinner label="Checking for knowledge base in current folder..." />
182
+ </Box>
183
+ )}
184
+
185
+ {step === 'select-source' && (
186
+ <Box flexDirection="column">
187
+ <Box marginBottom={1}>
188
+ <Text bold color="#eba1b5">Choose your data source:</Text>
189
+ </Box>
190
+ {knowledgeFound && (
191
+ <Box marginBottom={1}>
192
+ <Text color="white">qanda.json found in current directory</Text>
193
+ </Box>
194
+ )}
195
+ <SelectMenu
196
+ title="Data Source"
197
+ options={knowledgeSources}
198
+ onSelect={handleSourceSelect}
199
+ />
200
+ </Box>
201
+ )}
202
+
203
+ {step === 'running-evaluation' && (
204
+ <Box flexDirection="column">
205
+ <Box marginBottom={2}>
206
+ <Spinner label="Running evaluation" />
207
+ </Box>
208
+
209
+ <Box flexDirection="column" marginBottom={1}>
210
+ <Box>
211
+ <Box width={12}>
212
+ <Text dimColor>Endpoint:</Text>
213
+ </Box>
214
+ <Text>{agentEndpoint || 'http://localhost:8000'}</Text>
215
+ </Box>
216
+
217
+ <Box>
218
+ <Box width={12}>
219
+ <Text dimColor>Data Source:</Text>
220
+ </Box>
221
+ <Text>{knowledgeSources.find(s => s.value === knowledgeSource)?.label || 'None'}</Text>
222
+ </Box>
223
+ </Box>
224
+
225
+ <ProgressBar
226
+ progress={evaluationProgress}
227
+ />
228
+
229
+ {currentQuestion && (
230
+ <Box flexDirection="column" marginTop={1} marginBottom={1}>
231
+ <Box>
232
+ <Text bold color="#eba1b5">Current Question:</Text>
233
+ </Box>
234
+ <Box paddingLeft={2}>
235
+ <Text color="white">{currentQuestion}</Text>
236
+ </Box>
237
+ </Box>
238
+ )}
239
+
240
+ {currentLLMResponse && (
241
+ <Box flexDirection="column" marginTop={1}>
242
+ <Box>
243
+ <Text bold color="green">LLM Response:</Text>
244
+ </Box>
245
+ <Box paddingLeft={2}>
246
+ <Text>{currentLLMResponse.length > 200 ? currentLLMResponse.substring(0, 200) + '...' : currentLLMResponse}</Text>
247
+ </Box>
248
+ </Box>
249
+ )}
250
+ </Box>
251
+ )}
252
+
253
+ {step === 'complete' && evaluationResult && (
254
+ <Box flexDirection="column">
255
+ <Summary
256
+ totalTests={evaluationResult.totalTests}
257
+ passed={evaluationResult.passed}
258
+ failed={evaluationResult.failed}
259
+ duration={evaluationResult.duration}
260
+ evaluationUrl={evaluationResult.evaluationUrl}
261
+ />
262
+ </Box>
263
+ )}
264
+ </Box>
265
+ );
266
+ };