@pstdio/prompt-utils
Tiny utilities for building high-quality LLM prompts and working with JSON streams. This package provides a comprehensive set of tools for creating well-formatted prompts, handling incomplete JSON data, and working with various data transformations commonly needed in LLM workflows.
Install
npm i @pstdio/prompt-utils
Overview
The @pstdio/prompt-utils
package consists of three main categories of utilities:
- Template Functions: For building clean, well-formatted prompts
- JSON Utilities: For handling streaming and partial JSON data
- Helper Utilities: For common data processing tasks
Template Functions
These utilities help you build clean, properly formatted prompts with consistent indentation and spacing.
prompt
A template tag for creating multiline prompts with automatic indentation cleanup.
Features:
- Strips common indentation from all lines
- Trims outer blank lines
- Collapses 3+ consecutive blank lines into 2
- Preserves relative indentation within the content
import { prompt } from "@pstdio/prompt-utils";
const instructions = prompt`
You are a helpful assistant.
Please follow these guidelines:
- Be concise and clear
- Provide examples when helpful
Always maintain a professional tone.
`;
console.log(instructions);
// Output:
// You are a helpful assistant.
//
// Please follow these guidelines:
// - Be concise and clear
// - Provide examples when helpful
//
// Always maintain a professional tone.
Variable Interpolation:
const topic = "machine learning";
const complexity = "beginner";
const prompt = prompt`
Explain ${topic} concepts for a ${complexity} audience.
Use simple examples and avoid jargon.
`;
line
A template tag for creating single-line content by normalizing whitespace and newlines.
import { line } from "@pstdio/prompt-utils";
const title = line`
Summarize the following
customer feedback and
identify key themes
`;
console.log(title);
// Output: "Summarize the following customer feedback and identify key themes"
listAnd
and listOr
Oxford-style list joiners for creating natural language lists.
import { listAnd, listOr } from "@pstdio/prompt-utils";
// listAnd examples
console.log(listAnd(["apple"])); // "apple"
console.log(listAnd(["apple", "banana"])); // "apple and banana"
console.log(listAnd(["apple", "banana", "cherry"])); // "apple, banana and cherry"
// listOr examples
console.log(listOr(["red", "green", "blue"])); // "red, green or blue"
// Single line option for multi-line templates
const items = ["task A", "task B", "task C"];
const instruction = prompt`
Complete ${listAnd(items, true)} before proceeding.
Each task should be done carefully.
`;
section
Wraps content in labeled XML-like tags while preserving inner formatting.
import { section, prompt } from "@pstdio/prompt-utils";
const context = prompt`
The user is asking about TypeScript.
They are a beginner programmer.
`;
const instructions = prompt`
Provide a clear explanation.
Use simple examples.
`;
const fullPrompt = prompt`
${section("CONTEXT", context)}
${section("INSTRUCTIONS", instructions)}
`;
console.log(fullPrompt);
// Output:
// <CONTEXT>
// The user is asking about TypeScript.
// They are a beginner programmer.
// </CONTEXT>
//
// <INSTRUCTIONS>
// Provide a clear explanation.
// Use simple examples.
// </INSTRUCTIONS>
JSON Utilities
These utilities help handle JSON data, especially in streaming scenarios where data might be incomplete.
parseJSONStream
Attempts to parse potentially incomplete JSON streams by trying progressively shorter prefixes and auto-closing unclosed structures.
import { parseJSONStream } from "@pstdio/prompt-utils";
// Complete JSON - works normally
parseJSONStream('{"name": "John", "age": 30}');
// Returns: { name: "John", age: 30 }
// Incomplete object - salvages valid parts
parseJSONStream('{"name": "John", "age": 30, "email"');
// Returns: { name: "John", age: 30 }
// Incomplete nested object
parseJSONStream('{"user": {"id": 123, "name": "John"');
// Returns: { user: { id: 123 } }
// Incomplete array
parseJSONStream("[1, 2, 3,");
// Returns: [1, 2, 3]
// Invalid JSON
parseJSONStream("not valid json");
// Returns: null
// Empty string
parseJSONStream("");
// Returns: null
Use Cases:
- Processing streaming LLM responses that output JSON
- Handling network timeouts during JSON transfer
- Debugging partial JSON data
getSchema
Generates a simple JSON-like schema from any JavaScript value.
import { getSchema } from "@pstdio/prompt-utils";
// Primitive types
getSchema("hello"); // { type: "string" }
getSchema(42); // { type: "number" }
getSchema(true); // { type: "boolean" }
getSchema(null); // { type: "null" }
// Arrays
getSchema([1, 2, 3]);
// { type: "array", items: { type: "number" } }
getSchema([]);
// { type: "array", items: { type: "any" } }
// Objects
getSchema({
name: "John",
age: 30,
active: true,
});
// {
// type: "object",
// properties: {
// name: { type: "string" },
// age: { type: "number" },
// active: { type: "boolean" }
// }
// }
// Complex nested structures
getSchema({
users: [{ id: 1, name: "Alice", tags: ["admin", "active"] }],
});
// {
// type: "object",
// properties: {
// users: {
// type: "array",
// items: {
// type: "object",
// properties: {
// id: { type: "number" },
// name: { type: "string" },
// tags: { type: "array", items: { type: "string" } }
// }
// }
// }
// }
// }
safeParse
JSON.parse that gracefully falls back to the original string on failure.
import { safeParse } from "@pstdio/prompt-utils";
// Valid JSON
safeParse<{ name: string }>('{"name": "John"}');
// Returns: { name: "John" }
// Invalid JSON - returns original string
safeParse("not json");
// Returns: "not json"
// TypeScript support
interface User {
id: number;
name: string;
}
const result = safeParse<User>('{"id": 123, "name": "Alice"}');
// Type: User | string
safeStringify
Stable JSON stringification with BigInt support and numeric string normalization.
import { safeStringify } from "@pstdio/prompt-utils";
const data = {
id: BigInt(123456789012345),
count: "42", // String that looks like a number
name: "test",
quote: 'He said "hello"',
};
safeStringify(data);
// Returns consistent string representation
// BigInts converted to numbers
// Numeric strings converted to numbers
// Quotes handled safely
Helper Utilities
hashString
Fast FNV-1a 32-bit hash function that works in both browser and Node.js.
import { hashString } from "@pstdio/prompt-utils";
hashString("hello world");
// Returns: "779a65e7" (hex string)
hashString("different input");
// Returns: "1a2b3c4d" (different hex string)
Use Cases:
- Creating cache keys
- Generating deterministic IDs
- Content deduplication
shortUID
Generates short, LLM-friendly unique identifiers.
import { shortUID } from "@pstdio/prompt-utils";
shortUID();
// Returns: "r4k2m8" (6 random chars with default "r" prefix)
shortUID("task");
// Returns: "task7n3x1q"
shortUID("user");
// Returns: "user9m4k2p"
Features:
- Uses LLM-friendly alphabet (no confusing characters like 0/O, 1/l)
- Short length (6 characters + prefix)
- Customizable prefix for categorization
Real-World Examples
Building Complex Prompts
import { prompt, section, listAnd } from "@pstdio/prompt-utils";
function createCodeReviewPrompt(language: string, concerns: string[], codeSnippet: string) {
const context = prompt`
Language: ${language}
Review Focus: ${listAnd(concerns)}
`;
const instructions = prompt`
1. Analyze the code for the specified concerns
2. Provide specific, actionable feedback
3. Suggest improvements with examples
4. Rate overall code quality (1-10)
`;
const code = section("CODE", codeSnippet);
return prompt`
${section("CONTEXT", context)}
${section("INSTRUCTIONS", instructions)}
${code}
`;
}
const reviewPrompt = createCodeReviewPrompt(
"TypeScript",
["performance", "security", "maintainability"],
`
function processUser(data: any) {
return data.users.map(u => u.name.toUpperCase());
}
`,
);
Processing Streaming JSON from LLMs
import { parseJSONStream } from "@pstdio/prompt-utils";
// Simulate processing a streaming response
function processStreamingResponse(chunks: string[]) {
let accumulated = "";
for (const chunk of chunks) {
accumulated += chunk;
// Try to parse the current accumulated data
const parsed = parseJSONStream(accumulated);
if (parsed) {
console.log("Successfully parsed:", parsed);
// Process the valid JSON...
}
}
}
// Example chunks from streaming response
processStreamingResponse([
'{"analysis": {',
'"sentiment": "positive",',
'"confidence": 0.8',
// Stream might end abruptly here
]);
Schema-Driven Validation
import { getSchema, safeStringify } from "@pstdio/prompt-utils";
function validateAndDescribeData(data: unknown) {
const schema = getSchema(data);
return {
data: safeStringify(data),
schema: safeStringify(schema),
summary: `Data contains ${schema.type} with ${
schema.properties
? Object.keys(schema.properties).length + " properties"
: schema.items
? "items of type " + schema.items.type
: "primitive value"
}`,
};
}
Best Practices
Template Functions
- Use
prompt
for multi-line content that needs clean formatting - Use
line
for titles, labels, and single-line content - Use
section
to create clearly delimited blocks in your prompts - Combine with
listAnd
/listOr
for natural language lists
JSON Utilities
- Use
parseJSONStream
when dealing with potentially incomplete data - Use
safeParse
when you want to handle both JSON and plain text - Use
getSchema
to understand the structure of dynamic data - Use
safeStringify
for consistent serialization
Helper Utilities
- Use
hashString
for creating deterministic keys or IDs - Use
shortUID
when you need human/LLM-readable unique identifiers
TypeScript Support
All functions include full TypeScript definitions:
// Template tags
function prompt(strings: TemplateStringsArray, ...values: any[]): string;
function line(strings: TemplateStringsArray, ...values: any[]): string;
// List functions
function listAnd(items: string[], singleLine?: boolean): string;
function listOr(items: string[], singleLine?: boolean): string;
// Section function
function section(label: string, body: string): string;
// JSON utilities
function parseJSONStream(input: string): any | null;
function getSchema(input: any): Schema;
function safeParse<T = object>(str: string): T | string;
function safeStringify(result: object): string;
// Helper utilities
function hashString(str: string): string;
function shortUID(prefix?: string): string;
ESM-only package - use import
syntax.