legiontret 1.0.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/index.d.ts +76 -0
  2. package/index.js +388 -0
  3. package/package.json +34 -0
package/index.d.ts ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * LegionTret TypeScript declarations
3
+ */
4
+ declare module 'legiontret' {
5
+ interface ClientOptions {
6
+ host?: string;
7
+ port?: number;
8
+ timeout?: number;
9
+ }
10
+
11
+ interface GenerateOptions {
12
+ system?: string;
13
+ stream?: boolean;
14
+ options?: GenerateParams;
15
+ format?: string;
16
+ }
17
+
18
+ interface GenerateParams {
19
+ num_predict?: number;
20
+ temperature?: number;
21
+ top_p?: number;
22
+ top_k?: number;
23
+ repeat_penalty?: number;
24
+ seed?: number;
25
+ stop?: string[];
26
+ }
27
+
28
+ interface ChatMessage {
29
+ role: 'system' | 'user' | 'assistant';
30
+ content: string;
31
+ images?: string[];
32
+ }
33
+
34
+ interface ModelInfo {
35
+ name: string;
36
+ model: string;
37
+ modified_at: string;
38
+ size: number;
39
+ digest: string;
40
+ details: {
41
+ format: string;
42
+ family: string;
43
+ families: string[];
44
+ parameter_size: string;
45
+ quantization_level: string;
46
+ };
47
+ }
48
+
49
+ class LegionTretError extends Error {
50
+ statusCode?: number;
51
+ }
52
+
53
+ class ConnectionError extends LegionTretError {}
54
+ class ModelNotFoundError extends LegionTretError {}
55
+
56
+ class Client {
57
+ constructor(options?: ClientOptions);
58
+ listModels(): Promise<ModelInfo[]>;
59
+ showModel(name: string): Promise<any>;
60
+ deleteModel(name: string): Promise<any>;
61
+ generate(model: string, prompt: string, options?: GenerateOptions): Promise<any>;
62
+ generateStream(model: string, prompt: string, options?: GenerateOptions): AsyncGenerator<string>;
63
+ chat(model: string, messages: ChatMessage[], options?: any): Promise<any>;
64
+ chatStream(model: string, messages: ChatMessage[], options?: any): AsyncGenerator<string>;
65
+ embeddings(model: string, prompt: string): Promise<any>;
66
+ openaiModels(): Promise<any>;
67
+ openaiChat(model: string, messages: ChatMessage[], options?: any): Promise<any>;
68
+ isRunning(): Promise<boolean>;
69
+ version(): Promise<string>;
70
+ systemInfo(): Promise<any>;
71
+ search(query: string): Promise<any[]>;
72
+ }
73
+
74
+ export { Client, LegionTretError, ConnectionError, ModelNotFoundError };
75
+ export default Client;
76
+ }
package/index.js ADDED
@@ -0,0 +1,388 @@
1
+ /**
2
+ * LegionTret JavaScript Client
3
+ * A simple JavaScript/TypeScript library for interacting with the LegionTret API.
4
+ *
5
+ * Usage:
6
+ * import { Client } from 'legiontret';
7
+ *
8
+ * const client = new Client();
9
+ *
10
+ * // Generate text
11
+ * const response = await client.generate('llama3', 'Why is the sky blue?');
12
+ * console.log(response);
13
+ *
14
+ * // Chat
15
+ * const response = await client.chat('llama3', [
16
+ * { role: 'user', content: 'Hello!' }
17
+ * ]);
18
+ * console.log(response);
19
+ *
20
+ * // List models
21
+ * const models = await client.listModels();
22
+ * models.forEach(m => console.log(m.name));
23
+ */
24
+
25
+ const DEFAULT_HOST = '127.0.0.1';
26
+ const DEFAULT_PORT = 11434;
27
+ const DEFAULT_TIMEOUT = 300000; // 5 minutes
28
+
29
+ /**
30
+ * Custom error types for LegionTret
31
+ */
32
+ class LegionTretError extends Error {
33
+ constructor(message, statusCode) {
34
+ super(message);
35
+ this.name = 'LegionTretError';
36
+ this.statusCode = statusCode;
37
+ }
38
+ }
39
+
40
+ class ConnectionError extends LegionTretError {
41
+ constructor(host, port) {
42
+ super(`Cannot connect to LegionTret at ${host}:${port}. Make sure the server is running: legiontret serve`);
43
+ this.name = 'ConnectionError';
44
+ }
45
+ }
46
+
47
+ class ModelNotFoundError extends LegionTretError {
48
+ constructor(modelName) {
49
+ super(`Model not found: ${modelName}`);
50
+ this.name = 'ModelNotFoundError';
51
+ }
52
+ }
53
+
54
+ /**
55
+ * LegionTret API Client
56
+ */
57
+ class Client {
58
+ /**
59
+ * Create a new LegionTret client.
60
+ * @param {Object} options - Client options
61
+ * @param {string} [options.host='127.0.0.1'] - Host address
62
+ * @param {number} [options.port=11434] - Port number
63
+ * @param {number} [options.timeout=300000] - Request timeout in ms
64
+ */
65
+ constructor(options = {}) {
66
+ this.host = options.host || DEFAULT_HOST;
67
+ this.port = options.port || DEFAULT_PORT;
68
+ this.timeout = options.timeout || DEFAULT_TIMEOUT;
69
+ this.baseUrl = `http://${this.host}:${this.port}`;
70
+ }
71
+
72
+ /**
73
+ * Build a full URL from a path
74
+ * @private
75
+ */
76
+ _url(path) {
77
+ return `${this.baseUrl}${path}`;
78
+ }
79
+
80
+ /**
81
+ * Make a request to the API
82
+ * @private
83
+ */
84
+ async _request(method, path, body = null, { stream = false } = {}) {
85
+ const options = {
86
+ method,
87
+ headers: { 'Content-Type': 'application/json' },
88
+ signal: AbortSignal.timeout(this.timeout),
89
+ };
90
+
91
+ if (body && method !== 'GET') {
92
+ options.body = JSON.stringify(body);
93
+ }
94
+
95
+ try {
96
+ const response = await fetch(this._url(path), options);
97
+
98
+ if (!response.ok) {
99
+ if (response.status === 404) {
100
+ throw new ModelNotFoundError(body?.model || 'unknown');
101
+ }
102
+ const errorText = await response.text();
103
+ throw new LegionTretError(`HTTP ${response.status}: ${errorText}`, response.status);
104
+ }
105
+
106
+ return response;
107
+ } catch (error) {
108
+ if (error.name === 'TypeError' && error.message.includes('fetch')) {
109
+ throw new ConnectionError(this.host, this.port);
110
+ }
111
+ throw error;
112
+ }
113
+ }
114
+
115
+ // ─── Model Management ──────────────────────────────────────────────
116
+
117
+ /**
118
+ * List all locally available models.
119
+ * @returns {Promise<Array>} List of model objects
120
+ */
121
+ async listModels() {
122
+ const response = await this._request('GET', '/api/tags');
123
+ const data = await response.json();
124
+ return data.models || [];
125
+ }
126
+
127
+ /**
128
+ * Show details for a specific model.
129
+ * @param {string} name - Model name
130
+ * @returns {Promise<Object>} Model details
131
+ */
132
+ async showModel(name) {
133
+ const response = await this._request('POST', '/api/show', { name });
134
+ return response.json();
135
+ }
136
+
137
+ /**
138
+ * Delete a local model.
139
+ * @param {string} name - Model name to delete
140
+ * @returns {Promise<Object>} Response
141
+ */
142
+ async deleteModel(name) {
143
+ const response = await this._request('DELETE', '/api/delete', { name });
144
+ return response.json();
145
+ }
146
+
147
+ // ─── Generation ────────────────────────────────────────────────────
148
+
149
+ /**
150
+ * Generate text from a prompt.
151
+ * @param {string} model - Model name
152
+ * @param {string} prompt - Text prompt
153
+ * @param {Object} [options] - Generation options
154
+ * @param {string} [options.system] - System prompt
155
+ * @param {boolean} [options.stream=false] - Stream the response
156
+ * @param {Object} [options.options] - Generation parameters
157
+ * @param {string} [options.format] - Output format
158
+ * @returns {Promise<Object|AsyncGenerator>} Response or stream
159
+ */
160
+ async generate(model, prompt, options = {}) {
161
+ const body = {
162
+ model,
163
+ prompt,
164
+ stream: options.stream || false,
165
+ };
166
+ if (options.system) body.system = options.system;
167
+ if (options.options) body.options = options.options;
168
+ if (options.format) body.format = options.format;
169
+
170
+ const response = await this._request('POST', '/api/generate', body, {
171
+ stream: options.stream,
172
+ });
173
+
174
+ if (options.stream) {
175
+ return this._parseStream(response);
176
+ }
177
+
178
+ return response.json();
179
+ }
180
+
181
+ /**
182
+ * Generate text with streaming (async generator).
183
+ * @param {string} model - Model name
184
+ * @param {string} prompt - Text prompt
185
+ * @param {Object} [options] - Generation options
186
+ * @yields {string} Text chunks
187
+ */
188
+ async *generateStream(model, prompt, options = {}) {
189
+ const body = {
190
+ model,
191
+ prompt,
192
+ stream: true,
193
+ };
194
+ if (options.system) body.system = options.system;
195
+ if (options.options) body.options = options.options;
196
+
197
+ const response = await this._request('POST', '/api/generate', body, { stream: true });
198
+ yield* this._parseTextStream(response);
199
+ }
200
+
201
+ // ─── Chat ──────────────────────────────────────────────────────────
202
+
203
+ /**
204
+ * Send a chat completion request.
205
+ * @param {string} model - Model name
206
+ * @param {Array<Object>} messages - Chat messages
207
+ * @param {Object} [options] - Generation options
208
+ * @returns {Promise<Object|AsyncGenerator>} Response or stream
209
+ */
210
+ async chat(model, messages, options = {}) {
211
+ const body = {
212
+ model,
213
+ messages,
214
+ stream: options.stream || false,
215
+ };
216
+ if (options.options) body.options = options.options;
217
+ if (options.format) body.format = options.format;
218
+
219
+ const response = await this._request('POST', '/api/chat', body, {
220
+ stream: options.stream,
221
+ });
222
+
223
+ if (options.stream) {
224
+ return this._parseStream(response);
225
+ }
226
+
227
+ return response.json();
228
+ }
229
+
230
+ /**
231
+ * Chat with streaming (async generator).
232
+ * @param {string} model - Model name
233
+ * @param {Array<Object>} messages - Chat messages
234
+ * @param {Object} [options] - Generation options
235
+ * @yields {string} Text chunks
236
+ */
237
+ async *chatStream(model, messages, options = {}) {
238
+ const body = {
239
+ model,
240
+ messages,
241
+ stream: true,
242
+ };
243
+ if (options.options) body.options = options.options;
244
+
245
+ const response = await this._request('POST', '/api/chat', body, { stream: true });
246
+ yield* this._parseTextStream(response);
247
+ }
248
+
249
+ // ─── Embeddings ────────────────────────────────────────────────────
250
+
251
+ /**
252
+ * Generate embeddings for a prompt.
253
+ * @param {string} model - Model name
254
+ * @param {string} prompt - Text to embed
255
+ * @returns {Promise<Object>} Embedding response
256
+ */
257
+ async embeddings(model, prompt) {
258
+ const response = await this._request('POST', '/api/embeddings', { model, prompt });
259
+ return response.json();
260
+ }
261
+
262
+ // ─── OpenAI-Compatible ─────────────────────────────────────────────
263
+
264
+ /**
265
+ * List models using OpenAI-compatible endpoint.
266
+ * @returns {Promise<Object>} Model list
267
+ */
268
+ async openaiModels() {
269
+ const response = await this._request('GET', '/api/v1/models');
270
+ return response.json();
271
+ }
272
+
273
+ /**
274
+ * Chat completion using OpenAI-compatible endpoint.
275
+ * @param {string} model - Model name
276
+ * @param {Array<Object>} messages - Messages
277
+ * @param {Object} [options] - Options
278
+ * @returns {Promise<Object>} Chat completion
279
+ */
280
+ async openaiChat(model, messages, options = {}) {
281
+ const body = {
282
+ model,
283
+ messages,
284
+ temperature: options.temperature || 0.7,
285
+ max_tokens: options.maxTokens || 2048,
286
+ stream: options.stream || false,
287
+ };
288
+
289
+ const response = await this._request('POST', '/api/v1/chat/completions', body, {
290
+ stream: options.stream,
291
+ });
292
+ return response.json();
293
+ }
294
+
295
+ // ─── System ────────────────────────────────────────────────────────
296
+
297
+ /**
298
+ * Check if the LegionTret server is running.
299
+ * @returns {Promise<boolean>}
300
+ */
301
+ async isRunning() {
302
+ try {
303
+ await this._request('GET', '/health');
304
+ return true;
305
+ } catch {
306
+ return false;
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Get the server version.
312
+ * @returns {Promise<string>}
313
+ */
314
+ async version() {
315
+ const response = await this._request('GET', '/api/version');
316
+ const data = await response.json();
317
+ return data.version || 'unknown';
318
+ }
319
+
320
+ /**
321
+ * Get system information.
322
+ * @returns {Promise<Object>}
323
+ */
324
+ async systemInfo() {
325
+ const response = await this._request('GET', '/api/system');
326
+ return response.json();
327
+ }
328
+
329
+ /**
330
+ * Search for models in the registry.
331
+ * @param {string} query - Search query
332
+ * @returns {Promise<Array>} Matching models
333
+ */
334
+ async search(query) {
335
+ const response = await this._request('GET', `/api/search?q=${encodeURIComponent(query)}`);
336
+ const data = await response.json();
337
+ return data.models || [];
338
+ }
339
+
340
+ // ─── Private Helpers ───────────────────────────────────────────────
341
+
342
+ /**
343
+ * Parse a streaming response as JSON chunks
344
+ * @private
345
+ */
346
+ async *_parseStream(response) {
347
+ const reader = response.body.getReader();
348
+ const decoder = new TextDecoder();
349
+ let buffer = '';
350
+
351
+ while (true) {
352
+ const { done, value } = await reader.read();
353
+ if (done) break;
354
+
355
+ buffer += decoder.decode(value, { stream: true });
356
+ const lines = buffer.split('\n');
357
+ buffer = lines.pop() || '';
358
+
359
+ for (const line of lines) {
360
+ if (line.trim()) {
361
+ try {
362
+ yield JSON.parse(line);
363
+ } catch {
364
+ // Skip malformed lines
365
+ }
366
+ }
367
+ }
368
+ }
369
+ }
370
+
371
+ /**
372
+ * Parse a streaming response and yield text content
373
+ * @private
374
+ */
375
+ async *_parseTextStream(response) {
376
+ for await (const chunk of this._parseStream(response)) {
377
+ if (chunk.response) {
378
+ yield chunk.response;
379
+ } else if (chunk.message?.content) {
380
+ yield chunk.message.content;
381
+ }
382
+ }
383
+ }
384
+ }
385
+
386
+ // Export
387
+ module.exports = { Client, LegionTretError, ConnectionError, ModelNotFoundError };
388
+ module.exports.default = Client;
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "legiontret",
3
+ "version": "1.0.0",
4
+ "description": "JavaScript client for LegionTret - Run LLMs locally",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "scripts": {
8
+ "test": "echo \"No tests yet\" && exit 0"
9
+ },
10
+ "keywords": [
11
+ "llm",
12
+ "ai",
13
+ "legiontret",
14
+ "ollama",
15
+ "gemma",
16
+ "llama",
17
+ "mistral",
18
+ "local-ai"
19
+ ],
20
+ "author": "Death Legion Team",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/deathlegionteam/legiontret.git"
25
+ },
26
+ "engines": {
27
+ "node": ">=16.0.0"
28
+ },
29
+ "files": [
30
+ "index.js",
31
+ "index.d.ts",
32
+ "README.md"
33
+ ]
34
+ }