@kood/claude-code 0.1.2 → 0.1.4
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 +129 -5
- 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/commands/git.md +275 -0
- package/templates/hono/docs/deployment/cloudflare.md +527 -190
- package/templates/hono/docs/deployment/docker.md +514 -0
- package/templates/hono/docs/deployment/index.md +179 -214
- package/templates/hono/docs/deployment/railway.md +416 -0
- package/templates/hono/docs/deployment/vercel.md +567 -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 -3
- 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/commands/git.md +275 -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/commands/git.md +275 -0
- package/templates/tanstack-start/docs/deployment/cloudflare.md +223 -50
- package/templates/tanstack-start/docs/deployment/index.md +320 -30
- package/templates/tanstack-start/docs/deployment/nitro.md +195 -14
- package/templates/tanstack-start/docs/deployment/railway.md +302 -150
- package/templates/tanstack-start/docs/deployment/vercel.md +345 -75
- 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 -5
- package/templates/tanstack-start/docs/library/prisma/schema.md +123 -25
- package/templates/tanstack-start/docs/library/prisma/setup.md +0 -7
- 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
- package/templates/tanstack-start/docs/git/index.md +0 -203
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
# AI SDK - Hono 통합 가이드
|
|
2
|
+
|
|
3
|
+
> Vercel AI SDK를 Hono와 함께 사용하기
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 개요
|
|
8
|
+
|
|
9
|
+
AI SDK는 TypeScript 기반의 AI 애플리케이션 개발 라이브러리입니다. Hono의 경량화된 API와 결합하여 고성능 AI 엔드포인트를 구축할 수 있습니다.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 설치
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install ai @ai-sdk/openai
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 프로바이더별 설치
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @ai-sdk/anthropic # Claude
|
|
23
|
+
npm install @ai-sdk/google # Gemini
|
|
24
|
+
npm install @ai-sdk/mistral # Mistral
|
|
25
|
+
npm install @ai-sdk/groq # Groq
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 빠른 시작
|
|
31
|
+
|
|
32
|
+
### 기본 채팅 API
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { Hono } from 'hono'
|
|
36
|
+
import { streamText, convertToModelMessages } from 'ai'
|
|
37
|
+
import { openai } from '@ai-sdk/openai'
|
|
38
|
+
|
|
39
|
+
const app = new Hono()
|
|
40
|
+
|
|
41
|
+
app.post('/api/chat', async (c) => {
|
|
42
|
+
const { messages } = await c.req.json()
|
|
43
|
+
|
|
44
|
+
const result = streamText({
|
|
45
|
+
model: openai('gpt-4o'),
|
|
46
|
+
messages: convertToModelMessages(messages),
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
return result.toUIMessageStreamResponse()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
export default app
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 텍스트 생성 API
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { Hono } from 'hono'
|
|
59
|
+
import { generateText } from 'ai'
|
|
60
|
+
import { openai } from '@ai-sdk/openai'
|
|
61
|
+
|
|
62
|
+
const app = new Hono()
|
|
63
|
+
|
|
64
|
+
app.post('/api/generate', async (c) => {
|
|
65
|
+
const { prompt } = await c.req.json()
|
|
66
|
+
|
|
67
|
+
const { text } = await generateText({
|
|
68
|
+
model: openai('gpt-4o'),
|
|
69
|
+
prompt,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
return c.json({ text })
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
export default app
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 핵심 함수
|
|
81
|
+
|
|
82
|
+
| 함수 | 용도 | 응답 타입 |
|
|
83
|
+
|------|------|----------|
|
|
84
|
+
| `generateText` | 텍스트 생성 (비스트리밍) | `Promise<{ text }>` |
|
|
85
|
+
| `streamText` | 텍스트 스트리밍 | `StreamTextResult` |
|
|
86
|
+
| `generateObject` | 구조화된 객체 생성 | `Promise<{ object }>` |
|
|
87
|
+
| `streamObject` | 객체 스트리밍 | `StreamObjectResult` |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Hono 미들웨어 패턴
|
|
92
|
+
|
|
93
|
+
### AI 컨텍스트 미들웨어
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import { Hono } from 'hono'
|
|
97
|
+
import { createMiddleware } from 'hono/factory'
|
|
98
|
+
import { openai } from '@ai-sdk/openai'
|
|
99
|
+
|
|
100
|
+
type AIVariables = {
|
|
101
|
+
aiModel: ReturnType<typeof openai>
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const aiMiddleware = createMiddleware<{ Variables: AIVariables }>(
|
|
105
|
+
async (c, next) => {
|
|
106
|
+
c.set('aiModel', openai('gpt-4o'))
|
|
107
|
+
await next()
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
const app = new Hono<{ Variables: AIVariables }>()
|
|
112
|
+
|
|
113
|
+
app.use('/api/ai/*', aiMiddleware)
|
|
114
|
+
|
|
115
|
+
app.post('/api/ai/chat', async (c) => {
|
|
116
|
+
const model = c.get('aiModel')
|
|
117
|
+
const { messages } = await c.req.json()
|
|
118
|
+
|
|
119
|
+
const result = streamText({
|
|
120
|
+
model,
|
|
121
|
+
messages,
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
return result.toUIMessageStreamResponse()
|
|
125
|
+
})
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Rate Limiting 미들웨어
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { Hono } from 'hono'
|
|
132
|
+
import { rateLimiter } from 'hono-rate-limiter'
|
|
133
|
+
import { streamText } from 'ai'
|
|
134
|
+
import { openai } from '@ai-sdk/openai'
|
|
135
|
+
|
|
136
|
+
const app = new Hono()
|
|
137
|
+
|
|
138
|
+
// AI 엔드포인트에 Rate Limiting 적용
|
|
139
|
+
app.use(
|
|
140
|
+
'/api/ai/*',
|
|
141
|
+
rateLimiter({
|
|
142
|
+
windowMs: 60 * 1000, // 1분
|
|
143
|
+
limit: 10, // 최대 10 요청
|
|
144
|
+
standardHeaders: 'draft-6',
|
|
145
|
+
keyGenerator: (c) => c.req.header('x-forwarded-for') ?? 'anonymous',
|
|
146
|
+
})
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
app.post('/api/ai/chat', async (c) => {
|
|
150
|
+
const { messages } = await c.req.json()
|
|
151
|
+
|
|
152
|
+
const result = streamText({
|
|
153
|
+
model: openai('gpt-4o'),
|
|
154
|
+
messages,
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
return result.toUIMessageStreamResponse()
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 스트리밍 응답 처리
|
|
164
|
+
|
|
165
|
+
### 기본 스트리밍
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { Hono } from 'hono'
|
|
169
|
+
import { streamText } from 'ai'
|
|
170
|
+
import { openai } from '@ai-sdk/openai'
|
|
171
|
+
|
|
172
|
+
const app = new Hono()
|
|
173
|
+
|
|
174
|
+
app.post('/api/stream', async (c) => {
|
|
175
|
+
const { prompt } = await c.req.json()
|
|
176
|
+
|
|
177
|
+
const result = streamText({
|
|
178
|
+
model: openai('gpt-4o'),
|
|
179
|
+
prompt,
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
// UI 메시지 스트림 (프론트엔드용)
|
|
183
|
+
return result.toUIMessageStreamResponse()
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
app.post('/api/stream-text', async (c) => {
|
|
187
|
+
const { prompt } = await c.req.json()
|
|
188
|
+
|
|
189
|
+
const result = streamText({
|
|
190
|
+
model: openai('gpt-4o'),
|
|
191
|
+
prompt,
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
// 텍스트 스트림 (SSE)
|
|
195
|
+
return result.toTextStreamResponse()
|
|
196
|
+
})
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### 커스텀 스트림 처리
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { Hono } from 'hono'
|
|
203
|
+
import { streamText } from 'ai'
|
|
204
|
+
import { openai } from '@ai-sdk/openai'
|
|
205
|
+
import { stream } from 'hono/streaming'
|
|
206
|
+
|
|
207
|
+
const app = new Hono()
|
|
208
|
+
|
|
209
|
+
app.post('/api/custom-stream', async (c) => {
|
|
210
|
+
const { prompt } = await c.req.json()
|
|
211
|
+
|
|
212
|
+
const result = streamText({
|
|
213
|
+
model: openai('gpt-4o'),
|
|
214
|
+
prompt,
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
return stream(c, async (stream) => {
|
|
218
|
+
for await (const chunk of result.textStream) {
|
|
219
|
+
await stream.write(chunk)
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
})
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 도구 (Tool) 통합
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
import { Hono } from 'hono'
|
|
231
|
+
import { streamText, tool, convertToModelMessages } from 'ai'
|
|
232
|
+
import { openai } from '@ai-sdk/openai'
|
|
233
|
+
import { z } from 'zod'
|
|
234
|
+
|
|
235
|
+
const app = new Hono()
|
|
236
|
+
|
|
237
|
+
app.post('/api/chat-with-tools', async (c) => {
|
|
238
|
+
const { messages } = await c.req.json()
|
|
239
|
+
|
|
240
|
+
const result = streamText({
|
|
241
|
+
model: openai('gpt-4o'),
|
|
242
|
+
messages: convertToModelMessages(messages),
|
|
243
|
+
tools: {
|
|
244
|
+
getWeather: tool({
|
|
245
|
+
description: 'Get weather for a location',
|
|
246
|
+
inputSchema: z.object({
|
|
247
|
+
location: z.string().describe('City name'),
|
|
248
|
+
}),
|
|
249
|
+
execute: async ({ location }) => {
|
|
250
|
+
// 실제 날씨 API 호출
|
|
251
|
+
return { location, temperature: 22, condition: 'Sunny' }
|
|
252
|
+
},
|
|
253
|
+
}),
|
|
254
|
+
searchDatabase: tool({
|
|
255
|
+
description: 'Search the database',
|
|
256
|
+
inputSchema: z.object({
|
|
257
|
+
query: z.string(),
|
|
258
|
+
}),
|
|
259
|
+
execute: async ({ query }) => {
|
|
260
|
+
// 데이터베이스 검색
|
|
261
|
+
return { results: [] }
|
|
262
|
+
},
|
|
263
|
+
}),
|
|
264
|
+
},
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
return result.toUIMessageStreamResponse()
|
|
268
|
+
})
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## 구조화된 출력
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
import { Hono } from 'hono'
|
|
277
|
+
import { generateObject, streamObject } from 'ai'
|
|
278
|
+
import { openai } from '@ai-sdk/openai'
|
|
279
|
+
import { z } from 'zod'
|
|
280
|
+
|
|
281
|
+
const app = new Hono()
|
|
282
|
+
|
|
283
|
+
const userSchema = z.object({
|
|
284
|
+
name: z.string(),
|
|
285
|
+
age: z.number(),
|
|
286
|
+
email: z.string().email(),
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
// 비스트리밍 객체 생성
|
|
290
|
+
app.post('/api/generate-user', async (c) => {
|
|
291
|
+
const { prompt } = await c.req.json()
|
|
292
|
+
|
|
293
|
+
const { object } = await generateObject({
|
|
294
|
+
model: openai('gpt-4o'),
|
|
295
|
+
schema: userSchema,
|
|
296
|
+
prompt,
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
return c.json(object)
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
// 스트리밍 객체 생성
|
|
303
|
+
app.post('/api/stream-user', async (c) => {
|
|
304
|
+
const { prompt } = await c.req.json()
|
|
305
|
+
|
|
306
|
+
const result = streamObject({
|
|
307
|
+
model: openai('gpt-4o'),
|
|
308
|
+
schema: userSchema,
|
|
309
|
+
prompt,
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
return result.toTextStreamResponse()
|
|
313
|
+
})
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## 에러 처리
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
import { Hono } from 'hono'
|
|
322
|
+
import { HTTPException } from 'hono/http-exception'
|
|
323
|
+
import { streamText } from 'ai'
|
|
324
|
+
import { openai } from '@ai-sdk/openai'
|
|
325
|
+
|
|
326
|
+
const app = new Hono()
|
|
327
|
+
|
|
328
|
+
app.post('/api/chat', async (c) => {
|
|
329
|
+
try {
|
|
330
|
+
const { messages } = await c.req.json()
|
|
331
|
+
|
|
332
|
+
if (!messages || !Array.isArray(messages)) {
|
|
333
|
+
throw new HTTPException(400, { message: 'Invalid messages format' })
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const result = streamText({
|
|
337
|
+
model: openai('gpt-4o'),
|
|
338
|
+
messages,
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
return result.toUIMessageStreamResponse()
|
|
342
|
+
} catch (error) {
|
|
343
|
+
if (error instanceof HTTPException) {
|
|
344
|
+
throw error
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
console.error('AI Error:', error)
|
|
348
|
+
throw new HTTPException(500, { message: 'AI processing failed' })
|
|
349
|
+
}
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
// 글로벌 에러 핸들러
|
|
353
|
+
app.onError((err, c) => {
|
|
354
|
+
if (err instanceof HTTPException) {
|
|
355
|
+
return c.json({ error: err.message }, err.status)
|
|
356
|
+
}
|
|
357
|
+
return c.json({ error: 'Internal server error' }, 500)
|
|
358
|
+
})
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## Cloudflare Workers 배포
|
|
364
|
+
|
|
365
|
+
### 기본 설정
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
// src/index.ts
|
|
369
|
+
import { Hono } from 'hono'
|
|
370
|
+
import { streamText, convertToModelMessages } from 'ai'
|
|
371
|
+
import { createOpenAI } from '@ai-sdk/openai'
|
|
372
|
+
|
|
373
|
+
type Bindings = {
|
|
374
|
+
OPENAI_API_KEY: string
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const app = new Hono<{ Bindings: Bindings }>()
|
|
378
|
+
|
|
379
|
+
app.post('/api/chat', async (c) => {
|
|
380
|
+
const openai = createOpenAI({
|
|
381
|
+
apiKey: c.env.OPENAI_API_KEY,
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
const { messages } = await c.req.json()
|
|
385
|
+
|
|
386
|
+
const result = streamText({
|
|
387
|
+
model: openai('gpt-4o'),
|
|
388
|
+
messages: convertToModelMessages(messages),
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
return result.toUIMessageStreamResponse()
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
export default app
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### wrangler.toml
|
|
398
|
+
|
|
399
|
+
```toml
|
|
400
|
+
name = "ai-api"
|
|
401
|
+
main = "src/index.ts"
|
|
402
|
+
compatibility_date = "2024-01-01"
|
|
403
|
+
|
|
404
|
+
[vars]
|
|
405
|
+
# 환경 변수는 Cloudflare 대시보드에서 설정
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
## 환경 변수
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
# .env
|
|
414
|
+
OPENAI_API_KEY=sk-...
|
|
415
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
416
|
+
GOOGLE_GENERATIVE_AI_API_KEY=...
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## 관련 문서
|
|
422
|
+
|
|
423
|
+
- [프로바이더](./providers.md) - AI 프로바이더 설정
|
|
424
|
+
- [OpenRouter](./openrouter.md) - 통합 AI 게이트웨이 (수백 개 모델)
|
|
425
|
+
- [스트리밍](./streaming.md) - 텍스트 생성과 스트리밍
|
|
426
|
+
- [도구](./tools.md) - Tool Calling 구현
|
|
427
|
+
- [구조화된 출력](./structured-output.md) - 타입 안전한 객체 생성
|