Skip to content

Commit c335190

Browse files
committed
refactor: simplify header generation and improve session management logic
1 parent 3ef0e0c commit c335190

4 files changed

Lines changed: 15 additions & 70 deletions

File tree

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,9 +360,8 @@ copilot-api start --header-mode savings
360360
361361
VS Code Copilot extension compatibility:
362362
363-
- **Headers**: Both `X-Initiator` and `X-Interaction-Id`
364-
- **Logic**: Replicates VS Code extension's behavior(sort of)
365-
- **Session Management**: UUID-based session tracking
363+
- **Headers**: `X-Initiator`
364+
- **Logic**: If last message is user then it is user, otherwise agent
366365
- **Use case**: Use this when you want to mimic the behavior of the VS Code extension to avoid potential abuse detection. (I am not sure if they do that, but it's better to be cautious.)
367366

368367
```bash

src/lib/headers.ts

Lines changed: 10 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,28 @@
1-
import { randomUUID } from "node:crypto"
2-
31
import type { ChatCompletionsPayload } from "~/services/copilot/create-chat-completions"
42

53
export type HeaderMode = "savings" | "compatible"
64

75
export interface RequestHeaders {
86
"X-Initiator": "user" | "agent"
9-
"X-Interaction-Id"?: string
107
}
118

12-
/**
13-
* Simple session manager for compatible mode
14-
*/
15-
class SessionManager {
16-
private sessionId: string = randomUUID()
17-
18-
newSession(): string {
19-
this.sessionId = randomUUID()
20-
return this.sessionId
21-
}
22-
23-
getCurrentSession(): string {
24-
return this.sessionId
25-
}
26-
}
27-
28-
const sessionManager = new SessionManager()
29-
309
/**
3110
* Generate headers based on mode
3211
*/
3312
export function generateSessionHeaders(
3413
payload: ChatCompletionsPayload,
3514
mode: HeaderMode,
3615
): RequestHeaders {
37-
return mode === "savings" ?
38-
{
39-
"X-Initiator": getSavingsInitiator(payload),
40-
}
41-
: {
42-
"X-Initiator": getCompatibleInitiator(payload),
43-
"X-Interaction-Id": getSessionId(payload),
44-
}
16+
return {
17+
"X-Initiator":
18+
mode === "savings" ?
19+
getSavingsInitiator(payload)
20+
: getCompatibleInitiator(payload),
21+
}
4522
}
4623

4724
/**
48-
* Savings mode: default behavior
25+
* Savings mode: if any message is assistant or tool, then it is agent
4926
*/
5027
function getSavingsInitiator(
5128
payload: ChatCompletionsPayload,
@@ -58,42 +35,11 @@ function getSavingsInitiator(
5835
}
5936

6037
/**
61-
* Compatible mode: replicate VS Code extension logic
38+
* Compatible mode: if last message is user then it is user
6239
*/
6340
function getCompatibleInitiator(
6441
payload: ChatCompletionsPayload,
6542
): "user" | "agent" {
66-
// VS Code: userInitiatedRequest = iterationNumber === 0 && !isContinuation
67-
const hasAssistantMessage = payload.messages.some(
68-
(msg) => msg.role === "assistant",
69-
)
70-
const hasToolCalls = payload.messages.some(
71-
(msg) => msg.tool_calls && msg.tool_calls.length > 0,
72-
)
73-
const hasToolMessages = payload.messages.some((msg) => msg.role === "tool")
74-
75-
const isFirstIteration = !hasAssistantMessage
76-
const isContinuation = hasToolCalls || hasToolMessages
77-
78-
return isFirstIteration && !isContinuation ? "user" : "agent"
79-
}
80-
81-
/**
82-
* Detect if this is the start of a new conversation
83-
*/
84-
function isStartOfConversation(payload: ChatCompletionsPayload): boolean {
85-
const hasAssistantMessage = payload.messages.some(
86-
(msg) => msg.role === "assistant",
87-
)
88-
return !hasAssistantMessage
89-
}
90-
91-
/**
92-
* Get session ID for compatible mode
93-
*/
94-
function getSessionId(payload: ChatCompletionsPayload): string {
95-
if (isStartOfConversation(payload)) {
96-
return sessionManager.newSession()
97-
}
98-
return sessionManager.getCurrentSession()
43+
const lastMessage = payload.messages.at(-1)
44+
return lastMessage?.role === "user" ? "user" : "agent"
9945
}

src/services/copilot/create-chat-completions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ export const createChatCompletions = async (
2323
// Build headers
2424
const headers: Record<string, string> = {
2525
...copilotHeaders(state, enableVision),
26-
...sessionHeaders, // This includes X-Initiator and optionally X-Interaction-Id
26+
...sessionHeaders, // This includes X-Initiator
2727
}
2828

2929
// Optional: Add debug logging
3030
if (state.headerMode === "compatible") {
3131
consola.debug(
32-
`Compatible mode headers: X-Initiator=${sessionHeaders["X-Initiator"]}, X-Interaction-Id=${sessionHeaders["X-Interaction-Id"]?.slice(0, 8)}...`,
32+
`Compatible mode headers: X-Initiator=${sessionHeaders["X-Initiator"]}`,
3333
)
3434
}
3535

src/start.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export async function runServer(options: RunServerOptions): Promise<void> {
4646
state.headerMode = options.headerMode as "savings" | "compatible"
4747

4848
if (state.headerMode === "compatible") {
49-
consola.info("Using compatible mode - full VS Code extension compatibility")
49+
consola.info("Using compatible mode - VS Code extension compatibility")
5050
} else {
5151
consola.info("Using savings mode - optimized for premium requests savings")
5252
}

0 commit comments

Comments
 (0)