Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/static-environment-section.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@perstack/runtime": patch
"perstack": patch
---

Remove dynamic content from system prompt to improve cross-run prefix caching
22 changes: 10 additions & 12 deletions packages/runtime/src/messages/instruction-message.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import os from "node:os"
import { createId } from "@paralleldrive/cuid2"
import { type Expert, type InstructionMessage, isCoordinatorExpert } from "@perstack/core"
import { dedent } from "ts-dedent"

function getEnvironmentSection(startedAt: number): string {
const lines = [
`- Current time: ${new Date(startedAt).toISOString()}`,
`- Working directory: ${process.cwd()}`,
]
function getEnvironmentSection(): string {
const lines = [`- Platform: ${os.platform()} ${os.release()} (${os.arch()})`]
if (process.env.PERSTACK_SANDBOX === "1") {
lines.push(
"- Sandbox: This is an isolated container environment (Ubuntu). You can freely install packages with `sudo apt-get install` and run arbitrary commands without affecting the host system.",
Expand All @@ -15,18 +13,18 @@ function getEnvironmentSection(startedAt: number): string {
return `Environment:\n${lines.join("\n")}`
}

function getDelegateMetaInstruction(startedAt: number): string {
function getDelegateMetaInstruction(): string {
return dedent`
Before starting work, investigate the workspace and understand the current state. Then use the todo tool to create a plan of action. Work through the todos step by step, marking each completed as you go.

When the task is complete, call attemptCompletion with a result parameter containing your final response.
When you cannot help, call attemptCompletion without a result.

${getEnvironmentSection(startedAt)}
${getEnvironmentSection()}
`
}

function getCoordinatorMetaInstruction(startedAt: number): string {
function getCoordinatorMetaInstruction(): string {
return dedent`
Your role:
- Act as the coordinator for the given task: define the task, set goals, and delegate.
Expand Down Expand Up @@ -62,14 +60,14 @@ function getCoordinatorMetaInstruction(startedAt: number): string {
When the task is complete, call attemptCompletion with a result parameter containing your final response.
When you cannot help, call attemptCompletion without a result.

${getEnvironmentSection(startedAt)}
${getEnvironmentSection()}
`
}

export function createInstructionMessage(expert: Expert, startedAt: number): InstructionMessage {
export function createInstructionMessage(expert: Expert): InstructionMessage {
const metaInstruction = isCoordinatorExpert(expert.name)
? getCoordinatorMetaInstruction(startedAt)
: getDelegateMetaInstruction(startedAt)
? getCoordinatorMetaInstruction()
: getDelegateMetaInstruction()

const preamble = dedent`
You are Perstack, an AI expert that tackles tasks requested by users by utilizing all available tools.
Expand Down
14 changes: 6 additions & 8 deletions packages/runtime/src/messages/message.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -551,8 +551,6 @@ describe("@perstack/messages: message", () => {
})

describe("@perstack/messages: instruction-message", () => {
const startedAt = 1700000000000

describe("createInstructionMessage", () => {
it("creates instruction message with preamble and expert instruction as separate parts", () => {
const expert = {
Expand All @@ -566,7 +564,7 @@ describe("@perstack/messages: instruction-message", () => {
runtime: ["local" as const],
minRuntimeVersion: "v1.0" as const,
}
const result = createInstructionMessage(expert, startedAt)
const result = createInstructionMessage(expert)
expect(result.type).toBe("instructionMessage")
expect(result.cache).toBeUndefined()
expect(result.contents).toHaveLength(2)
Expand Down Expand Up @@ -600,7 +598,7 @@ describe("@perstack/messages: instruction-message", () => {
runtime: ["local" as const],
minRuntimeVersion: "v1.0" as const,
}
const result = createInstructionMessage(expert, startedAt)
const result = createInstructionMessage(expert)
expect(result.contents).toHaveLength(3)
expect(result.contents[2].text).toContain("Always use this skill carefully.")
expect(result.contents[2].text).toContain('"test-skill" skill rules:')
Expand Down Expand Up @@ -628,11 +626,11 @@ describe("@perstack/messages: instruction-message", () => {
runtime: ["local" as const],
minRuntimeVersion: "v1.0" as const,
}
const result = createInstructionMessage(expert, startedAt)
const result = createInstructionMessage(expert)
expect(result.contents).toHaveLength(2)
})

it("uses startedAt for timestamp in instruction", () => {
it("includes platform info in environment section", () => {
const expert = {
key: "test-expert",
name: "Test Expert",
Expand All @@ -644,8 +642,8 @@ describe("@perstack/messages: instruction-message", () => {
runtime: ["local" as const],
minRuntimeVersion: "v1.0" as const,
}
const result = createInstructionMessage(expert, startedAt)
expect(result.contents[0].text).toContain("2023-11-14T22:13:20.000Z")
const result = createInstructionMessage(expert)
expect(result.contents[0].text).toContain("Platform:")
})
})
})
2 changes: 1 addition & 1 deletion packages/runtime/src/state-machine/states/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export async function initLogic({
return startRun(setting, checkpoint, {
initialCheckpoint: checkpoint,
inputMessages: [
createInstructionMessage(expert, setting.startedAt),
createInstructionMessage(expert),
createUserMessage([{ type: "textPart", text: setting.input.text }]),
],
model: setting.model,
Expand Down
Loading