This commit is contained in:
Ian Arawjo 2025-02-22 23:16:29 -05:00
parent fed59d06c0
commit 2ae5b33592
10 changed files with 57 additions and 33 deletions

View File

@ -78,8 +78,8 @@ const displayJoinedTexts = (
return textInfos.map((info, idx) => {
const llm_name =
typeof info !== "string"
? typeof info.llm === "string"
? info.llm
? typeof info.llm === "string" || typeof info.llm === "number"
? StringLookup.get(info.llm)
: info.llm?.name
: "";
const ps = (
@ -326,7 +326,7 @@ const JoinNode: React.FC<JoinNodeProps> = ({ data, id }) => {
let joined_texts: (TemplateVarInfo | string)[] = [];
const [groupedRespsByLLM, nonLLMRespGroup] = groupResponsesBy(
resp_objs,
(r) => (typeof r.llm === "string" ? r.llm : r.llm?.key),
(r) => (typeof r.llm === "string" || typeof r.llm === "number" ? StringLookup.get(r.llm) : r.llm?.key),
);
// eslint-disable-next-line
Object.entries(groupedRespsByLLM).forEach(([llm_key, resp_objs]) => {

View File

@ -487,7 +487,7 @@ const PromptNode: React.FC<PromptNodeProps> = ({
(info: TemplateVarInfo) => {
// Add to unique LLMs list, if necessary
if (
typeof info?.llm !== "string" &&
(typeof info?.llm !== "string" && typeof info?.llm !== "number") &&
info?.llm?.name !== undefined &&
!llm_names.has(info.llm.name)
) {
@ -508,7 +508,7 @@ const PromptNode: React.FC<PromptNodeProps> = ({
// Append any present system message retroactively as the first message in the chat history:
if (
typeof info?.llm !== "string" &&
(typeof info?.llm !== "string" && typeof info?.llm !== "number") &&
typeof info?.llm?.settings?.system_msg === "string" &&
updated_chat_hist[0].role !== "system"
)
@ -521,7 +521,7 @@ const PromptNode: React.FC<PromptNodeProps> = ({
messages: updated_chat_hist,
fill_history: info.fill_history ?? {},
metavars: info.metavars ?? {},
llm: typeof info?.llm === "string" ? info.llm : info?.llm?.name,
llm: (typeof info?.llm === "string" || typeof info?.llm === "number") ? (StringLookup.get(info.llm) ?? "(LLM lookup failed)") : info?.llm?.name,
uid: uuid(),
};
},
@ -969,8 +969,8 @@ Soft failing by replacing undefined with empty strings.`,
// Add a meta var to keep track of which LLM produced this response
o.metavars[llm_metavar_key] =
typeof resp_obj.llm === "string"
? resp_obj.llm
(typeof resp_obj.llm === "string" || typeof resp_obj.llm === "number")
? (StringLookup.get(resp_obj.llm) ?? "(LLM lookup failed)")
: resp_obj.llm.name;
return o;
}),

View File

@ -28,7 +28,7 @@ import {
} from "./backend/utils";
import { fromMarkdown } from "mdast-util-from-markdown";
import StorageCache from "./backend/cache";
import StorageCache, { StringLookup } from "./backend/cache";
import { ResponseBox } from "./ResponseBoxes";
import { Root, RootContent } from "mdast";
import { Dict, TemplateVarInfo } from "./backend/typing";
@ -289,7 +289,7 @@ const SplitNode: React.FC<SplitNodeProps> = ({ data, id }) => {
.map((resp_obj: TemplateVarInfo | string) => {
if (typeof resp_obj === "string")
return splitText(resp_obj, formatting, true);
const texts = splitText(resp_obj?.text ?? "", formatting, true);
const texts = splitText(StringLookup.get(resp_obj?.text) ?? "", formatting, true);
if (texts !== undefined && texts.length >= 1)
return texts.map(
(t: string) =>

View File

@ -225,8 +225,8 @@ const VisNode: React.FC<VisNodeProps> = ({ data, id }) => {
const get_llm = (resp_obj: LLMResponse) => {
if (selectedLLMGroup === "LLM")
return typeof resp_obj.llm === "string"
? resp_obj.llm
return typeof resp_obj.llm === "string" || typeof resp_obj.llm === "number"
? (StringLookup.get(resp_obj.llm) ?? "(LLM lookup failed)")
: resp_obj.llm?.name;
else return resp_obj.metavars[selectedLLMGroup] as string;
};

View File

@ -17,7 +17,6 @@ test("merge response objects", () => {
// Merging two response objects
const A: RawLLMResponseObject = {
responses: ["x", "y", "z"],
raw_response: ["x", "y", "z"],
prompt: "this is a test",
query: {},
llm: NativeLLM.OpenAI_ChatGPT,
@ -27,7 +26,6 @@ test("merge response objects", () => {
};
const B: RawLLMResponseObject = {
responses: ["a", "b", "c"],
raw_response: { B: "B" },
prompt: "this is a test 2",
query: {},
llm: NativeLLM.OpenAI_ChatGPT,
@ -40,7 +38,6 @@ test("merge response objects", () => {
expect(JSON.stringify(C.responses)).toBe(
JSON.stringify(["x", "y", "z", "a", "b", "c"]),
);
expect(C.raw_response).toHaveLength(4);
expect(Object.keys(C.vars)).toHaveLength(2);
expect(Object.keys(C.vars)).toContain("varB1");
expect(Object.keys(C.metavars)).toHaveLength(1);

View File

@ -260,6 +260,7 @@ function filterVarsByLLM(vars: PromptVarsDict, llm_key: string): Dict {
typeof v === "number" ||
v?.llm === undefined ||
typeof v.llm === "string" ||
typeof v.llm === "number" ||
v.llm.key === llm_key,
);
});

View File

@ -164,7 +164,7 @@ export class StringLookup {
return s.stringToIndex.get(str)!; // Return existing index
}
// Add new string
// Add new string to the table
const index = s.indexToString.length;
s.indexToString.push(str);
s.stringToIndex.set(str, index);
@ -179,6 +179,30 @@ export class StringLookup {
return s.indexToString[index]; // O(1) lookup
}
/**
* Transforms a Dict by interning all strings encountered, up to 1 level of depth,
* and returning the modified Dict with the strings as hash indexes instead.
*/
public static internDict(d: Dict, inplace?: boolean): Dict {
const newDict = inplace ? d : {} as Dict;
const entries = Object.entries(d);
for (const [key, value] of entries) {
if (typeof value === "string") {
newDict[key] = StringLookup.intern(value);
} else if (Array.isArray(value) && value.every(v => typeof v === "string")) {
newDict[key] = value.map(v => StringLookup.intern(v));
} else if (typeof value === "object" && value !== null) {
newDict[key] = StringLookup.internDict(value as Dict, inplace);
} else {
if (!inplace)
newDict[key] = value;
}
}
return newDict as Map<string, unknown>;
}
/** Serializes interned strings and their mappings */
public static toJSON() {
const s = StringLookup.getInstance();

View File

@ -21,7 +21,7 @@ import {
repairCachedResponses,
compressBase64Image,
} from "./utils";
import StorageCache from "./cache";
import StorageCache, { StringLookup } from "./cache";
import { UserForcedPrematureExit } from "./errors";
import { typecastSettingsDict } from "../ModelSettingSchemas";
@ -131,7 +131,6 @@ export class PromptPipeline {
query: query ?? {},
uid: uuid(),
responses: extracted_resps,
raw_response: contains_imgs ? {} : response ?? {}, // don't double-store images
llm,
vars: mergeDicts(info, chat_history?.fill_history) ?? {},
metavars: mergeDicts(metavars, chat_history?.metavars) ?? {},
@ -147,6 +146,9 @@ export class PromptPipeline {
resp_obj,
past_resp_obj,
) as RawLLMResponseObject;
// Hash strings present in the response object, to improve performance
StringLookup.internDict(resp_obj, true);
// Save the current state of cache'd responses to a JSON file
// NOTE: We do this to save money --in case something breaks between calls, can ensure we got the data!
@ -291,7 +293,6 @@ export class PromptPipeline {
query: cached_resp.query,
uid: cached_resp.uid ?? uuid(),
responses: extracted_resps.slice(0, n),
raw_response: cached_resp.raw_response,
llm: cached_resp.llm || NativeLLM.OpenAI_ChatGPT,
// We want to use the new info, since 'vars' could have changed even though
// the prompt text is the same (e.g., "this is a tool -> this is a {x} where x='tool'")
@ -300,6 +301,10 @@ export class PromptPipeline {
};
if (chat_history !== undefined)
resp.chat_history = chat_history.messages;
// Hash any strings in the cache'd response object, if any
StringLookup.internDict(resp, true);
yield resp;
continue;
}

View File

@ -185,7 +185,7 @@ export interface BaseLLMResponseObject {
/** Any associated metavariables. */
metavars: Dict<StringOrHash>;
/** The LLM to query (usually a dict of settings) */
llm: string | LLMSpec;
llm: StringOrHash | LLMSpec;
/** Optional: The chat history to pass the LLM */
chat_history?: ChatHistory;
}
@ -195,7 +195,8 @@ export interface RawLLMResponseObject extends BaseLLMResponseObject {
// A snapshot of the exact query (payload) sent to the LLM's API
query: Dict;
// The raw JSON response from the LLM
raw_response: Dict;
// NOTE: This is now deprecated since it wastes precious storage space.
// raw_response: Dict;
// Extracted responses (1 or more) from raw_response
responses: LLMResponseData[];
// Token lengths (if given)
@ -246,10 +247,10 @@ export interface TemplateVarInfo {
image?: StringOrHash; // base-64 encoding
fill_history?: Dict<StringOrHash>;
metavars?: Dict<StringOrHash>;
associate_id?: string;
associate_id?: StringOrHash;
prompt?: StringOrHash;
uid?: ResponseUID;
llm?: string | LLMSpec;
llm?: StringOrHash | LLMSpec;
chat_history?: ChatHistory;
}

View File

@ -48,7 +48,7 @@ import {
fromModelId,
ChatMessage as BedrockChatMessage,
} from "@mirai73/bedrock-fm";
import StorageCache from "./cache";
import StorageCache, { StringLookup } from "./cache";
import Compressor from "compressorjs";
import { Models } from "@mirai73/bedrock-fm/lib/bedrock";
@ -1904,13 +1904,8 @@ export function merge_response_objs(
else if (!resp_obj_A && resp_obj_B) return resp_obj_B;
resp_obj_A = resp_obj_A as RawLLMResponseObject; // required by typescript
resp_obj_B = resp_obj_B as RawLLMResponseObject;
let raw_resp_A = resp_obj_A.raw_response;
let raw_resp_B = resp_obj_B.raw_response;
if (!Array.isArray(raw_resp_A)) raw_resp_A = [raw_resp_A];
if (!Array.isArray(raw_resp_B)) raw_resp_B = [raw_resp_B];
const res: RawLLMResponseObject = {
responses: resp_obj_A.responses.concat(resp_obj_B.responses),
raw_response: raw_resp_A.concat(raw_resp_B),
prompt: resp_obj_B.prompt,
query: resp_obj_B.query,
llm: resp_obj_B.llm,
@ -2065,7 +2060,7 @@ export const stripLLMDetailsFromResponses = (
): LLMResponse[] =>
resps.map((r) => ({
...r,
llm: typeof r?.llm === "string" ? r?.llm : r?.llm?.name ?? "undefined",
llm: (typeof r?.llm === "string" || typeof r?.llm === "number" ? StringLookup.get(r?.llm) : r?.llm?.name) ?? "undefined",
}));
// NOTE: The typing is purposefully general since we are trying to cast to an expected format.
@ -2114,6 +2109,7 @@ export const tagMetadataWithLLM = (input_data: LLMResponsesByVarDict) => {
typeof r === "number" ||
!r?.llm ||
typeof r.llm === "string" ||
typeof r.llm === "number" ||
!r.llm.key
)
return r;
@ -2130,14 +2126,14 @@ export const extractLLMLookup = (
(StringOrHash | TemplateVarInfo | BaseLLMResponseObject | LLMResponse)[]
>,
) => {
const llm_lookup: Dict<string | LLMSpec> = {};
const llm_lookup: Dict<StringOrHash | LLMSpec> = {};
Object.values(input_data).forEach((resp_objs) => {
resp_objs.forEach((r) => {
const llm_name =
typeof r === "string" || typeof r === "number"
? undefined
: !r.llm || typeof r.llm === "string"
? r.llm
: !r.llm || typeof r.llm === "string" || typeof r.llm === "number"
? StringLookup.get(r.llm)
: r.llm.key;
if (
typeof r === "string" || typeof r === "number" ||