@openai/agents-core
Version:
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.
328 lines • 12.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.normalizeStructuredToolOutputs = normalizeStructuredToolOutputs;
exports.convertStructuredToolOutputToInputItem = convertStructuredToolOutputToInputItem;
const base64_1 = require("../utils/base64.js");
const inlineData_1 = require("../utils/inlineData.js");
/**
* Accepts whatever the tool returned and attempts to coerce it into the structured protocol
* shapes we expose to downstream model adapters (input_text/input_image/input_file). Tools are
* allowed to return either a single structured object or an array of them; anything else falls
* back to the legacy string pipeline.
*/
function normalizeStructuredToolOutputs(output) {
if (Array.isArray(output)) {
if (output.length === 0) {
return null;
}
const structured = [];
for (const item of output) {
const normalized = normalizeStructuredToolOutput(item);
if (!normalized) {
return null;
}
structured.push(normalized);
}
return structured;
}
const normalized = normalizeStructuredToolOutput(output);
return normalized ? [normalized] : null;
}
/**
* Best-effort normalization of a single tool output item. If the object already matches the
* protocol shape we simply cast it; otherwise we copy the recognised fields into the canonical
* structure. Returning null lets the caller know we should revert to plain-string handling.
*/
function normalizeStructuredToolOutput(value) {
if (!isRecord(value)) {
return null;
}
const type = value.type;
if (type === 'text' && typeof value.text === 'string') {
const output = { type: 'text', text: value.text };
if (isRecord(value.providerData)) {
output.providerData = value.providerData;
}
return output;
}
if (type === 'image') {
const output = { type: 'image' };
let imageString;
let imageFileId;
const fallbackImageMediaType = (0, inlineData_1.getInlineMediaType)(value);
const imageField = value.image;
if (typeof imageField === 'string' && imageField.length > 0) {
imageString = imageField;
}
else if (isRecord(imageField)) {
const imageObj = imageField;
const inlineMediaType = (0, inlineData_1.getInlineMediaType)(imageObj) ?? fallbackImageMediaType;
if (isNonEmptyString(imageObj.url)) {
imageString = imageObj.url;
}
else if (isNonEmptyString(imageObj.data)) {
imageString = (0, inlineData_1.formatInlineData)(imageObj.data, inlineMediaType);
}
else if (imageObj.data instanceof Uint8Array &&
imageObj.data.length > 0) {
imageString = (0, inlineData_1.formatInlineData)(imageObj.data, inlineMediaType);
}
if (!imageString) {
const candidateId = (isNonEmptyString(imageObj.fileId) && imageObj.fileId) ||
(isNonEmptyString(imageObj.id) && imageObj.id) ||
undefined;
if (candidateId) {
imageFileId = candidateId;
}
}
}
if (!imageString &&
typeof value.imageUrl === 'string' &&
value.imageUrl.length > 0) {
imageString = value.imageUrl;
}
if (!imageFileId &&
typeof value.fileId === 'string' &&
value.fileId.length > 0) {
imageFileId = value.fileId;
}
if (!imageString &&
typeof value.data === 'string' &&
value.data.length > 0) {
imageString = fallbackImageMediaType
? (0, inlineData_1.formatInlineData)(value.data, fallbackImageMediaType)
: value.data;
}
else if (!imageString &&
value.data instanceof Uint8Array &&
value.data.length > 0) {
imageString = (0, inlineData_1.formatInlineData)(value.data, fallbackImageMediaType);
}
if (typeof value.detail === 'string' && value.detail.length > 0) {
output.detail = value.detail;
}
if (imageString) {
output.image = imageString;
}
else if (imageFileId) {
output.image = { fileId: imageFileId };
}
else {
return null;
}
if (isRecord(value.providerData)) {
output.providerData = value.providerData;
}
return output;
}
if (type === 'file') {
const fileValue = normalizeFileValue(value);
if (!fileValue) {
return null;
}
const output = { type: 'file', file: fileValue };
if (isRecord(value.providerData)) {
output.providerData = value.providerData;
}
return output;
}
return null;
}
/**
* Translates the normalized tool output into the protocol `input_*` items. This is the last hop
* before we hand the data to model-specific adapters, so we generate the exact schema expected by
* the protocol definitions.
*/
function convertStructuredToolOutputToInputItem(output) {
if (output.type === 'text') {
const result = {
type: 'input_text',
text: output.text,
};
if (output.providerData) {
result.providerData = output.providerData;
}
return result;
}
if (output.type === 'image') {
const result = { type: 'input_image' };
if (typeof output.detail === 'string' && output.detail.length > 0) {
result.detail = output.detail;
}
if (typeof output.image === 'string' && output.image.length > 0) {
result.image = output.image;
}
else if (isRecord(output.image)) {
const imageObj = output.image;
const inlineMediaType = (0, inlineData_1.getInlineMediaType)(imageObj);
if (isNonEmptyString(imageObj.url)) {
result.image = imageObj.url;
}
else if (isNonEmptyString(imageObj.data)) {
result.image =
inlineMediaType && !imageObj.data.startsWith('data:')
? asDataUrl(imageObj.data, inlineMediaType)
: imageObj.data;
}
else if (imageObj.data instanceof Uint8Array &&
imageObj.data.length > 0) {
const base64 = (0, base64_1.encodeUint8ArrayToBase64)(imageObj.data);
result.image = asDataUrl(base64, inlineMediaType);
}
else {
const referencedId = (isNonEmptyString(imageObj.fileId) && imageObj.fileId) ||
(isNonEmptyString(imageObj.id) && imageObj.id) ||
undefined;
if (referencedId) {
result.image = { id: referencedId };
}
}
}
if (output.providerData) {
result.providerData = output.providerData;
}
return result;
}
if (output.type === 'file') {
const result = { type: 'input_file' };
const fileValue = output.file;
if (typeof fileValue === 'string') {
result.file = fileValue;
}
else if (fileValue && typeof fileValue === 'object') {
const record = fileValue;
if ('data' in record && record.data) {
const mediaType = record.mediaType ?? 'text/plain';
if (typeof record.data === 'string') {
result.file = asDataUrl(record.data, mediaType);
}
else {
const base64 = (0, base64_1.encodeUint8ArrayToBase64)(record.data);
result.file = asDataUrl(base64, mediaType);
}
}
else if (typeof record.url === 'string' && record.url.length > 0) {
result.file = { url: record.url };
}
else {
const referencedId = (typeof record.id === 'string' &&
record.id.length > 0 &&
record.id) ||
(typeof record.fileId === 'string' && record.fileId.length > 0
? record.fileId
: undefined);
if (referencedId) {
result.file = { id: referencedId };
}
}
if (typeof record.filename === 'string' && record.filename.length > 0) {
result.filename = record.filename;
}
}
if (output.providerData) {
result.providerData = output.providerData;
}
return result;
}
const exhaustiveCheck = output;
return exhaustiveCheck;
}
function normalizeFileValue(value) {
const directFile = value.file;
if (typeof directFile === 'string' && directFile.length > 0) {
return directFile;
}
const normalizedObject = normalizeFileObjectCandidate(directFile);
if (normalizedObject) {
return normalizedObject;
}
const legacyValue = normalizeLegacyFileValue(value);
if (legacyValue) {
return legacyValue;
}
return null;
}
function normalizeFileObjectCandidate(value) {
if (!isRecord(value)) {
return null;
}
if ('data' in value && value.data !== undefined) {
const dataValue = value.data;
const hasStringData = typeof dataValue === 'string' && dataValue.length > 0;
const hasBinaryData = dataValue instanceof Uint8Array && dataValue.length > 0;
if (!hasStringData && !hasBinaryData) {
return null;
}
if (!isNonEmptyString(value.mediaType) ||
!isNonEmptyString(value.filename)) {
return null;
}
return {
data: typeof dataValue === 'string' ? dataValue : new Uint8Array(dataValue),
mediaType: value.mediaType,
filename: value.filename,
};
}
if (isNonEmptyString(value.url)) {
const result = { url: value.url };
if (isNonEmptyString(value.filename)) {
result.filename = value.filename;
}
return result;
}
const referencedId = (isNonEmptyString(value.id) && value.id) ||
(isNonEmptyString(value.fileId) && value.fileId);
if (referencedId) {
const result = { id: referencedId };
if (isNonEmptyString(value.filename)) {
result.filename = value.filename;
}
return result;
}
return null;
}
function normalizeLegacyFileValue(value) {
const filename = typeof value.filename === 'string' && value.filename.length > 0
? value.filename
: undefined;
const mediaType = typeof value.mediaType === 'string' && value.mediaType.length > 0
? value.mediaType
: undefined;
if (typeof value.fileData === 'string' && value.fileData.length > 0) {
if (!mediaType || !filename) {
return null;
}
return { data: value.fileData, mediaType, filename };
}
if (value.fileData instanceof Uint8Array && value.fileData.length > 0) {
if (!mediaType || !filename) {
return null;
}
return { data: new Uint8Array(value.fileData), mediaType, filename };
}
if (typeof value.fileUrl === 'string' && value.fileUrl.length > 0) {
const result = { url: value.fileUrl };
if (filename) {
result.filename = filename;
}
return result;
}
if (typeof value.fileId === 'string' && value.fileId.length > 0) {
const result = { id: value.fileId };
if (filename) {
result.filename = filename;
}
return result;
}
return null;
}
function isRecord(value) {
return typeof value === 'object' && value !== null;
}
function isNonEmptyString(value) {
return typeof value === 'string' && value.length > 0;
}
function asDataUrl(base64, mediaType) {
return mediaType ? `data:${mediaType};base64,${base64}` : base64;
}
//# sourceMappingURL=toolOutputNormalization.js.map