@kood/claude-code 0.1.2 → 0.1.3
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 +12 -3
- package/package.json +2 -2
- package/templates/hono/CLAUDE.md +20 -2
- package/templates/hono/docs/architecture/architecture.md +909 -0
- package/templates/hono/docs/deployment/cloudflare.md +537 -190
- package/templates/hono/docs/deployment/docker.md +517 -0
- package/templates/hono/docs/deployment/index.md +181 -213
- package/templates/hono/docs/deployment/railway.md +416 -0
- package/templates/hono/docs/deployment/vercel.md +572 -0
- package/templates/hono/docs/git/git.md +285 -0
- package/templates/hono/docs/library/ai-sdk/index.md +427 -0
- package/templates/hono/docs/library/ai-sdk/openrouter.md +479 -0
- package/templates/hono/docs/library/ai-sdk/providers.md +468 -0
- package/templates/hono/docs/library/ai-sdk/streaming.md +447 -0
- package/templates/hono/docs/library/ai-sdk/structured-output.md +493 -0
- package/templates/hono/docs/library/ai-sdk/tools.md +513 -0
- package/templates/hono/docs/library/hono/env-setup.md +458 -0
- package/templates/hono/docs/library/hono/index.md +1 -0
- package/templates/hono/docs/library/pino/index.md +437 -0
- package/templates/hono/docs/library/prisma/cloudflare-d1.md +503 -0
- package/templates/hono/docs/library/prisma/config.md +362 -0
- package/templates/hono/docs/library/prisma/index.md +86 -13
- package/templates/hono/docs/skills/gemini-review/SKILL.md +116 -116
- package/templates/hono/docs/skills/gemini-review/references/checklists.md +125 -125
- package/templates/hono/docs/skills/gemini-review/references/prompt-templates.md +191 -191
- package/templates/npx/CLAUDE.md +309 -0
- package/templates/npx/docs/git/git.md +307 -0
- package/templates/npx/docs/library/commander/index.md +164 -0
- package/templates/npx/docs/library/fs-extra/index.md +171 -0
- package/templates/npx/docs/library/prompts/index.md +253 -0
- package/templates/npx/docs/mcp/index.md +60 -0
- package/templates/npx/docs/skills/gemini-review/SKILL.md +220 -0
- package/templates/npx/docs/skills/gemini-review/references/checklists.md +134 -0
- package/templates/npx/docs/skills/gemini-review/references/prompt-templates.md +301 -0
- package/templates/tanstack-start/CLAUDE.md +43 -5
- package/templates/tanstack-start/docs/architecture/architecture.md +134 -4
- package/templates/tanstack-start/docs/deployment/cloudflare.md +234 -51
- package/templates/tanstack-start/docs/deployment/index.md +322 -32
- package/templates/tanstack-start/docs/deployment/nitro.md +201 -20
- package/templates/tanstack-start/docs/deployment/railway.md +305 -153
- package/templates/tanstack-start/docs/deployment/vercel.md +353 -78
- package/templates/tanstack-start/docs/git/{index.md → git.md} +81 -7
- package/templates/tanstack-start/docs/guides/best-practices.md +203 -1
- package/templates/tanstack-start/docs/guides/env-setup.md +450 -0
- package/templates/tanstack-start/docs/library/ai-sdk/hooks.md +472 -0
- package/templates/tanstack-start/docs/library/ai-sdk/index.md +264 -0
- package/templates/tanstack-start/docs/library/ai-sdk/openrouter.md +371 -0
- package/templates/tanstack-start/docs/library/ai-sdk/providers.md +403 -0
- package/templates/tanstack-start/docs/library/ai-sdk/streaming.md +320 -0
- package/templates/tanstack-start/docs/library/ai-sdk/structured-output.md +454 -0
- package/templates/tanstack-start/docs/library/ai-sdk/tools.md +473 -0
- package/templates/tanstack-start/docs/library/pino/index.md +320 -0
- package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +404 -0
- package/templates/tanstack-start/docs/library/prisma/config.md +377 -0
- package/templates/tanstack-start/docs/library/prisma/index.md +3 -1
- package/templates/tanstack-start/docs/library/prisma/schema.md +123 -25
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +80 -2
- package/templates/tanstack-start/docs/skills/gemini-review/SKILL.md +116 -116
- package/templates/tanstack-start/docs/skills/gemini-review/references/checklists.md +138 -144
- package/templates/tanstack-start/docs/skills/gemini-review/references/prompt-templates.md +186 -187
- package/templates/hono/docs/git/index.md +0 -180
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
# AI SDK - React Hooks
|
|
2
|
+
|
|
3
|
+
> **상위 문서**: [AI SDK](./index.md)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 개요
|
|
8
|
+
|
|
9
|
+
AI SDK는 React 애플리케이션을 위한 훅을 제공합니다:
|
|
10
|
+
- `useChat`: 채팅 인터페이스
|
|
11
|
+
- `useCompletion`: 텍스트 완성
|
|
12
|
+
- `useObject`: 구조화된 객체 생성 (실험적)
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @ai-sdk/react
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## useChat
|
|
21
|
+
|
|
22
|
+
채팅 인터페이스를 쉽게 구현할 수 있는 훅입니다.
|
|
23
|
+
|
|
24
|
+
### 기본 사용
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
'use client'
|
|
28
|
+
|
|
29
|
+
import { useChat } from '@ai-sdk/react'
|
|
30
|
+
|
|
31
|
+
export default function Chat() {
|
|
32
|
+
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
|
|
33
|
+
api: '/api/chat',
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div>
|
|
38
|
+
{messages.map((m) => (
|
|
39
|
+
<div key={m.id}>
|
|
40
|
+
<strong>{m.role}:</strong> {m.content}
|
|
41
|
+
</div>
|
|
42
|
+
))}
|
|
43
|
+
|
|
44
|
+
<form onSubmit={handleSubmit}>
|
|
45
|
+
<input
|
|
46
|
+
value={input}
|
|
47
|
+
onChange={handleInputChange}
|
|
48
|
+
placeholder="Type a message..."
|
|
49
|
+
disabled={isLoading}
|
|
50
|
+
/>
|
|
51
|
+
<button type="submit" disabled={isLoading}>
|
|
52
|
+
{isLoading ? 'Sending...' : 'Send'}
|
|
53
|
+
</button>
|
|
54
|
+
</form>
|
|
55
|
+
</div>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 반환값
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
const {
|
|
64
|
+
// 메시지 관련
|
|
65
|
+
messages, // Message[] - 전체 메시지 목록
|
|
66
|
+
setMessages, // 메시지 직접 설정
|
|
67
|
+
|
|
68
|
+
// 입력 관련
|
|
69
|
+
input, // string - 현재 입력값
|
|
70
|
+
setInput, // 입력값 직접 설정
|
|
71
|
+
handleInputChange, // 입력 변경 핸들러
|
|
72
|
+
|
|
73
|
+
// 제출 관련
|
|
74
|
+
handleSubmit, // 폼 제출 핸들러
|
|
75
|
+
append, // 메시지 추가 및 전송
|
|
76
|
+
reload, // 마지막 응답 재생성
|
|
77
|
+
|
|
78
|
+
// 상태 관련
|
|
79
|
+
isLoading, // 응답 대기 중
|
|
80
|
+
error, // 에러 객체
|
|
81
|
+
stop, // 스트리밍 중단
|
|
82
|
+
} = useChat()
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 메시지 타입
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
interface Message {
|
|
89
|
+
id: string
|
|
90
|
+
role: 'user' | 'assistant' | 'system'
|
|
91
|
+
content: string
|
|
92
|
+
createdAt?: Date
|
|
93
|
+
toolInvocations?: ToolInvocation[]
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### API Route 설정
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// app/api/chat/route.ts
|
|
101
|
+
import { streamText, convertToModelMessages } from 'ai'
|
|
102
|
+
import { openai } from '@ai-sdk/openai'
|
|
103
|
+
|
|
104
|
+
export async function POST(req: Request) {
|
|
105
|
+
const { messages } = await req.json()
|
|
106
|
+
|
|
107
|
+
const result = streamText({
|
|
108
|
+
model: openai('gpt-4o'),
|
|
109
|
+
messages: convertToModelMessages(messages),
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
return result.toUIMessageStreamResponse()
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## useChat 고급 기능
|
|
119
|
+
|
|
120
|
+
### 초기 메시지
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
const { messages } = useChat({
|
|
124
|
+
api: '/api/chat',
|
|
125
|
+
initialMessages: [
|
|
126
|
+
{ id: '1', role: 'assistant', content: 'Hello! How can I help you?' },
|
|
127
|
+
],
|
|
128
|
+
})
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 동적 요청 데이터
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
const [temperature, setTemperature] = useState(0.7)
|
|
135
|
+
|
|
136
|
+
const { messages, handleSubmit } = useChat({
|
|
137
|
+
api: '/api/chat',
|
|
138
|
+
body: {
|
|
139
|
+
temperature,
|
|
140
|
+
userId: 'user-123',
|
|
141
|
+
},
|
|
142
|
+
headers: {
|
|
143
|
+
Authorization: `Bearer ${token}`,
|
|
144
|
+
},
|
|
145
|
+
})
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
서버에서 추가 데이터 접근:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// app/api/chat/route.ts
|
|
152
|
+
export async function POST(req: Request) {
|
|
153
|
+
const { messages, temperature, userId } = await req.json()
|
|
154
|
+
|
|
155
|
+
const result = streamText({
|
|
156
|
+
model: openai('gpt-4o'),
|
|
157
|
+
messages: convertToModelMessages(messages),
|
|
158
|
+
temperature, // 동적 파라미터 사용
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
return result.toUIMessageStreamResponse()
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 콜백 함수
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
const { messages } = useChat({
|
|
169
|
+
api: '/api/chat',
|
|
170
|
+
onResponse: (response) => {
|
|
171
|
+
console.log('Received response:', response)
|
|
172
|
+
},
|
|
173
|
+
onFinish: (message) => {
|
|
174
|
+
console.log('Finished:', message)
|
|
175
|
+
},
|
|
176
|
+
onError: (error) => {
|
|
177
|
+
console.error('Error:', error)
|
|
178
|
+
toast.error('Something went wrong')
|
|
179
|
+
},
|
|
180
|
+
})
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 메시지 직접 추가
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
const { append } = useChat()
|
|
187
|
+
|
|
188
|
+
// 사용자 메시지 추가 및 전송
|
|
189
|
+
const handleClick = async () => {
|
|
190
|
+
await append({
|
|
191
|
+
role: 'user',
|
|
192
|
+
content: 'Tell me a joke',
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 스트리밍 중단
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
const { stop, isLoading } = useChat()
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<button onClick={stop} disabled={!isLoading}>
|
|
204
|
+
Stop generating
|
|
205
|
+
</button>
|
|
206
|
+
)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### 마지막 응답 재생성
|
|
210
|
+
|
|
211
|
+
```tsx
|
|
212
|
+
const { reload, messages } = useChat()
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
<button onClick={reload} disabled={messages.length === 0}>
|
|
216
|
+
Regenerate response
|
|
217
|
+
</button>
|
|
218
|
+
)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### UI 업데이트 쓰로틀링
|
|
222
|
+
|
|
223
|
+
대량의 스트리밍 데이터가 있을 때 성능 최적화:
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
const { messages } = useChat({
|
|
227
|
+
api: '/api/chat',
|
|
228
|
+
experimental_throttle: 50, // 50ms마다 UI 업데이트
|
|
229
|
+
})
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## useCompletion
|
|
235
|
+
|
|
236
|
+
단순 텍스트 완성을 위한 훅입니다.
|
|
237
|
+
|
|
238
|
+
### 기본 사용
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
'use client'
|
|
242
|
+
|
|
243
|
+
import { useCompletion } from '@ai-sdk/react'
|
|
244
|
+
|
|
245
|
+
export default function Completion() {
|
|
246
|
+
const { completion, input, handleInputChange, handleSubmit, isLoading } = useCompletion({
|
|
247
|
+
api: '/api/completion',
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<div>
|
|
252
|
+
<form onSubmit={handleSubmit}>
|
|
253
|
+
<input
|
|
254
|
+
value={input}
|
|
255
|
+
onChange={handleInputChange}
|
|
256
|
+
placeholder="Enter a prompt..."
|
|
257
|
+
/>
|
|
258
|
+
<button type="submit" disabled={isLoading}>
|
|
259
|
+
Complete
|
|
260
|
+
</button>
|
|
261
|
+
</form>
|
|
262
|
+
|
|
263
|
+
<div>{completion}</div>
|
|
264
|
+
</div>
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### 반환값
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
const {
|
|
273
|
+
completion, // string - 생성된 텍스트
|
|
274
|
+
complete, // 직접 완성 요청
|
|
275
|
+
input, // string - 현재 입력값
|
|
276
|
+
setInput, // 입력값 설정
|
|
277
|
+
handleInputChange, // 입력 변경 핸들러
|
|
278
|
+
handleSubmit, // 폼 제출 핸들러
|
|
279
|
+
isLoading, // 로딩 상태
|
|
280
|
+
error, // 에러 객체
|
|
281
|
+
stop, // 스트리밍 중단
|
|
282
|
+
setCompletion, // 완성 텍스트 직접 설정
|
|
283
|
+
} = useCompletion()
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### API Route
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
// app/api/completion/route.ts
|
|
290
|
+
import { streamText } from 'ai'
|
|
291
|
+
import { openai } from '@ai-sdk/openai'
|
|
292
|
+
|
|
293
|
+
export async function POST(req: Request) {
|
|
294
|
+
const { prompt } = await req.json()
|
|
295
|
+
|
|
296
|
+
const result = streamText({
|
|
297
|
+
model: openai('gpt-4o'),
|
|
298
|
+
prompt,
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
return result.toUIMessageStreamResponse()
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### 프로그래매틱 호출
|
|
306
|
+
|
|
307
|
+
```tsx
|
|
308
|
+
const { complete, completion } = useCompletion({
|
|
309
|
+
api: '/api/completion',
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
const handleClick = async () => {
|
|
313
|
+
await complete('Write a haiku about programming')
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### 콜백
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
const { completion } = useCompletion({
|
|
321
|
+
onResponse: (response) => {
|
|
322
|
+
console.log('Response received')
|
|
323
|
+
},
|
|
324
|
+
onFinish: (prompt, completion) => {
|
|
325
|
+
console.log('Finished:', completion)
|
|
326
|
+
},
|
|
327
|
+
onError: (error) => {
|
|
328
|
+
console.error('Error:', error)
|
|
329
|
+
},
|
|
330
|
+
})
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## useObject (실험적)
|
|
336
|
+
|
|
337
|
+
구조화된 객체를 스트리밍으로 생성합니다.
|
|
338
|
+
|
|
339
|
+
### 기본 사용
|
|
340
|
+
|
|
341
|
+
```tsx
|
|
342
|
+
'use client'
|
|
343
|
+
|
|
344
|
+
import { experimental_useObject as useObject } from '@ai-sdk/react'
|
|
345
|
+
import { z } from 'zod'
|
|
346
|
+
|
|
347
|
+
const schema = z.object({
|
|
348
|
+
name: z.string(),
|
|
349
|
+
age: z.number(),
|
|
350
|
+
hobbies: z.array(z.string()),
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
export default function ObjectGenerator() {
|
|
354
|
+
const { object, submit, isLoading, error } = useObject({
|
|
355
|
+
api: '/api/generate-object',
|
|
356
|
+
schema,
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
return (
|
|
360
|
+
<div>
|
|
361
|
+
<button onClick={() => submit('Generate a random person')} disabled={isLoading}>
|
|
362
|
+
Generate
|
|
363
|
+
</button>
|
|
364
|
+
|
|
365
|
+
{object && (
|
|
366
|
+
<pre>{JSON.stringify(object, null, 2)}</pre>
|
|
367
|
+
)}
|
|
368
|
+
</div>
|
|
369
|
+
)
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### API Route
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
// app/api/generate-object/route.ts
|
|
377
|
+
import { streamObject } from 'ai'
|
|
378
|
+
import { openai } from '@ai-sdk/openai'
|
|
379
|
+
import { z } from 'zod'
|
|
380
|
+
|
|
381
|
+
const schema = z.object({
|
|
382
|
+
name: z.string(),
|
|
383
|
+
age: z.number(),
|
|
384
|
+
hobbies: z.array(z.string()),
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
export async function POST(req: Request) {
|
|
388
|
+
const { prompt } = await req.json()
|
|
389
|
+
|
|
390
|
+
const result = streamObject({
|
|
391
|
+
model: openai('gpt-4o'),
|
|
392
|
+
schema,
|
|
393
|
+
prompt,
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
return result.toTextStreamResponse()
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## 도구 호출 표시
|
|
403
|
+
|
|
404
|
+
채팅에서 도구 호출을 표시하는 방법:
|
|
405
|
+
|
|
406
|
+
```tsx
|
|
407
|
+
'use client'
|
|
408
|
+
|
|
409
|
+
import { useChat } from '@ai-sdk/react'
|
|
410
|
+
|
|
411
|
+
export default function Chat() {
|
|
412
|
+
const { messages } = useChat({
|
|
413
|
+
api: '/api/chat',
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
return (
|
|
417
|
+
<div>
|
|
418
|
+
{messages.map((m) => (
|
|
419
|
+
<div key={m.id}>
|
|
420
|
+
<strong>{m.role}:</strong>
|
|
421
|
+
|
|
422
|
+
{/* 텍스트 콘텐츠 */}
|
|
423
|
+
{m.content}
|
|
424
|
+
|
|
425
|
+
{/* 도구 호출 표시 */}
|
|
426
|
+
{m.toolInvocations?.map((tool, i) => (
|
|
427
|
+
<div key={i} className="tool-call">
|
|
428
|
+
<strong>Tool: {tool.toolName}</strong>
|
|
429
|
+
<pre>Input: {JSON.stringify(tool.args, null, 2)}</pre>
|
|
430
|
+
{tool.state === 'result' && (
|
|
431
|
+
<pre>Result: {JSON.stringify(tool.result, null, 2)}</pre>
|
|
432
|
+
)}
|
|
433
|
+
</div>
|
|
434
|
+
))}
|
|
435
|
+
</div>
|
|
436
|
+
))}
|
|
437
|
+
</div>
|
|
438
|
+
)
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## 스트림 프로토콜
|
|
445
|
+
|
|
446
|
+
AI SDK v5에서는 기본 스트림 프로토콜이 변경되었습니다:
|
|
447
|
+
|
|
448
|
+
```tsx
|
|
449
|
+
// v5 기본값 (data protocol)
|
|
450
|
+
const { messages } = useChat()
|
|
451
|
+
|
|
452
|
+
// 레거시 텍스트 프로토콜 사용
|
|
453
|
+
const { messages } = useChat({
|
|
454
|
+
streamProtocol: 'text',
|
|
455
|
+
})
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
## 타입 안전성
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
import { useChat, Message } from '@ai-sdk/react'
|
|
464
|
+
|
|
465
|
+
// 메시지 타입 확장
|
|
466
|
+
interface CustomMessage extends Message {
|
|
467
|
+
customField?: string
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// 타입 지정
|
|
471
|
+
const { messages } = useChat<CustomMessage>()
|
|
472
|
+
```
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# Vercel AI SDK
|
|
2
|
+
|
|
3
|
+
> **Version**: 4.x / 5.x | TypeScript AI Toolkit
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🚀 Quick Reference (복사용)
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// 텍스트 생성
|
|
11
|
+
import { generateText, streamText } from 'ai'
|
|
12
|
+
import { openai } from '@ai-sdk/openai'
|
|
13
|
+
|
|
14
|
+
const { text } = await generateText({
|
|
15
|
+
model: openai('gpt-4o'),
|
|
16
|
+
prompt: 'Hello, world!',
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
// 스트리밍
|
|
20
|
+
const result = streamText({
|
|
21
|
+
model: openai('gpt-4o'),
|
|
22
|
+
prompt: 'Write a story.',
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
for await (const chunk of result.textStream) {
|
|
26
|
+
console.log(chunk)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 구조화된 출력
|
|
30
|
+
import { generateObject } from 'ai'
|
|
31
|
+
import { z } from 'zod'
|
|
32
|
+
|
|
33
|
+
const { object } = await generateObject({
|
|
34
|
+
model: openai('gpt-4o'),
|
|
35
|
+
schema: z.object({ name: z.string(), age: z.number() }),
|
|
36
|
+
prompt: 'Generate a user.',
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// React Hook
|
|
40
|
+
import { useChat } from '@ai-sdk/react'
|
|
41
|
+
|
|
42
|
+
const { messages, input, handleInputChange, handleSubmit } = useChat()
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 문서 구조
|
|
48
|
+
|
|
49
|
+
- [프로바이더 설정](./providers.md) - OpenAI, Anthropic, Google 등
|
|
50
|
+
- [OpenRouter](./openrouter.md) - 통합 AI 게이트웨이 (수백 개 모델)
|
|
51
|
+
- [텍스트 생성](./streaming.md) - generateText, streamText
|
|
52
|
+
- [React Hooks](./hooks.md) - useChat, useCompletion
|
|
53
|
+
- [Tool Calling](./tools.md) - 도구 정의 및 호출
|
|
54
|
+
- [구조화된 출력](./structured-output.md) - generateObject, 스키마 기반 출력
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 설치
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# 코어 패키지
|
|
62
|
+
npm install ai
|
|
63
|
+
|
|
64
|
+
# 프로바이더 패키지 (필요한 것만 설치)
|
|
65
|
+
npm install @ai-sdk/openai # OpenAI
|
|
66
|
+
npm install @ai-sdk/anthropic # Anthropic (Claude)
|
|
67
|
+
npm install @ai-sdk/google # Google (Gemini)
|
|
68
|
+
|
|
69
|
+
# React Hook 사용 시
|
|
70
|
+
npm install @ai-sdk/react
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 핵심 개념
|
|
76
|
+
|
|
77
|
+
### 프로바이더 (Providers)
|
|
78
|
+
|
|
79
|
+
AI 모델 서비스와의 연결을 담당합니다.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { openai } from '@ai-sdk/openai'
|
|
83
|
+
import { anthropic } from '@ai-sdk/anthropic'
|
|
84
|
+
import { google } from '@ai-sdk/google'
|
|
85
|
+
|
|
86
|
+
// 모델 인스턴스 생성
|
|
87
|
+
const gpt4 = openai('gpt-4o')
|
|
88
|
+
const claude = anthropic('claude-3-5-sonnet-20241022')
|
|
89
|
+
const gemini = google('gemini-1.5-pro')
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 코어 함수
|
|
93
|
+
|
|
94
|
+
| 함수 | 용도 | 반환 |
|
|
95
|
+
|------|------|------|
|
|
96
|
+
| `generateText` | 텍스트 생성 (비스트리밍) | `{ text, toolCalls, ... }` |
|
|
97
|
+
| `streamText` | 텍스트 스트리밍 | `{ textStream, ... }` |
|
|
98
|
+
| `generateObject` | 구조화된 객체 생성 | `{ object }` |
|
|
99
|
+
| `streamObject` | 구조화된 객체 스트리밍 | `{ partialObjectStream }` |
|
|
100
|
+
|
|
101
|
+
### React Hooks
|
|
102
|
+
|
|
103
|
+
| Hook | 용도 |
|
|
104
|
+
|------|------|
|
|
105
|
+
| `useChat` | 채팅 인터페이스 |
|
|
106
|
+
| `useCompletion` | 텍스트 완성 |
|
|
107
|
+
| `useObject` | 구조화된 객체 생성 (실험적) |
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 기본 사용법
|
|
112
|
+
|
|
113
|
+
### 텍스트 생성
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { generateText } from 'ai'
|
|
117
|
+
import { openai } from '@ai-sdk/openai'
|
|
118
|
+
|
|
119
|
+
const { text, usage } = await generateText({
|
|
120
|
+
model: openai('gpt-4o'),
|
|
121
|
+
prompt: 'Explain quantum computing in simple terms.',
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
console.log(text)
|
|
125
|
+
console.log(`Tokens used: ${usage.totalTokens}`)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 스트리밍
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { streamText } from 'ai'
|
|
132
|
+
import { openai } from '@ai-sdk/openai'
|
|
133
|
+
|
|
134
|
+
const result = streamText({
|
|
135
|
+
model: openai('gpt-4o'),
|
|
136
|
+
prompt: 'Write a haiku about programming.',
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
for await (const chunk of result.textStream) {
|
|
140
|
+
process.stdout.write(chunk)
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 채팅 (메시지 기반)
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { generateText } from 'ai'
|
|
148
|
+
import { openai } from '@ai-sdk/openai'
|
|
149
|
+
|
|
150
|
+
const { text } = await generateText({
|
|
151
|
+
model: openai('gpt-4o'),
|
|
152
|
+
messages: [
|
|
153
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
154
|
+
{ role: 'user', content: 'What is TypeScript?' },
|
|
155
|
+
],
|
|
156
|
+
})
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## TanStack Start 통합
|
|
162
|
+
|
|
163
|
+
### API Route 설정
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// app/api/chat/route.ts
|
|
167
|
+
import { streamText, convertToModelMessages } from 'ai'
|
|
168
|
+
import { openai } from '@ai-sdk/openai'
|
|
169
|
+
|
|
170
|
+
export async function POST(req: Request) {
|
|
171
|
+
const { messages } = await req.json()
|
|
172
|
+
|
|
173
|
+
const result = streamText({
|
|
174
|
+
model: openai('gpt-4o'),
|
|
175
|
+
messages: convertToModelMessages(messages),
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
return result.toUIMessageStreamResponse()
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 클라이언트 컴포넌트
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
'use client'
|
|
186
|
+
|
|
187
|
+
import { useChat } from '@ai-sdk/react'
|
|
188
|
+
|
|
189
|
+
export default function Chat() {
|
|
190
|
+
const { messages, input, handleInputChange, handleSubmit } = useChat({
|
|
191
|
+
api: '/api/chat',
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<div>
|
|
196
|
+
{messages.map((m) => (
|
|
197
|
+
<div key={m.id}>
|
|
198
|
+
<strong>{m.role}:</strong> {m.content}
|
|
199
|
+
</div>
|
|
200
|
+
))}
|
|
201
|
+
|
|
202
|
+
<form onSubmit={handleSubmit}>
|
|
203
|
+
<input value={input} onChange={handleInputChange} placeholder="Say something..." />
|
|
204
|
+
<button type="submit">Send</button>
|
|
205
|
+
</form>
|
|
206
|
+
</div>
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## 환경 변수
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
# .env
|
|
217
|
+
OPENAI_API_KEY=sk-...
|
|
218
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
219
|
+
GOOGLE_GENERATIVE_AI_API_KEY=...
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## v5 주요 변경사항
|
|
225
|
+
|
|
226
|
+
### 스트림 프로토콜 변경
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
// v4 - text protocol 명시 필요
|
|
230
|
+
const { messages } = useChat({ streamProtocol: 'text' })
|
|
231
|
+
|
|
232
|
+
// v5 - 기본값이 data protocol
|
|
233
|
+
const { messages } = useChat()
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### 프로바이더 초기화 변경
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
// v4 (deprecated)
|
|
240
|
+
const openai = new OpenAI({ /* ... */ })
|
|
241
|
+
|
|
242
|
+
// v5
|
|
243
|
+
import { createOpenAI } from '@ai-sdk/openai'
|
|
244
|
+
const openai = createOpenAI({ /* ... */ })
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### responseMessages 제거
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
// v4
|
|
251
|
+
const { text, responseMessages } = await generateText({ ... })
|
|
252
|
+
|
|
253
|
+
// v5
|
|
254
|
+
const { text, response } = await generateText({ ... })
|
|
255
|
+
const responseMessages = response.messages
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 참고 자료
|
|
261
|
+
|
|
262
|
+
- [AI SDK 공식 문서](https://sdk.vercel.ai/docs)
|
|
263
|
+
- [AI SDK GitHub](https://github.com/vercel/ai)
|
|
264
|
+
- [AI SDK 예제](https://github.com/vercel/ai/tree/main/examples)
|