移除对 copilot 的兼容。更新示例和文档,统一使用 session_id 代替 conversationId
This commit is contained in:
@@ -18,7 +18,7 @@ export class ChatSessionBridge {
|
||||
) {}
|
||||
|
||||
async resolve(context: {
|
||||
conversationId?: string;
|
||||
clientSessionId?: string;
|
||||
accessToken?: string;
|
||||
projectId?: string;
|
||||
traceId?: string;
|
||||
@@ -28,7 +28,8 @@ export class ChatSessionBridge {
|
||||
created: boolean;
|
||||
}> {
|
||||
const requestContext: ChatRequestContext = {
|
||||
conversationId: context.conversationId?.trim() || `conv-${randomUUID().slice(0, 12)}`,
|
||||
clientSessionId:
|
||||
context.clientSessionId?.trim() || `agent-${randomUUID().slice(0, 12)}`,
|
||||
accessToken: context.accessToken,
|
||||
projectId: context.projectId,
|
||||
traceId: context.traceId?.trim() || `trace-${randomUUID().slice(0, 12)}`,
|
||||
@@ -46,7 +47,7 @@ export class ChatSessionBridge {
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
{
|
||||
conversationId: requestContext.conversationId,
|
||||
clientSessionId: requestContext.clientSessionId,
|
||||
sessionId: current.sessionId,
|
||||
err: error,
|
||||
},
|
||||
@@ -55,7 +56,7 @@ export class ChatSessionBridge {
|
||||
}
|
||||
}
|
||||
|
||||
const session = await this.runtime.createSession(requestContext.conversationId);
|
||||
const session = await this.runtime.createSession(requestContext.clientSessionId);
|
||||
const binding = this.registry.upsert(requestContext, session.id);
|
||||
this.sessionContexts.set(binding.sessionId, requestContext);
|
||||
return { binding, requestContext, created: true };
|
||||
|
||||
+11
-11
@@ -8,7 +8,7 @@ import { type ChatSessionBridge } from "../chat/sessionBridge.js";
|
||||
|
||||
const payloadSchema = z.object({
|
||||
message: z.string().min(1).max(10000),
|
||||
conversation_id: z.string().max(128).optional(),
|
||||
session_id: z.string().max(128).optional(),
|
||||
});
|
||||
|
||||
export const buildChatRouter = (
|
||||
@@ -36,7 +36,7 @@ export const buildChatRouter = (
|
||||
const traceId = req.header("x-trace-id") ?? undefined;
|
||||
|
||||
const { binding, requestContext, created } = await sessionBridge.resolve({
|
||||
conversationId: parsed.data.conversation_id,
|
||||
clientSessionId: parsed.data.session_id,
|
||||
accessToken,
|
||||
projectId,
|
||||
traceId,
|
||||
@@ -44,7 +44,7 @@ export const buildChatRouter = (
|
||||
|
||||
logger.info(
|
||||
{
|
||||
conversationId: requestContext.conversationId,
|
||||
clientSessionId: requestContext.clientSessionId,
|
||||
sessionId: binding.sessionId,
|
||||
created,
|
||||
traceId: requestContext.traceId,
|
||||
@@ -53,7 +53,7 @@ export const buildChatRouter = (
|
||||
"processing chat request",
|
||||
);
|
||||
|
||||
// 当前先走“发送 prompt 后回读最近消息”的兼容实现。
|
||||
// 当前先走“发送 prompt 后回读最近消息”的 opencode SDK 实现。
|
||||
// 后续切到真正的 opencode 事件流时,只需要替换这里的取数方式。
|
||||
const messages = await runtime.sendPrompt(binding.sessionId, parsed.data.message);
|
||||
const assistantMessage = messages.find(
|
||||
@@ -66,13 +66,13 @@ export const buildChatRouter = (
|
||||
res.setHeader("Connection", "keep-alive");
|
||||
res.setHeader("X-Accel-Buffering", "no");
|
||||
|
||||
const conversationId = requestContext.conversationId;
|
||||
const clientSessionId = requestContext.clientSessionId;
|
||||
const parts = assistantMessage?.parts ?? [];
|
||||
const textContent = collectTextContent(parts);
|
||||
if (textContent) {
|
||||
res.write(
|
||||
toSse("token", {
|
||||
conversationId,
|
||||
session_id: clientSessionId,
|
||||
content: textContent,
|
||||
}),
|
||||
);
|
||||
@@ -81,7 +81,7 @@ export const buildChatRouter = (
|
||||
for (const toolCall of collectToolCalls(parts)) {
|
||||
res.write(
|
||||
toSse("tool_call", {
|
||||
conversationId,
|
||||
session_id: clientSessionId,
|
||||
tool: toolCall.tool,
|
||||
params: toolCall.params,
|
||||
}),
|
||||
@@ -91,7 +91,7 @@ export const buildChatRouter = (
|
||||
if (assistantMessage?.info.role === "assistant" && assistantMessage.info.error) {
|
||||
res.write(
|
||||
toSse("error", {
|
||||
conversationId,
|
||||
session_id: clientSessionId,
|
||||
message: getErrorMessage(assistantMessage.info.error),
|
||||
detail: assistantMessage.info.error.name,
|
||||
}),
|
||||
@@ -99,13 +99,13 @@ export const buildChatRouter = (
|
||||
} else if (!assistantMessage) {
|
||||
res.write(
|
||||
toSse("error", {
|
||||
conversationId,
|
||||
session_id: clientSessionId,
|
||||
message: "assistant response unavailable",
|
||||
detail: "no assistant message found after prompt",
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
res.write(toSse("done", { conversationId }));
|
||||
res.write(toSse("done", { session_id: clientSessionId }));
|
||||
}
|
||||
|
||||
res.end();
|
||||
@@ -125,7 +125,7 @@ export const buildChatRouter = (
|
||||
const toSse = (event: string, data: Record<string, unknown>) =>
|
||||
`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
|
||||
|
||||
// 先把 opencode 的 Part 结构压平成前端当前消费的 SSE 语义。
|
||||
// 先把 opencode 的 Part 结构压平成 Agent API 的 SSE 语义。
|
||||
const collectTextContent = (parts: Part[]) =>
|
||||
parts
|
||||
.filter((part): part is Extract<Part, { type: "text" }> => part.type === "text")
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ app.post("/internal/tools/dynamic-http-call", async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
app.use("/api/v1/copilot/chat", buildChatRouter(sessionBridge, opencodeRuntime));
|
||||
app.use("/api/v1/agent/chat", buildChatRouter(sessionBridge, opencodeRuntime));
|
||||
|
||||
const server = app.listen(config.PORT, config.HOST, () => {
|
||||
logger.info(
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import crypto from "node:crypto";
|
||||
|
||||
export type SessionBinding = {
|
||||
conversationId: string;
|
||||
clientSessionId: string;
|
||||
sessionId: string;
|
||||
lastUsedAt: number;
|
||||
};
|
||||
|
||||
export type SessionContext = {
|
||||
conversationId: string;
|
||||
clientSessionId: string;
|
||||
accessToken?: string;
|
||||
projectId?: string;
|
||||
};
|
||||
@@ -22,7 +22,7 @@ export class SessionRegistry {
|
||||
|
||||
upsert(context: SessionContext, sessionId: string): SessionBinding {
|
||||
const binding: SessionBinding = {
|
||||
conversationId: context.conversationId,
|
||||
clientSessionId: context.clientSessionId,
|
||||
sessionId,
|
||||
lastUsedAt: Date.now(),
|
||||
};
|
||||
@@ -62,12 +62,12 @@ export class SessionRegistry {
|
||||
}
|
||||
|
||||
private makeKey(context: SessionContext): string {
|
||||
// 会话隔离不能只看 conversationId;同一浏览器会话切换用户或项目时必须映射到不同 opencode session。
|
||||
// 会话隔离不能只看前端 session_id;同一浏览器会话切换用户或项目时必须映射到不同 opencode session。
|
||||
const digest = crypto
|
||||
.createHash("sha256")
|
||||
.update(
|
||||
[
|
||||
context.conversationId,
|
||||
context.clientSessionId,
|
||||
context.accessToken ?? "",
|
||||
context.projectId ?? "",
|
||||
].join("|"),
|
||||
|
||||
Reference in New Issue
Block a user