chat-nest-server 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +30 -11
  2. package/dist/index.js +45 -5
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,9 +1,10 @@
1
1
  # chat-nest-server
2
2
 
3
- > Streaming AI backend server for Chat Nest with built-in cost protection and cancellation propagation.
3
+ > Streaming AI backend server for Chat Nest with built-in cost protection and cancellation propagation using Server-Side Events (SSE).
4
4
 
5
5
  This package exposes an Express-compatible request handler that:
6
- - Streams AI responses
6
+ - Streams AI responses using Server-Side Events (SSE)
7
+ - Sends real-time tokens via SSE protocol
7
8
  - Enforces rate limits and budgets
8
9
  - Supports abort propagation
9
10
  - Protects against runaway usage
@@ -12,7 +13,10 @@ This package exposes an Express-compatible request handler that:
12
13
 
13
14
  ## ✨ Features
14
15
 
15
- - Streaming responses over HTTP
16
+ - Server-Side Events (SSE) streaming over HTTP
17
+ - Real-time token streaming via SSE protocol
18
+ - SSE event types: `start`, `token`, `done`, `error`, `ping`
19
+ - Heartbeat pings to keep connection alive
16
20
  - End-to-end cancellation support
17
21
  - Daily token budget enforcement
18
22
  - Rate limiting
@@ -31,6 +35,8 @@ npm install chat-nest-server
31
35
  ## 🚀 Usage
32
36
  Express Integration
33
37
 
38
+ The handler automatically uses Server-Side Events (SSE) for streaming responses:
39
+
34
40
  ```
35
41
  import express from "express";
36
42
  import cors from "cors";
@@ -53,6 +59,13 @@ app.listen(3001, () => {
53
59
  });
54
60
  ```
55
61
 
62
+ The handler sends SSE-formatted events:
63
+ - `event: start\ndata: \n\n` - Stream started
64
+ - `event: token\ndata: <token>\n\n` - Each token chunk
65
+ - `event: done\ndata: \n\n` - Stream completed
66
+ - `event: error\ndata: <error_json>\n\n` - Error occurred
67
+ - `event: ping\ndata: \n\n` - Heartbeat (every 15s)
68
+
56
69
  ---
57
70
 
58
71
  ## 🔐 Environment Variables
@@ -63,21 +76,27 @@ app.listen(3001, () => {
63
76
 
64
77
  ## 💰 Cost Controls
65
78
 
66
- ```
67
79
  The server enforces:
68
80
 
69
- Maximum tokens per request
81
+ - Maximum tokens per request
82
+ - Daily token budget
83
+ - Request rate limiting
84
+ - Prompt size trimming
85
+ - Retry classification
70
86
 
71
- Daily token budget
87
+ This prevents accidental overspending and abuse.
72
88
 
73
- Request rate limiting
89
+ ## 🔄 Server-Side Events (SSE)
74
90
 
75
- Prompt size trimming
91
+ This package uses SSE protocol for efficient streaming:
76
92
 
77
- Retry classification
93
+ - **Content-Type**: `text/event-stream`
94
+ - **Connection**: `keep-alive`
95
+ - **Cache-Control**: `no-cache`
96
+ - **Heartbeat**: Ping every 15 seconds to keep connection alive
97
+ - **Event Format**: `event: <type>\ndata: <data>\n\n`
78
98
 
79
- This prevents accidental overspending and abuse.
80
- ```
99
+ SSE provides better efficiency and real-time streaming compared to traditional polling or chunked responses.
81
100
 
82
101
  ---
83
102
 
package/dist/index.js CHANGED
@@ -64,7 +64,19 @@ function createChatHandler(config) {
64
64
  return async function handler(req, res) {
65
65
  const abortController = new AbortController();
66
66
  let streamStarted = false;
67
+ let heartbeatInterval = null;
68
+ const writeSSE = (event, data) => {
69
+ if (abortController.signal.aborted) return;
70
+ res.write(`event: ${event}
71
+ data: ${data}
72
+
73
+ `);
74
+ };
67
75
  res.on("close", () => {
76
+ if (heartbeatInterval) {
77
+ clearInterval(heartbeatInterval);
78
+ heartbeatInterval = null;
79
+ }
68
80
  if (streamStarted && !abortController.signal.aborted) {
69
81
  abortController.abort();
70
82
  }
@@ -90,8 +102,16 @@ function createChatHandler(config) {
90
102
  });
91
103
  return;
92
104
  }
93
- res.setHeader("Content-Type", "text/plain");
94
- res.setHeader("Transfer-Encoding", "chunked");
105
+ res.setHeader("Content-Type", "text/event-stream");
106
+ res.setHeader("Cache-Control", "no-cache");
107
+ res.setHeader("Connection", "keep-alive");
108
+ res.flushHeaders();
109
+ writeSSE("start", "");
110
+ heartbeatInterval = setInterval(() => {
111
+ if (!abortController.signal.aborted) {
112
+ writeSSE("ping", "");
113
+ }
114
+ }, 15e3);
95
115
  const stream = await client.chat.completions.create(
96
116
  {
97
117
  model: AI_MODEL,
@@ -115,25 +135,45 @@ function createChatHandler(config) {
115
135
  }
116
136
  const token = chunk.choices[0]?.delta?.content;
117
137
  if (token) {
118
- res.write(token);
138
+ writeSSE("token", JSON.stringify(token));
119
139
  }
120
140
  }
141
+ writeSSE("done", "");
121
142
  } catch (error) {
122
143
  if (abortController.signal.aborted) {
123
144
  console.log("Stream aborted by client");
124
145
  } else {
125
- throw error;
146
+ const errorData = JSON.stringify({
147
+ message: error instanceof Error ? error.message : "Unknown error"
148
+ });
149
+ writeSSE("error", errorData);
126
150
  }
127
151
  } finally {
152
+ if (heartbeatInterval) {
153
+ clearInterval(heartbeatInterval);
154
+ heartbeatInterval = null;
155
+ }
128
156
  recordTokenUsage(estimatedTotalTokens);
129
157
  res.end();
130
158
  }
131
159
  } catch (error) {
132
160
  if (error?.name === "AbortError") {
161
+ if (heartbeatInterval) {
162
+ clearInterval(heartbeatInterval);
163
+ heartbeatInterval = null;
164
+ }
133
165
  return;
134
166
  }
135
167
  console.error("AI error:", error);
136
- res.status(500).json({ error: "AI request failed" });
168
+ if (!res.headersSent) {
169
+ res.status(500).json({ error: "AI request failed" });
170
+ } else {
171
+ const errorData = JSON.stringify({
172
+ message: "AI request failed"
173
+ });
174
+ writeSSE("error", errorData);
175
+ res.end();
176
+ }
137
177
  }
138
178
  };
139
179
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "chat-nest-server",
3
3
  "description": "Streaming AI backend server with cost controls, rate limiting, and cancellation support.",
4
- "version": "1.0.1",
4
+ "version": "1.1.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "license": "ISC",