From 9869f1f261408b0543962ea350b5aef739770155 Mon Sep 17 00:00:00 2001 From: Jeremy Moseley Date: Thu, 18 Jul 2024 20:40:50 -0700 Subject: [PATCH 1/3] Handle base64 encoded content headers --- content.ts | 13 +++++++++++-- package.json | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/content.ts b/content.ts index c88cf05..f398a4a 100644 --- a/content.ts +++ b/content.ts @@ -194,6 +194,7 @@ export class Content { title, prompt, stream, + base64encodeContentHeaders: true, }); const reader = res.body!.getReader(); const decoder = new TextDecoder("utf-8"); @@ -207,7 +208,8 @@ export class Content { res.headers.get("publishedVersion"), ); const commands: ContentCommand[] = JSON.parse( - res.headers.get("commands") || "[]", + Buffer.from(res.headers.get("commands") || "", "base64").toString() || + "[]", ); const readableStream = new Readable({ @@ -317,6 +319,7 @@ export class Content { const res = await this.apiClient.POST(`/content/${this._id}/refine`, { prompt, stream: true, + base64encodeContentHeaders: true, }); const reader = res.body!.getReader(); const decoder = new TextDecoder("utf-8"); @@ -325,8 +328,13 @@ export class Content { const createdAt = res.headers.get("createdAt") || ""; const userEmail = res.headers.get("userEmail") || undefined; const commands: ContentCommand[] = JSON.parse( - res.headers.get("commands") || "[]", + Buffer.from(res.headers.get("commands") || "", "base64").toString() || + "[]", ); + const title = Buffer.from( + res.headers.get("title") || "", + "base64", + ).toString(); const status = res.headers.get("status") as ContentStatus; const publishedVersion: number | undefined = numberOrUndefined( res.headers.get("publishedVersion"), @@ -337,6 +345,7 @@ export class Content { this._userEmail = userEmail; this._status = status; this._publishedVersion = publishedVersion; + this._title = title; const readableStream = new Readable({ read() {}, diff --git a/package.json b/package.json index 1798e45..504dec2 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "url": "https://github.com/cortexclick/cortex-sdk", "type": "git" }, - "version": "0.0.4", + "version": "0.0.5", "type": "module", "main": "index.js", "scripts": { From 2ed583d0090b6b773ef6a4e811a6c4ab6ec1552a Mon Sep 17 00:00:00 2001 From: Jeremy Moseley Date: Fri, 19 Jul 2024 15:54:20 -0700 Subject: [PATCH 2/3] Parse the metadata from the stream instead of headers. --- chat.ts | 4 +- content.ts | 93 +++++++++++++++++++++------------------------- utils/streaming.ts | 10 +++-- 3 files changed, 51 insertions(+), 56 deletions(-) diff --git a/chat.ts b/chat.ts index de1213f..d084116 100644 --- a/chat.ts +++ b/chat.ts @@ -150,7 +150,7 @@ export class Chat { decoder, readableStream, opts.statusStream, - ).then((content) => { + ).then(([content]) => { const messages: Message[] = [ { role: "user", @@ -296,7 +296,7 @@ export class Chat { decoder, readableStream, opts.statusStream, - ).then((content) => { + ).then(([content]) => { this.messages.push( { role: "user", diff --git a/content.ts b/content.ts index f398a4a..9758b97 100644 --- a/content.ts +++ b/content.ts @@ -89,6 +89,18 @@ export type ContentListOptions = { cortexName?: string; }; +export type ContentMetadata = { + id: string; + title: string; + version: number; + commands: ContentCommand[]; + cortex: string; + createdAt: string; + userEmail: string; + status: ContentStatus; + publishedVersion: number | null; +}; + export class Content { get id() { return this._id; @@ -194,46 +206,37 @@ export class Content { title, prompt, stream, - base64encodeContentHeaders: true, + noContentInHeaders: true, }); const reader = res.body!.getReader(); const decoder = new TextDecoder("utf-8"); - const id: string = res.headers.get("id") || ""; - const version: number = parseInt(res.headers.get("version") || "0"); - const userEmail = res.headers.get("userEmail") || undefined; - const createdAt: string = res.headers.get("createdAt") || ""; - const status: ContentStatus = res.headers.get("status") as ContentStatus; - const publishedVersion: number | undefined = numberOrUndefined( - res.headers.get("publishedVersion"), - ); - const commands: ContentCommand[] = JSON.parse( - Buffer.from(res.headers.get("commands") || "", "base64").toString() || - "[]", - ); - const readableStream = new Readable({ read() {}, }); - const contentPromise = processStream( + const contentPromise = processStream( reader, decoder, readableStream, opts.statusStream, - ).then((content) => { + ).then(([content, metadata]) => { + if (!metadata) { + throw new Error("Metadata not found in stream"); + } + return new Content( client, - id, - title, + metadata.id, + metadata.title, content, - commands, - version, - createdAt, - status, - cortex.name, - userEmail, - publishedVersion, + metadata.commands, + metadata.version, + metadata.createdAt, + metadata.status, + metadata.cortex, + metadata.userEmail, + metadata.publishedVersion || undefined, ); }); @@ -319,45 +322,33 @@ export class Content { const res = await this.apiClient.POST(`/content/${this._id}/refine`, { prompt, stream: true, - base64encodeContentHeaders: true, + noContentInHeaders: true, }); const reader = res.body!.getReader(); const decoder = new TextDecoder("utf-8"); - const version: number = parseInt(res.headers.get("version") || "0"); - const createdAt = res.headers.get("createdAt") || ""; - const userEmail = res.headers.get("userEmail") || undefined; - const commands: ContentCommand[] = JSON.parse( - Buffer.from(res.headers.get("commands") || "", "base64").toString() || - "[]", - ); - const title = Buffer.from( - res.headers.get("title") || "", - "base64", - ).toString(); - const status = res.headers.get("status") as ContentStatus; - const publishedVersion: number | undefined = numberOrUndefined( - res.headers.get("publishedVersion"), - ); - this._version = version; - this._commands = commands; - this._createdAt = createdAt; - this._userEmail = userEmail; - this._status = status; - this._publishedVersion = publishedVersion; - this._title = title; - const readableStream = new Readable({ read() {}, }); - const contentPromise = processStream( + const contentPromise = processStream( reader, decoder, readableStream, opts.statusStream, - ).then((content) => { + ).then(([content, metadata]) => { + if (!metadata) { + throw new Error("Metadata not found in stream"); + } + this._content = content; + this._version = metadata.version; + this._commands = metadata.commands; + this._createdAt = metadata.createdAt; + this._userEmail = metadata.userEmail; + this._status = metadata.status; + this._publishedVersion = metadata.publishedVersion || undefined; + this._title = metadata.title; return this; }); diff --git a/utils/streaming.ts b/utils/streaming.ts index 1f1300f..f1fbc9c 100644 --- a/utils/streaming.ts +++ b/utils/streaming.ts @@ -1,15 +1,17 @@ import { Readable } from "stream"; -export async function processStream( +export async function processStream>( reader: ReadableStreamDefaultReader, decoder: TextDecoder, contentStream: Readable, statusStream?: Readable, -): Promise { +): Promise<[string, Metadata | undefined]> { let buffer = ""; let fullContent = ""; let isStatusStreamOpen = true; + let metadata: Metadata | undefined = undefined; + const processNextChunk = async (): Promise => { const { done, value } = await reader.read(); if (done) { @@ -43,6 +45,8 @@ export async function processStream( // t:s = status message else if (json.messageType === "status" && statusStream) { statusStream.push(line + "\n"); + } else if (json.messageType === "metadata") { + metadata = json.metadata; } } catch (e) { console.error("Error parsing JSON:", e); @@ -57,5 +61,5 @@ export async function processStream( contentStream.emit("error", error); }); - return fullContent; + return [fullContent, metadata]; } From c8380a09ec87051e5f5bdb847999e83c7a5778bd Mon Sep 17 00:00:00 2001 From: Jeremy Moseley Date: Sat, 20 Jul 2024 09:19:52 -0700 Subject: [PATCH 3/3] Extract the right parameter for metadata. --- utils/streaming.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/streaming.ts b/utils/streaming.ts index f1fbc9c..fa57489 100644 --- a/utils/streaming.ts +++ b/utils/streaming.ts @@ -46,7 +46,7 @@ export async function processStream>( else if (json.messageType === "status" && statusStream) { statusStream.push(line + "\n"); } else if (json.messageType === "metadata") { - metadata = json.metadata; + metadata = json.data; } } catch (e) { console.error("Error parsing JSON:", e);