inference-server 1.0.0-beta.19

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 (227) hide show
  1. package/README.md +216 -0
  2. package/dist/api/openai/enums.d.ts +4 -0
  3. package/dist/api/openai/enums.js +17 -0
  4. package/dist/api/openai/enums.js.map +1 -0
  5. package/dist/api/openai/handlers/chat.d.ts +3 -0
  6. package/dist/api/openai/handlers/chat.js +358 -0
  7. package/dist/api/openai/handlers/chat.js.map +1 -0
  8. package/dist/api/openai/handlers/completions.d.ts +3 -0
  9. package/dist/api/openai/handlers/completions.js +169 -0
  10. package/dist/api/openai/handlers/completions.js.map +1 -0
  11. package/dist/api/openai/handlers/embeddings.d.ts +3 -0
  12. package/dist/api/openai/handlers/embeddings.js +74 -0
  13. package/dist/api/openai/handlers/embeddings.js.map +1 -0
  14. package/dist/api/openai/handlers/images.d.ts +0 -0
  15. package/dist/api/openai/handlers/images.js +4 -0
  16. package/dist/api/openai/handlers/images.js.map +1 -0
  17. package/dist/api/openai/handlers/models.d.ts +3 -0
  18. package/dist/api/openai/handlers/models.js +23 -0
  19. package/dist/api/openai/handlers/models.js.map +1 -0
  20. package/dist/api/openai/handlers/transcription.d.ts +0 -0
  21. package/dist/api/openai/handlers/transcription.js +4 -0
  22. package/dist/api/openai/handlers/transcription.js.map +1 -0
  23. package/dist/api/openai/index.d.ts +7 -0
  24. package/dist/api/openai/index.js +14 -0
  25. package/dist/api/openai/index.js.map +1 -0
  26. package/dist/api/parseJSONRequestBody.d.ts +2 -0
  27. package/dist/api/parseJSONRequestBody.js +24 -0
  28. package/dist/api/parseJSONRequestBody.js.map +1 -0
  29. package/dist/api/v1/index.d.ts +2 -0
  30. package/dist/api/v1/index.js +29 -0
  31. package/dist/api/v1/index.js.map +1 -0
  32. package/dist/cli.d.ts +1 -0
  33. package/dist/cli.js +10 -0
  34. package/dist/cli.js.map +1 -0
  35. package/dist/engines/gpt4all/engine.d.ts +34 -0
  36. package/dist/engines/gpt4all/engine.js +357 -0
  37. package/dist/engines/gpt4all/engine.js.map +1 -0
  38. package/dist/engines/gpt4all/util.d.ts +3 -0
  39. package/dist/engines/gpt4all/util.js +29 -0
  40. package/dist/engines/gpt4all/util.js.map +1 -0
  41. package/dist/engines/index.d.ts +19 -0
  42. package/dist/engines/index.js +21 -0
  43. package/dist/engines/index.js.map +1 -0
  44. package/dist/engines/node-llama-cpp/engine.d.ts +49 -0
  45. package/dist/engines/node-llama-cpp/engine.js +666 -0
  46. package/dist/engines/node-llama-cpp/engine.js.map +1 -0
  47. package/dist/engines/node-llama-cpp/types.d.ts +13 -0
  48. package/dist/engines/node-llama-cpp/types.js +2 -0
  49. package/dist/engines/node-llama-cpp/types.js.map +1 -0
  50. package/dist/engines/node-llama-cpp/util.d.ts +15 -0
  51. package/dist/engines/node-llama-cpp/util.js +84 -0
  52. package/dist/engines/node-llama-cpp/util.js.map +1 -0
  53. package/dist/engines/node-llama-cpp/validateModelFile.d.ts +8 -0
  54. package/dist/engines/node-llama-cpp/validateModelFile.js +36 -0
  55. package/dist/engines/node-llama-cpp/validateModelFile.js.map +1 -0
  56. package/dist/engines/stable-diffusion-cpp/engine.d.ts +90 -0
  57. package/dist/engines/stable-diffusion-cpp/engine.js +294 -0
  58. package/dist/engines/stable-diffusion-cpp/engine.js.map +1 -0
  59. package/dist/engines/stable-diffusion-cpp/types.d.ts +3 -0
  60. package/dist/engines/stable-diffusion-cpp/types.js +2 -0
  61. package/dist/engines/stable-diffusion-cpp/types.js.map +1 -0
  62. package/dist/engines/stable-diffusion-cpp/util.d.ts +4 -0
  63. package/dist/engines/stable-diffusion-cpp/util.js +55 -0
  64. package/dist/engines/stable-diffusion-cpp/util.js.map +1 -0
  65. package/dist/engines/stable-diffusion-cpp/validateModelFiles.d.ts +19 -0
  66. package/dist/engines/stable-diffusion-cpp/validateModelFiles.js +91 -0
  67. package/dist/engines/stable-diffusion-cpp/validateModelFiles.js.map +1 -0
  68. package/dist/engines/transformers-js/engine.d.ts +37 -0
  69. package/dist/engines/transformers-js/engine.js +538 -0
  70. package/dist/engines/transformers-js/engine.js.map +1 -0
  71. package/dist/engines/transformers-js/types.d.ts +7 -0
  72. package/dist/engines/transformers-js/types.js +2 -0
  73. package/dist/engines/transformers-js/types.js.map +1 -0
  74. package/dist/engines/transformers-js/util.d.ts +7 -0
  75. package/dist/engines/transformers-js/util.js +36 -0
  76. package/dist/engines/transformers-js/util.js.map +1 -0
  77. package/dist/engines/transformers-js/validateModelFiles.d.ts +17 -0
  78. package/dist/engines/transformers-js/validateModelFiles.js +133 -0
  79. package/dist/engines/transformers-js/validateModelFiles.js.map +1 -0
  80. package/dist/experiments/ChatWithVision.d.ts +11 -0
  81. package/dist/experiments/ChatWithVision.js +91 -0
  82. package/dist/experiments/ChatWithVision.js.map +1 -0
  83. package/dist/experiments/StableDiffPromptGenerator.d.ts +0 -0
  84. package/dist/experiments/StableDiffPromptGenerator.js +4 -0
  85. package/dist/experiments/StableDiffPromptGenerator.js.map +1 -0
  86. package/dist/experiments/VoiceFunctionCall.d.ts +18 -0
  87. package/dist/experiments/VoiceFunctionCall.js +51 -0
  88. package/dist/experiments/VoiceFunctionCall.js.map +1 -0
  89. package/dist/http.d.ts +19 -0
  90. package/dist/http.js +54 -0
  91. package/dist/http.js.map +1 -0
  92. package/dist/index.d.ts +7 -0
  93. package/dist/index.js +8 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/instance.d.ts +88 -0
  96. package/dist/instance.js +594 -0
  97. package/dist/instance.js.map +1 -0
  98. package/dist/lib/acquireFileLock.d.ts +7 -0
  99. package/dist/lib/acquireFileLock.js +38 -0
  100. package/dist/lib/acquireFileLock.js.map +1 -0
  101. package/dist/lib/calculateContextIdentity.d.ts +7 -0
  102. package/dist/lib/calculateContextIdentity.js +39 -0
  103. package/dist/lib/calculateContextIdentity.js.map +1 -0
  104. package/dist/lib/calculateFileChecksum.d.ts +1 -0
  105. package/dist/lib/calculateFileChecksum.js +16 -0
  106. package/dist/lib/calculateFileChecksum.js.map +1 -0
  107. package/dist/lib/copyDirectory.d.ts +6 -0
  108. package/dist/lib/copyDirectory.js +27 -0
  109. package/dist/lib/copyDirectory.js.map +1 -0
  110. package/dist/lib/decodeAudio.d.ts +1 -0
  111. package/dist/lib/decodeAudio.js +26 -0
  112. package/dist/lib/decodeAudio.js.map +1 -0
  113. package/dist/lib/downloadModelFile.d.ts +10 -0
  114. package/dist/lib/downloadModelFile.js +58 -0
  115. package/dist/lib/downloadModelFile.js.map +1 -0
  116. package/dist/lib/flattenMessageTextContent.d.ts +2 -0
  117. package/dist/lib/flattenMessageTextContent.js +11 -0
  118. package/dist/lib/flattenMessageTextContent.js.map +1 -0
  119. package/dist/lib/getCacheDirPath.d.ts +12 -0
  120. package/dist/lib/getCacheDirPath.js +31 -0
  121. package/dist/lib/getCacheDirPath.js.map +1 -0
  122. package/dist/lib/loadImage.d.ts +12 -0
  123. package/dist/lib/loadImage.js +30 -0
  124. package/dist/lib/loadImage.js.map +1 -0
  125. package/dist/lib/logger.d.ts +12 -0
  126. package/dist/lib/logger.js +98 -0
  127. package/dist/lib/logger.js.map +1 -0
  128. package/dist/lib/math.d.ts +7 -0
  129. package/dist/lib/math.js +30 -0
  130. package/dist/lib/math.js.map +1 -0
  131. package/dist/lib/resolveModelFileLocation.d.ts +15 -0
  132. package/dist/lib/resolveModelFileLocation.js +41 -0
  133. package/dist/lib/resolveModelFileLocation.js.map +1 -0
  134. package/dist/lib/util.d.ts +7 -0
  135. package/dist/lib/util.js +61 -0
  136. package/dist/lib/util.js.map +1 -0
  137. package/dist/lib/validateModelFile.d.ts +9 -0
  138. package/dist/lib/validateModelFile.js +62 -0
  139. package/dist/lib/validateModelFile.js.map +1 -0
  140. package/dist/lib/validateModelOptions.d.ts +3 -0
  141. package/dist/lib/validateModelOptions.js +23 -0
  142. package/dist/lib/validateModelOptions.js.map +1 -0
  143. package/dist/pool.d.ts +61 -0
  144. package/dist/pool.js +512 -0
  145. package/dist/pool.js.map +1 -0
  146. package/dist/server.d.ts +59 -0
  147. package/dist/server.js +221 -0
  148. package/dist/server.js.map +1 -0
  149. package/dist/standalone.d.ts +1 -0
  150. package/dist/standalone.js +306 -0
  151. package/dist/standalone.js.map +1 -0
  152. package/dist/store.d.ts +60 -0
  153. package/dist/store.js +203 -0
  154. package/dist/store.js.map +1 -0
  155. package/dist/types/completions.d.ts +57 -0
  156. package/dist/types/completions.js +2 -0
  157. package/dist/types/completions.js.map +1 -0
  158. package/dist/types/index.d.ts +326 -0
  159. package/dist/types/index.js +2 -0
  160. package/dist/types/index.js.map +1 -0
  161. package/docs/engines.md +28 -0
  162. package/docs/gpu.md +72 -0
  163. package/docs/http-api.md +147 -0
  164. package/examples/all-options.js +108 -0
  165. package/examples/chat-cli.js +56 -0
  166. package/examples/chat-server.js +65 -0
  167. package/examples/concurrency.js +70 -0
  168. package/examples/express.js +70 -0
  169. package/examples/pool.js +91 -0
  170. package/package.json +113 -0
  171. package/src/api/openai/enums.ts +20 -0
  172. package/src/api/openai/handlers/chat.ts +408 -0
  173. package/src/api/openai/handlers/completions.ts +196 -0
  174. package/src/api/openai/handlers/embeddings.ts +92 -0
  175. package/src/api/openai/handlers/images.ts +3 -0
  176. package/src/api/openai/handlers/models.ts +33 -0
  177. package/src/api/openai/handlers/transcription.ts +2 -0
  178. package/src/api/openai/index.ts +16 -0
  179. package/src/api/parseJSONRequestBody.ts +26 -0
  180. package/src/api/v1/DRAFT.md +16 -0
  181. package/src/api/v1/index.ts +37 -0
  182. package/src/cli.ts +9 -0
  183. package/src/engines/gpt4all/engine.ts +441 -0
  184. package/src/engines/gpt4all/util.ts +31 -0
  185. package/src/engines/index.ts +28 -0
  186. package/src/engines/node-llama-cpp/engine.ts +811 -0
  187. package/src/engines/node-llama-cpp/types.ts +17 -0
  188. package/src/engines/node-llama-cpp/util.ts +126 -0
  189. package/src/engines/node-llama-cpp/validateModelFile.ts +46 -0
  190. package/src/engines/stable-diffusion-cpp/engine.ts +369 -0
  191. package/src/engines/stable-diffusion-cpp/types.ts +54 -0
  192. package/src/engines/stable-diffusion-cpp/util.ts +58 -0
  193. package/src/engines/stable-diffusion-cpp/validateModelFiles.ts +119 -0
  194. package/src/engines/transformers-js/engine.ts +659 -0
  195. package/src/engines/transformers-js/types.ts +25 -0
  196. package/src/engines/transformers-js/util.ts +40 -0
  197. package/src/engines/transformers-js/validateModelFiles.ts +168 -0
  198. package/src/experiments/ChatWithVision.ts +103 -0
  199. package/src/experiments/StableDiffPromptGenerator.ts +2 -0
  200. package/src/experiments/VoiceFunctionCall.ts +71 -0
  201. package/src/http.ts +72 -0
  202. package/src/index.ts +7 -0
  203. package/src/instance.ts +723 -0
  204. package/src/lib/acquireFileLock.ts +38 -0
  205. package/src/lib/calculateContextIdentity.ts +53 -0
  206. package/src/lib/calculateFileChecksum.ts +18 -0
  207. package/src/lib/copyDirectory.ts +29 -0
  208. package/src/lib/decodeAudio.ts +39 -0
  209. package/src/lib/downloadModelFile.ts +70 -0
  210. package/src/lib/flattenMessageTextContent.ts +19 -0
  211. package/src/lib/getCacheDirPath.ts +34 -0
  212. package/src/lib/loadImage.ts +46 -0
  213. package/src/lib/logger.ts +112 -0
  214. package/src/lib/math.ts +31 -0
  215. package/src/lib/resolveModelFileLocation.ts +49 -0
  216. package/src/lib/util.ts +75 -0
  217. package/src/lib/validateModelFile.ts +71 -0
  218. package/src/lib/validateModelOptions.ts +31 -0
  219. package/src/pool.ts +651 -0
  220. package/src/server.ts +270 -0
  221. package/src/standalone.ts +320 -0
  222. package/src/store.ts +278 -0
  223. package/src/types/completions.ts +86 -0
  224. package/src/types/index.ts +488 -0
  225. package/tsconfig.json +29 -0
  226. package/tsconfig.release.json +11 -0
  227. package/vitest.config.ts +18 -0
package/docs/gpu.md ADDED
@@ -0,0 +1,72 @@
1
+
2
+ ### On GPU usage
3
+
4
+ Only one model instance can run on gpu at a time. Instances can not switch between gpu and cpu. If left unconfigured, the first spawned instance of a model will automatically acquire gpu lock and use it. Note that if `minInstances` is set to something greater than 0 then the order in which models are configured will matter because initial instances will also be spawned in that order.
5
+
6
+ Automatic / unconfigured gpu usage:
7
+
8
+ ```ts
9
+ {
10
+ models: {
11
+ 'model1': {
12
+ task: 'text-completion',
13
+ engine: 'gpt4all',
14
+ url: 'https://gpt4all.io/models/gguf/Meta-Llama-3-8B-Instruct.Q4_0.gguf',
15
+ // first instance will automatically pick up gpu, then a second one will be spawned on cpu
16
+ minInstances: 2,
17
+ },
18
+ 'model2': {
19
+ task: 'text-completion',
20
+ engine: 'gpt4all',
21
+ url: 'https://huggingface.co/NousResearch/Nous-Hermes-2-Mistral-7B-DPO-GGUF/resolve/main/Nous-Hermes-2-Mistral-7B-DPO.Q4_0.gguf',
22
+ minInstances: 1, // this will always spawn on cpu because model1 already auto locked gpu
23
+ },
24
+ },
25
+ }
26
+ ```
27
+
28
+ Another practical strategy is to explicitly configure gpu usage. The same model can be configured multiple times with different options so that the gpu instance can be targeted specifically. Like
29
+
30
+ ```ts
31
+ {
32
+ models: {
33
+ 'model1': {
34
+ task: 'text-completion',
35
+ engine: 'gpt4all',
36
+ url: 'https://gpt4all.io/models/gguf/Meta-Llama-3-8B-Instruct.Q4_0.gguf',
37
+ device: { gpu: true }, // this is effectively also maxInstances: 1
38
+ },
39
+ 'model2': {
40
+ task: 'text-completion',
41
+ engine: 'gpt4all',
42
+ url: 'https://gpt4all.io/models/gguf/Meta-Llama-3-8B-Instruct.Q4_0.gguf',
43
+ // will spawn up to 2 cpu instances
44
+ device: { gpu: false },
45
+ maxInstances: 2,
46
+ },
47
+ },
48
+ }
49
+ ```
50
+
51
+ It is possible to configure multiple models to use gpu, but only one gpu instance can be utilized at a time. If simultaneous requests to gpu models come in, each request will wait for the processing instance to release gpu lock before it can start. Then, depending on which models are requested in which order, instances may be disposed and spawned, or reused.
52
+
53
+ ```ts
54
+ {
55
+ models: {
56
+ 'model1': {
57
+ task: 'text-completion',
58
+ engine: 'gpt4all',
59
+ url: 'https://gpt4all.io/models/gguf/Meta-Llama-3-8B-Instruct.Q4_0.gguf',
60
+ device: { gpu: true },
61
+ },
62
+ 'model2': {
63
+ task: 'text-completion',
64
+ engine: 'gpt4all',
65
+ url: 'https://huggingface.co/NousResearch/Nous-Hermes-2-Mistral-7B-DPO-GGUF/resolve/main/Nous-Hermes-2-Mistral-7B-DPO.Q4_0.gguf',
66
+ device: { gpu: true },
67
+ },
68
+ },
69
+ }
70
+ ```
71
+
72
+ Note that switching the model that runs on gpu a lot (iE incoming requests `model1->model2->model1->model2) will lead to inefficient cache usage for chat completions and generally make requests slower, because models need to be unloaded and loaded + chat history has to be reingested.
@@ -0,0 +1,147 @@
1
+ ### HTTP API
2
+
3
+ Note that the HTTP API is currently not secure (ie it's probably DoS-able, only minimal input validation). You should not host this on a public server without additional protections.
4
+
5
+ On the packaged web server there is currently only one additional HTTP endpoint:
6
+
7
+ - `GET /` - Prints info about spawned instances, available models and ongoing downloads.
8
+
9
+ #### OpenAI-Style API
10
+
11
+ `/openai/v1` is the default base path. The following endpoints and parameters are supported:
12
+
13
+ | Endpoints | gpt4all | node-llama-cpp | transformers-js |
14
+ | ----------------------- | ------- | -------------- | --------------- |
15
+ | v1/chat/completions | ✅ | ✅ | 🚧 |
16
+ | v1/completions | ✅ | ✅ | 🚧 |
17
+ | v1/embeddings | ✅ | ✅ | 🚧 |
18
+ | v1/models | ✅ | ✅ | ✅ |
19
+ | v1/audio/transcriptions | ❌ | ❌ | 🚧 |
20
+
21
+ | Text Compl Params | gpt4all | node-llama-cpp |
22
+ | ------------------- | ------- | -------------- |
23
+ | stream | ✅ | ✅ |
24
+ | temperature | ✅ | ✅ |
25
+ | max_tokens | ✅ | ✅ |
26
+ | top_p | ✅ | ✅ |
27
+ | stop | ✅ | ✅ |
28
+ | seed | ❌ | ✅ |
29
+ | frequency_penalty | ❌ | ✅ |
30
+ | presence_penalty | ❌ | ✅ |
31
+ | best_of | ❌ | ❌ |
32
+ | n | ❌ | ❌ |
33
+ | logprobs | ❌ | ❌ |
34
+ | top_logprobs | ❌ | ❌ |
35
+ | logit_bias | ❌ | ✅ |
36
+ | response_format | ❌ | ✅ |
37
+ | tools | ❌ | ✅ |
38
+ | tool_choice | ❌ | ❌ |
39
+ | suffix | ❌ | ❌ |
40
+ | echo | ❌ | ❌ |
41
+
42
+ Some additional llama.cpp- and gpt4all specific parameters are supported:
43
+
44
+ | Non-spec params | gpt4all | node-llama-cpp |
45
+ | ------------------- | ------- | -------------- |
46
+ | top_k | ✅ | ✅ |
47
+ | min_p | ✅ | ✅ |
48
+ | repeat_penalty_num | ✅ | ✅ |
49
+ | repeat_penalty | ✅ | - |
50
+
51
+ #### Functionality
52
+
53
+ | Feature | gpt4all | node-llama-cpp |
54
+ | --------------------- | ------- | -------------- |
55
+ | Streaming | ✅ | ✅ |
56
+ | Chat context cache | ✅ | ✅ |
57
+ | System prompt | ✅ | ✅ |
58
+ | Grammar | ❌ | ✅ |
59
+ | Function Calling | ❌ | ✅ |
60
+
61
+ #### Usage
62
+
63
+ ```js lllms.js
64
+ import { startHTTPServer } from 'lllms'
65
+
66
+ // Starts a http server for up to two instances of phi3 and serves them via openai API.
67
+ // startHTTPServer is only a thin wrapper around the ModelServer class that spawns a web server.
68
+ startHTTPServer({
69
+ concurrency: 2,
70
+ models: {
71
+ 'phi3-mini-4k': {
72
+ task: 'text-completion',
73
+ engine: 'node-llama-cpp',
74
+ url: 'https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-gguf/resolve/main/Phi-3-mini-4k-instruct-q4.gguf',
75
+ preload: {
76
+ // Note that for preloading to be utilized, requests must
77
+ // also have these leading messages before the user message.
78
+ messages: [
79
+ {
80
+ role: 'system',
81
+ content: 'You are the Batman.',
82
+ },
83
+ ],
84
+ },
85
+ // Use these to control resource usage.
86
+ contextSize: 1024, // Maximum context size. Will be determined automatically if not set.
87
+ maxInstances: 2, // How many active sessions you wanna be able to cache at the same time.
88
+ minInstances: 1, // To always keep at least one instance ready. Defaults to 0.
89
+ ttl: 300, // Idle sessions will be disposed after this many seconds.
90
+ // Set defaults for completions. These can be overridden per request.
91
+ // If unset, default values depend on the engine.
92
+ completionDefaults: {
93
+ temperature: 1,
94
+ },
95
+ },
96
+ },
97
+ // HTTP listen options. If you don't need a web server, use `startModelServer` or `new ModelServer()`.
98
+ // Apart from `listen` they take the same configuration.
99
+ listen: {
100
+ port: 3000,
101
+ },
102
+ })
103
+ // During download requests to a model will stall to get processed once the model is ready.
104
+ // http://localhost:3000 will serve a JSON of the current state of the server.
105
+ ```
106
+ ```sh
107
+ $ curl http://localhost:3000/openai/v1/chat/completions \
108
+ -H "Content-Type: application/json" \
109
+ -d '{
110
+ "model": "phi3-mini-4k",
111
+ "messages": [
112
+ {
113
+ "role": "system",
114
+ "content": "You are the Batman."
115
+ },
116
+ {
117
+ "role": "user",
118
+ "content": "im robin, lets count to 10!"
119
+ }
120
+ ]
121
+ }'
122
+ ```
123
+ ```json
124
+ {
125
+ "id": "phi3-mini-4k:pfBGvlYg-z6dPZUn9",
126
+ "model": "phi3-mini-4k",
127
+ "object": "chat.completion",
128
+ "created": 1720412918,
129
+ "system_fingerprint": "b38af554bea1fb9867db54ebeff59d0590c5ce48",
130
+ "choices": [
131
+ {
132
+ "index": 0,
133
+ "message": {
134
+ "role": "assistant",
135
+ "content": "Hello, Robin! As Batman, my focus is on protecting Gotham City and ensuring justice prevails. However, let's have a quick exercise to lighten the mood. Ready?\n\n1... 2... 3... 4... 5... 6... 7... 8... 9... And 10! Great job!\n\nRemember, my mission as Batman never ends, but it's always good to recharge and have fun alongside our partners. Let's keep Gotham safe together."
136
+ },
137
+ "logprobs": null,
138
+ "finish_reason": "stop"
139
+ }
140
+ ],
141
+ "usage": {
142
+ "prompt_tokens": 12,
143
+ "completion_tokens": 118,
144
+ "total_tokens": 130
145
+ }
146
+ }
147
+ ```
@@ -0,0 +1,108 @@
1
+ import { startHTTPServer } from '../dist/http.js'
2
+
3
+ // Starts a http server for up to two instances of phi3 and serves them via openai API.
4
+ // startHTTPServer is only a thin wrapper around the ModelServer class that spawns a web server.
5
+ startHTTPServer({
6
+ log: 'info', // 'debug', 'info', 'warn', 'error' - or pass a function as custom logger.
7
+ // Limit how many instances may be handed out concurrently for processing.
8
+ // If its exceeded, requests will be queued up and stall until a model becomes available.
9
+ // Defaults to 1 = process one request at a time.
10
+ concurrency: 2,
11
+ // Where to cache to disk. Defaults to `~/.cache/node/inference-server`
12
+ // cachePath: '/path/to/cache',
13
+ models: {
14
+ // Specify as many models as you want. Identifiers can use a-zA-Z0-9_:\-\.
15
+ // Required are `task`, `engine`, `url` and/or `file`.
16
+ 'my-model': {
17
+ task: 'text-completion', // 'text-completion', 'embedding', 'image-to-text', 'speech-to-text'
18
+ engine: 'node-llama-cpp', // 'node-llama-cpp', 'transformers-js', 'gpt4all'
19
+ // Model weights may be specified by file and/or url.
20
+ url: 'https://huggingface.co/HuggingFaceTB/smollm-135M-instruct-v0.2-Q8_0-GGUF/blob/main/smollm-135m-instruct-add-basics-q8_0.gguf',
21
+ // specify sha256 hash to verify the downloaded file.
22
+ sha256: 'a98d3857b95b96c156d954780d28f39dcb35b642e72892ee08ddff70719e6220',
23
+ // The preparation process downloads and verifies model files before instantiating the model.
24
+ // Use this to control when that happens. Options are:
25
+ // - 'on-demand' = prepare on first request. This is the default.
26
+ // - 'blocking' = prepare immediately on startup
27
+ // - 'async' = prepare in background but don't block startup. Requests to the model during the preparation process will resolve once its ready.
28
+ // Note that if minInstances > 0 then this is effectively always "blocking" because the model preparation will happen immediately.
29
+ prepare: 'on-demand',
30
+ // What should be preloaded in context, for text completion / chat models.
31
+ preload: {
32
+ // Note that for preloading to be utilized, requests must
33
+ // also have these leading messages before the user message.
34
+ messages: [
35
+ {
36
+ role: 'system',
37
+ content: 'You are a helpful assistant.',
38
+ },
39
+ ],
40
+ // toolDocumentation: true, // Tool docs may also be preloaded. See `tools` below.
41
+ },
42
+ // Options to control resource usage.
43
+ contextSize: 2046, // Maximum context size. Will be determined automatically if not set.
44
+ maxInstances: 2, // How many active sessions you wanna be able to cache at the same time.
45
+ minInstances: 1, // To always keep at least one instance ready. Defaults to 0.
46
+ // Idle instances will be disposed after this many seconds.
47
+ ttl: 300, // Defaults to 5min. Set it to zero to immediately dispose of instances after use.
48
+ // Set defaults for completions. These can be overridden per request.
49
+ // If unset, default values depend on the engine.
50
+ completionDefaults: {
51
+ temperature: 1,
52
+ },
53
+ // Configure hardware / device to use.
54
+ device: {
55
+ // GPU will be used automatically if left unset.
56
+ // Only one model can use the gpu at a time.
57
+ // gpu: true, // Force gpu use for instance of this model. (This effectively limits maxInstance to 1.)
58
+ // cpuThreads: 4, // Only gpt4all and node-llama-cpp
59
+ // memLock: true, // Only node-llama-cpp.
60
+ },
61
+ // node-llama-cpp text-completion models may have GBNF grammars and tools configured.
62
+ // You can define multiple grammars for a model. `json` grammar will alway be available.
63
+ // Key is the grammar name (that later can be used as value for `grammar` in a request). Value is a string containing the GBNF grammar.
64
+ grammars: {
65
+ // For example:
66
+ // 'custom-grammar': fs.readFileSync('custom-grammar.gbnf', 'utf8'), // Supply your own grammar
67
+ // 'chess': await LlamaGrammar.getFor(llama, 'chess') // Or reuse a grammar shipped with (node-)llama-cpp
68
+ },
69
+ // Avilable tools may be defined on the model or during requests.
70
+ // Note that for using `preload` with `toolDocumentation` they _must_ be defined here (on the model).
71
+ tools: {
72
+ includeParamsDocumentation: true, // Include parameter documentation in tool documentation.
73
+ parallelism: 2, // How many tools may be executed in parallel. Defaults to 1.
74
+ definitions: {
75
+ getLocationWeather: {
76
+ description: 'Get the weather in a location',
77
+ parameters: {
78
+ type: 'object',
79
+ properties: {
80
+ location: {
81
+ type: 'string',
82
+ description: 'The city and state, e.g. San Francisco, CA',
83
+ },
84
+ unit: {
85
+ type: 'string',
86
+ enum: ['celsius', 'fahrenheit'],
87
+ },
88
+ },
89
+ required: ['location'],
90
+ },
91
+ // Handler is optional. If its set, the model will ingest the return value and respond with the final assistant message.
92
+ // If unset the model will respond with a tool call message instead. In this case you need to push tool call results into the message array.
93
+ handler: async (parameters) => {
94
+ const { location, unit } = parameters
95
+ // Call a weather API or something
96
+ return `The temperature in ${location} is 23°C`
97
+ },
98
+ }
99
+ }
100
+ }
101
+ },
102
+ },
103
+ // HTTP listen options. If you don't need a web server, use `startModelServer` or `new ModelServer()`.
104
+ // Accepted arguments are identical, apart from `listen`.
105
+ listen: {
106
+ port: 3000,
107
+ },
108
+ })
@@ -0,0 +1,56 @@
1
+ import readline from 'node:readline'
2
+ import chalk from 'chalk'
3
+ import { ModelServer } from '#package/index.js'
4
+
5
+ // A command-line chat example using the ModelServer.
6
+
7
+ const modelServer = new ModelServer({
8
+ // log: 'info',
9
+ models: {
10
+ 'my-model': {
11
+ task: 'text-completion',
12
+ minInstances: 1,
13
+ url: 'https://huggingface.co/HuggingFaceTB/smollm-135M-instruct-v0.2-Q8_0-GGUF/blob/main/smollm-135m-instruct-add-basics-q8_0.gguf',
14
+ sha256: 'a98d3857b95b96c156d954780d28f39dcb35b642e72892ee08ddff70719e6220',
15
+ engine: 'node-llama-cpp',
16
+ // device: { gpu: false },
17
+ },
18
+ },
19
+ })
20
+
21
+ console.log('Initializing models...')
22
+
23
+ await modelServer.start()
24
+
25
+ const rl = readline.createInterface({
26
+ input: process.stdin,
27
+ output: process.stdout,
28
+ })
29
+
30
+ const messages = []
31
+
32
+ while (true) {
33
+ const input = await new Promise((resolve) => {
34
+ rl.question(chalk.bold(chalk.dim('user > ')), (input) => {
35
+ resolve(input)
36
+ })
37
+ })
38
+ messages.push({
39
+ role: 'user',
40
+ content: input,
41
+ })
42
+ process.stdout.write(chalk.bold(chalk.dim('model > ')))
43
+ const result = await modelServer.processChatCompletionTask(
44
+ {
45
+ model: 'my-model',
46
+ messages,
47
+ },
48
+ {
49
+ onChunk: (chunk) => {
50
+ process.stdout.write(chunk.text)
51
+ },
52
+ },
53
+ )
54
+ messages.push(result.message)
55
+ process.stdout.write(' ' + chalk.dim(`[${result.finishReason}]`) + '\n')
56
+ }
@@ -0,0 +1,65 @@
1
+ import http from 'node:http'
2
+ import { startModelServer } from '#package/index.js'
3
+
4
+ // A minimal chat server using the ModelServer.
5
+
6
+ const modelServer = await startModelServer({
7
+ log: 'info',
8
+ concurrency: 2,
9
+ models: {
10
+ 'phi3-mini-4k': {
11
+ task: 'text-completion',
12
+ url: 'https://gpt4all.io/models/gguf/Phi-3-mini-4k-instruct.Q4_0.gguf',
13
+ engine: 'gpt4all',
14
+ maxInstances: 2,
15
+ },
16
+ },
17
+ })
18
+
19
+ const httpServer = http.createServer((req, res) => {
20
+ if (req.url === '/chat' && req.method === 'POST') {
21
+ let body = ''
22
+ req.on('data', (chunk) => {
23
+ body += chunk.toString()
24
+ })
25
+ req.on('end', async () => {
26
+ const req = JSON.parse(body)
27
+ const completion = await modelServer.processChatCompletionTask(req)
28
+ res.writeHead(200, { 'Content-Type': 'application/json' })
29
+ res.end(JSON.stringify(completion, null, 2))
30
+ })
31
+ } else {
32
+ res.writeHead(404, { 'Content-Type': 'text/plain' })
33
+ res.end('Not found')
34
+ }
35
+ })
36
+ httpServer.listen(3000).on('listening', () => {
37
+ console.log('HTTP Server up')
38
+ })
39
+
40
+ /*
41
+ curl http://localhost:3000/chat \
42
+ -H "Content-Type: application/json" \
43
+ -d '{
44
+ "model": "phi3-mini-4k",
45
+ "messages": [
46
+ {
47
+ "role": "user",
48
+ "content": "how to find my kernel version on linux=?"
49
+ }
50
+ ]
51
+ }'
52
+ */
53
+
54
+ /*
55
+ {
56
+ "finishReason": "eogToken",
57
+ "message": {
58
+ "role": "assistant",
59
+ "content": "To find your kernel version on Linux, you can use the following methods: [...]"
60
+ },
61
+ "promptTokens": 10,
62
+ "completionTokens": 344,
63
+ "totalTokens": 354
64
+ }
65
+ */
@@ -0,0 +1,70 @@
1
+ import { startHTTPServer } from '#package/http.js'
2
+ import OpenAI from 'openai'
3
+ import readline from 'node:readline'
4
+
5
+ // Printing two parallel completion processes to the console.
6
+
7
+ const httpServer = await startHTTPServer({
8
+ listen: { port: 3000 },
9
+ concurrency: 2, // two clients may process chat completions at the same time.
10
+ models: {
11
+ 'my-model': {
12
+ task: 'text-completion',
13
+ engine: 'node-llama-cpp',
14
+ url: 'https://huggingface.co/HuggingFaceTB/smollm-135M-instruct-v0.2-Q8_0-GGUF/blob/main/smollm-135m-instruct-add-basics-q8_0.gguf',
15
+ sha256: 'a98d3857b95b96c156d954780d28f39dcb35b642e72892ee08ddff70719e6220',
16
+ minInstances: 1, // one instance / session will always be ready
17
+ maxInstances: 2, // up to two may be spawned
18
+ device: { gpu: false, cpuThreads: 4 }, // configure so they're roughly the same speed
19
+ },
20
+ },
21
+ })
22
+ const openai = new OpenAI({
23
+ baseURL: 'http://localhost:3000/openai/v1/',
24
+ apiKey: 'yes',
25
+ })
26
+ let sentence1 = 'Sometimes I feel like'
27
+ let sentence2 = 'The locality of'
28
+ const clearLine = () => {
29
+ readline.cursorTo(process.stdout, 0)
30
+ readline.clearLine(process.stdout, 0)
31
+ }
32
+ const updateOutputs = () => {
33
+ const truncateLine = (line) => {
34
+ return line.length > process.stdout.columns
35
+ ? '...' + line.slice(line.length - process.stdout.columns + 3)
36
+ : line
37
+ }
38
+ readline.moveCursor(process.stdout, 0, -2)
39
+ clearLine()
40
+ process.stdout.write(truncateLine(sentence1) + '\n')
41
+ clearLine()
42
+ process.stdout.write(truncateLine(sentence2) + '\n')
43
+ }
44
+ const completeSentence = async (prompt, onTokens) => {
45
+ const completion = await openai.completions.create({
46
+ stream_options: { include_usage: true },
47
+ model: 'my-model',
48
+ stream: true,
49
+ temperature: 1,
50
+ stop: ['.'],
51
+ prompt,
52
+ })
53
+ for await (const chunk of completion) {
54
+ if (chunk.choices[0].text) {
55
+ onTokens(chunk.choices[0].text.replaceAll('\n', '\\n'))
56
+ }
57
+ }
58
+ onTokens('.')
59
+ }
60
+ setInterval(updateOutputs, 200)
61
+ console.log(sentence1)
62
+ console.log(sentence2)
63
+ while (true) {
64
+ await Promise.all([
65
+ completeSentence(sentence1, (text) => (sentence1 += text)),
66
+ completeSentence(sentence2, (text) => (sentence2 += text)),
67
+ ])
68
+ }
69
+ httpServer.close()
70
+ clearInterval(updateOutputs)
@@ -0,0 +1,70 @@
1
+ import http from 'node:http'
2
+ import express from 'express'
3
+ import OpenAI from 'openai'
4
+ import { ModelServer } from '#package/server.js'
5
+ import { createExpressMiddleware } from '#package/http.js'
6
+
7
+ // Demonstration of using the ModelServer + Express middleware to serve an OpenAI API.
8
+
9
+ // Create a server with a single model, limiting to 2 instances that can run concurrently.
10
+ // Models will be downloaded on-demand or during ModelServer.start() if minInstances > 0.
11
+ const modelServer = new ModelServer({
12
+ concurrency: 2,
13
+ models: {
14
+ 'my-model': {
15
+ task: 'text-completion',
16
+ url: 'https://huggingface.co/HuggingFaceTB/smollm-135M-instruct-v0.2-Q8_0-GGUF/blob/main/smollm-135m-instruct-add-basics-q8_0.gguf',
17
+ sha256: 'a98d3857b95b96c156d954780d28f39dcb35b642e72892ee08ddff70719e6220',
18
+ engine: 'node-llama-cpp',
19
+ maxInstances: 2,
20
+ },
21
+ },
22
+ })
23
+
24
+ await modelServer.start()
25
+
26
+ const app = express()
27
+ app.use(express.json(), createExpressMiddleware(modelServer))
28
+ const server = http.createServer(app)
29
+ server.listen(3001)
30
+
31
+ console.log('Server up, sending chat completion request...')
32
+
33
+ const openai = new OpenAI({
34
+ baseURL: 'http://localhost:3001/openai/v1/',
35
+ apiKey: '123',
36
+ })
37
+
38
+ const completion = await openai.chat.completions.create({
39
+ model: 'my-model',
40
+ messages: [{ role: 'user', content: 'Lets count to three!' }],
41
+ stop: ['Two'],
42
+ })
43
+
44
+ console.log(JSON.stringify(completion, null, 2))
45
+
46
+ /*
47
+ {
48
+ "id": "my-model:pU2BHWUv-kHdAeVn8",
49
+ "model": "my-model",
50
+ "object": "chat.completion",
51
+ "created": 1714431837,
52
+ "system_fingerprint": "0159c68a067a360e4be3e285d3e309440c070734",
53
+ "choices": [
54
+ {
55
+ "index": 0,
56
+ "message": {
57
+ "role": "assistant",
58
+ "content": "Sure, let's count together: 1 (one), 2 (two), and 3 (three). If you have any other questions or need further assistance, feel free to ask!"
59
+ },
60
+ "logprobs": null,
61
+ "finish_reason": "stop"
62
+ }
63
+ ],
64
+ "usage": {
65
+ "prompt_tokens": 6,
66
+ "completion_tokens": 41,
67
+ "total_tokens": 47
68
+ }
69
+ }
70
+ */
@@ -0,0 +1,91 @@
1
+ import os from 'node:os'
2
+ import path from 'node:path'
3
+ import chalk from 'chalk'
4
+ import { ModelPool } from '#package/index.js'
5
+ import { elapsedMillis } from '#package/lib/util.js'
6
+ import * as LlamaCppEngine from '#package/engines/node-llama-cpp/engine.js'
7
+
8
+ // Complete multiple prompts concurrently using ModelPool.
9
+
10
+ async function onPrepareInstance(instance) {
11
+ // can be used to set up the instance before it's used.
12
+ // the model will not be loaded until this promise resolves.
13
+ // console.log('Instance about to load:', instance)
14
+ // throwing here will put the instance in an error state
15
+ }
16
+
17
+ const pool = new ModelPool(
18
+ {
19
+ // to see what's going on, set the log level to 'debug'
20
+ // log: 'debug',
21
+ // global processing concurrency limit, across all instances of all models
22
+ concurrency: 2,
23
+ models: {
24
+ 'my-model': {
25
+ task: 'text-completion',
26
+ // note that this path needs to be absolute and the file needs to be downloaded beforehand.
27
+ location: path.resolve(
28
+ os.homedir(),
29
+ '.cache/inference-server/huggingface.co/HuggingFaceTB/smollm-135M-instruct-v0.2-Q8_0-GGUF-main/smollm-135m-instruct-add-basics-q8_0.gguf',
30
+ ),
31
+ engine: 'node-llama-cpp',
32
+ minInstances: 1, // setting this to something greater 0 will load the model on pool.init()
33
+ maxInstances: 2, // allow the pool to spawn additional instances of this model
34
+ },
35
+ },
36
+ },
37
+ onPrepareInstance,
38
+ )
39
+
40
+ process.on('exit', () => {
41
+ pool.dispose()
42
+ })
43
+
44
+ console.log('Initializing pool...')
45
+ await pool.init({
46
+ 'node-llama-cpp': LlamaCppEngine,
47
+ })
48
+
49
+ async function createCompletion(prompt) {
50
+ const req = {
51
+ model: 'my-model',
52
+ prompt,
53
+ temperature: 3,
54
+ maxTokens: 200,
55
+ }
56
+ const completionModel = await pool.requestInstance(req)
57
+ const completionBegin = process.hrtime.bigint()
58
+ const task = completionModel.instance.processTextCompletionTask(req)
59
+ const result = await task.result
60
+ completionModel.release()
61
+ const elapsed = Math.max(elapsedMillis(completionBegin), 1000)
62
+ return {
63
+ text: result.text,
64
+ instance: completionModel.instance.id,
65
+ device: completionModel.instance.gpu ? 'GPU' : 'CPU',
66
+ speed: Math.round(result.completionTokens / (elapsed / 1000)),
67
+ }
68
+ }
69
+
70
+ const printResult = (title) => (result) => {
71
+ console.log(
72
+ chalk.yellow(title) +
73
+ ' ' +
74
+ chalk.bold(result.instance) +
75
+ ' ' +
76
+ chalk.dim(`generated ${result.speed} tokens/s on ${result.device}`),
77
+ )
78
+ console.log(chalk.dim(prompt) + result.text)
79
+ }
80
+
81
+ const completionCount = 20
82
+ const prompt = 'Locality of '
83
+
84
+ const res = await createCompletion(prompt)
85
+ printResult('Initial completion')(res)
86
+
87
+ console.log(`Processing ${completionCount} completions...`)
88
+
89
+ for (let i = 1; i <= completionCount; i++) {
90
+ createCompletion(prompt).then(printResult(`#${i}`))
91
+ }