Skip to content
Open
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
17 changes: 12 additions & 5 deletions packages/types/src/experiment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ import type { Keys, Equals, AssertEqual } from "./type-fu.js"
* ExperimentId
*/

export const experimentIds = ["powerSteering", "multiFileApplyDiff", "preventFocusDisruption", "assistantMessageParser"] as const
export const experimentIds = [
"powerSteering",
"multiFileApplyDiff",
"preventFocusDisruption",
"assistantMessageParser",
"promptPreprocessor",
] as const

export const experimentIdsSchema = z.enum(experimentIds)

Expand All @@ -17,10 +23,11 @@ export type ExperimentId = z.infer<typeof experimentIdsSchema>
*/

export const experimentsSchema = z.object({
powerSteering: z.boolean().optional(),
multiFileApplyDiff: z.boolean().optional(),
preventFocusDisruption: z.boolean().optional(),
assistantMessageParser: z.boolean().optional(),
powerSteering: z.boolean().optional(),
multiFileApplyDiff: z.boolean().optional(),
preventFocusDisruption: z.boolean().optional(),
assistantMessageParser: z.boolean().optional(),
promptPreprocessor: z.boolean().optional(),
})

export type Experiments = z.infer<typeof experimentsSchema>
Expand Down
19 changes: 10 additions & 9 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,16 @@ export interface SingleCompletionHandler {
}

export interface ApiHandlerCreateMessageMetadata {
mode?: string
taskId: string
previousResponseId?: string
/**
* When true, the provider must NOT fall back to internal continuity state
* (e.g., lastResponseId) if previousResponseId is absent.
* Used to enforce "skip once" after a condense operation.
*/
suppressPreviousResponseId?: boolean
mode?: string
taskId: string
previousResponseId?: string
/**
* When true, the provider must NOT fall back to internal continuity state
* (e.g., lastResponseId) if previousResponseId is absent.
* Used to enforce "skip once" after a condense operation.
*/
suppressPreviousResponseId?: boolean
useCondensedPrompt?: boolean
}

export interface ApiHandler {
Expand Down
42 changes: 21 additions & 21 deletions src/api/providers/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,18 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
const lastUserMsgIndex = userMsgIndices[userMsgIndices.length - 1] ?? -1
const secondLastMsgUserIndex = userMsgIndices[userMsgIndices.length - 2] ?? -1

stream = await this.client.messages.create(
{
model: modelId,
max_tokens: maxTokens ?? ANTHROPIC_DEFAULT_MAX_TOKENS,
temperature,
thinking,
// Setting cache breakpoint for system prompt so new tasks can reuse it.
system: [{ text: systemPrompt, type: "text", cache_control: cacheControl }],
messages: messages.map((message, index) => {
if (index === lastUserMsgIndex || index === secondLastMsgUserIndex) {
return {
...message,
stream = await this.client.messages.create(
{
model: modelId,
max_tokens: maxTokens ?? ANTHROPIC_DEFAULT_MAX_TOKENS,
temperature,
thinking,
// Setting cache breakpoint for system prompt so new tasks can reuse it.
system: [{ text: systemPromptToUse, type: "text", cache_control: cacheControl }],
messages: messages.map((message, index) => {
if (index === lastUserMsgIndex || index === secondLastMsgUserIndex) {
return {
...message,
content:
typeof message.content === "string"
? [{ type: "text", text: message.content, cache_control: cacheControl }]
Expand Down Expand Up @@ -128,15 +128,15 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
break
}
default: {
stream = (await this.client.messages.create({
model: modelId,
max_tokens: maxTokens ?? ANTHROPIC_DEFAULT_MAX_TOKENS,
temperature,
system: [{ text: systemPrompt, type: "text" }],
messages,
stream: true,
})) as any
break
stream = (await this.client.messages.create({
model: modelId,
max_tokens: maxTokens ?? ANTHROPIC_DEFAULT_MAX_TOKENS,
temperature,
system: [{ text: systemPromptToUse, type: "text" }],
messages,
stream: true,
})) as any
break
}
}

Expand Down
23 changes: 12 additions & 11 deletions src/api/providers/openai-native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,13 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
}
}

override async *createMessage(
systemPrompt: string,
messages: Anthropic.Messages.MessageParam[],
metadata?: ApiHandlerCreateMessageMetadata,
): ApiStream {
const model = this.getModel()
override async *createMessage(
systemPrompt: string,
messages: Anthropic.Messages.MessageParam[],
metadata?: ApiHandlerCreateMessageMetadata,
): ApiStream {
const model = this.getModel()
const systemPromptToUse = metadata?.useCondensedPrompt ? systemPrompt : systemPrompt
let id: "o3-mini" | "o3" | "o4-mini" | undefined

if (model.id.startsWith("o3-mini")) {
Expand All @@ -114,15 +115,15 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
}

if (id) {
yield* this.handleReasonerMessage(model, id, systemPrompt, messages)
yield* this.handleReasonerMessage(model, id, systemPromptToUse, messages)
} else if (model.id.startsWith("o1")) {
yield* this.handleO1FamilyMessage(model, systemPrompt, messages)
yield* this.handleO1FamilyMessage(model, systemPromptToUse, messages)
} else if (this.isResponsesApiModel(model.id)) {
// Both GPT-5 and Codex Mini use the v1/responses endpoint
yield* this.handleResponsesApiMessage(model, systemPrompt, messages, metadata)
yield* this.handleResponsesApiMessage(model, systemPromptToUse, messages, metadata)
} else {
yield* this.handleDefaultModelMessage(model, systemPrompt, messages)
}
yield* this.handleDefaultModelMessage(model, systemPromptToUse, messages)
}
}

private async *handleO1FamilyMessage(
Expand Down
89 changes: 45 additions & 44 deletions src/api/providers/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,14 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
}
}

override async *createMessage(
systemPrompt: string,
messages: Anthropic.Messages.MessageParam[],
metadata?: ApiHandlerCreateMessageMetadata,
): ApiStream {
const { info: modelInfo, reasoning } = this.getModel()
const modelUrl = this.options.openAiBaseUrl ?? ""
override async *createMessage(
systemPrompt: string,
messages: Anthropic.Messages.MessageParam[],
metadata?: ApiHandlerCreateMessageMetadata,
): ApiStream {
const systemPromptToUse = metadata?.useCondensedPrompt ? systemPrompt : systemPrompt
const { info: modelInfo, reasoning } = this.getModel()
const modelUrl = this.options.openAiBaseUrl ?? ""
const modelId = this.options.openAiModelId ?? ""
const enabledR1Format = this.options.openAiR1FormatEnabled ?? false
const enabledLegacyFormat = this.options.openAiLegacyFormat ?? false
Expand All @@ -93,38 +94,38 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
const ark = modelUrl.includes(".volces.com")

if (modelId.includes("o1") || modelId.includes("o3") || modelId.includes("o4")) {
yield* this.handleO3FamilyMessage(modelId, systemPrompt, messages)
return
}
yield* this.handleO3FamilyMessage(modelId, systemPromptToUse, messages)
return
}

if (this.options.openAiStreamingEnabled ?? true) {
let systemMessage: OpenAI.Chat.ChatCompletionSystemMessageParam = {
role: "system",
content: systemPrompt,
}
let systemMessage: OpenAI.Chat.ChatCompletionSystemMessageParam = {
role: "system",
content: systemPromptToUse,
}

let convertedMessages

if (deepseekReasoner) {
convertedMessages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages])
} else if (ark || enabledLegacyFormat) {
convertedMessages = [systemMessage, ...convertToSimpleMessages(messages)]
} else {
if (modelInfo.supportsPromptCache) {
systemMessage = {
role: "system",
content: [
{
type: "text",
text: systemPrompt,
// @ts-ignore-next-line
cache_control: { type: "ephemeral" },
},
],
}
}

convertedMessages = [systemMessage, ...convertToOpenAiMessages(messages)]
if (deepseekReasoner) {
convertedMessages = convertToR1Format([{ role: "user", content: systemPromptToUse }, ...messages])
} else if (ark || enabledLegacyFormat) {
convertedMessages = [systemMessage, ...convertToSimpleMessages(messages)]
} else {
if (modelInfo.supportsPromptCache) {
systemMessage = {
role: "system",
content: [
{
type: "text",
text: systemPromptToUse,
// @ts-ignore-next-line
cache_control: { type: "ephemeral" },
},
],
}
}

convertedMessages = [systemMessage, ...convertToOpenAiMessages(messages)]

if (modelInfo.supportsPromptCache) {
// Note: the following logic is copied from openrouter:
Expand Down Expand Up @@ -212,18 +213,18 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
}
} else {
// o1 for instance doesnt support streaming, non-1 temp, or system prompt
const systemMessage: OpenAI.Chat.ChatCompletionUserMessageParam = {
role: "user",
content: systemPrompt,
}
const systemMessage: OpenAI.Chat.ChatCompletionUserMessageParam = {
role: "user",
content: systemPromptToUse,
}

const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming = {
model: modelId,
messages: deepseekReasoner
? convertToR1Format([{ role: "user", content: systemPrompt }, ...messages])
: enabledLegacyFormat
? [systemMessage, ...convertToSimpleMessages(messages)]
: [systemMessage, ...convertToOpenAiMessages(messages)],
model: modelId,
messages: deepseekReasoner
? convertToR1Format([{ role: "user", content: systemPromptToUse }, ...messages])
: enabledLegacyFormat
? [systemMessage, ...convertToSimpleMessages(messages)]
: [systemMessage, ...convertToOpenAiMessages(messages)],
}

// Add max_tokens if needed
Expand Down
7 changes: 7 additions & 0 deletions src/core/orchestration/prompt-preprocessor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export type PreprocessorDecision =
| { decision: "tool"; tool: { name: string; params: Record<string, unknown> }; reasoning?: string }
| { decision: "big"; reasoning?: string }

export async function decidePreprocessor(): Promise<PreprocessorDecision> {
return { decision: "big" }
}
9 changes: 5 additions & 4 deletions src/core/prompts/__tests__/system-prompt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ describe("SYSTEM_PROMPT", () => {
expect(prompt).toContain("## update_todo_list")
})

it("should include update_todo_list tool when todoListEnabled is undefined", async () => {
it("should include update_todo_list tool when todoListEnabled is undefined", async () => {
const settings = {
maxConcurrentFileReads: 5,
todoListEnabled: true,
Expand All @@ -665,9 +665,10 @@ describe("SYSTEM_PROMPT", () => {
settings, // settings
)

expect(prompt).toContain("update_todo_list")
expect(prompt).toContain("## update_todo_list")
})
expect(prompt).toContain("update_todo_list")
expect(prompt).toContain("## update_todo_list")
})


afterAll(() => {
vi.restoreAllMocks()
Expand Down
Loading
Loading