promptgun 1.3.0 → 1.4.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 +151 -39
- package/build/index.d.ts +48 -18
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,29 +1,34 @@
|
|
|
1
1
|
# Promptgun
|
|
2
2
|
|
|
3
|
-
The simplest, most advanced LLM prompting and agent library
|
|
3
|
+
The simplest, most advanced LLM prompting and agent client library for OpenAI:
|
|
4
|
+
|
|
5
|
+
- **Type-safe** structured output, even when streaming
|
|
6
|
+
- **Function calling / tools** with inline or reusable definitions
|
|
7
|
+
- **Feedback loops**, response iteration based on response check
|
|
8
|
+
- **Conversations**: trivally do multiple prompts while retaining context
|
|
9
|
+
- **Strict JSON mode**: a prompting superpower for guaranteed schema compliance
|
|
10
|
+
- **Image generation**: easy-to-use, well-documented, type-safe
|
|
11
|
+
- **Live model docs right in your IDE**, hourly-updated from OpenAI website
|
|
12
|
+
- Future: multi-provider support (drop me a message if you need it)
|
|
13
|
+
|
|
14
|
+
For example:
|
|
4
15
|
|
|
5
16
|
```typescript
|
|
6
17
|
const restaurants = ai
|
|
7
18
|
.chat("Recommend 3 restaurants in Paris perfect for today's weather")
|
|
8
|
-
.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const response = await fetch(`https://api.weather.com/v1/current?location=${location}`)
|
|
14
|
-
return response.json()
|
|
15
|
-
}
|
|
16
|
-
})
|
|
17
|
-
)
|
|
18
|
-
.getArray(spec => spec.object(o => o
|
|
19
|
+
.tool('get-weather', s => s.string(), async loc => {
|
|
20
|
+
const res = await fetch(`https://api.weather.com/v1/current?location=${loc}`)
|
|
21
|
+
return res.json()
|
|
22
|
+
})
|
|
23
|
+
.getArray(s => s.object(o => o
|
|
19
24
|
.hasString('name')
|
|
20
25
|
.hasString('cuisine')
|
|
21
26
|
.hasString('weatherReason', 'Why this restaurant fits today\'s weather')
|
|
22
27
|
))
|
|
23
28
|
|
|
24
|
-
// Process each restaurant as it streams in
|
|
25
29
|
for await (const restaurant of restaurants) {
|
|
26
|
-
|
|
30
|
+
// do stuff with a type-safe restaurant object,
|
|
31
|
+
// streamed as soon as they come in
|
|
27
32
|
}
|
|
28
33
|
```
|
|
29
34
|
|
|
@@ -65,6 +70,7 @@ for await (const parsedPartialJson of parsedPartialJsonStream) {
|
|
|
65
70
|
```
|
|
66
71
|
|
|
67
72
|
### Data output - single
|
|
73
|
+
Simply put `await` in front of your prompt to make it a single output rather than a streamed one:
|
|
68
74
|
```typescript
|
|
69
75
|
const restaurants /* type: {name: string, address?: string}[] */ = await ai
|
|
70
76
|
.chat('Give 5 top restaurants in London')
|
|
@@ -134,22 +140,21 @@ await conversation
|
|
|
134
140
|
)
|
|
135
141
|
```
|
|
136
142
|
|
|
137
|
-
## Response
|
|
143
|
+
## Response Iteration with `.check()`
|
|
138
144
|
|
|
139
|
-
|
|
145
|
+
Iterate on AI responses by providing feedback for corrections. The AI will automatically retry with your feedback until validation passes or max attempts are reached.
|
|
140
146
|
|
|
141
147
|
### Basic Example
|
|
142
148
|
```typescript
|
|
143
149
|
const password = await ai
|
|
144
150
|
.chat("Generate a secure password")
|
|
145
|
-
.getObject(o => o.hasString('
|
|
146
|
-
.check(
|
|
147
|
-
|
|
148
|
-
if (pwd
|
|
149
|
-
if (!/[
|
|
150
|
-
if (!/[0-9]/.test(pwd)) return 'Password must contain a number';
|
|
151
|
+
.getObject(o => o.hasString('pwd'))
|
|
152
|
+
.check(({pwd}) => {
|
|
153
|
+
if (pwd.length < 8) return 'Password must be at least 8 characters'
|
|
154
|
+
if (!/[A-Z]/.test(pwd)) return 'Password must contain uppercase letter'
|
|
155
|
+
if (!/[0-9]/.test(pwd)) return 'Password must contain a number'
|
|
151
156
|
// Return nothing/null/undefined when validation passes
|
|
152
|
-
}, 5)
|
|
157
|
+
}, 5) // Allow 5 attempts (default: 10)
|
|
153
158
|
```
|
|
154
159
|
|
|
155
160
|
### Multiple Checks
|
|
@@ -160,17 +165,18 @@ const user = await ai
|
|
|
160
165
|
.getObject(o => o
|
|
161
166
|
.hasString('email')
|
|
162
167
|
.hasNumber('age')
|
|
163
|
-
.hasString('country')
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
168
|
+
.hasString('country')
|
|
169
|
+
)
|
|
170
|
+
.check(({email}) => {
|
|
171
|
+
if (!email.includes('@')) {
|
|
172
|
+
return 'Email must be valid'
|
|
167
173
|
}
|
|
168
174
|
}, 3)
|
|
169
|
-
.check(
|
|
170
|
-
if (
|
|
171
|
-
return 'Age must be between 0 and 150'
|
|
175
|
+
.check(({age}) => {
|
|
176
|
+
if (age < 0 || age > 150) {
|
|
177
|
+
return 'Age must be between 0 and 150'
|
|
172
178
|
}
|
|
173
|
-
}, 5)
|
|
179
|
+
}, 5)
|
|
174
180
|
```
|
|
175
181
|
|
|
176
182
|
### Error Handling
|
|
@@ -179,20 +185,20 @@ You can return errors as strings or throw them:
|
|
|
179
185
|
const result = await ai
|
|
180
186
|
.chat("Calculate the answer")
|
|
181
187
|
.getObject(o => o.hasNumber('result'))
|
|
182
|
-
.check(
|
|
188
|
+
.check(({result}) => {
|
|
183
189
|
// Option 1: Return error string
|
|
184
|
-
if (
|
|
190
|
+
if (result < 0) return 'Result must be positive'
|
|
185
191
|
|
|
186
192
|
// Option 2: Throw error
|
|
187
|
-
if (
|
|
188
|
-
throw new Error('Result too large')
|
|
193
|
+
if (result > 1000) {
|
|
194
|
+
throw new Error('Result too large')
|
|
189
195
|
}
|
|
190
196
|
|
|
191
197
|
// Option 3: Return array of errors
|
|
192
|
-
const errors = []
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
})
|
|
198
|
+
const errors = []
|
|
199
|
+
if (result === 0) errors.push('Result cannot be zero')
|
|
200
|
+
return errors
|
|
201
|
+
})
|
|
196
202
|
```
|
|
197
203
|
|
|
198
204
|
### Important Notes
|
|
@@ -201,6 +207,112 @@ const result = await ai
|
|
|
201
207
|
- **Text responses**: `.check()` works with both JSON (`.getObject()`, `.getArray()`) and text responses
|
|
202
208
|
- **Feedback loop**: Failed validations are sent back to the AI as feedback, allowing it to learn and correct
|
|
203
209
|
|
|
210
|
+
## Tools
|
|
211
|
+
|
|
212
|
+
### Inline Tool Definitions
|
|
213
|
+
Tools can be added directly using `.tool()`:
|
|
214
|
+
```typescript
|
|
215
|
+
const result = await ai
|
|
216
|
+
.chat("What's the weather in Paris?")
|
|
217
|
+
.tool('get-weather', s => s.string(), async location => {
|
|
218
|
+
const res = await fetch(`https://api.weather.com/v1/current?location=${location}`)
|
|
219
|
+
return res.json()
|
|
220
|
+
})
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Reusable Tool Definitions
|
|
224
|
+
For tools used across multiple prompts, define them once with `aiTool()`:
|
|
225
|
+
```typescript
|
|
226
|
+
const getWeather = aiTool('get-weather', s => s.string(), async location => {
|
|
227
|
+
const res = await fetch(`https://api.weather.com/v1/current?location=${location}`)
|
|
228
|
+
return res.json()
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
// Use in multiple prompts
|
|
232
|
+
const parisWeather = await ai
|
|
233
|
+
.chat("What's the weather in Paris?")
|
|
234
|
+
.tools(getWeather)
|
|
235
|
+
|
|
236
|
+
const londonWeather = await ai
|
|
237
|
+
.chat("What's the weather in London?")
|
|
238
|
+
.tools(getWeather)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
You can pass multiple tools using `.tools()`:
|
|
242
|
+
```typescript
|
|
243
|
+
await ai
|
|
244
|
+
.chat("Plan a trip")
|
|
245
|
+
.tools(getWeather, getFlights, getHotels)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Or add them one by one with `.tool()`:
|
|
249
|
+
```typescript
|
|
250
|
+
await ai
|
|
251
|
+
.chat("Plan a trip")
|
|
252
|
+
.tool(getWeather)
|
|
253
|
+
.tool(getFlights)
|
|
254
|
+
.tool(getHotels)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Strict JSON Mode
|
|
258
|
+
|
|
259
|
+
Enable OpenAI's strict JSON mode for guaranteed type-safe responses using `.strict()`:
|
|
260
|
+
```typescript
|
|
261
|
+
const user = await ai
|
|
262
|
+
.chat("Get user info")
|
|
263
|
+
.getObject(o => o
|
|
264
|
+
.hasString('name')
|
|
265
|
+
.hasNumber('age')
|
|
266
|
+
)
|
|
267
|
+
.strict()
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Important: Nullable vs Optional Properties
|
|
271
|
+
|
|
272
|
+
In strict mode, you **cannot use optional properties** (TypeScript's `?`). Instead, use nullable types:
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// ❌ Not allowed in strict mode - optional properties
|
|
276
|
+
const user = await ai
|
|
277
|
+
.chat("Get user info")
|
|
278
|
+
.getObject(o => o
|
|
279
|
+
.hasString('name')
|
|
280
|
+
.canHaveString('nickname') // ❌ This is {nickname?: string}
|
|
281
|
+
)
|
|
282
|
+
.strict()
|
|
283
|
+
|
|
284
|
+
// ✅ Allowed in strict mode - nullable properties
|
|
285
|
+
const user = await ai
|
|
286
|
+
.chat("Get user info")
|
|
287
|
+
.getObject(o => o
|
|
288
|
+
.hasString('name')
|
|
289
|
+
.has('nickname', s => s.string().orNull()) // ✅ This is {nickname: string | null}
|
|
290
|
+
)
|
|
291
|
+
.strict()
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**The difference:**
|
|
295
|
+
- **Optional** (`canHaveString`): Property may or may not exist → `{nickname?: string}` → Not allowed in strict mode
|
|
296
|
+
- **Nullable** (`string().orNull()`): Property always exists but value can be null → `{nickname: string | null}` → Allowed in strict mode
|
|
297
|
+
|
|
298
|
+
## Model Selection
|
|
299
|
+
|
|
300
|
+
Switch models using either a string or the `AiModel` enum:
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
// Using a string
|
|
304
|
+
const result = await ai
|
|
305
|
+
.chat('Hello')
|
|
306
|
+
.model('gpt-5')
|
|
307
|
+
|
|
308
|
+
// Using the enum - get in-place documentation!
|
|
309
|
+
const result = await ai
|
|
310
|
+
.chat('Hello')
|
|
311
|
+
.model(AiModel.GPT_5)
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**Why use the enum?** The `AiModel` enum provides in-place documentation for each model, updated hourly from the OpenAI website. This gives you real-time information about capabilities, context windows, and pricing right in your IDE.
|
|
315
|
+
|
|
204
316
|
## Other options
|
|
205
317
|
Using the flex tier
|
|
206
318
|
```typescript
|
package/build/index.d.ts
CHANGED
|
@@ -1125,6 +1125,17 @@ export declare const AiModel: {
|
|
|
1125
1125
|
|
|
1126
1126
|
export declare type AiModelId = (typeof AiModel)[keyof typeof AiModel];
|
|
1127
1127
|
|
|
1128
|
+
export declare function aiTool<T>(name: string, parameters: (spec: EmptyTypeSpec<unknown>) => TypeSpec<T>, fn: ToolFunction<T>): ToolDefinition<T>;
|
|
1129
|
+
|
|
1130
|
+
export declare function aiTool<T>(name: string, description: string, parameters: (spec: EmptyTypeSpec<unknown>) => TypeSpec<T>, fn: ToolFunction<T>): ToolDefinition<T>;
|
|
1131
|
+
|
|
1132
|
+
export declare function aiTool<T>(config: {
|
|
1133
|
+
name: string;
|
|
1134
|
+
description?: string;
|
|
1135
|
+
parameters: (spec: EmptyTypeSpec<unknown>) => TypeSpec<T>;
|
|
1136
|
+
function: ToolFunction<T>;
|
|
1137
|
+
}): ToolDefinition<T>;
|
|
1138
|
+
|
|
1128
1139
|
export declare type AnyJson = string | null | boolean | number | JsonArray | JsonObject;
|
|
1129
1140
|
|
|
1130
1141
|
export declare type ApiKeyChain = {
|
|
@@ -1246,17 +1257,16 @@ export declare class BasicPrompt<PSArgs> implements AsyncGenerator<string>, Prom
|
|
|
1246
1257
|
* This error is propagated immediately without retry delays.
|
|
1247
1258
|
*
|
|
1248
1259
|
* @example
|
|
1249
|
-
*
|
|
1260
|
+
* Iterate on password generation until requirements are met:
|
|
1250
1261
|
* ```ts
|
|
1251
1262
|
* const result = await ai
|
|
1252
1263
|
* .chat("Generate a secure password")
|
|
1253
|
-
* .getObject(o => o.hasString('
|
|
1254
|
-
* .check(
|
|
1255
|
-
*
|
|
1256
|
-
* if (pwd
|
|
1257
|
-
* if (!/[
|
|
1258
|
-
*
|
|
1259
|
-
* }, 5);
|
|
1264
|
+
* .getObject(o => o.hasString('pwd'))
|
|
1265
|
+
* .check(({pwd}) => {
|
|
1266
|
+
* if (pwd.length < 8) return 'Password must be at least 8 characters'
|
|
1267
|
+
* if (!/[A-Z]/.test(pwd)) return 'Password must contain uppercase letter'
|
|
1268
|
+
* if (!/[0-9]/.test(pwd)) return 'Password must contain a number'
|
|
1269
|
+
* }, 5)
|
|
1260
1270
|
* ```
|
|
1261
1271
|
*/
|
|
1262
1272
|
check(check: CheckCallback<string>, attempts?: number): BasicPrompt<PSArgs>;
|
|
@@ -1280,6 +1290,14 @@ export declare class BasicPrompt<PSArgs> implements AsyncGenerator<string>, Prom
|
|
|
1280
1290
|
getObject<T_obj extends {}>(properties: (arg: TypeMemberSpec<{}>) => TypeMemberSpec<T_obj>): JsonPrompt<PSArgs, T_obj>;
|
|
1281
1291
|
getArray<T_arr extends AnyJson>(element: (arg: EmptyTypeSpec<unknown>) => TypeSpec<T_arr>): JsonArrayPrompt<PSArgs, T_arr>;
|
|
1282
1292
|
getAny(): JsonPrompt<PSArgs, AnyJson>;
|
|
1293
|
+
tool<T>(name: string, parameters: (spec: EmptyTypeSpec<unknown>) => TypeSpec<T>, fn: ToolFunction<T>): BasicPrompt<PSArgs>;
|
|
1294
|
+
tool<T>(name: string, description: string, parameters: (spec: EmptyTypeSpec<unknown>) => TypeSpec<T>, fn: ToolFunction<T>): BasicPrompt<PSArgs>;
|
|
1295
|
+
tool<T>(config: {
|
|
1296
|
+
name: string;
|
|
1297
|
+
description?: string;
|
|
1298
|
+
parameters: (spec: EmptyTypeSpec<unknown>) => TypeSpec<T>;
|
|
1299
|
+
function: ToolFunction<T>;
|
|
1300
|
+
}): BasicPrompt<PSArgs>;
|
|
1283
1301
|
tools(tools: ToolDefinition[]): this;
|
|
1284
1302
|
tools(...tools: (ToolDefinition | undefined)[]): this;
|
|
1285
1303
|
private getResponseAsString;
|
|
@@ -1321,12 +1339,15 @@ export declare type DeepPartial<T> = T extends object ? {
|
|
|
1321
1339
|
[P in keyof T]?: DeepPartial<T[P]>;
|
|
1322
1340
|
} : T;
|
|
1323
1341
|
|
|
1324
|
-
|
|
1342
|
+
/**
|
|
1343
|
+
* @deprecated Use {@link aiTool} instead
|
|
1344
|
+
*/
|
|
1345
|
+
export declare function defineTool<T>(nameOrConfig: string | {
|
|
1325
1346
|
name: string;
|
|
1326
1347
|
description?: string;
|
|
1327
1348
|
parameters: (spec: EmptyTypeSpec<unknown>) => TypeSpec<T>;
|
|
1328
1349
|
function: ToolFunction<T>;
|
|
1329
|
-
}): ToolDefinition<T>;
|
|
1350
|
+
}, parametersOrDescription?: string | ((spec: EmptyTypeSpec<unknown>) => TypeSpec<T>), fnOrParameters?: ToolFunction<T> | ((spec: EmptyTypeSpec<unknown>) => TypeSpec<T>), maybeFn?: ToolFunction<T>): ToolDefinition<T>;
|
|
1330
1351
|
|
|
1331
1352
|
export declare type DeveloperMessage = {
|
|
1332
1353
|
system?: undefined;
|
|
@@ -1613,21 +1634,22 @@ export declare class JsonPrompt<PSArgs, Json extends AnyJson = AnyJson> extends
|
|
|
1613
1634
|
* This error is propagated immediately without retry delays.
|
|
1614
1635
|
*
|
|
1615
1636
|
* @example
|
|
1616
|
-
*
|
|
1637
|
+
* Iterate on JSON response until valid:
|
|
1617
1638
|
* ```ts
|
|
1618
1639
|
* const user = await ai
|
|
1619
1640
|
* .chat("Get user info for john@example.com")
|
|
1620
1641
|
* .getObject(o => o
|
|
1621
1642
|
* .hasString('email')
|
|
1622
|
-
* .hasNumber('age')
|
|
1623
|
-
*
|
|
1624
|
-
*
|
|
1625
|
-
*
|
|
1643
|
+
* .hasNumber('age')
|
|
1644
|
+
* )
|
|
1645
|
+
* .check(({email, age}) => {
|
|
1646
|
+
* if (!email.includes('@')) {
|
|
1647
|
+
* return 'Email must be valid'
|
|
1626
1648
|
* }
|
|
1627
|
-
* if (
|
|
1628
|
-
* return 'Age must be between 0 and 150'
|
|
1649
|
+
* if (age < 0 || age > 150) {
|
|
1650
|
+
* return 'Age must be between 0 and 150'
|
|
1629
1651
|
* }
|
|
1630
|
-
* }, 3)
|
|
1652
|
+
* }, 3)
|
|
1631
1653
|
* ```
|
|
1632
1654
|
*/
|
|
1633
1655
|
check(check: CheckCallback<Json>, attempts?: number): JsonPrompt<PSArgs, Json>;
|
|
@@ -1639,6 +1661,14 @@ export declare class JsonPrompt<PSArgs, Json extends AnyJson = AnyJson> extends
|
|
|
1639
1661
|
protected getResponse(): Promise<Json>;
|
|
1640
1662
|
private _responseAsStringPromise?;
|
|
1641
1663
|
protected getResponseAsString(): Promise<string>;
|
|
1664
|
+
tool<T>(name: string, parameters: (spec: EmptyTypeSpec<unknown>) => TypeSpec<T>, fn: ToolFunction<T>): JsonPrompt<PSArgs, Json>;
|
|
1665
|
+
tool<T>(name: string, description: string, parameters: (spec: EmptyTypeSpec<unknown>) => TypeSpec<T>, fn: ToolFunction<T>): JsonPrompt<PSArgs, Json>;
|
|
1666
|
+
tool<T>(config: {
|
|
1667
|
+
name: string;
|
|
1668
|
+
description?: string;
|
|
1669
|
+
parameters: (spec: EmptyTypeSpec<unknown>) => TypeSpec<T>;
|
|
1670
|
+
function: ToolFunction<T>;
|
|
1671
|
+
}): JsonPrompt<PSArgs, Json>;
|
|
1642
1672
|
tools(tools: ToolDefinition[]): this;
|
|
1643
1673
|
tools(...tools: (ToolDefinition | undefined)[]): this;
|
|
1644
1674
|
protected _getResponseAsString(): Promise<string>;
|