✅ Fix chat interface - restore continuous conversation flow
🎯 Major improvements to MissionControl component: - Always keep input field visible and functional after AI responses - Auto-clear input after submitting questions for better UX - Add dynamic visual indicators (first question vs follow-up) - Improve response layout with clear separation and hints - Enable proper chat-like experience for continuous learning 🌟 Additional enhancements: - Better language-specific messaging throughout interface - Clearer visual hierarchy between input and response areas - Intuitive flow that guides users to ask follow-up questions - Maintains responsive design and accessibility 🔧 Technical changes: - Enhanced MissionControl state management - Improved component layout and styling - Better TypeScript integration across components - Updated tsconfig for stricter type checking
This commit is contained in:
158
node_modules/openai/uploads.mjs
generated
vendored
158
node_modules/openai/uploads.mjs
generated
vendored
@@ -1,2 +1,158 @@
|
||||
export * from "./core/uploads.mjs";
|
||||
import { FormData, File, getMultipartRequestOptions, isFsReadStream, } from "./_shims/index.mjs";
|
||||
export { fileFromPath } from "./_shims/index.mjs";
|
||||
export const isResponseLike = (value) => value != null &&
|
||||
typeof value === 'object' &&
|
||||
typeof value.url === 'string' &&
|
||||
typeof value.blob === 'function';
|
||||
export const isFileLike = (value) => value != null &&
|
||||
typeof value === 'object' &&
|
||||
typeof value.name === 'string' &&
|
||||
typeof value.lastModified === 'number' &&
|
||||
isBlobLike(value);
|
||||
/**
|
||||
* The BlobLike type omits arrayBuffer() because @types/node-fetch@^2.6.4 lacks it; but this check
|
||||
* adds the arrayBuffer() method type because it is available and used at runtime
|
||||
*/
|
||||
export const isBlobLike = (value) => value != null &&
|
||||
typeof value === 'object' &&
|
||||
typeof value.size === 'number' &&
|
||||
typeof value.type === 'string' &&
|
||||
typeof value.text === 'function' &&
|
||||
typeof value.slice === 'function' &&
|
||||
typeof value.arrayBuffer === 'function';
|
||||
export const isUploadable = (value) => {
|
||||
return isFileLike(value) || isResponseLike(value) || isFsReadStream(value);
|
||||
};
|
||||
/**
|
||||
* Helper for creating a {@link File} to pass to an SDK upload method from a variety of different data formats
|
||||
* @param value the raw content of the file. Can be an {@link Uploadable}, {@link BlobLikePart}, or {@link AsyncIterable} of {@link BlobLikePart}s
|
||||
* @param {string=} name the name of the file. If omitted, toFile will try to determine a file name from bits if possible
|
||||
* @param {Object=} options additional properties
|
||||
* @param {string=} options.type the MIME type of the content
|
||||
* @param {number=} options.lastModified the last modified timestamp
|
||||
* @returns a {@link File} with the given properties
|
||||
*/
|
||||
export async function toFile(value, name, options) {
|
||||
// If it's a promise, resolve it.
|
||||
value = await value;
|
||||
// If we've been given a `File` we don't need to do anything
|
||||
if (isFileLike(value)) {
|
||||
return value;
|
||||
}
|
||||
if (isResponseLike(value)) {
|
||||
const blob = await value.blob();
|
||||
name || (name = new URL(value.url).pathname.split(/[\\/]/).pop() ?? 'unknown_file');
|
||||
// we need to convert the `Blob` into an array buffer because the `Blob` class
|
||||
// that `node-fetch` defines is incompatible with the web standard which results
|
||||
// in `new File` interpreting it as a string instead of binary data.
|
||||
const data = isBlobLike(blob) ? [(await blob.arrayBuffer())] : [blob];
|
||||
return new File(data, name, options);
|
||||
}
|
||||
const bits = await getBytes(value);
|
||||
name || (name = getName(value) ?? 'unknown_file');
|
||||
if (!options?.type) {
|
||||
const type = bits[0]?.type;
|
||||
if (typeof type === 'string') {
|
||||
options = { ...options, type };
|
||||
}
|
||||
}
|
||||
return new File(bits, name, options);
|
||||
}
|
||||
async function getBytes(value) {
|
||||
let parts = [];
|
||||
if (typeof value === 'string' ||
|
||||
ArrayBuffer.isView(value) || // includes Uint8Array, Buffer, etc.
|
||||
value instanceof ArrayBuffer) {
|
||||
parts.push(value);
|
||||
}
|
||||
else if (isBlobLike(value)) {
|
||||
parts.push(await value.arrayBuffer());
|
||||
}
|
||||
else if (isAsyncIterableIterator(value) // includes Readable, ReadableStream, etc.
|
||||
) {
|
||||
for await (const chunk of value) {
|
||||
parts.push(chunk); // TODO, consider validating?
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error(`Unexpected data type: ${typeof value}; constructor: ${value?.constructor
|
||||
?.name}; props: ${propsForError(value)}`);
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
function propsForError(value) {
|
||||
const props = Object.getOwnPropertyNames(value);
|
||||
return `[${props.map((p) => `"${p}"`).join(', ')}]`;
|
||||
}
|
||||
function getName(value) {
|
||||
return (getStringFromMaybeBuffer(value.name) ||
|
||||
getStringFromMaybeBuffer(value.filename) ||
|
||||
// For fs.ReadStream
|
||||
getStringFromMaybeBuffer(value.path)?.split(/[\\/]/).pop());
|
||||
}
|
||||
const getStringFromMaybeBuffer = (x) => {
|
||||
if (typeof x === 'string')
|
||||
return x;
|
||||
if (typeof Buffer !== 'undefined' && x instanceof Buffer)
|
||||
return String(x);
|
||||
return undefined;
|
||||
};
|
||||
const isAsyncIterableIterator = (value) => value != null && typeof value === 'object' && typeof value[Symbol.asyncIterator] === 'function';
|
||||
export const isMultipartBody = (body) => body && typeof body === 'object' && body.body && body[Symbol.toStringTag] === 'MultipartBody';
|
||||
/**
|
||||
* Returns a multipart/form-data request if any part of the given request body contains a File / Blob value.
|
||||
* Otherwise returns the request as is.
|
||||
*/
|
||||
export const maybeMultipartFormRequestOptions = async (opts) => {
|
||||
if (!hasUploadableValue(opts.body))
|
||||
return opts;
|
||||
const form = await createForm(opts.body);
|
||||
return getMultipartRequestOptions(form, opts);
|
||||
};
|
||||
export const multipartFormRequestOptions = async (opts) => {
|
||||
const form = await createForm(opts.body);
|
||||
return getMultipartRequestOptions(form, opts);
|
||||
};
|
||||
export const createForm = async (body) => {
|
||||
const form = new FormData();
|
||||
await Promise.all(Object.entries(body || {}).map(([key, value]) => addFormValue(form, key, value)));
|
||||
return form;
|
||||
};
|
||||
const hasUploadableValue = (value) => {
|
||||
if (isUploadable(value))
|
||||
return true;
|
||||
if (Array.isArray(value))
|
||||
return value.some(hasUploadableValue);
|
||||
if (value && typeof value === 'object') {
|
||||
for (const k in value) {
|
||||
if (hasUploadableValue(value[k]))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const addFormValue = async (form, key, value) => {
|
||||
if (value === undefined)
|
||||
return;
|
||||
if (value == null) {
|
||||
throw new TypeError(`Received null for "${key}"; to pass null in FormData, you must use the string 'null'`);
|
||||
}
|
||||
// TODO: make nested formats configurable
|
||||
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
||||
form.append(key, String(value));
|
||||
}
|
||||
else if (isUploadable(value)) {
|
||||
const file = await toFile(value);
|
||||
form.append(key, file);
|
||||
}
|
||||
else if (Array.isArray(value)) {
|
||||
await Promise.all(value.map((entry) => addFormValue(form, key + '[]', entry)));
|
||||
}
|
||||
else if (typeof value === 'object') {
|
||||
await Promise.all(Object.entries(value).map(([name, prop]) => addFormValue(form, `${key}[${name}]`, prop)));
|
||||
}
|
||||
else {
|
||||
throw new TypeError(`Invalid value given to form, expected a string, number, boolean, object, Array, File or Blob but got ${value} instead`);
|
||||
}
|
||||
};
|
||||
//# sourceMappingURL=uploads.mjs.map
|
||||
Reference in New Issue
Block a user