@novaqore/ai 0.1.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 +10 -8
- package/index.js +50 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<a href="https://www.npmjs.com/package/@novaqore/ai"><img src="https://img.shields.io/npm/v/@novaqore/ai?color=blue&label=npm" alt="npm version" /></a>
|
|
11
11
|
<a href="https://github.com/novaqore/ai/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="license" /></a>
|
|
12
12
|
<a href="https://img.shields.io/node/v/@novaqore/ai"><img src="https://img.shields.io/node/v/@novaqore/ai?color=brightgreen" alt="node version" /></a>
|
|
13
|
-
<a href="https://discord.gg/
|
|
13
|
+
<a href="https://discord.gg/JAzgcf6r"><img src="https://img.shields.io/discord/1234567890?color=5865F2&label=discord&logo=discord&logoColor=white" alt="Discord" /></a>
|
|
14
14
|
<br><br>
|
|
15
15
|
<a href="https://novaqore.ai/developer"><strong>🔑 Get Your Quantum Keys</strong></a> |
|
|
16
16
|
<a href="https://novaqore.ai"><strong>🌐 Website</strong></a> |
|
|
@@ -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 |
|
|
@@ -272,7 +274,7 @@ Technical deep dives into the cryptography, architecture, and threat models behi
|
|
|
272
274
|
We are building this in the open. Join us.
|
|
273
275
|
|
|
274
276
|
<p>
|
|
275
|
-
<a href="https://discord.gg/
|
|
277
|
+
<a href="https://discord.gg/JAzgcf6r"><strong>Discord</strong></a> |
|
|
276
278
|
<a href="https://x.com/novaqore"><strong>X (Twitter)</strong></a> |
|
|
277
279
|
<a href="https://tiktok.com/@novaqore"><strong>TikTok</strong></a> |
|
|
278
280
|
<a href="https://youtube.com/@novaqore"><strong>YouTube</strong></a>
|
package/index.js
CHANGED
|
@@ -10,6 +10,9 @@ class NovaQoreAI {
|
|
|
10
10
|
#uid;
|
|
11
11
|
#quantumKey;
|
|
12
12
|
#keyId;
|
|
13
|
+
#authToken;
|
|
14
|
+
#getAuthToken;
|
|
15
|
+
#abort;
|
|
13
16
|
|
|
14
17
|
constructor(config) {
|
|
15
18
|
if (config === undefined) {
|
|
@@ -45,11 +48,17 @@ class NovaQoreAI {
|
|
|
45
48
|
this.#uid = config.uid;
|
|
46
49
|
this.#quantumKey = config.quantumKey;
|
|
47
50
|
this.#keyId = config.keyId;
|
|
51
|
+
this.#authToken = config.authToken || null;
|
|
52
|
+
this.#getAuthToken = config.getAuthToken || null;
|
|
48
53
|
this.version = version;
|
|
49
54
|
this.description = "NovaQore AI - Quantum-encrypted LLM client by NovaQore";
|
|
50
55
|
this.methods = ["chat", "health"];
|
|
51
56
|
}
|
|
52
57
|
|
|
58
|
+
setAuthToken(token) {
|
|
59
|
+
this.#authToken = token;
|
|
60
|
+
}
|
|
61
|
+
|
|
53
62
|
async #encryptPayload(payload) {
|
|
54
63
|
const quantumKey = new Uint8Array(Buffer.from(this.#quantumKey, "base64"));
|
|
55
64
|
|
|
@@ -86,6 +95,8 @@ class NovaQoreAI {
|
|
|
86
95
|
throw new Error("Messages must be a non-empty array");
|
|
87
96
|
}
|
|
88
97
|
|
|
98
|
+
this.#abort = new AbortController();
|
|
99
|
+
|
|
89
100
|
try {
|
|
90
101
|
const payload = {
|
|
91
102
|
messages,
|
|
@@ -97,15 +108,24 @@ class NovaQoreAI {
|
|
|
97
108
|
|
|
98
109
|
const { ciphertext, encrypted, sharedSecret } = await this.#encryptPayload(payload);
|
|
99
110
|
|
|
111
|
+
const headers = { "Content-Type": "application/json" };
|
|
112
|
+
if (this.#getAuthToken) {
|
|
113
|
+
this.#authToken = await this.#getAuthToken();
|
|
114
|
+
}
|
|
115
|
+
if (this.#authToken) {
|
|
116
|
+
headers["Authorization"] = `Bearer ${this.#authToken}`;
|
|
117
|
+
}
|
|
118
|
+
|
|
100
119
|
const res = await fetch(`${BASE_URL}/v1/chat/completions`, {
|
|
101
120
|
method: "POST",
|
|
102
|
-
headers
|
|
121
|
+
headers,
|
|
103
122
|
body: JSON.stringify({
|
|
104
123
|
uid: this.#uid,
|
|
105
124
|
keyId: this.#keyId,
|
|
106
125
|
ciphertext,
|
|
107
126
|
encrypted,
|
|
108
127
|
}),
|
|
128
|
+
signal: this.#abort.signal,
|
|
109
129
|
});
|
|
110
130
|
|
|
111
131
|
if (!res.ok) {
|
|
@@ -120,12 +140,16 @@ class NovaQoreAI {
|
|
|
120
140
|
}
|
|
121
141
|
|
|
122
142
|
if (options.stream) {
|
|
123
|
-
|
|
143
|
+
const stream = this.#readStream(res, sharedSecret);
|
|
144
|
+
const stop = () => this.#abort.abort();
|
|
145
|
+
return { stream, stop };
|
|
124
146
|
}
|
|
125
147
|
|
|
126
148
|
const { encrypted: encryptedResponse } = await res.json();
|
|
127
|
-
|
|
149
|
+
const result = this.#decryptResponse(encryptedResponse, sharedSecret);
|
|
150
|
+
return { result, stop: () => {} };
|
|
128
151
|
} catch (err) {
|
|
152
|
+
if (err.name === "AbortError") return;
|
|
129
153
|
if (err.message) throw err;
|
|
130
154
|
throw new Error("Failed to connect to NovaQore AI");
|
|
131
155
|
}
|
|
@@ -137,31 +161,36 @@ class NovaQoreAI {
|
|
|
137
161
|
let buffer = "";
|
|
138
162
|
let expectedSeq = 0;
|
|
139
163
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
164
|
+
try {
|
|
165
|
+
while (true) {
|
|
166
|
+
const { done, value } = await reader.read();
|
|
167
|
+
if (done) break;
|
|
168
|
+
buffer += decoder.decode(value, { stream: true });
|
|
144
169
|
|
|
145
|
-
|
|
146
|
-
|
|
170
|
+
const lines = buffer.split("\n");
|
|
171
|
+
buffer = lines.pop();
|
|
147
172
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
173
|
+
for (const line of lines) {
|
|
174
|
+
const trimmed = line.trim();
|
|
175
|
+
if (!trimmed || !trimmed.startsWith("data: ")) continue;
|
|
176
|
+
const data = trimmed.slice(6);
|
|
152
177
|
|
|
153
|
-
|
|
178
|
+
if (data === "[DONE]") return;
|
|
154
179
|
|
|
155
|
-
|
|
156
|
-
|
|
180
|
+
const { encrypted } = JSON.parse(data);
|
|
181
|
+
const chunk = this.#decryptResponse(encrypted, sharedSecret);
|
|
157
182
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
183
|
+
if (chunk.seq !== expectedSeq) {
|
|
184
|
+
throw new Error(`Chunk out of order: expected ${expectedSeq}, got ${chunk.seq}`);
|
|
185
|
+
}
|
|
186
|
+
expectedSeq++;
|
|
162
187
|
|
|
163
|
-
|
|
188
|
+
yield chunk;
|
|
189
|
+
}
|
|
164
190
|
}
|
|
191
|
+
} catch (err) {
|
|
192
|
+
if (err.name === "AbortError") return;
|
|
193
|
+
throw err;
|
|
165
194
|
}
|
|
166
195
|
}
|
|
167
196
|
|
package/package.json
CHANGED