Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ellipse on token limit exceed and flex layout options in PromptTemplateString #694

Merged
merged 7 commits into from
Sep 6, 2024
38 changes: 37 additions & 1 deletion docs/genaisrc/genaiscript.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 37 additions & 1 deletion genaisrc/genaiscript.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions packages/core/src/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -469,17 +469,17 @@
await generator(ctx, structuredClone(messages))
const node = ctx.node
checkCancelled(cancellationToken)
// expand template
const { errors, prompt } = await renderPromptNode(
const { errors, userPrompt } = await renderPromptNode(
options.model,
node,
{
trace,
}
)
if (prompt?.trim().length) {
trace.detailsFenced(`💬 message`, prompt, "markdown")
messages.push({ role: "user", content: prompt })
if (userPrompt?.trim().length) {
trace.detailsFenced(`💬 message`, userPrompt, "markdown")
messages.push({ role: "user", content: userPrompt })

Check failure on line 482 in packages/core/src/chat.ts

View workflow job for this annotation

GitHub Actions / build

The variable 'prompt' has been renamed to 'userPrompt'. This could potentially break any code that relies on the original 'prompt' variable.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable 'prompt' has been renamed to 'userPrompt'. This could potentially break any code that relies on the original 'prompt' variable.

generated by pr-review-commit variable_renaming

needsNewTurn = true
} else trace.item("no message")
if (errors?.length) {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,4 @@ export const CONSOLE_COLOR_WARNING = 95
export const CONSOLE_COLOR_ERROR = 91

export const PLAYWRIGHT_DEFAULT_BROWSER = "chromium"
export const MAX_TOKENS_ELLIPSE = "..."
4 changes: 2 additions & 2 deletions packages/core/src/expander.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export async function callExpander(
const node = ctx.node
if (provider !== MODEL_PROVIDER_AICI) {
const {
prompt,
userPrompt,
assistantPrompt,
images: imgs,
errors,
Expand All @@ -84,7 +84,7 @@ export async function callExpander(
chatParticipants: cps,
fileOutputs: fos,
} = await renderPromptNode(model, node, { trace })
text = prompt
text = userPrompt
assistantText = assistantPrompt
images = imgs
schemas = schs
Expand Down
38 changes: 37 additions & 1 deletion packages/core/src/genaisrc/genaiscript.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 46 additions & 21 deletions packages/core/src/promptdom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
import { MarkdownTrace, TraceOptions } from "./trace"
import { arrayify, assert, toStringList, trimNewlines } from "./util"
import { YAMLStringify } from "./yaml"
import { MARKDOWN_PROMPT_FENCE, PROMPT_FENCE } from "./constants"
import {
MARKDOWN_PROMPT_FENCE,
MAX_TOKENS_ELLIPSE,
PROMPT_FENCE,
} from "./constants"
import { parseModelIdentifier } from "./models"
import { toChatCompletionUserMessage } from "./chat"
import { errorMessage } from "./error"
Expand Down Expand Up @@ -417,11 +421,11 @@
await visitor.afterNode?.(node)
}

export interface PromptNodeRender {
prompt: string
userPrompt: string
assistantPrompt: string
images: PromptImage[]
errors: unknown[]

Check failure on line 428 in packages/core/src/promptdom.ts

View workflow job for this annotation

GitHub Actions / build

The 'prompt' property in the 'PromptNodeRender' interface has been renamed to 'userPrompt'. This could potentially break any code that relies on the original 'prompt' property.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'prompt' property in the 'PromptNodeRender' interface has been renamed to 'userPrompt'. This could potentially break any code that relies on the original 'prompt' property.

generated by pr-review-commit interface_change

schemas: Record<string, JSONSchema>
functions: ToolCallback[]
fileMerges: FileMergeHandler[]
Expand Down Expand Up @@ -550,20 +554,20 @@
resolved?: string
tokens?: number
maxTokens?: number
preview?: string
}) => {
if (
!n.error &&
n.resolved !== undefined &&
n.maxTokens !== undefined &&
n.tokens > n.maxTokens
) {
const value = n.resolved.slice(
0,
Math.floor((n.maxTokens * n.resolved.length) / n.tokens)
)
n.resolved = value
const end = Math.floor((n.maxTokens * n.resolved.length) / n.tokens)
const value = n.resolved.slice(0, end) + MAX_TOKENS_ELLIPSE
n.resolved = n.preview = value
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is slicing the n.resolved string without checking if the end index is within the string length. This could potentially lead to unexpected behavior if end is greater than the string length. Please add a check to ensure end is within the string length. 😊

generated by pr-review-commit missing_error_handling

n.tokens = estimateTokens(value, encoder)
truncated = true
trace.log(`truncated text to ${n.tokens} tokens`)
}
}

Expand All @@ -574,12 +578,15 @@
n.maxTokens !== undefined &&
n.tokens > n.maxTokens
) {
n.resolved.content = n.resolved.content.slice(
0,
Math.floor((n.maxTokens * n.resolved.content.length) / n.tokens)
const end = Math.floor(
(n.maxTokens * n.resolved.content.length) / n.tokens
)
n.resolved.content =
n.resolved.content.slice(0, end) + MAX_TOKENS_ELLIPSE
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is slicing the n.resolved.content string without checking if the end index is within the string length. This could potentially lead to unexpected behavior if end is greater than the string length. Please add a check to ensure end is within the string length. 😊

generated by pr-review-commit missing_error_handling

n.tokens = estimateTokens(n.resolved.content, encoder)
n.preview = n.resolved.content
truncated = true
trace.log(`truncated def ${n.name} to ${n.tokens} tokens`)
}
}

Expand All @@ -593,6 +600,20 @@
return truncated
}

async function flexPromptNode(
model: string,
node: PromptNode,
options?: TraceOptions
): Promise<boolean> {
const FLEX_BASIS_DEFAULT = 1
const FLEX_GROW_DEFAULT = Infinity
const { trace } = options || {}
const encoder = await resolveTokenEncoder(model)
let flexed = false

return flexed
}

async function tracePromptNode(
trace: MarkdownTrace,
root: PromptNode,
Expand Down Expand Up @@ -640,7 +661,11 @@
const truncated = await truncatePromptNode(model, node, options)
if (truncated) await tracePromptNode(trace, node, { label: "truncated" })

let prompt = ""
const flexed = await flexPromptNode(model, node, options)
if (flexed) await tracePromptNode(trace, node, { label: "flexed" })

let systemPrompt = ""
let userPrompt = ""
let assistantPrompt = ""
const images: PromptImage[] = []
const errors: unknown[] = []
Expand All @@ -655,12 +680,12 @@
text: async (n) => {
if (n.error) errors.push(n.error)
const value = n.resolved
if (value != undefined) prompt += value + "\n"
if (value != undefined) userPrompt += value + "\n"
},
def: async (n) => {
if (n.error) errors.push(n.error)
const value = n.resolved
if (value !== undefined) prompt += renderDefNode(n) + "\n"
if (value !== undefined) userPrompt += renderDefNode(n) + "\n"
},
assistant: async (n) => {
if (n.error) errors.push(n.error)
Expand All @@ -670,7 +695,7 @@
stringTemplate: async (n) => {
if (n.error) errors.push(n.error)
const value = n.resolved
if (value != undefined) prompt += value + "\n"
if (value != undefined) userPrompt += value + "\n"
},
image: async (n) => {
if (n.error) errors.push(n.error)
Expand All @@ -691,8 +716,8 @@
const value = n.resolved
if (value) {
for (const [filename, content] of Object.entries(value)) {
prompt += content
prompt += "\n"
userPrompt += content
userPrompt += "\n"
if (trace)
trace.detailsFenced(
`📦 import template ${filename}`,
Expand Down Expand Up @@ -727,7 +752,7 @@
${trimNewlines(schemaText)}
\`\`\`
`
prompt += text
userPrompt += text
n.tokens = estimateTokens(text, encoder)
if (trace && format !== "json")
trace.detailsFenced(
Expand Down Expand Up @@ -771,7 +796,7 @@

const fods = fileOutputs?.filter((f) => !!f.description)
if (fods?.length > 0) {
prompt += `
userPrompt += `
## File generation rules

When generating files, use the following rules which are formatted as "file glob: description":
Expand All @@ -782,15 +807,15 @@
}

const messages: ChatCompletionMessageParam[] = [
toChatCompletionUserMessage(prompt, images),
toChatCompletionUserMessage(userPrompt, images),
]
if (assistantPrompt)
messages.push(<ChatCompletionAssistantMessageParam>{
role: "assistant",
content: assistantPrompt,
})
const res = {
prompt,
const res = <PromptNodeRender>{
userPrompt,
assistantPrompt,
images,
schemas,
Expand Down
Loading