@pageai/ralph-loop 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.
- package/.agent/PROMPT.md +58 -0
- package/.agent/STEERING.md +3 -0
- package/.agent/logs/LOG.md +13 -0
- package/.agent/prd/.gitkeep +0 -0
- package/.agent/screenshots/.gitkeep +0 -0
- package/.agent/skills/component-refactoring/SKILL.md +247 -0
- package/.agent/skills/component-refactoring/references/complexity-patterns.md +485 -0
- package/.agent/skills/component-refactoring/references/component-splitting.md +419 -0
- package/.agent/skills/component-refactoring/references/hook-extraction.md +317 -0
- package/.agent/skills/e2e-tester/SKILL.md +595 -0
- package/.agent/skills/frontend-code-review/SKILL.md +73 -0
- package/.agent/skills/frontend-code-review/references/code-quality.md +28 -0
- package/.agent/skills/frontend-code-review/references/performance.md +36 -0
- package/.agent/skills/frontend-testing/SKILL.md +316 -0
- package/.agent/skills/frontend-testing/assets/component-test.template.tsx +293 -0
- package/.agent/skills/frontend-testing/assets/hook-test.template.ts +207 -0
- package/.agent/skills/frontend-testing/assets/utility-test.template.ts +154 -0
- package/.agent/skills/frontend-testing/references/async-testing.md +345 -0
- package/.agent/skills/frontend-testing/references/checklist.md +188 -0
- package/.agent/skills/frontend-testing/references/common-patterns.md +449 -0
- package/.agent/skills/frontend-testing/references/mocking.md +289 -0
- package/.agent/skills/frontend-testing/references/workflow.md +265 -0
- package/.agent/skills/prd-creator/JSON.md +613 -0
- package/.agent/skills/prd-creator/PRD.md +196 -0
- package/.agent/skills/prd-creator/SKILL.md +143 -0
- package/.agent/skills/skill-creator/SKILL.md +355 -0
- package/.agent/skills/skill-creator/references/output-patterns.md +86 -0
- package/.agent/skills/skill-creator/references/workflows.md +28 -0
- package/.agent/skills/skill-creator/scripts/init_skill.py +300 -0
- package/.agent/skills/skill-creator/scripts/package_skill.py +110 -0
- package/.agent/skills/vercel-react-best-practices/AGENTS.md +2249 -0
- package/.agent/skills/vercel-react-best-practices/SKILL.md +125 -0
- package/.agent/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/.agent/skills/vercel-react-best-practices/rules/advanced-use-latest.md +49 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-dependencies.md +36 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/.agent/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/.agent/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-cache-react.md +26 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +79 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/.agent/skills/vitest-best-practices/AGENTS.md +84 -0
- package/.agent/skills/vitest-best-practices/SKILL.md +130 -0
- package/.agent/skills/vitest-best-practices/references/aaa-pattern.md +260 -0
- package/.agent/skills/vitest-best-practices/references/assertions.md +393 -0
- package/.agent/skills/vitest-best-practices/references/async-testing.md +454 -0
- package/.agent/skills/vitest-best-practices/references/error-handling.md +382 -0
- package/.agent/skills/vitest-best-practices/references/organization.md +212 -0
- package/.agent/skills/vitest-best-practices/references/parameterized-tests.md +297 -0
- package/.agent/skills/vitest-best-practices/references/performance.md +528 -0
- package/.agent/skills/vitest-best-practices/references/snapshot-testing.md +483 -0
- package/.agent/skills/vitest-best-practices/references/test-doubles.md +499 -0
- package/.agent/skills/vitest-best-practices/references/vitest-features.md +529 -0
- package/.agent/skills/web-design-guidelines/SKILL.md +39 -0
- package/.agent/tasks/.gitkeep +0 -0
- package/.agent/tasks.json +1 -0
- package/.claude/agents/code-reviewer.md +172 -0
- package/.claude/commands/aw.md +50 -0
- package/.claude/hooks/play-sound.js +87 -0
- package/.claude/hooks/pre-tool-use.js +40 -0
- package/.claude/settings.json +54 -0
- package/.claude/settings.local.json +13 -0
- package/.mcp.json +31 -0
- package/AGENTS.md +44 -0
- package/CLAUDE.md +1 -0
- package/README.md +236 -0
- package/bin/cli.js +156 -0
- package/bin/lib/copy.js +149 -0
- package/bin/lib/display.js +137 -0
- package/package.json +65 -0
- package/ralph.sh +333 -0
- package/scripts/lib/args.sh +44 -0
- package/scripts/lib/cleanup.sh +53 -0
- package/scripts/lib/constants.sh +25 -0
- package/scripts/lib/display.sh +196 -0
- package/scripts/lib/logging.sh +30 -0
- package/scripts/lib/notify.sh +41 -0
- package/scripts/lib/output.sh +147 -0
- package/scripts/lib/preflight.sh +57 -0
- package/scripts/lib/preview.sh +77 -0
- package/scripts/lib/promise.sh +76 -0
- package/scripts/lib/spinner.sh +85 -0
- package/scripts/lib/terminal.sh +57 -0
- package/scripts/lib/timing.sh +223 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# Hook Extraction Patterns
|
|
2
|
+
|
|
3
|
+
This document provides detailed guidance on extracting custom hooks from complex components.
|
|
4
|
+
|
|
5
|
+
## When to Extract Hooks
|
|
6
|
+
|
|
7
|
+
Extract a custom hook when you identify:
|
|
8
|
+
|
|
9
|
+
1. **Coupled state groups** - Multiple `useState` hooks that are always used together
|
|
10
|
+
1. **Complex effects** - `useEffect` with multiple dependencies or cleanup logic
|
|
11
|
+
1. **Business logic** - Data transformations, validations, or calculations
|
|
12
|
+
1. **Reusable patterns** - Logic that appears in multiple components
|
|
13
|
+
|
|
14
|
+
## Extraction Process
|
|
15
|
+
|
|
16
|
+
### Step 1: Identify State Groups
|
|
17
|
+
|
|
18
|
+
Look for state variables that are logically related:
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
// ❌ These belong together - extract to hook
|
|
22
|
+
const [modelConfig, setModelConfig] = useState<ModelConfig>(...)
|
|
23
|
+
const [completionParams, setCompletionParams] = useState<FormValue>({})
|
|
24
|
+
const [modelModeType, setModelModeType] = useState<ModelModeType>(...)
|
|
25
|
+
|
|
26
|
+
// These are model-related state that should be in useModelConfig()
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Step 2: Identify Related Effects
|
|
30
|
+
|
|
31
|
+
Find effects that modify the grouped state:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
// ❌ These effects belong with the state above
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (hasFetchedDetail && !modelModeType) {
|
|
37
|
+
const mode = currModel?.model_properties.mode
|
|
38
|
+
if (mode) {
|
|
39
|
+
const newModelConfig = produce(modelConfig, (draft) => {
|
|
40
|
+
draft.mode = mode
|
|
41
|
+
})
|
|
42
|
+
setModelConfig(newModelConfig)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}, [textGenerationModelList, hasFetchedDetail, modelModeType, currModel])
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Step 3: Create the Hook
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
// hooks/use-model-config.ts
|
|
52
|
+
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
|
53
|
+
import type { ModelConfig } from '@/models/debug'
|
|
54
|
+
import { produce } from 'immer'
|
|
55
|
+
import { useEffect, useState } from 'react'
|
|
56
|
+
import { ModelModeType } from '@/types/app'
|
|
57
|
+
|
|
58
|
+
interface UseModelConfigParams {
|
|
59
|
+
initialConfig?: Partial<ModelConfig>
|
|
60
|
+
currModel?: { model_properties?: { mode?: ModelModeType } }
|
|
61
|
+
hasFetchedDetail: boolean
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface UseModelConfigReturn {
|
|
65
|
+
modelConfig: ModelConfig
|
|
66
|
+
setModelConfig: (config: ModelConfig) => void
|
|
67
|
+
completionParams: FormValue
|
|
68
|
+
setCompletionParams: (params: FormValue) => void
|
|
69
|
+
modelModeType: ModelModeType
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const useModelConfig = ({
|
|
73
|
+
initialConfig,
|
|
74
|
+
currModel,
|
|
75
|
+
hasFetchedDetail,
|
|
76
|
+
}: UseModelConfigParams): UseModelConfigReturn => {
|
|
77
|
+
const [modelConfig, setModelConfig] = useState<ModelConfig>({
|
|
78
|
+
provider: 'langgenius/openai/openai',
|
|
79
|
+
model_id: 'gpt-3.5-turbo',
|
|
80
|
+
mode: ModelModeType.unset,
|
|
81
|
+
// ... default values
|
|
82
|
+
...initialConfig,
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const [completionParams, setCompletionParams] = useState<FormValue>({})
|
|
86
|
+
|
|
87
|
+
const modelModeType = modelConfig.mode
|
|
88
|
+
|
|
89
|
+
// Fill old app data missing model mode
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (hasFetchedDetail && !modelModeType) {
|
|
92
|
+
const mode = currModel?.model_properties?.mode
|
|
93
|
+
if (mode) {
|
|
94
|
+
setModelConfig(produce(modelConfig, (draft) => {
|
|
95
|
+
draft.mode = mode
|
|
96
|
+
}))
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}, [hasFetchedDetail, modelModeType, currModel])
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
modelConfig,
|
|
103
|
+
setModelConfig,
|
|
104
|
+
completionParams,
|
|
105
|
+
setCompletionParams,
|
|
106
|
+
modelModeType,
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Step 4: Update Component
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// Before: 50+ lines of state management
|
|
115
|
+
const Configuration: FC = () => {
|
|
116
|
+
const [modelConfig, setModelConfig] = useState<ModelConfig>(...)
|
|
117
|
+
// ... lots of related state and effects
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// After: Clean component
|
|
121
|
+
const Configuration: FC = () => {
|
|
122
|
+
const {
|
|
123
|
+
modelConfig,
|
|
124
|
+
setModelConfig,
|
|
125
|
+
completionParams,
|
|
126
|
+
setCompletionParams,
|
|
127
|
+
modelModeType,
|
|
128
|
+
} = useModelConfig({
|
|
129
|
+
currModel,
|
|
130
|
+
hasFetchedDetail,
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
// Component now focuses on UI
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Naming Conventions
|
|
138
|
+
|
|
139
|
+
### Hook Names
|
|
140
|
+
|
|
141
|
+
- Use `use` prefix: `useModelConfig`, `useDatasetConfig`
|
|
142
|
+
- Be specific: `useAdvancedPromptConfig` not `usePrompt`
|
|
143
|
+
- Include domain: `useWorkflowVariables`, `useMCPServer`
|
|
144
|
+
|
|
145
|
+
### File Names
|
|
146
|
+
|
|
147
|
+
- Kebab-case: `use-model-config.ts`
|
|
148
|
+
- Place in `hooks/` subdirectory when multiple hooks exist
|
|
149
|
+
- Place alongside component for single-use hooks
|
|
150
|
+
|
|
151
|
+
### Return Type Names
|
|
152
|
+
|
|
153
|
+
- Suffix with `Return`: `UseModelConfigReturn`
|
|
154
|
+
- Suffix params with `Params`: `UseModelConfigParams`
|
|
155
|
+
|
|
156
|
+
## Common Hook Patterns
|
|
157
|
+
|
|
158
|
+
### 1. Data Fetching Hook (React Query)
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// Pattern: Use @tanstack/react-query for data fetching
|
|
162
|
+
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
|
163
|
+
import { get } from '@/service/base'
|
|
164
|
+
import { useInvalid } from '@/service/use-base'
|
|
165
|
+
|
|
166
|
+
const NAME_SPACE = 'appConfig'
|
|
167
|
+
|
|
168
|
+
// Query keys for cache management
|
|
169
|
+
export const appConfigQueryKeys = {
|
|
170
|
+
detail: (appId: string) => [NAME_SPACE, 'detail', appId] as const,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Main data hook
|
|
174
|
+
export const useAppConfig = (appId: string) => {
|
|
175
|
+
return useQuery({
|
|
176
|
+
enabled: !!appId,
|
|
177
|
+
queryKey: appConfigQueryKeys.detail(appId),
|
|
178
|
+
queryFn: () => get<AppDetailResponse>(`/apps/${appId}`),
|
|
179
|
+
select: data => data?.model_config || null,
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Invalidation hook for refreshing data
|
|
184
|
+
export const useInvalidAppConfig = () => {
|
|
185
|
+
return useInvalid([NAME_SPACE])
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Usage in component
|
|
189
|
+
const Component = () => {
|
|
190
|
+
const { data: config, isLoading, error, refetch } = useAppConfig(appId)
|
|
191
|
+
const invalidAppConfig = useInvalidAppConfig()
|
|
192
|
+
|
|
193
|
+
const handleRefresh = () => {
|
|
194
|
+
invalidAppConfig() // Invalidates cache and triggers refetch
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return <div>...</div>
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### 2. Form State Hook
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
// Pattern: Form state + validation + submission
|
|
205
|
+
export const useConfigForm = (initialValues: ConfigFormValues) => {
|
|
206
|
+
const [values, setValues] = useState(initialValues)
|
|
207
|
+
const [errors, setErrors] = useState<Record<string, string>>({})
|
|
208
|
+
const [isSubmitting, setIsSubmitting] = useState(false)
|
|
209
|
+
|
|
210
|
+
const validate = useCallback(() => {
|
|
211
|
+
const newErrors: Record<string, string> = {}
|
|
212
|
+
if (!values.name) newErrors.name = 'Name is required'
|
|
213
|
+
setErrors(newErrors)
|
|
214
|
+
return Object.keys(newErrors).length === 0
|
|
215
|
+
}, [values])
|
|
216
|
+
|
|
217
|
+
const handleChange = useCallback((field: string, value: any) => {
|
|
218
|
+
setValues(prev => ({ ...prev, [field]: value }))
|
|
219
|
+
}, [])
|
|
220
|
+
|
|
221
|
+
const handleSubmit = useCallback(async (onSubmit: (values: ConfigFormValues) => Promise<void>) => {
|
|
222
|
+
if (!validate()) return
|
|
223
|
+
setIsSubmitting(true)
|
|
224
|
+
try {
|
|
225
|
+
await onSubmit(values)
|
|
226
|
+
} finally {
|
|
227
|
+
setIsSubmitting(false)
|
|
228
|
+
}
|
|
229
|
+
}, [values, validate])
|
|
230
|
+
|
|
231
|
+
return { values, errors, isSubmitting, handleChange, handleSubmit }
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### 3. Modal State Hook
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
// Pattern: Multiple modal management
|
|
239
|
+
type ModalType = 'edit' | 'delete' | 'duplicate' | null
|
|
240
|
+
|
|
241
|
+
export const useModalState = () => {
|
|
242
|
+
const [activeModal, setActiveModal] = useState<ModalType>(null)
|
|
243
|
+
const [modalData, setModalData] = useState<any>(null)
|
|
244
|
+
|
|
245
|
+
const openModal = useCallback((type: ModalType, data?: any) => {
|
|
246
|
+
setActiveModal(type)
|
|
247
|
+
setModalData(data)
|
|
248
|
+
}, [])
|
|
249
|
+
|
|
250
|
+
const closeModal = useCallback(() => {
|
|
251
|
+
setActiveModal(null)
|
|
252
|
+
setModalData(null)
|
|
253
|
+
}, [])
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
activeModal,
|
|
257
|
+
modalData,
|
|
258
|
+
openModal,
|
|
259
|
+
closeModal,
|
|
260
|
+
isOpen: useCallback((type: ModalType) => activeModal === type, [activeModal]),
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### 4. Toggle/Boolean Hook
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
// Pattern: Boolean state with convenience methods
|
|
269
|
+
export const useToggle = (initialValue = false) => {
|
|
270
|
+
const [value, setValue] = useState(initialValue)
|
|
271
|
+
|
|
272
|
+
const toggle = useCallback(() => setValue(v => !v), [])
|
|
273
|
+
const setTrue = useCallback(() => setValue(true), [])
|
|
274
|
+
const setFalse = useCallback(() => setValue(false), [])
|
|
275
|
+
|
|
276
|
+
return [value, { toggle, setTrue, setFalse, set: setValue }] as const
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Usage
|
|
280
|
+
const [isExpanded, { toggle, setTrue: expand, setFalse: collapse }] = useToggle()
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Testing Extracted Hooks
|
|
284
|
+
|
|
285
|
+
After extraction, test hooks in isolation:
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
// use-model-config.spec.ts
|
|
289
|
+
import { renderHook, act } from '@testing-library/react'
|
|
290
|
+
import { useModelConfig } from './use-model-config'
|
|
291
|
+
|
|
292
|
+
describe('useModelConfig', () => {
|
|
293
|
+
it('should initialize with default values', () => {
|
|
294
|
+
const { result } = renderHook(() => useModelConfig({
|
|
295
|
+
hasFetchedDetail: false,
|
|
296
|
+
}))
|
|
297
|
+
|
|
298
|
+
expect(result.current.modelConfig.provider).toBe('langgenius/openai/openai')
|
|
299
|
+
expect(result.current.modelModeType).toBe(ModelModeType.unset)
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
it('should update model config', () => {
|
|
303
|
+
const { result } = renderHook(() => useModelConfig({
|
|
304
|
+
hasFetchedDetail: true,
|
|
305
|
+
}))
|
|
306
|
+
|
|
307
|
+
act(() => {
|
|
308
|
+
result.current.setModelConfig({
|
|
309
|
+
...result.current.modelConfig,
|
|
310
|
+
model_id: 'gpt-4',
|
|
311
|
+
})
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
expect(result.current.modelConfig.model_id).toBe('gpt-4')
|
|
315
|
+
})
|
|
316
|
+
})
|
|
317
|
+
```
|