mirror of
https://gitee.com/zhangsken/opencode.git
synced 2026-10-16 02:24:10 +08:00
fix(opencode): add namespaced session identity headers (#52370)
This commit is contained in:
@@ -206,6 +206,8 @@ const layer = Layer.effect(
|
|||||||
model,
|
model,
|
||||||
http: {
|
http: {
|
||||||
headers: {
|
headers: {
|
||||||
|
"x-opencode-session-id": session.id,
|
||||||
|
...(session.parentID ? { "x-opencode-parent-session-id": session.parentID } : {}),
|
||||||
"x-session-affinity": session.id,
|
"x-session-affinity": session.id,
|
||||||
"X-Session-Id": session.id,
|
"X-Session-Id": session.id,
|
||||||
...(session.parentID ? { "x-parent-session-id": session.parentID } : {}),
|
...(session.parentID ? { "x-parent-session-id": session.parentID } : {}),
|
||||||
|
|||||||
@@ -1103,10 +1103,12 @@ describe("SessionRunnerLLM", () => {
|
|||||||
expect(requests).toHaveLength(2)
|
expect(requests).toHaveLength(2)
|
||||||
expect(requests.map((request) => request.http?.headers)).toEqual([
|
expect(requests.map((request) => request.http?.headers)).toEqual([
|
||||||
{
|
{
|
||||||
|
"x-opencode-session-id": sessionID,
|
||||||
"x-session-affinity": sessionID,
|
"x-session-affinity": sessionID,
|
||||||
"X-Session-Id": sessionID,
|
"X-Session-Id": sessionID,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"x-opencode-session-id": sessionID,
|
||||||
"x-session-affinity": sessionID,
|
"x-session-affinity": sessionID,
|
||||||
"X-Session-Id": sessionID,
|
"X-Session-Id": sessionID,
|
||||||
},
|
},
|
||||||
@@ -2528,6 +2530,7 @@ describe("SessionRunnerLLM", () => {
|
|||||||
yield* session.resume(sessionID)
|
yield* session.resume(sessionID)
|
||||||
|
|
||||||
expect(requests[0]?.http?.headers).toEqual({
|
expect(requests[0]?.http?.headers).toEqual({
|
||||||
|
"x-opencode-session-id": sessionID,
|
||||||
"x-session-affinity": sessionID,
|
"x-session-affinity": sessionID,
|
||||||
"X-Session-Id": sessionID,
|
"X-Session-Id": sessionID,
|
||||||
})
|
})
|
||||||
@@ -2551,7 +2554,13 @@ describe("SessionRunnerLLM", () => {
|
|||||||
requests.length = 0
|
requests.length = 0
|
||||||
yield* session.resume(sessionID)
|
yield* session.resume(sessionID)
|
||||||
|
|
||||||
expect(requests[0]?.http?.headers?.["x-parent-session-id"]).toBe(parentID)
|
expect(requests[0]?.http?.headers).toEqual({
|
||||||
|
"x-opencode-session-id": sessionID,
|
||||||
|
"x-opencode-parent-session-id": parentID,
|
||||||
|
"x-session-affinity": sessionID,
|
||||||
|
"X-Session-Id": sessionID,
|
||||||
|
"x-parent-session-id": parentID,
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -185,6 +185,8 @@ export const prepare = Effect.fn("LLMRequestPrep.prepare")(function* (input: Pre
|
|||||||
params,
|
params,
|
||||||
messageTransformOptions: options,
|
messageTransformOptions: options,
|
||||||
headers: {
|
headers: {
|
||||||
|
"x-opencode-session-id": input.sessionID,
|
||||||
|
...(input.parentSessionID ? { "x-opencode-parent-session-id": input.parentSessionID } : {}),
|
||||||
...(input.model.providerID.startsWith("opencode")
|
...(input.model.providerID.startsWith("opencode")
|
||||||
? {
|
? {
|
||||||
...(opencodeProjectID ? { "x-opencode-project": opencodeProjectID } : {}),
|
...(opencodeProjectID ? { "x-opencode-project": opencodeProjectID } : {}),
|
||||||
|
|||||||
@@ -756,72 +756,89 @@ describe("session.llm.stream", () => {
|
|||||||
const vivgridFixture = { providerID: "vivgrid", modelID: "gemini-3.1-pro-preview" }
|
const vivgridFixture = { providerID: "vivgrid", modelID: "gemini-3.1-pro-preview" }
|
||||||
const opencodeFixture = { providerID: "opencode-test", modelID: vivgridFixture.modelID }
|
const opencodeFixture = { providerID: "opencode-test", modelID: vivgridFixture.modelID }
|
||||||
|
|
||||||
it.instance(
|
const headerCases = [
|
||||||
"sends the parent session header for opencode providers",
|
{ providerID: opencodeFixture.providerID, child: false },
|
||||||
() =>
|
{ providerID: opencodeFixture.providerID, child: true },
|
||||||
Effect.gen(function* () {
|
{ providerID: "custom-test", child: false },
|
||||||
const fixture = loadFixture(vivgridFixture.providerID, vivgridFixture.modelID)
|
{ providerID: "custom-test", child: true },
|
||||||
const request = waitRequest(
|
]
|
||||||
"/chat/completions",
|
headerCases.forEach((input) => {
|
||||||
new Response(createChatStream("Hello"), {
|
it.instance(
|
||||||
status: 200,
|
`sends session identity headers for ${input.child ? "child" : "root"} sessions on ${input.providerID}`,
|
||||||
headers: { "Content-Type": "text/event-stream" },
|
() =>
|
||||||
}),
|
Effect.gen(function* () {
|
||||||
)
|
const fixture = loadFixture(vivgridFixture.providerID, vivgridFixture.modelID)
|
||||||
const resolved = yield* Provider.use.getModel(
|
const request = waitRequest(
|
||||||
ProviderV2.ID.make(opencodeFixture.providerID),
|
"/chat/completions",
|
||||||
ModelV2.ID.make(opencodeFixture.modelID),
|
new Response(createChatStream("Hello"), {
|
||||||
)
|
status: 200,
|
||||||
const sessionID = SessionID.make("session-child")
|
headers: { "Content-Type": "text/event-stream" },
|
||||||
const parentSessionID = SessionID.make("session-parent")
|
}),
|
||||||
const agent = {
|
)
|
||||||
name: "test",
|
const resolved = yield* Provider.use.getModel(
|
||||||
mode: "primary",
|
ProviderV2.ID.make(input.providerID),
|
||||||
options: {},
|
ModelV2.ID.make(opencodeFixture.modelID),
|
||||||
permission: [{ permission: "*", pattern: "*", action: "allow" }],
|
)
|
||||||
} satisfies Agent.Info
|
const sessionID = SessionID.make("session-child")
|
||||||
const user = {
|
const parentSessionID = input.child ? SessionID.make("session-parent") : undefined
|
||||||
id: MessageID.make("msg_user-parent-header"),
|
const agent = {
|
||||||
sessionID,
|
name: "test",
|
||||||
role: "user",
|
mode: "primary",
|
||||||
time: { created: Date.now() },
|
options: {},
|
||||||
agent: agent.name,
|
permission: [{ permission: "*", pattern: "*", action: "allow" }],
|
||||||
model: {
|
} satisfies Agent.Info
|
||||||
providerID: ProviderV2.ID.make(opencodeFixture.providerID),
|
const user = {
|
||||||
modelID: resolved.id,
|
id: MessageID.make("msg_user-parent-header"),
|
||||||
},
|
sessionID,
|
||||||
} satisfies SessionV1.User
|
role: "user",
|
||||||
|
time: { created: Date.now() },
|
||||||
yield* drain({
|
agent: agent.name,
|
||||||
user,
|
model: {
|
||||||
sessionID,
|
providerID: ProviderV2.ID.make(input.providerID),
|
||||||
parentSessionID,
|
modelID: resolved.id,
|
||||||
model: resolved,
|
|
||||||
agent,
|
|
||||||
system: ["You are a helpful assistant."],
|
|
||||||
messages: [{ role: "user", content: "Hello" }],
|
|
||||||
tools: {},
|
|
||||||
})
|
|
||||||
|
|
||||||
expect((yield* Effect.promise(() => request)).headers.get("x-parent-session-id")).toBe(parentSessionID)
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
config: () => {
|
|
||||||
const fixture = loadFixture(vivgridFixture.providerID, vivgridFixture.modelID)
|
|
||||||
return {
|
|
||||||
enabled_providers: [opencodeFixture.providerID],
|
|
||||||
provider: {
|
|
||||||
[opencodeFixture.providerID]: {
|
|
||||||
name: "OpenCode Test",
|
|
||||||
npm: "@ai-sdk/openai-compatible",
|
|
||||||
models: { [fixture.model.id]: configModel(fixture.model) as ConfigModel },
|
|
||||||
options: { apiKey: "test-key", baseURL: `${state.server!.url.origin}/v1` },
|
|
||||||
},
|
},
|
||||||
},
|
} satisfies SessionV1.User
|
||||||
}
|
|
||||||
|
yield* drain({
|
||||||
|
user,
|
||||||
|
sessionID,
|
||||||
|
parentSessionID,
|
||||||
|
model: resolved,
|
||||||
|
agent,
|
||||||
|
system: ["You are a helpful assistant."],
|
||||||
|
messages: [{ role: "user", content: "Hello" }],
|
||||||
|
tools: {},
|
||||||
|
})
|
||||||
|
|
||||||
|
const headers = (yield* Effect.promise(() => request)).headers
|
||||||
|
expect(headers.get("x-opencode-session-id")).toBe(sessionID)
|
||||||
|
expect(headers.get("x-opencode-parent-session-id")).toBe(parentSessionID ?? null)
|
||||||
|
expect(headers.get("x-parent-session-id")).toBe(parentSessionID ?? null)
|
||||||
|
if (input.providerID.startsWith("opencode")) {
|
||||||
|
expect(headers.get("x-opencode-session")).toBe(sessionID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
expect(headers.get("x-session-affinity")).toBe(sessionID)
|
||||||
|
expect(headers.get("X-Session-Id")).toBe(sessionID)
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
config: () => {
|
||||||
|
const fixture = loadFixture(vivgridFixture.providerID, vivgridFixture.modelID)
|
||||||
|
return {
|
||||||
|
enabled_providers: [input.providerID],
|
||||||
|
provider: {
|
||||||
|
[input.providerID]: {
|
||||||
|
name: "OpenCode Test",
|
||||||
|
npm: "@ai-sdk/openai-compatible",
|
||||||
|
models: { [fixture.model.id]: configModel(fixture.model) as ConfigModel },
|
||||||
|
options: { apiKey: "test-key", baseURL: `${state.server!.url.origin}/v1` },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
)
|
||||||
)
|
})
|
||||||
|
|
||||||
it.instance(
|
it.instance(
|
||||||
"sends temperature, tokens, and reasoning options for openai-compatible models",
|
"sends temperature, tokens, and reasoning options for openai-compatible models",
|
||||||
|
|||||||
Reference in New Issue
Block a user