@@ -2,11 +2,12 @@ import * as Eff from 'effect'
2
2
import * as React from 'react'
3
3
import * as M from '@material-ui/core'
4
4
import ClearIcon from '@material-ui/icons/Clear'
5
+ import DeleteIcon from '@material-ui/icons/Delete'
6
+ import GetAppIcon from '@material-ui/icons/GetApp'
5
7
6
8
import JsonDisplay from 'components/JsonDisplay'
7
9
8
10
import * as Model from '../../Model'
9
- import * as Bedrock from '../../Model/Bedrock'
10
11
11
12
const useModelIdOverrideStyles = M . makeStyles ( ( t ) => ( {
12
13
root : {
@@ -15,34 +16,32 @@ const useModelIdOverrideStyles = M.makeStyles((t) => ({
15
16
} ,
16
17
} ) )
17
18
18
- function ModelIdOverride ( ) {
19
- const classes = useModelIdOverrideStyles ( )
19
+ type ModelIdOverrideProps = Model . Assistant . API [ 'devTools' ] [ 'modelIdOverride' ]
20
20
21
- const [ modelId , setModelId ] = React . useState ( Bedrock . getModelIdOverride )
21
+ function ModelIdOverride ( { value, setValue } : ModelIdOverrideProps ) {
22
+ const classes = useModelIdOverrideStyles ( )
22
23
23
24
const handleModelIdChange = React . useCallback (
24
25
( event : React . ChangeEvent < HTMLInputElement > ) => {
25
- setModelId ( event . target . value )
26
+ setValue ( event . target . value )
26
27
} ,
27
- [ ] ,
28
+ [ setValue ] ,
28
29
)
29
30
30
- React . useEffect ( ( ) => Bedrock . setModelIdOverride ( modelId ) , [ modelId ] )
31
-
32
- const handleClear = React . useCallback ( ( ) => setModelId ( '' ) , [ ] )
31
+ const handleClear = React . useCallback ( ( ) => setValue ( '' ) , [ setValue ] )
33
32
34
33
return (
35
34
< div className = { classes . root } >
36
35
< M . TextField
37
36
label = "Bedrock Model ID"
38
- placeholder = { Bedrock . DEFAULT_MODEL_ID }
39
- value = { modelId }
37
+ placeholder = { Model . Assistant . DEFAULT_MODEL_ID }
38
+ value = { value }
40
39
onChange = { handleModelIdChange }
41
40
fullWidth
42
41
helperText = "Leave empty to use default"
43
42
InputLabelProps = { { shrink : true } }
44
43
InputProps = { {
45
- endAdornment : modelId ? (
44
+ endAdornment : value ? (
46
45
< M . InputAdornment position = "end" >
47
46
< M . IconButton
48
47
aria-label = "Clear model ID override"
@@ -62,6 +61,84 @@ function ModelIdOverride() {
62
61
)
63
62
}
64
63
64
+ const useRecordingControlsStyles = M . makeStyles ( ( t ) => ( {
65
+ root : {
66
+ alignItems : 'center' ,
67
+ display : 'flex' ,
68
+ gap : t . spacing ( 1 ) ,
69
+ margin : t . spacing ( 2 , 0 ) ,
70
+ padding : t . spacing ( 0 , 2 ) ,
71
+ } ,
72
+ label : {
73
+ ...t . typography . body2 ,
74
+ flexGrow : 1 ,
75
+ padding : t . spacing ( 0 , 2 ) ,
76
+ } ,
77
+ } ) )
78
+
79
+ type RecordingControlsProps = Model . Assistant . API [ 'devTools' ] [ 'recording' ]
80
+
81
+ function RecordingControls ( { enabled, log, enable, clear } : RecordingControlsProps ) {
82
+ const classes = useRecordingControlsStyles ( )
83
+
84
+ const handleToggleRecording = React . useCallback ( ( ) => {
85
+ enable ( ! enabled )
86
+ } , [ enabled , enable ] )
87
+
88
+ const handleDownload = React . useCallback ( ( ) => {
89
+ const data = `[\n${ log . join ( ',\n' ) } \n]`
90
+ const blob = new Blob ( [ data ] , { type : 'application/json' } )
91
+ const url = URL . createObjectURL ( blob )
92
+ const a = document . createElement ( 'a' )
93
+ a . href = url
94
+ a . download = `qurator-session-${ new Date ( ) . toISOString ( ) } .json`
95
+ document . body . appendChild ( a )
96
+ a . click ( )
97
+ document . body . removeChild ( a )
98
+ URL . revokeObjectURL ( url )
99
+ } , [ log ] )
100
+
101
+ return (
102
+ < div className = { classes . root } >
103
+ < M . Button
104
+ onClick = { handleToggleRecording }
105
+ variant = "contained"
106
+ color = "primary"
107
+ size = "small"
108
+ >
109
+ { enabled ? 'Stop' : 'Start' } Recording
110
+ </ M . Button >
111
+
112
+ { ( enabled || log . length > 0 ) && (
113
+ < div className = { classes . label } >
114
+ { log . length > 0 ? `${ log . length } item(s) recorded` : 'Recording...' }
115
+ </ div >
116
+ ) }
117
+
118
+ { log . length > 0 && (
119
+ < >
120
+ < M . Button
121
+ onClick = { handleDownload }
122
+ size = "small"
123
+ variant = "outlined"
124
+ startIcon = { < GetAppIcon /> }
125
+ >
126
+ Download Log
127
+ </ M . Button >
128
+ < M . Button
129
+ onClick = { clear }
130
+ size = "small"
131
+ variant = "outlined"
132
+ startIcon = { < DeleteIcon /> }
133
+ >
134
+ Clear Log
135
+ </ M . Button >
136
+ </ >
137
+ ) }
138
+ </ div >
139
+ )
140
+ }
141
+
65
142
const useStyles = M . makeStyles ( ( t ) => ( {
66
143
root : {
67
144
display : 'flex' ,
@@ -86,9 +163,11 @@ const useStyles = M.makeStyles((t) => ({
86
163
87
164
interface DevToolsProps {
88
165
state : Model . Assistant . API [ 'state' ]
166
+ modelIdOverride : Model . Assistant . API [ 'devTools' ] [ 'modelIdOverride' ]
167
+ recording : Model . Assistant . API [ 'devTools' ] [ 'recording' ]
89
168
}
90
169
91
- export default function DevTools ( { state } : DevToolsProps ) {
170
+ export default function DevTools ( { state, modelIdOverride , recording } : DevToolsProps ) {
92
171
const classes = useStyles ( )
93
172
94
173
const context = Model . Context . useAggregatedContext ( )
@@ -108,7 +187,10 @@ export default function DevTools({ state }: DevToolsProps) {
108
187
< section className = { classes . root } >
109
188
< h1 className = { classes . heading } > Qurator Developer Tools</ h1 >
110
189
< div className = { classes . contents } >
111
- < ModelIdOverride />
190
+ < ModelIdOverride { ...modelIdOverride } />
191
+ < M . Divider />
192
+ < RecordingControls { ...recording } />
193
+ < M . Divider />
112
194
< JsonDisplay className = { classes . json } name = "Context" value = { context } />
113
195
< JsonDisplay className = { classes . json } name = "State" value = { state } />
114
196
< JsonDisplay className = { classes . json } name = "Prompt" value = { prompt } />
0 commit comments