File size: 2,132 Bytes
f6678ab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import { streamText, convertToModelMessages } from "ai";
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
import type { Request, Response } from "express";

export const DEFAULT_MODEL = "google/gemini-2.5-flash";

export function getProvider() {
  const apiKey = process.env.OPENROUTER_API_KEY;
  if (!apiKey) {
    throw new Error("OPENROUTER_API_KEY environment variable is required");
  }
  return createOpenRouter({ apiKey });
}

interface StreamChatOptions {
  systemPrompt: string;
  tools: Parameters<typeof streamText>[0]["tools"];
  logPrefix: string;
}

export async function streamChatResponse(
  req: Request,
  res: Response,
  { systemPrompt, tools, logPrefix }: StreamChatOptions,
) {
  try {
    const { messages, context, model } = req.body;

    if (!messages || !Array.isArray(messages)) {
      res.status(400).json({ error: "messages array is required" });
      return;
    }

    const provider = getProvider();
    const modelId = model || process.env.OPENROUTER_MODEL || DEFAULT_MODEL;
    const modelMessages = await convertToModelMessages(messages);

    const result = streamText({
      model: provider.chat(modelId),
      system: systemPrompt,
      messages: modelMessages,
      tools,
    });

    const webResponse = result.toUIMessageStreamResponse({
      onError: (error) => {
        console.error(`[${logPrefix}] stream error:`, error);
        return error instanceof Error ? error.message : "Stream error";
      },
    });

    res.writeHead(
      webResponse.status,
      Object.fromEntries(webResponse.headers.entries()),
    );
    const reader = webResponse.body!.getReader();
    const pump = async (): Promise<void> => {
      const { done, value } = await reader.read();
      if (done) {
        res.end();
        return;
      }
      res.write(value);
      return pump();
    };
    await pump();
  } catch (error: unknown) {
    const message =
      error instanceof Error ? error.message : "Internal server error";
    console.error(`[${logPrefix}] error:`, message);

    if (!res.headersSent) {
      res.status(500).json({ error: message });
    }
  }
}