ai-functions
Version:
Core AI primitives for building intelligent applications
134 lines • 4.43 kB
JavaScript
/**
* Tagged template literal utilities
*
* Provides support for tagged template syntax across all AI functions:
* - fn`prompt ${variable}` - template literal syntax
* - Objects/arrays auto-convert to YAML
* - Options chaining: fn`prompt`({ model: '...' })
*
* @packageDocumentation
*/
import { stringify } from 'yaml';
/**
* Parse a tagged template literal into a prompt string
* Objects and arrays are converted to YAML for readability
*/
export function parseTemplate(strings, ...values) {
return strings.reduce((result, str, i) => {
const value = values[i];
if (value === undefined) {
return result + str;
}
// Convert objects/arrays to YAML
if (typeof value === 'object' && value !== null) {
const yaml = stringify(value).trim();
return result + str + '\n' + yaml;
}
// Primitives use toString()
return result + str + String(value);
}, '');
}
/**
* Create a chainable promise that supports both await and options chaining
*/
export function createChainablePromise(executor, defaultOptions) {
// Create the base promise with a no-op catch to prevent unhandled rejection
// The actual error will still be thrown when .then() or await is called
const basePromise = executor(defaultOptions);
// Prevent unhandled rejection warning by attaching a no-op catch
// This doesn't swallow the error - it just prevents the warning when the
// promise is not immediately awaited (e.g., when chaining options)
basePromise.catch(() => { });
// Create a function that accepts options
const chainable = ((options) => {
return executor({ ...defaultOptions, ...options });
});
// Make it thenable
chainable.then = basePromise.then.bind(basePromise);
chainable.catch = basePromise.catch.bind(basePromise);
chainable.finally = basePromise.finally.bind(basePromise);
return chainable;
}
/**
* Create a function that supports both tagged templates and regular calls
*/
export function createTemplateFunction(handler) {
function templateFn(promptOrStrings, ...args) {
// Tagged template literal
if (Array.isArray(promptOrStrings) && 'raw' in promptOrStrings) {
const prompt = parseTemplate(promptOrStrings, ...args);
return createChainablePromise((options) => handler(prompt, options));
}
// Regular function call
return handler(promptOrStrings, args[0]);
}
return templateFn;
}
/**
* Add batch capability to a template function
*/
export function withBatch(fn, batchHandler) {
const batchable = fn;
batchable.batch = batchHandler;
return batchable;
}
/**
* Create an async iterable from a streaming generator
*/
export function createAsyncIterable(items) {
if (Array.isArray(items)) {
return {
async *[Symbol.asyncIterator]() {
for (const item of items) {
yield item;
}
},
};
}
return {
[Symbol.asyncIterator]: items,
};
}
export function createStreamableList(getItems, streamItems) {
const promise = getItems();
const asyncIterator = streamItems
? streamItems
: async function* () {
const items = await promise;
for (const item of items) {
yield item;
}
};
// Create a proper Promise-like object with async iterator
const result = Object.create(null);
// Add Promise methods
Object.defineProperty(result, 'then', {
value: promise.then.bind(promise),
writable: false,
enumerable: false,
});
Object.defineProperty(result, 'catch', {
value: promise.catch.bind(promise),
writable: false,
enumerable: false,
});
Object.defineProperty(result, 'finally', {
value: promise.finally.bind(promise),
writable: false,
enumerable: false,
});
// Add async iterator
Object.defineProperty(result, Symbol.asyncIterator, {
value: asyncIterator,
writable: false,
enumerable: false,
});
// Add Symbol.toStringTag for Promise compatibility
Object.defineProperty(result, Symbol.toStringTag, {
value: 'Promise',
writable: false,
enumerable: false,
});
return result;
}
//# sourceMappingURL=template.js.map