@novaqore/ai 0.2.0 → 0.3.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.
- package/README.md +8 -6
- package/index.js +46 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -137,11 +137,11 @@ import NovaQoreAI from "@novaqore/ai";
|
|
|
137
137
|
|
|
138
138
|
const nq = new NovaQoreAI();
|
|
139
139
|
|
|
140
|
-
const
|
|
140
|
+
const { result } = await nq.chat([
|
|
141
141
|
{ role: "user", content: "Hello" }
|
|
142
142
|
]);
|
|
143
143
|
|
|
144
|
-
console.log(
|
|
144
|
+
console.log(result.choices[0].message.content);
|
|
145
145
|
```
|
|
146
146
|
|
|
147
147
|
NovaQore AI auto-detects the service file. If no service file is found, it falls back to environment variables automatically.
|
|
@@ -177,7 +177,7 @@ If you call `new NovaQoreAI()` with no arguments and no service file is present,
|
|
|
177
177
|
### System prompt
|
|
178
178
|
|
|
179
179
|
```javascript
|
|
180
|
-
const
|
|
180
|
+
const { result } = await nq.chat([
|
|
181
181
|
{ role: "system", content: "You are a helpful assistant that responds in haikus." },
|
|
182
182
|
{ role: "user", content: "Tell me about the ocean" }
|
|
183
183
|
]);
|
|
@@ -186,7 +186,7 @@ const res = await nq.chat([
|
|
|
186
186
|
### Tool use
|
|
187
187
|
|
|
188
188
|
```javascript
|
|
189
|
-
const
|
|
189
|
+
const { result } = await nq.chat(
|
|
190
190
|
[{ role: "user", content: "What's the weather in NYC?" }],
|
|
191
191
|
{
|
|
192
192
|
tools: [
|
|
@@ -208,7 +208,7 @@ const res = await nq.chat(
|
|
|
208
208
|
}
|
|
209
209
|
);
|
|
210
210
|
|
|
211
|
-
const toolCall =
|
|
211
|
+
const toolCall = result.choices[0].message.tool_calls[0];
|
|
212
212
|
console.log(toolCall.function.name); // "get_weather"
|
|
213
213
|
console.log(toolCall.function.arguments); // '{"location":"New York City"}'
|
|
214
214
|
```
|
|
@@ -216,7 +216,7 @@ console.log(toolCall.function.arguments); // '{"location":"New York City"}'
|
|
|
216
216
|
### Streaming
|
|
217
217
|
|
|
218
218
|
```javascript
|
|
219
|
-
const stream = await nq.chat(
|
|
219
|
+
const { stream, stop } = await nq.chat(
|
|
220
220
|
[{ role: "user", content: "Tell me about the ocean" }],
|
|
221
221
|
{ stream: true }
|
|
222
222
|
);
|
|
@@ -228,6 +228,8 @@ for await (const chunk of stream) {
|
|
|
228
228
|
console.log();
|
|
229
229
|
```
|
|
230
230
|
|
|
231
|
+
Call `stop()` at any time to abort the stream (e.g. wire it to a stop button in your UI).
|
|
232
|
+
|
|
231
233
|
### Options
|
|
232
234
|
|
|
233
235
|
| Option | Type | Default | Description |
|
package/index.js
CHANGED
|
@@ -10,7 +10,9 @@ class NovaQoreAI {
|
|
|
10
10
|
#uid;
|
|
11
11
|
#quantumKey;
|
|
12
12
|
#keyId;
|
|
13
|
-
#
|
|
13
|
+
#authToken;
|
|
14
|
+
#getAuthToken;
|
|
15
|
+
#abort;
|
|
14
16
|
|
|
15
17
|
constructor(config) {
|
|
16
18
|
if (config === undefined) {
|
|
@@ -46,12 +48,17 @@ class NovaQoreAI {
|
|
|
46
48
|
this.#uid = config.uid;
|
|
47
49
|
this.#quantumKey = config.quantumKey;
|
|
48
50
|
this.#keyId = config.keyId;
|
|
49
|
-
this.#
|
|
51
|
+
this.#authToken = config.authToken || null;
|
|
52
|
+
this.#getAuthToken = config.getAuthToken || null;
|
|
50
53
|
this.version = version;
|
|
51
54
|
this.description = "NovaQore AI - Quantum-encrypted LLM client by NovaQore";
|
|
52
55
|
this.methods = ["chat", "health"];
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
setAuthToken(token) {
|
|
59
|
+
this.#authToken = token;
|
|
60
|
+
}
|
|
61
|
+
|
|
55
62
|
async #encryptPayload(payload) {
|
|
56
63
|
const quantumKey = new Uint8Array(Buffer.from(this.#quantumKey, "base64"));
|
|
57
64
|
|
|
@@ -88,6 +95,8 @@ class NovaQoreAI {
|
|
|
88
95
|
throw new Error("Messages must be a non-empty array");
|
|
89
96
|
}
|
|
90
97
|
|
|
98
|
+
this.#abort = new AbortController();
|
|
99
|
+
|
|
91
100
|
try {
|
|
92
101
|
const payload = {
|
|
93
102
|
messages,
|
|
@@ -100,8 +109,11 @@ class NovaQoreAI {
|
|
|
100
109
|
const { ciphertext, encrypted, sharedSecret } = await this.#encryptPayload(payload);
|
|
101
110
|
|
|
102
111
|
const headers = { "Content-Type": "application/json" };
|
|
103
|
-
if (this.#
|
|
104
|
-
|
|
112
|
+
if (this.#getAuthToken) {
|
|
113
|
+
this.#authToken = await this.#getAuthToken();
|
|
114
|
+
}
|
|
115
|
+
if (this.#authToken) {
|
|
116
|
+
headers["Authorization"] = `Bearer ${this.#authToken}`;
|
|
105
117
|
}
|
|
106
118
|
|
|
107
119
|
const res = await fetch(`${BASE_URL}/v1/chat/completions`, {
|
|
@@ -113,6 +125,7 @@ class NovaQoreAI {
|
|
|
113
125
|
ciphertext,
|
|
114
126
|
encrypted,
|
|
115
127
|
}),
|
|
128
|
+
signal: this.#abort.signal,
|
|
116
129
|
});
|
|
117
130
|
|
|
118
131
|
if (!res.ok) {
|
|
@@ -127,12 +140,16 @@ class NovaQoreAI {
|
|
|
127
140
|
}
|
|
128
141
|
|
|
129
142
|
if (options.stream) {
|
|
130
|
-
|
|
143
|
+
const stream = this.#readStream(res, sharedSecret);
|
|
144
|
+
const stop = () => this.#abort.abort();
|
|
145
|
+
return { stream, stop };
|
|
131
146
|
}
|
|
132
147
|
|
|
133
148
|
const { encrypted: encryptedResponse } = await res.json();
|
|
134
|
-
|
|
149
|
+
const result = this.#decryptResponse(encryptedResponse, sharedSecret);
|
|
150
|
+
return { result, stop: () => {} };
|
|
135
151
|
} catch (err) {
|
|
152
|
+
if (err.name === "AbortError") return;
|
|
136
153
|
if (err.message) throw err;
|
|
137
154
|
throw new Error("Failed to connect to NovaQore AI");
|
|
138
155
|
}
|
|
@@ -144,31 +161,36 @@ class NovaQoreAI {
|
|
|
144
161
|
let buffer = "";
|
|
145
162
|
let expectedSeq = 0;
|
|
146
163
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
164
|
+
try {
|
|
165
|
+
while (true) {
|
|
166
|
+
const { done, value } = await reader.read();
|
|
167
|
+
if (done) break;
|
|
168
|
+
buffer += decoder.decode(value, { stream: true });
|
|
151
169
|
|
|
152
|
-
|
|
153
|
-
|
|
170
|
+
const lines = buffer.split("\n");
|
|
171
|
+
buffer = lines.pop();
|
|
154
172
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
173
|
+
for (const line of lines) {
|
|
174
|
+
const trimmed = line.trim();
|
|
175
|
+
if (!trimmed || !trimmed.startsWith("data: ")) continue;
|
|
176
|
+
const data = trimmed.slice(6);
|
|
159
177
|
|
|
160
|
-
|
|
178
|
+
if (data === "[DONE]") return;
|
|
161
179
|
|
|
162
|
-
|
|
163
|
-
|
|
180
|
+
const { encrypted } = JSON.parse(data);
|
|
181
|
+
const chunk = this.#decryptResponse(encrypted, sharedSecret);
|
|
164
182
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
183
|
+
if (chunk.seq !== expectedSeq) {
|
|
184
|
+
throw new Error(`Chunk out of order: expected ${expectedSeq}, got ${chunk.seq}`);
|
|
185
|
+
}
|
|
186
|
+
expectedSeq++;
|
|
169
187
|
|
|
170
|
-
|
|
188
|
+
yield chunk;
|
|
189
|
+
}
|
|
171
190
|
}
|
|
191
|
+
} catch (err) {
|
|
192
|
+
if (err.name === "AbortError") return;
|
|
193
|
+
throw err;
|
|
172
194
|
}
|
|
173
195
|
}
|
|
174
196
|
|
package/package.json
CHANGED