Skip to content
Closed
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
5 changes: 4 additions & 1 deletion packages/core/src/session/runner/llm.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -204,7 +204,10 @@ const layer = Layer.effect(
const promptCacheKey = /^ses_[0-9a-f]{64}$/.test(session.id) ? session.id.slice(4) : session.id
const request = LLM.request({
model,
providerOptions: { openai: { promptCacheKey } },
providerOptions: {
openai: { promptCacheKey },
openrouter: { promptCacheKey },
},
system: [agent.info?.system, system.baseline]
.filter((part): part is string => part !== undefined && part.length > 0)
.map(SystemPart.make),
Expand Down
4 changes: 4 additions & 0 deletions packages/core/test/session-runner.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2507,6 +2507,10 @@ describe("SessionRunnerLLM", () => {
sessionID,
otherSessionID,
])
expect(requests.map((request) => request.providerOptions?.openrouter?.promptCacheKey)).toEqual([
sessionID,
otherSessionID,
])
yield* Deferred.succeed(streamGate, undefined)
yield* Fiber.join(first)
yield* Fiber.join(second)
Expand Down
3 changes: 3 additions & 0 deletions packages/llm/src/protocols/openai-chat.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -95,6 +95,7 @@ export const bodyFields = {
tool_choice: Schema.optional(OpenAIChatToolChoice),
stream: Schema.Literal(true),
stream_options: Schema.optional(Schema.Struct({ include_usage: Schema.Boolean })),
prompt_cache_key: Schema.optional(Schema.String),
store: Schema.optional(Schema.Boolean),
reasoning_effort: Schema.optional(OpenAIOptions.OpenAIReasoningEffort),
max_tokens: Schema.optional(Schema.Number),
Expand DownExpand Up@@ -332,11 +333,13 @@ const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request:

const lowerOptions = Effect.fn("OpenAIChat.lowerOptions")(function* (request: LLMRequest) {
const store = OpenAIOptions.store(request)
const promptCacheKey = request.model.provider === "openai" ? OpenAIOptions.promptCacheKey(request) : undefined
const reasoningEffort = OpenAIOptions.reasoningEffort(request)
if (reasoningEffort && !OpenAIOptions.isReasoningEffort(reasoningEffort))
return yield* invalid(`OpenAI Chat does not support reasoning effort ${reasoningEffort}`)
return {
...(store !== undefined ? { store } : {}),
...(promptCacheKey ? { prompt_cache_key: promptCacheKey } : {}),
...(reasoningEffort ? { reasoning_effort: reasoningEffort } : {}),
}
})
Expand Down
14 changes: 14 additions & 0 deletions packages/llm/test/provider/openai-chat.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,6 +29,20 @@ const request = LLM.request({
})

describe("OpenAI Chat route", () => {
it.effect("maps the prompt cache key for native OpenAI models", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIChat.OpenAIChatBody>(
LLM.request({
model: OpenAI.configure({ baseURL: "https://api.openai.test/v1/", apiKey: "test" }).chat("gpt-4o-mini"),
prompt: "hello",
providerOptions: { openai: { promptCacheKey: "session_123" } },
}),
)

expect(prepared.body.prompt_cache_key).toBe("session_123")
}),
)

it.effect("prepares OpenAI Chat payload", () =>
Effect.gen(function* () {
// Pass the OpenAIChat payload type so `prepared.body` is statically
Expand Down
12 changes: 12 additions & 0 deletions packages/llm/test/provider/openai-compatible-chat.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,18 @@ const providerFamilies = [
] as const

describe("OpenAI-compatible Chat route", () => {
it.effect("does not send native OpenAI cache options", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare(
LLM.updateRequest(request, {
providerOptions: { openai: { promptCacheKey: "session_123" } },
}),
)

expect(prepared.body).not.toHaveProperty("prompt_cache_key")
}),
)

it.effect("prepares generic Chat target", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare(
Expand Down
Loading