-
Notifications
You must be signed in to change notification settings - Fork 380
/
Copy pathPromptHelper.ts
131 lines (120 loc) · 3.72 KB
/
PromptHelper.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import { tokenizers, type Tokenizer } from "@llamaindex/env";
import type { SimplePrompt } from "./Prompt.js";
import { SentenceSplitter } from "./TextSplitter.js";
import {
DEFAULT_CHUNK_OVERLAP_RATIO,
DEFAULT_CONTEXT_WINDOW,
DEFAULT_NUM_OUTPUTS,
DEFAULT_PADDING,
} from "./constants.js";
export function getEmptyPromptTxt(prompt: SimplePrompt) {
return prompt({});
}
/**
* Get biggest empty prompt size from a list of prompts.
* Used to calculate the maximum size of inputs to the LLM.
* @param prompts
* @returns
*/
export function getBiggestPrompt(prompts: SimplePrompt[]) {
const emptyPromptTexts = prompts.map(getEmptyPromptTxt);
const emptyPromptLengths = emptyPromptTexts.map((text) => text.length);
const maxEmptyPromptLength = Math.max(...emptyPromptLengths);
const maxEmptyPromptIndex = emptyPromptLengths.indexOf(maxEmptyPromptLength);
return prompts[maxEmptyPromptIndex];
}
/**
* A collection of helper functions for working with prompts.
*/
export class PromptHelper {
contextWindow = DEFAULT_CONTEXT_WINDOW;
numOutput = DEFAULT_NUM_OUTPUTS;
chunkOverlapRatio = DEFAULT_CHUNK_OVERLAP_RATIO;
chunkSizeLimit?: number;
tokenizer: Tokenizer;
separator = " ";
// eslint-disable-next-line max-params
constructor(
contextWindow = DEFAULT_CONTEXT_WINDOW,
numOutput = DEFAULT_NUM_OUTPUTS,
chunkOverlapRatio = DEFAULT_CHUNK_OVERLAP_RATIO,
chunkSizeLimit?: number,
tokenizer?: (text: string) => Uint32Array,
separator = " ",
) {
this.contextWindow = contextWindow;
this.numOutput = numOutput;
this.chunkOverlapRatio = chunkOverlapRatio;
this.chunkSizeLimit = chunkSizeLimit;
this.tokenizer = tokenizers.tokenizer();
this.tokenizer.encode = tokenizer ?? this.tokenizer.encode;
this.separator = separator;
}
/**
* Given a prompt, return the maximum size of the inputs to the prompt.
* @param prompt
* @returns
*/
private getAvailableContextSize(prompt: SimplePrompt) {
const emptyPromptText = getEmptyPromptTxt(prompt);
const promptTokens = this.tokenizer.encode(emptyPromptText);
const numPromptTokens = promptTokens.length;
return this.contextWindow - numPromptTokens - this.numOutput;
}
/**
* Find the maximum size of each chunk given a prompt.
* @param prompt
* @param numChunks
* @param padding
* @returns
*/
private getAvailableChunkSize(
prompt: SimplePrompt,
numChunks = 1,
padding = 5,
) {
const availableContextSize = this.getAvailableContextSize(prompt);
const result = Math.floor(availableContextSize / numChunks) - padding;
if (this.chunkSizeLimit) {
return Math.min(this.chunkSizeLimit, result);
} else {
return result;
}
}
/**
* Creates a text splitter with the correct chunk sizes and overlaps given a prompt.
* @param prompt
* @param numChunks
* @param padding
* @returns
*/
getTextSplitterGivenPrompt(
prompt: SimplePrompt,
numChunks = 1,
padding = DEFAULT_PADDING,
) {
const chunkSize = this.getAvailableChunkSize(prompt, numChunks, padding);
if (chunkSize === 0) {
throw new Error("Got 0 as available chunk size");
}
const chunkOverlap = this.chunkOverlapRatio * chunkSize;
const textSplitter = new SentenceSplitter({ chunkSize, chunkOverlap });
return textSplitter;
}
/**
* Repack resplits the strings based on the optimal text splitter.
* @param prompt
* @param textChunks
* @param padding
* @returns
*/
repack(
prompt: SimplePrompt,
textChunks: string[],
padding = DEFAULT_PADDING,
) {
const textSplitter = this.getTextSplitterGivenPrompt(prompt, 1, padding);
const combinedStr = textChunks.join("\n\n");
return textSplitter.splitText(combinedStr);
}
}