@cobbl-ai/sdk 0.1.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/README.md +516 -0
- package/dist/index.d.mts +348 -0
- package/dist/index.d.ts +348 -0
- package/dist/index.js +236 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +233 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +73 -0
- package/src/__tests__/client.test.ts +650 -0
- package/src/__tests__/errors.test.ts +100 -0
- package/src/__tests__/integration.test.ts +346 -0
- package/src/client.ts +257 -0
- package/src/errors.ts +70 -0
- package/src/index.ts +43 -0
- package/src/types.ts +94 -0
package/README.md
ADDED
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
# @cobbl-ai/sdk
|
|
2
|
+
|
|
3
|
+
The official TypeScript/JavaScript SDK for [Cobbl](https://cobbl.ai) - a feedback-driven PromptOps platform for LLM applications.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@cobbl-ai/sdk)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- 🚀 **Simple API** - Run prompts and collect feedback with just a few lines of code
|
|
11
|
+
- 🔒 **Type-safe** - Full TypeScript support with comprehensive type definitions
|
|
12
|
+
- 🎯 **Framework agnostic** - Works with Node.js, Next.js, Express, and any JavaScript framework
|
|
13
|
+
- 📦 **Zero config** - Works out of the box with sensible defaults
|
|
14
|
+
- 🌐 **Cross-platform** - Supports both CommonJS and ES modules
|
|
15
|
+
- ⚡ **Optimized** - Minimal bundle size, tree-shakeable exports
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# npm
|
|
21
|
+
npm install @cobbl-ai/sdk
|
|
22
|
+
|
|
23
|
+
# yarn
|
|
24
|
+
yarn add @cobbl-ai/sdk
|
|
25
|
+
|
|
26
|
+
# pnpm
|
|
27
|
+
pnpm add @cobbl-ai/sdk
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { CobblClient } from '@cobbl-ai/sdk'
|
|
34
|
+
|
|
35
|
+
// Initialize the client
|
|
36
|
+
const client = new CobblClient({
|
|
37
|
+
apiKey: process.env.PROMPTI_API_KEY,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// Run a prompt
|
|
41
|
+
const result = await client.runPrompt('sales_summary', {
|
|
42
|
+
topic: 'Q4 Results',
|
|
43
|
+
tone: 'friendly',
|
|
44
|
+
audience: 'investors',
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
console.log(result.output) // AI-generated response
|
|
48
|
+
console.log(result.runId) // Save this to link feedback later
|
|
49
|
+
|
|
50
|
+
// Submit user feedback
|
|
51
|
+
await client.submitFeedback({
|
|
52
|
+
runId: result.runId,
|
|
53
|
+
helpful: 'not_helpful',
|
|
54
|
+
userFeedback: 'The response was too formal and lengthy',
|
|
55
|
+
})
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Configuration
|
|
59
|
+
|
|
60
|
+
### Initializing the Client
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { CobblClient } from '@cobbl-ai/sdk'
|
|
64
|
+
|
|
65
|
+
const client = new CobblClient({
|
|
66
|
+
apiKey: 'your-api-key', // Your Cobbl API key
|
|
67
|
+
})
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Environment Variables
|
|
71
|
+
|
|
72
|
+
We recommend storing your API key in environment variables:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# .env
|
|
76
|
+
PROMPTI_API_KEY=your_api_key_here
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Then load it in your application:
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const client = new CobblClient({
|
|
83
|
+
apiKey: process.env.PROMPTI_API_KEY,
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## API Reference
|
|
88
|
+
|
|
89
|
+
### `runPrompt(promptSlug, input)`
|
|
90
|
+
|
|
91
|
+
Execute a prompt with the given input variables.
|
|
92
|
+
|
|
93
|
+
**Parameters:**
|
|
94
|
+
|
|
95
|
+
- `promptSlug` (string): The unique slug identifier for the prompt
|
|
96
|
+
- `input` (PromptInput): Input variables to populate the prompt template
|
|
97
|
+
|
|
98
|
+
**Returns:** `Promise<RunPromptResponse>`
|
|
99
|
+
|
|
100
|
+
**Response:**
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
{
|
|
104
|
+
runId: string // Unique ID for this run - use for feedback
|
|
105
|
+
output: string // AI-generated response
|
|
106
|
+
tokenUsage: {
|
|
107
|
+
inputTokens: number
|
|
108
|
+
outputTokens: number
|
|
109
|
+
totalTokens: number
|
|
110
|
+
}
|
|
111
|
+
renderedPrompt: string // The prompt sent to the LLM
|
|
112
|
+
promptVersion: {
|
|
113
|
+
// Metadata about the prompt version used
|
|
114
|
+
id: string
|
|
115
|
+
versionNumber: number
|
|
116
|
+
template: string
|
|
117
|
+
variables: Array<{
|
|
118
|
+
key: string
|
|
119
|
+
type: string
|
|
120
|
+
required: boolean
|
|
121
|
+
}>
|
|
122
|
+
provider: 'openai' | 'anthropic' | 'google'
|
|
123
|
+
model: string
|
|
124
|
+
// ... more fields
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Example:**
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const result = await client.runPrompt('customer_email', {
|
|
133
|
+
customerName: 'John Doe',
|
|
134
|
+
issue: 'login_problem',
|
|
135
|
+
urgency: 'high',
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
console.log(result.output)
|
|
139
|
+
// => "Dear John Doe, We understand you're experiencing login issues..."
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### `submitFeedback(feedback)`
|
|
143
|
+
|
|
144
|
+
Submit user feedback for a prompt run.
|
|
145
|
+
|
|
146
|
+
**Parameters:**
|
|
147
|
+
|
|
148
|
+
- `feedback` (FeedbackSubmission):
|
|
149
|
+
- `runId` (string): The run ID from a previous `runPrompt` call
|
|
150
|
+
- `helpful` ('helpful' | 'not_helpful'): Whether the output was helpful
|
|
151
|
+
- `userFeedback` (string): Detailed feedback message
|
|
152
|
+
|
|
153
|
+
**Returns:** `Promise<SubmitFeedbackResponse>`
|
|
154
|
+
|
|
155
|
+
**Response:**
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
{
|
|
159
|
+
feedbackId: string // Unique ID for the feedback item
|
|
160
|
+
message: string // Success message
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Example:**
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
await client.submitFeedback({
|
|
168
|
+
runId: result.runId,
|
|
169
|
+
helpful: 'not_helpful',
|
|
170
|
+
userFeedback: 'The tone was too casual for a professional email',
|
|
171
|
+
})
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Advanced Usage
|
|
175
|
+
|
|
176
|
+
### Error Handling
|
|
177
|
+
|
|
178
|
+
The SDK throws `CobblError` for all error cases. You can catch and handle these errors:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { CobblClient, CobblError } from '@cobbl-ai/sdk'
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
const result = await client.runPrompt('my-prompt', { topic: 'test' })
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (error instanceof CobblError) {
|
|
187
|
+
console.error(`Error [${error.code}]: ${error.message}`)
|
|
188
|
+
|
|
189
|
+
// Handle specific error types
|
|
190
|
+
switch (error.code) {
|
|
191
|
+
case 'UNAUTHORIZED':
|
|
192
|
+
console.error('Invalid API key')
|
|
193
|
+
break
|
|
194
|
+
case 'NOT_FOUND':
|
|
195
|
+
console.error('Prompt not found')
|
|
196
|
+
break
|
|
197
|
+
case 'INVALID_REQUEST':
|
|
198
|
+
console.error('Invalid request:', error.details)
|
|
199
|
+
break
|
|
200
|
+
case 'RATE_LIMIT_EXCEEDED':
|
|
201
|
+
console.error('Rate limit exceeded, try again later')
|
|
202
|
+
break
|
|
203
|
+
default:
|
|
204
|
+
console.error('Unexpected error:', error)
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
console.error('Unknown error:', error)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Error Codes:**
|
|
213
|
+
|
|
214
|
+
- `INVALID_CONFIG` - Invalid SDK configuration
|
|
215
|
+
- `INVALID_REQUEST` - Malformed request (e.g., missing required fields)
|
|
216
|
+
- `UNAUTHORIZED` - Invalid or missing API key
|
|
217
|
+
- `NOT_FOUND` - Resource not found (e.g., prompt doesn't exist)
|
|
218
|
+
- `RATE_LIMIT_EXCEEDED` - Too many requests
|
|
219
|
+
- `SERVER_ERROR` - Server-side error
|
|
220
|
+
- `NETWORK_ERROR` - Network connectivity issue
|
|
221
|
+
- `API_ERROR` - Other API errors
|
|
222
|
+
|
|
223
|
+
### TypeScript Support
|
|
224
|
+
|
|
225
|
+
The SDK is written in TypeScript and includes full type definitions:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
import type {
|
|
229
|
+
PromptInput,
|
|
230
|
+
RunPromptResponse,
|
|
231
|
+
FeedbackSubmission,
|
|
232
|
+
TokenUsage,
|
|
233
|
+
} from '@cobbl-ai/sdk'
|
|
234
|
+
|
|
235
|
+
// Type-safe prompt inputs
|
|
236
|
+
const input: PromptInput = {
|
|
237
|
+
topic: 'AI Safety',
|
|
238
|
+
tone: 'professional',
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Type-safe response handling
|
|
242
|
+
const result: RunPromptResponse = await client.runPrompt('blog_post', input)
|
|
243
|
+
|
|
244
|
+
// Access token usage
|
|
245
|
+
const tokens: TokenUsage = result.tokenUsage
|
|
246
|
+
console.log(`Used ${tokens.totalTokens} tokens`)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Framework Examples
|
|
250
|
+
|
|
251
|
+
#### Express.js
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
import express from 'express'
|
|
255
|
+
import { CobblClient } from '@cobbl-ai/sdk'
|
|
256
|
+
|
|
257
|
+
const app = express()
|
|
258
|
+
const cobbl = new CobblClient({
|
|
259
|
+
apiKey: process.env.PROMPTI_API_KEY,
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
app.post('/api/generate', async (req, res) => {
|
|
263
|
+
try {
|
|
264
|
+
const result = await cobbl.runPrompt('content_generator', req.body)
|
|
265
|
+
res.json({
|
|
266
|
+
output: result.output,
|
|
267
|
+
runId: result.runId,
|
|
268
|
+
})
|
|
269
|
+
} catch (error) {
|
|
270
|
+
res.status(500).json({ error: 'Failed to generate content' })
|
|
271
|
+
}
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
app.post('/api/feedback', async (req, res) => {
|
|
275
|
+
try {
|
|
276
|
+
await cobbl.submitFeedback(req.body)
|
|
277
|
+
res.json({ success: true })
|
|
278
|
+
} catch (error) {
|
|
279
|
+
res.status(500).json({ error: 'Failed to submit feedback' })
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
#### Next.js API Routes
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
// app/api/generate/route.ts
|
|
288
|
+
import { CobblClient } from '@cobbl-ai/sdk'
|
|
289
|
+
import { NextResponse } from 'next/server'
|
|
290
|
+
|
|
291
|
+
const cobbl = new CobblClient({
|
|
292
|
+
apiKey: process.env.PROMPTI_API_KEY!,
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
export async function POST(request: Request) {
|
|
296
|
+
const body = await request.json()
|
|
297
|
+
|
|
298
|
+
try {
|
|
299
|
+
const result = await cobbl.runPrompt('summarizer', {
|
|
300
|
+
text: body.text,
|
|
301
|
+
length: 'short',
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
return NextResponse.json(result)
|
|
305
|
+
} catch (error) {
|
|
306
|
+
return NextResponse.json(
|
|
307
|
+
{ error: 'Failed to generate summary' },
|
|
308
|
+
{ status: 500 }
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
#### React Component (Client-Side via API)
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
'use client'
|
|
318
|
+
|
|
319
|
+
import { useState } from 'react'
|
|
320
|
+
|
|
321
|
+
export default function FeedbackForm({ runId }: { runId: string }) {
|
|
322
|
+
const [feedback, setFeedback] = useState('')
|
|
323
|
+
const [helpful, setHelpful] = useState<'helpful' | 'not_helpful'>('helpful')
|
|
324
|
+
|
|
325
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
326
|
+
e.preventDefault()
|
|
327
|
+
|
|
328
|
+
// Call your API route that uses the SDK
|
|
329
|
+
await fetch('/api/feedback', {
|
|
330
|
+
method: 'POST',
|
|
331
|
+
headers: { 'Content-Type': 'application/json' },
|
|
332
|
+
body: JSON.stringify({
|
|
333
|
+
runId,
|
|
334
|
+
helpful,
|
|
335
|
+
userFeedback: feedback
|
|
336
|
+
})
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
setFeedback('')
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return (
|
|
343
|
+
<form onSubmit={handleSubmit}>
|
|
344
|
+
<div>
|
|
345
|
+
<label>
|
|
346
|
+
<input
|
|
347
|
+
type="radio"
|
|
348
|
+
value="helpful"
|
|
349
|
+
checked={helpful === 'helpful'}
|
|
350
|
+
onChange={(e) => setHelpful(e.target.value as 'helpful')}
|
|
351
|
+
/>
|
|
352
|
+
Helpful
|
|
353
|
+
</label>
|
|
354
|
+
<label>
|
|
355
|
+
<input
|
|
356
|
+
type="radio"
|
|
357
|
+
value="not_helpful"
|
|
358
|
+
checked={helpful === 'not_helpful'}
|
|
359
|
+
onChange={(e) => setHelpful(e.target.value as 'not_helpful')}
|
|
360
|
+
/>
|
|
361
|
+
Not Helpful
|
|
362
|
+
</label>
|
|
363
|
+
</div>
|
|
364
|
+
|
|
365
|
+
<textarea
|
|
366
|
+
value={feedback}
|
|
367
|
+
onChange={(e) => setFeedback(e.target.value)}
|
|
368
|
+
placeholder="Tell us what could be improved..."
|
|
369
|
+
/>
|
|
370
|
+
|
|
371
|
+
<button type="submit">Submit Feedback</button>
|
|
372
|
+
</form>
|
|
373
|
+
)
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## Best Practices
|
|
378
|
+
|
|
379
|
+
### 1. Store Run IDs for Feedback
|
|
380
|
+
|
|
381
|
+
Always save the `runId` returned from `runPrompt()` so users can provide feedback later:
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
// Store in database with your application data
|
|
385
|
+
const result = await client.runPrompt('recommendation', { userId: '123' })
|
|
386
|
+
|
|
387
|
+
await db.recommendations.create({
|
|
388
|
+
userId: '123',
|
|
389
|
+
content: result.output,
|
|
390
|
+
promptRunId: result.runId, // ← Save this!
|
|
391
|
+
})
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### 2. Handle Errors Gracefully
|
|
395
|
+
|
|
396
|
+
Always wrap SDK calls in try-catch blocks and provide fallback behavior:
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
try {
|
|
400
|
+
const result = await client.runPrompt('greeting', { name: userName })
|
|
401
|
+
return result.output
|
|
402
|
+
} catch (error) {
|
|
403
|
+
// Log for debugging
|
|
404
|
+
console.error('Prompt failed:', error)
|
|
405
|
+
|
|
406
|
+
// Return fallback content
|
|
407
|
+
return `Hello, ${userName}!`
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### 3. Use Environment-Specific API Keys
|
|
412
|
+
|
|
413
|
+
Use different API keys for development, staging, and production:
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
const client = new CobblClient({
|
|
417
|
+
apiKey: process.env.PROMPTI_API_KEY,
|
|
418
|
+
})
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### 4. Implement Rate Limiting
|
|
422
|
+
|
|
423
|
+
Add rate limiting on your application side to avoid hitting API limits:
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
import rateLimit from 'express-rate-limit'
|
|
427
|
+
|
|
428
|
+
const limiter = rateLimit({
|
|
429
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
430
|
+
max: 100, // limit each IP to 100 requests per windowMs
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
app.use('/api/generate', limiter)
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
## Development
|
|
437
|
+
|
|
438
|
+
### Building from Source
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
# Install dependencies
|
|
442
|
+
pnpm install
|
|
443
|
+
|
|
444
|
+
# Build the SDK
|
|
445
|
+
pnpm build
|
|
446
|
+
|
|
447
|
+
# Type check
|
|
448
|
+
pnpm typecheck
|
|
449
|
+
|
|
450
|
+
# Clean build artifacts
|
|
451
|
+
pnpm clean
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Project Structure
|
|
455
|
+
|
|
456
|
+
```
|
|
457
|
+
sdk/
|
|
458
|
+
├── src/
|
|
459
|
+
│ ├── client.ts # Main SDK client
|
|
460
|
+
│ ├── errors.ts # Error classes
|
|
461
|
+
│ ├── types.ts # Type definitions
|
|
462
|
+
│ ├── shared-types.ts # Inlined types from shared package
|
|
463
|
+
│ └── index.ts # Public exports
|
|
464
|
+
├── dist/ # Compiled output (created by build)
|
|
465
|
+
│ ├── index.js # CommonJS bundle
|
|
466
|
+
│ ├── index.mjs # ES modules bundle
|
|
467
|
+
│ ├── index.d.ts # TypeScript declarations (CJS)
|
|
468
|
+
│ ├── index.d.mts # TypeScript declarations (ESM)
|
|
469
|
+
│ └── *.map # Source maps
|
|
470
|
+
├── examples/ # Usage examples
|
|
471
|
+
├── tsup.config.ts # Build configuration
|
|
472
|
+
├── package.json
|
|
473
|
+
└── README.md
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### Build System
|
|
477
|
+
|
|
478
|
+
This SDK uses [tsup](https://tsup.egoist.dev/) for building, which provides:
|
|
479
|
+
|
|
480
|
+
- **Zero config bundling** - Works out of the box
|
|
481
|
+
- **Dual package support** - Generates both CJS and ESM
|
|
482
|
+
- **Type bundling** - Inlines all type dependencies
|
|
483
|
+
- **Tree-shaking** - Removes unused code
|
|
484
|
+
- **Source maps** - For debugging
|
|
485
|
+
|
|
486
|
+
### Publishing to npm
|
|
487
|
+
|
|
488
|
+
Before publishing, make sure to:
|
|
489
|
+
|
|
490
|
+
1. Update the version in `package.json`
|
|
491
|
+
2. Update `CHANGELOG.md`
|
|
492
|
+
3. Build and test the package
|
|
493
|
+
|
|
494
|
+
```bash
|
|
495
|
+
# Test the package locally
|
|
496
|
+
pnpm pack
|
|
497
|
+
# This creates cobbl-ai
|
|
498
|
+
-sdk-0.1.0.tgz that you can test
|
|
499
|
+
|
|
500
|
+
# Publish to npm (requires npm login)
|
|
501
|
+
pnpm publish --access public
|
|
502
|
+
|
|
503
|
+
# Or publish with tag (for beta/alpha releases)
|
|
504
|
+
pnpm publish --tag beta --access public
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
## Support
|
|
508
|
+
|
|
509
|
+
- 📧 Email: support@cobbl.ai
|
|
510
|
+
- 🐛 Issues: [GitHub Issues](https://github.com/your-org/cobbl-ai/issues)
|
|
511
|
+
- 📚 Documentation: [docs.cobbl.ai](https://docs.cobbl.ai)
|
|
512
|
+
- 💬 Discord: [Join our community](https://discord.gg/cobbl)
|
|
513
|
+
|
|
514
|
+
## License
|
|
515
|
+
|
|
516
|
+
MIT © Cobbl
|