refactor(core): narrow v2 context epoch scope
This commit is contained in:
parent
5ca39084e4
commit
d856d92506
11 changed files with 339 additions and 827 deletions
|
|
@ -386,7 +386,6 @@ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* (
|
|||
|
||||
for (const [index, message] of request.messages.entries()) {
|
||||
if (message.role === "system") {
|
||||
yield* ProviderShared.guardSystemUpdatePlacement("Anthropic Messages", request.messages, index)
|
||||
if (supportsNativeSystemUpdates(request) && canUseNativeSystemUpdate(request.messages, index)) {
|
||||
messages.push(yield* lowerNativeSystemUpdate(message, breakpoints))
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -292,9 +292,8 @@ const lowerMessages = Effect.fn("BedrockConverse.lowerMessages")(function* (
|
|||
) {
|
||||
const messages: BedrockMessage[] = []
|
||||
|
||||
for (const [index, message] of request.messages.entries()) {
|
||||
for (const message of request.messages) {
|
||||
if (message.role === "system") {
|
||||
yield* ProviderShared.guardSystemUpdatePlacement("Bedrock Converse", request.messages, index)
|
||||
const part = yield* ProviderShared.wrappedSystemUpdate("Bedrock Converse", message)
|
||||
const content = textWithCache(breakpoints, part.text, part.cache)
|
||||
const previous = messages.at(-1)
|
||||
|
|
|
|||
|
|
@ -200,9 +200,8 @@ const lowerToolCall = (part: ToolCallPart) => ({
|
|||
const lowerMessages = Effect.fn("Gemini.lowerMessages")(function* (request: LLMRequest) {
|
||||
const contents: GeminiContent[] = []
|
||||
|
||||
for (const [index, message] of request.messages.entries()) {
|
||||
for (const message of request.messages) {
|
||||
if (message.role === "system") {
|
||||
yield* ProviderShared.guardSystemUpdatePlacement("Gemini", request.messages, index)
|
||||
const part = yield* ProviderShared.wrappedSystemUpdate("Gemini", message)
|
||||
const previous = contents.at(-1)
|
||||
if (previous?.role === "user")
|
||||
|
|
|
|||
|
|
@ -252,9 +252,8 @@ const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request:
|
|||
const system: OpenAIChatMessage[] =
|
||||
request.system.length === 0 ? [] : [{ role: "system", content: ProviderShared.joinText(request.system) }]
|
||||
const messages = [...system]
|
||||
for (const [index, message] of request.messages.entries()) {
|
||||
for (const message of request.messages) {
|
||||
if (message.role === "system") {
|
||||
yield* ProviderShared.guardSystemUpdatePlacement("OpenAI Chat", request.messages, index)
|
||||
const part = yield* ProviderShared.wrappedSystemUpdate("OpenAI Chat", message)
|
||||
const previous = messages.at(-1)
|
||||
if (previous?.role === "user")
|
||||
|
|
|
|||
|
|
@ -338,9 +338,8 @@ const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (requ
|
|||
const input: OpenAIResponsesInputItem[] = [...system]
|
||||
const store = OpenAIOptions.store(request)
|
||||
|
||||
for (const [index, message] of request.messages.entries()) {
|
||||
for (const message of request.messages) {
|
||||
if (message.role === "system") {
|
||||
yield* ProviderShared.guardSystemUpdatePlacement("OpenAI Responses", request.messages, index)
|
||||
const part = yield* ProviderShared.wrappedSystemUpdate("OpenAI Responses", message)
|
||||
const previous = input.at(-1)
|
||||
if (previous && "role" in previous && previous.role === "user")
|
||||
|
|
|
|||
|
|
@ -177,26 +177,6 @@ export const wrappedSystemUpdate = Effect.fn("ProviderShared.wrappedSystemUpdate
|
|||
return { type: "text" as const, text: wrapSystemUpdate(content), cache: content.at(-1)?.cache }
|
||||
})
|
||||
|
||||
export const guardSystemUpdatePlacement = Effect.fn("ProviderShared.guardSystemUpdatePlacement")(function* (
|
||||
route: string,
|
||||
messages: LLMRequest["messages"],
|
||||
index: number,
|
||||
) {
|
||||
const pending = new Set<string>()
|
||||
for (const message of messages.slice(0, index)) {
|
||||
for (const part of message.content) {
|
||||
if (message.role === "assistant" && part.type === "tool-call" && part.providerExecuted !== true)
|
||||
pending.add(part.id)
|
||||
if (message.role === "tool" && part.type === "tool-result") pending.delete(part.id)
|
||||
}
|
||||
}
|
||||
if (pending.size > 0)
|
||||
return yield* invalidRequest(
|
||||
`${route} chronological system updates cannot appear between a local tool call and its tool result`,
|
||||
)
|
||||
return yield* Effect.void
|
||||
})
|
||||
|
||||
/**
|
||||
* Parse the streamed JSON input of a tool call. Treats an empty string as
|
||||
* `"{}"` — providers occasionally finish a tool call without ever emitting
|
||||
|
|
|
|||
|
|
@ -127,9 +127,6 @@ describe("Anthropic Messages route", () => {
|
|||
|
||||
it.effect("falls back for unsupported native chronological system update placement", () =>
|
||||
Effect.gen(function* () {
|
||||
const placementError = (messages: Parameters<typeof LLM.request>[0]["messages"]) =>
|
||||
LLMClient.prepare(LLM.request({ model: opus48, messages, cache: "none" })).pipe(Effect.flip)
|
||||
|
||||
expect(
|
||||
(yield* LLMClient.prepare<AnthropicMessages.AnthropicMessagesBody>(
|
||||
LLM.request({
|
||||
|
|
@ -168,17 +165,6 @@ describe("Anthropic Messages route", () => {
|
|||
],
|
||||
},
|
||||
])
|
||||
expect(
|
||||
(yield* placementError([
|
||||
Message.user("Use the tool."),
|
||||
Message.assistant([
|
||||
ToolCallPart.make({ id: "call_1", name: "lookup", input: {} }),
|
||||
{ type: "text", text: "Waiting." },
|
||||
]),
|
||||
Message.system("Too early."),
|
||||
Message.tool({ id: "call_1", name: "lookup", result: "Done." }),
|
||||
])).message,
|
||||
).toContain("cannot appear between a local tool call and its tool result")
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import { Effect, Schema } from "effect"
|
||||
import { Schema } from "effect"
|
||||
import * as OpenAIChat from "../src/protocols/openai-chat"
|
||||
import * as OpenAIResponses from "../src/protocols/openai-responses"
|
||||
import {
|
||||
|
|
@ -10,7 +10,6 @@ import {
|
|||
Model,
|
||||
ModelID,
|
||||
ProviderID,
|
||||
ToolCallPart,
|
||||
Usage,
|
||||
} from "../src/schema"
|
||||
import { ProviderShared } from "../src/protocols/shared"
|
||||
|
|
@ -64,33 +63,6 @@ describe("llm schema", () => {
|
|||
expect(decoded.messages[0]).toMatchObject({ role: "system", content: [{ type: "text", text: "Operator update." }] })
|
||||
})
|
||||
|
||||
test("rejects chronological system updates between a local tool call and its result", async () => {
|
||||
const messages = [
|
||||
Message.assistant([
|
||||
ToolCallPart.make({ id: "call_1", name: "lookup", input: {} }),
|
||||
{ type: "text", text: "Waiting." },
|
||||
]),
|
||||
]
|
||||
|
||||
await expect(Effect.runPromise(ProviderShared.guardSystemUpdatePlacement("Test", messages, 1))).rejects.toThrow(
|
||||
"Test chronological system updates cannot appear between a local tool call and its tool result",
|
||||
)
|
||||
})
|
||||
|
||||
test("rejects chronological system updates between results for multiple local tool calls", async () => {
|
||||
const messages = [
|
||||
Message.assistant([
|
||||
ToolCallPart.make({ id: "call_1", name: "lookup", input: {} }),
|
||||
ToolCallPart.make({ id: "call_2", name: "lookup", input: {} }),
|
||||
]),
|
||||
Message.tool({ id: "call_1", name: "lookup", result: "first" }),
|
||||
]
|
||||
|
||||
await expect(Effect.runPromise(ProviderShared.guardSystemUpdatePlacement("Test", messages, 2))).rejects.toThrow(
|
||||
"Test chronological system updates cannot appear between a local tool call and its tool result",
|
||||
)
|
||||
})
|
||||
|
||||
test("rejects invalid event type", () => {
|
||||
expect(() => decodeLLMEvent({ type: "bogus" })).toThrow()
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue