@kood/claude-code 0.1.7 → 0.1.9
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/dist/index.js +118 -3
- package/package.json +8 -2
- package/templates/hono/CLAUDE.md +53 -326
- package/templates/hono/docs/architecture/architecture.md +93 -747
- package/templates/hono/docs/deployment/cloudflare.md +59 -513
- package/templates/hono/docs/deployment/docker.md +41 -356
- package/templates/hono/docs/deployment/index.md +49 -190
- package/templates/hono/docs/deployment/railway.md +36 -306
- package/templates/hono/docs/deployment/vercel.md +49 -434
- package/templates/hono/docs/library/ai-sdk/index.md +53 -290
- package/templates/hono/docs/library/ai-sdk/openrouter.md +19 -387
- package/templates/hono/docs/library/ai-sdk/providers.md +28 -394
- package/templates/hono/docs/library/ai-sdk/streaming.md +52 -353
- package/templates/hono/docs/library/ai-sdk/structured-output.md +63 -395
- package/templates/hono/docs/library/ai-sdk/tools.md +62 -431
- package/templates/hono/docs/library/hono/env-setup.md +24 -313
- package/templates/hono/docs/library/hono/error-handling.md +34 -295
- package/templates/hono/docs/library/hono/index.md +24 -122
- package/templates/hono/docs/library/hono/middleware.md +21 -188
- package/templates/hono/docs/library/hono/rpc.md +40 -341
- package/templates/hono/docs/library/hono/validation.md +35 -195
- package/templates/hono/docs/library/pino/index.md +42 -333
- package/templates/hono/docs/library/prisma/cloudflare-d1.md +64 -367
- package/templates/hono/docs/library/prisma/config.md +19 -260
- package/templates/hono/docs/library/prisma/index.md +64 -320
- package/templates/hono/docs/library/zod/index.md +53 -257
- package/templates/npx/CLAUDE.md +58 -276
- package/templates/npx/docs/references/patterns.md +160 -0
- package/templates/tanstack-start/CLAUDE.md +0 -4
- package/templates/tanstack-start/docs/architecture/architecture.md +44 -589
- package/templates/tanstack-start/docs/design/index.md +119 -12
- package/templates/tanstack-start/docs/guides/conventions.md +103 -0
- package/templates/tanstack-start/docs/guides/env-setup.md +34 -340
- package/templates/tanstack-start/docs/guides/getting-started.md +22 -209
- package/templates/tanstack-start/docs/guides/hooks.md +166 -0
- package/templates/tanstack-start/docs/guides/routes.md +166 -0
- package/templates/tanstack-start/docs/guides/services.md +143 -0
- package/templates/tanstack-start/docs/library/tanstack-query/index.md +18 -2
- package/templates/tanstack-start/docs/library/zod/index.md +16 -1
- package/templates/tanstack-start/docs/design/accessibility.md +0 -163
- package/templates/tanstack-start/docs/design/color.md +0 -93
- package/templates/tanstack-start/docs/design/spacing.md +0 -122
- package/templates/tanstack-start/docs/design/typography.md +0 -80
- package/templates/tanstack-start/docs/guides/best-practices.md +0 -950
- package/templates/tanstack-start/docs/guides/husky-lint-staged.md +0 -303
- package/templates/tanstack-start/docs/guides/prettier.md +0 -189
- package/templates/tanstack-start/docs/guides/project-templates.md +0 -710
- package/templates/tanstack-start/docs/library/tanstack-query/setup.md +0 -48
- package/templates/tanstack-start/docs/library/zod/basic-types.md +0 -74
|
@@ -1,55 +1,41 @@
|
|
|
1
|
-
# AI SDK - 구조화된 출력
|
|
1
|
+
# AI SDK - 구조화된 출력
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 개요
|
|
8
|
-
|
|
9
|
-
AI SDK의 `generateObject`와 `streamObject`를 Hono에서 사용하면 타입 안전한 구조화된 데이터를 생성할 수 있습니다. Zod 스키마를 사용하여 출력 형식을 정의합니다.
|
|
3
|
+
> Zod 스키마로 타입 안전한 객체 생성
|
|
10
4
|
|
|
11
5
|
---
|
|
12
6
|
|
|
13
7
|
## generateObject
|
|
14
8
|
|
|
15
|
-
### 기본 사용
|
|
16
|
-
|
|
17
9
|
```typescript
|
|
18
|
-
import { Hono } from 'hono'
|
|
19
10
|
import { generateObject } from 'ai'
|
|
20
11
|
import { openai } from '@ai-sdk/openai'
|
|
21
12
|
import { z } from 'zod'
|
|
22
13
|
|
|
23
|
-
const
|
|
14
|
+
const userSchema = z.object({
|
|
15
|
+
name: z.string(),
|
|
16
|
+
age: z.number(),
|
|
17
|
+
email: z.email(),
|
|
18
|
+
})
|
|
24
19
|
|
|
25
20
|
app.post('/api/generate-user', async (c) => {
|
|
26
21
|
const { prompt } = await c.req.json()
|
|
27
22
|
|
|
28
23
|
const { object } = await generateObject({
|
|
29
24
|
model: openai('gpt-4o'),
|
|
30
|
-
schema:
|
|
31
|
-
name: z.string(),
|
|
32
|
-
age: z.number(),
|
|
33
|
-
email: z.string().email(),
|
|
34
|
-
}),
|
|
25
|
+
schema: userSchema,
|
|
35
26
|
prompt,
|
|
36
27
|
})
|
|
37
28
|
|
|
38
|
-
// object
|
|
29
|
+
// object: { name: string, age: number, email: string }
|
|
39
30
|
return c.json(object)
|
|
40
31
|
})
|
|
41
32
|
```
|
|
42
33
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
```typescript
|
|
46
|
-
import { Hono } from 'hono'
|
|
47
|
-
import { generateObject } from 'ai'
|
|
48
|
-
import { openai } from '@ai-sdk/openai'
|
|
49
|
-
import { z } from 'zod'
|
|
34
|
+
---
|
|
50
35
|
|
|
51
|
-
|
|
36
|
+
## 복잡한 스키마
|
|
52
37
|
|
|
38
|
+
```typescript
|
|
53
39
|
const recipeSchema = z.object({
|
|
54
40
|
name: z.string().describe('Recipe name'),
|
|
55
41
|
ingredients: z.array(
|
|
@@ -60,106 +46,20 @@ const recipeSchema = z.object({
|
|
|
60
46
|
})
|
|
61
47
|
),
|
|
62
48
|
steps: z.array(z.string()),
|
|
63
|
-
prepTime: z.number().describe('
|
|
64
|
-
cookTime: z.number().describe('
|
|
65
|
-
servings: z.number(),
|
|
49
|
+
prepTime: z.number().describe('Prep time in minutes'),
|
|
50
|
+
cookTime: z.number().describe('Cook time in minutes'),
|
|
66
51
|
difficulty: z.enum(['easy', 'medium', 'hard']),
|
|
67
52
|
})
|
|
68
53
|
|
|
69
|
-
app.post('/api/
|
|
54
|
+
app.post('/api/recipe', async (c) => {
|
|
70
55
|
const { dish } = await c.req.json()
|
|
71
56
|
|
|
72
|
-
const { object
|
|
57
|
+
const { object } = await generateObject({
|
|
73
58
|
model: openai('gpt-4o'),
|
|
74
59
|
schema: recipeSchema,
|
|
75
60
|
prompt: `Generate a recipe for ${dish}.`,
|
|
76
61
|
})
|
|
77
62
|
|
|
78
|
-
return c.json(recipe)
|
|
79
|
-
})
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
---
|
|
83
|
-
|
|
84
|
-
## 출력 모드
|
|
85
|
-
|
|
86
|
-
### Object (기본)
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
app.post('/api/object', async (c) => {
|
|
90
|
-
const { prompt } = await c.req.json()
|
|
91
|
-
|
|
92
|
-
const { object } = await generateObject({
|
|
93
|
-
model: openai('gpt-4o'),
|
|
94
|
-
schema: z.object({
|
|
95
|
-
name: z.string(),
|
|
96
|
-
age: z.number(),
|
|
97
|
-
}),
|
|
98
|
-
prompt,
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
return c.json(object)
|
|
102
|
-
})
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### Array
|
|
106
|
-
|
|
107
|
-
배열 형태의 출력:
|
|
108
|
-
|
|
109
|
-
```typescript
|
|
110
|
-
app.post('/api/array', async (c) => {
|
|
111
|
-
const { prompt } = await c.req.json()
|
|
112
|
-
|
|
113
|
-
const { object } = await generateObject({
|
|
114
|
-
model: openai('gpt-4o'),
|
|
115
|
-
output: 'array',
|
|
116
|
-
schema: z.object({
|
|
117
|
-
name: z.string(),
|
|
118
|
-
class: z.string().describe('Character class'),
|
|
119
|
-
description: z.string(),
|
|
120
|
-
}),
|
|
121
|
-
prompt,
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
// object는 배열
|
|
125
|
-
return c.json({ characters: object })
|
|
126
|
-
})
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Enum
|
|
130
|
-
|
|
131
|
-
분류 작업에 유용:
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
app.post('/api/classify', async (c) => {
|
|
135
|
-
const { text } = await c.req.json()
|
|
136
|
-
|
|
137
|
-
const { object } = await generateObject({
|
|
138
|
-
model: openai('gpt-4o'),
|
|
139
|
-
output: 'enum',
|
|
140
|
-
enum: ['action', 'comedy', 'drama', 'horror', 'sci-fi'],
|
|
141
|
-
prompt: `Classify this movie: "${text}"`,
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
return c.json({ genre: object })
|
|
145
|
-
})
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### No Schema
|
|
149
|
-
|
|
150
|
-
스키마 없이 자유 형식 JSON 생성:
|
|
151
|
-
|
|
152
|
-
```typescript
|
|
153
|
-
app.post('/api/freeform', async (c) => {
|
|
154
|
-
const { prompt } = await c.req.json()
|
|
155
|
-
|
|
156
|
-
const { object } = await generateObject({
|
|
157
|
-
model: openai('gpt-4o'),
|
|
158
|
-
output: 'no-schema',
|
|
159
|
-
prompt,
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
// object는 any 타입
|
|
163
63
|
return c.json(object)
|
|
164
64
|
})
|
|
165
65
|
```
|
|
@@ -168,28 +68,17 @@ app.post('/api/freeform', async (c) => {
|
|
|
168
68
|
|
|
169
69
|
## streamObject
|
|
170
70
|
|
|
171
|
-
스트리밍으로
|
|
71
|
+
스트리밍으로 객체 생성.
|
|
172
72
|
|
|
173
73
|
```typescript
|
|
174
|
-
import { Hono } from 'hono'
|
|
175
74
|
import { streamObject } from 'ai'
|
|
176
|
-
import { openai } from '@ai-sdk/openai'
|
|
177
|
-
import { z } from 'zod'
|
|
178
|
-
|
|
179
|
-
const app = new Hono()
|
|
180
75
|
|
|
181
|
-
|
|
182
|
-
name: z.string(),
|
|
183
|
-
bio: z.string(),
|
|
184
|
-
skills: z.array(z.string()),
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
app.post('/api/stream-profile', async (c) => {
|
|
76
|
+
app.post('/api/stream-user', async (c) => {
|
|
188
77
|
const { prompt } = await c.req.json()
|
|
189
78
|
|
|
190
79
|
const result = streamObject({
|
|
191
80
|
model: openai('gpt-4o'),
|
|
192
|
-
schema:
|
|
81
|
+
schema: userSchema,
|
|
193
82
|
prompt,
|
|
194
83
|
})
|
|
195
84
|
|
|
@@ -197,297 +86,76 @@ app.post('/api/stream-profile', async (c) => {
|
|
|
197
86
|
})
|
|
198
87
|
```
|
|
199
88
|
|
|
200
|
-
### 부분 객체
|
|
89
|
+
### 부분 객체 수신
|
|
201
90
|
|
|
202
91
|
```typescript
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
import { stream } from 'hono/streaming'
|
|
208
|
-
|
|
209
|
-
const app = new Hono()
|
|
210
|
-
|
|
211
|
-
app.post('/api/stream-partial', async (c) => {
|
|
212
|
-
const { prompt } = await c.req.json()
|
|
213
|
-
|
|
214
|
-
const result = streamObject({
|
|
215
|
-
model: openai('gpt-4o'),
|
|
216
|
-
schema: z.object({
|
|
217
|
-
name: z.string(),
|
|
218
|
-
bio: z.string(),
|
|
219
|
-
skills: z.array(z.string()),
|
|
220
|
-
}),
|
|
221
|
-
prompt,
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
return stream(c, async (stream) => {
|
|
225
|
-
for await (const partialObject of result.partialObjectStream) {
|
|
226
|
-
await stream.write(JSON.stringify(partialObject) + '\n')
|
|
227
|
-
}
|
|
228
|
-
})
|
|
229
|
-
})
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
---
|
|
233
|
-
|
|
234
|
-
## 스키마 설계 팁
|
|
235
|
-
|
|
236
|
-
### 필드 설명 추가
|
|
237
|
-
|
|
238
|
-
```typescript
|
|
239
|
-
const schema = z.object({
|
|
240
|
-
title: z.string().describe('A catchy title for the article'),
|
|
241
|
-
summary: z.string().describe('A 2-3 sentence summary'),
|
|
242
|
-
tags: z.array(z.string()).describe('Relevant topic tags'),
|
|
243
|
-
})
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
### Optional 필드
|
|
247
|
-
|
|
248
|
-
```typescript
|
|
249
|
-
const schema = z.object({
|
|
250
|
-
name: z.string(),
|
|
251
|
-
nickname: z.string().optional(),
|
|
252
|
-
age: z.number().nullable(),
|
|
253
|
-
})
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
### 기본값
|
|
257
|
-
|
|
258
|
-
```typescript
|
|
259
|
-
const schema = z.object({
|
|
260
|
-
name: z.string(),
|
|
261
|
-
role: z.string().default('user'),
|
|
262
|
-
isActive: z.boolean().default(true),
|
|
263
|
-
})
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### Enum 사용
|
|
267
|
-
|
|
268
|
-
```typescript
|
|
269
|
-
const schema = z.object({
|
|
270
|
-
status: z.enum(['pending', 'approved', 'rejected']),
|
|
271
|
-
priority: z.enum(['low', 'medium', 'high', 'critical']),
|
|
272
|
-
})
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
### 중첩 객체
|
|
276
|
-
|
|
277
|
-
```typescript
|
|
278
|
-
const schema = z.object({
|
|
279
|
-
user: z.object({
|
|
280
|
-
name: z.string(),
|
|
281
|
-
email: z.string().email(),
|
|
282
|
-
}),
|
|
283
|
-
address: z.object({
|
|
284
|
-
street: z.string(),
|
|
285
|
-
city: z.string(),
|
|
286
|
-
country: z.string(),
|
|
287
|
-
}),
|
|
288
|
-
})
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
---
|
|
292
|
-
|
|
293
|
-
## 실용 예제
|
|
294
|
-
|
|
295
|
-
### 데이터 추출
|
|
296
|
-
|
|
297
|
-
```typescript
|
|
298
|
-
app.post('/api/extract', async (c) => {
|
|
299
|
-
const { text } = await c.req.json()
|
|
300
|
-
|
|
301
|
-
const { object } = await generateObject({
|
|
302
|
-
model: openai('gpt-4o'),
|
|
303
|
-
schema: z.object({
|
|
304
|
-
people: z.array(
|
|
305
|
-
z.object({
|
|
306
|
-
name: z.string(),
|
|
307
|
-
role: z.string().optional(),
|
|
308
|
-
})
|
|
309
|
-
),
|
|
310
|
-
organizations: z.array(z.string()),
|
|
311
|
-
locations: z.array(z.string()),
|
|
312
|
-
dates: z.array(z.string()),
|
|
313
|
-
}),
|
|
314
|
-
prompt: `Extract entities from this text: ${text}`,
|
|
315
|
-
})
|
|
316
|
-
|
|
317
|
-
return c.json(object)
|
|
318
|
-
})
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
### 요약 생성
|
|
322
|
-
|
|
323
|
-
```typescript
|
|
324
|
-
app.post('/api/summarize', async (c) => {
|
|
325
|
-
const { content } = await c.req.json()
|
|
326
|
-
|
|
327
|
-
const { object } = await generateObject({
|
|
328
|
-
model: openai('gpt-4o'),
|
|
329
|
-
schema: z.object({
|
|
330
|
-
summary: z.string().describe('One paragraph summary'),
|
|
331
|
-
keyPoints: z.array(z.string()).describe('3-5 key points'),
|
|
332
|
-
sentiment: z.enum(['positive', 'neutral', 'negative']),
|
|
333
|
-
topics: z.array(z.string()),
|
|
334
|
-
}),
|
|
335
|
-
prompt: `Summarize this content: ${content}`,
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
return c.json(object)
|
|
339
|
-
})
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
### 폼 데이터 생성
|
|
343
|
-
|
|
344
|
-
```typescript
|
|
345
|
-
app.post('/api/generate-form', async (c) => {
|
|
346
|
-
const { description } = await c.req.json()
|
|
347
|
-
|
|
348
|
-
const { object } = await generateObject({
|
|
349
|
-
model: openai('gpt-4o'),
|
|
350
|
-
schema: z.object({
|
|
351
|
-
fields: z.array(
|
|
352
|
-
z.object({
|
|
353
|
-
name: z.string(),
|
|
354
|
-
type: z.enum(['text', 'email', 'number', 'select', 'textarea']),
|
|
355
|
-
label: z.string(),
|
|
356
|
-
required: z.boolean(),
|
|
357
|
-
placeholder: z.string().optional(),
|
|
358
|
-
options: z.array(z.string()).optional(),
|
|
359
|
-
})
|
|
360
|
-
),
|
|
361
|
-
}),
|
|
362
|
-
prompt: `Generate form fields for: ${description}`,
|
|
363
|
-
})
|
|
364
|
-
|
|
365
|
-
return c.json(object)
|
|
92
|
+
const result = streamObject({
|
|
93
|
+
model: openai('gpt-4o'),
|
|
94
|
+
schema: userSchema,
|
|
95
|
+
prompt,
|
|
366
96
|
})
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
---
|
|
370
|
-
|
|
371
|
-
## 에러 처리
|
|
372
|
-
|
|
373
|
-
```typescript
|
|
374
|
-
import { Hono } from 'hono'
|
|
375
|
-
import { HTTPException } from 'hono/http-exception'
|
|
376
|
-
import { generateObject } from 'ai'
|
|
377
|
-
import { openai } from '@ai-sdk/openai'
|
|
378
|
-
import { z } from 'zod'
|
|
379
|
-
|
|
380
|
-
const app = new Hono()
|
|
381
|
-
|
|
382
|
-
app.post('/api/generate', async (c) => {
|
|
383
|
-
const { prompt } = await c.req.json()
|
|
384
97
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
}),
|
|
392
|
-
prompt,
|
|
393
|
-
})
|
|
394
|
-
|
|
395
|
-
return c.json(object)
|
|
396
|
-
} catch (error) {
|
|
397
|
-
if (error instanceof z.ZodError) {
|
|
398
|
-
// 스키마 검증 실패
|
|
399
|
-
return c.json(
|
|
400
|
-
{ error: 'Validation error', details: error.errors },
|
|
401
|
-
400
|
|
402
|
-
)
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
console.error('Generation error:', error)
|
|
406
|
-
throw new HTTPException(500, { message: 'Failed to generate object' })
|
|
407
|
-
}
|
|
408
|
-
})
|
|
98
|
+
for await (const partialObject of result.partialObjectStream) {
|
|
99
|
+
console.log('Partial:', partialObject)
|
|
100
|
+
// { name: 'Jo' }
|
|
101
|
+
// { name: 'John', age: 25 }
|
|
102
|
+
// { name: 'John', age: 25, email: 'john@...' }
|
|
103
|
+
}
|
|
409
104
|
```
|
|
410
105
|
|
|
411
106
|
---
|
|
412
107
|
|
|
413
|
-
##
|
|
414
|
-
|
|
415
|
-
### generateObject
|
|
108
|
+
## 출력 모드
|
|
416
109
|
|
|
417
110
|
```typescript
|
|
418
|
-
|
|
111
|
+
// Array 모드
|
|
112
|
+
const { object } = await generateObject({
|
|
419
113
|
model: openai('gpt-4o'),
|
|
420
|
-
|
|
421
|
-
|
|
114
|
+
output: 'array',
|
|
115
|
+
schema: z.object({ name: z.string(), role: z.string() }),
|
|
116
|
+
prompt: 'List 5 team members.',
|
|
422
117
|
})
|
|
118
|
+
// object: [{ name: 'Alice', role: 'Engineer' }, ...]
|
|
423
119
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
result.finishReason // 완료 이유
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
### streamObject
|
|
430
|
-
|
|
431
|
-
```typescript
|
|
432
|
-
const result = streamObject({
|
|
120
|
+
// Enum 모드
|
|
121
|
+
const { object } = await generateObject({
|
|
433
122
|
model: openai('gpt-4o'),
|
|
434
|
-
|
|
435
|
-
|
|
123
|
+
output: 'enum',
|
|
124
|
+
enum: ['positive', 'negative', 'neutral'],
|
|
125
|
+
prompt: 'Classify the sentiment: "I love this product!"',
|
|
436
126
|
})
|
|
437
|
-
|
|
438
|
-
result.partialObjectStream // AsyncIterable<PartialObject>
|
|
439
|
-
result.object // Promise<FinalObject>
|
|
440
|
-
result.usage // Promise<Usage>
|
|
127
|
+
// object: 'positive'
|
|
441
128
|
```
|
|
442
129
|
|
|
443
130
|
---
|
|
444
131
|
|
|
445
|
-
##
|
|
132
|
+
## 에러 처리
|
|
446
133
|
|
|
447
134
|
```typescript
|
|
448
|
-
import {
|
|
449
|
-
import { generateObject, streamObject } from 'ai'
|
|
450
|
-
import { createOpenAI } from '@ai-sdk/openai'
|
|
451
|
-
import { z } from 'zod'
|
|
452
|
-
|
|
453
|
-
type Bindings = {
|
|
454
|
-
OPENAI_API_KEY: string
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
const app = new Hono<{ Bindings: Bindings }>()
|
|
458
|
-
|
|
459
|
-
const productSchema = z.object({
|
|
460
|
-
name: z.string(),
|
|
461
|
-
description: z.string(),
|
|
462
|
-
price: z.number(),
|
|
463
|
-
features: z.array(z.string()),
|
|
464
|
-
})
|
|
465
|
-
|
|
466
|
-
app.post('/api/generate-product', async (c) => {
|
|
467
|
-
const openai = createOpenAI({ apiKey: c.env.OPENAI_API_KEY })
|
|
468
|
-
const { category } = await c.req.json()
|
|
135
|
+
import { NoObjectGeneratedError, InvalidResponseDataError } from 'ai'
|
|
469
136
|
|
|
137
|
+
try {
|
|
470
138
|
const { object } = await generateObject({
|
|
471
139
|
model: openai('gpt-4o'),
|
|
472
|
-
schema:
|
|
473
|
-
prompt
|
|
140
|
+
schema: userSchema,
|
|
141
|
+
prompt,
|
|
474
142
|
})
|
|
475
|
-
|
|
476
143
|
return c.json(object)
|
|
477
|
-
})
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
144
|
+
} catch (error) {
|
|
145
|
+
if (error instanceof NoObjectGeneratedError) {
|
|
146
|
+
return c.json({ error: 'Failed to generate object' }, 500)
|
|
147
|
+
}
|
|
148
|
+
if (error instanceof InvalidResponseDataError) {
|
|
149
|
+
return c.json({ error: 'Invalid response format' }, 500)
|
|
150
|
+
}
|
|
151
|
+
throw error
|
|
152
|
+
}
|
|
153
|
+
```
|
|
482
154
|
|
|
483
|
-
|
|
484
|
-
model: openai('gpt-4o'),
|
|
485
|
-
schema: productSchema,
|
|
486
|
-
prompt: `Generate a product in the ${category} category.`,
|
|
487
|
-
})
|
|
155
|
+
---
|
|
488
156
|
|
|
489
|
-
|
|
490
|
-
})
|
|
157
|
+
## 관련 문서
|
|
491
158
|
|
|
492
|
-
|
|
493
|
-
|
|
159
|
+
- [AI SDK 개요](./index.md)
|
|
160
|
+
- [스트리밍](./streaming.md)
|
|
161
|
+
- [도구](./tools.md)
|