mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
feat: Wire bot to real CoPilot streaming via bot chat proxy
Replaces the echo handler with actual CoPilot integration: - platform-api.ts: now calls /api/platform-linking/chat/session and /api/platform-linking/chat/stream (bot API key auth, no user JWT) - bot.ts: creates sessions on first message, streams CoPilot responses directly to the chat platform via thread.post(stream) - Adds PLATFORM_BOT_API_KEY support in bot headers The full flow now works: User messages bot → resolve user → create session → stream CoPilot → post response
This commit is contained in:
@@ -190,26 +190,27 @@ async function handleCoPilotMessage(
|
||||
const state = await thread.state;
|
||||
let sessionId = state?.sessionId;
|
||||
|
||||
// TODO: For now, we need a way to get a user token to call the chat API.
|
||||
// This will require either:
|
||||
// 1. A service-to-service token exchange endpoint
|
||||
// 2. Storing user tokens during the linking flow
|
||||
// 3. A bot-specific chat endpoint that accepts user_id directly
|
||||
//
|
||||
// For the MVP, we'll echo back to prove the pipeline works.
|
||||
// The CoPilot integration comes in the next iteration.
|
||||
|
||||
console.log(
|
||||
`[bot] Message from user ${userId.slice(-8)}: ${text.slice(0, 100)}`
|
||||
);
|
||||
|
||||
await thread.startTyping();
|
||||
|
||||
// MVP: Echo back with user info to prove linking works
|
||||
await thread.post(
|
||||
`✅ **Connected as AutoGPT user** \`${userId.slice(-8)}\`\n\n` +
|
||||
`> ${text}\n\n` +
|
||||
`_CoPilot integration coming soon. ` +
|
||||
`Session: ${sessionId ?? "new"}_`
|
||||
);
|
||||
try {
|
||||
// Create a session if we don't have one
|
||||
if (!sessionId) {
|
||||
sessionId = await api.createChatSession(userId);
|
||||
await thread.setState({ ...state, sessionId });
|
||||
console.log(`[bot] Created session ${sessionId} for user ${userId.slice(-8)}`);
|
||||
}
|
||||
|
||||
// Stream CoPilot response directly to the chat platform
|
||||
const stream = api.streamChat(userId, text, sessionId);
|
||||
await thread.post(stream);
|
||||
} catch (err: any) {
|
||||
console.error(`[bot] CoPilot error for user ${userId.slice(-8)}:`, err.message);
|
||||
await thread.post(
|
||||
"Sorry, I ran into an issue processing your message. Please try again."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,17 +100,24 @@ export class PlatformAPI {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new CoPilot chat session for a user.
|
||||
* Returns the session ID.
|
||||
* Create a new CoPilot chat session for a linked user.
|
||||
* Uses the bot chat proxy (no user JWT needed).
|
||||
*/
|
||||
async createChatSession(userToken: string): Promise<string> {
|
||||
const res = await fetch(`${this.baseUrl}/api/chat/sessions`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${userToken}`,
|
||||
},
|
||||
});
|
||||
async createChatSession(userId: string): Promise<string> {
|
||||
const res = await fetch(
|
||||
`${this.baseUrl}/api/platform-linking/chat/session`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...this.botHeaders(),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
user_id: userId,
|
||||
message: "session_init",
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(
|
||||
@@ -119,30 +126,32 @@ export class PlatformAPI {
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
return data.id;
|
||||
return data.session_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream a chat message to CoPilot and yield text chunks.
|
||||
* Uses SSE (Server-Sent Events) to stream the response.
|
||||
* Stream a chat message to CoPilot on behalf of a linked user.
|
||||
* Uses the bot chat proxy — authenticated via bot API key.
|
||||
* Yields text chunks from the SSE stream.
|
||||
*/
|
||||
async *streamChat(
|
||||
sessionId: string,
|
||||
userId: string,
|
||||
message: string,
|
||||
userToken: string
|
||||
sessionId?: string
|
||||
): AsyncGenerator<string> {
|
||||
const res = await fetch(
|
||||
`${this.baseUrl}/api/chat/sessions/${sessionId}/stream`,
|
||||
`${this.baseUrl}/api/platform-linking/chat/stream`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${userToken}`,
|
||||
Accept: "text/event-stream",
|
||||
...this.botHeaders(),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
user_id: userId,
|
||||
message,
|
||||
is_user_message: true,
|
||||
session_id: sessionId,
|
||||
}),
|
||||
}
|
||||
);
|
||||
@@ -168,7 +177,6 @@ export class PlatformAPI {
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split("\n");
|
||||
// Keep the last potentially incomplete line in the buffer
|
||||
buffer = lines.pop() ?? "";
|
||||
|
||||
for (const line of lines) {
|
||||
@@ -178,14 +186,12 @@ export class PlatformAPI {
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
// Extract text content from SSE events
|
||||
if (parsed.type === "text" && parsed.content) {
|
||||
yield parsed.content;
|
||||
} else if (typeof parsed === "string") {
|
||||
yield parsed;
|
||||
}
|
||||
} catch {
|
||||
// Non-JSON data line, yield as-is if it has content
|
||||
if (data && data !== "[DONE]") {
|
||||
yield data;
|
||||
}
|
||||
@@ -194,4 +200,12 @@ export class PlatformAPI {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private botHeaders(): Record<string, string> {
|
||||
const key = process.env.PLATFORM_BOT_API_KEY;
|
||||
if (key) {
|
||||
return { "X-Bot-API-Key": key };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user