mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2026-07-17 10:58:08 +00:00
Compare commits
481 Commits
evaluate
...
python-v2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b743c588e8 | ||
|
|
67843edc7c | ||
|
|
83ada4ca89 | ||
|
|
8d09b2c264 | ||
|
|
446668674e | ||
|
|
adea3811ea | ||
|
|
71db8bdc80 | ||
|
|
71d7f34d1a | ||
|
|
b4bcc5b37c | ||
|
|
f571e7e450 | ||
|
|
0455b80b7f | ||
|
|
271e6a529c | ||
|
|
5732928b93 | ||
|
|
6a22b81f44 | ||
|
|
f50bf856b3 | ||
|
|
df79e45195 | ||
|
|
0e9e5237c5 | ||
|
|
a1bb6084ed | ||
|
|
f30151491d | ||
|
|
c6bd8577a9 | ||
|
|
699410014a | ||
|
|
6c2542e540 | ||
|
|
2bb86f35ee | ||
|
|
255568fb9a | ||
|
|
53f109f519 | ||
|
|
667f29c2a1 | ||
|
|
97de30edd1 | ||
|
|
2c0a660e6e | ||
|
|
406e88b59a | ||
|
|
171f4e488e | ||
|
|
6adaa672b4 | ||
|
|
b68ebb7c15 | ||
|
|
0072860d24 | ||
|
|
ef9717dbe9 | ||
|
|
afbb30a523 | ||
|
|
11db71e0a7 | ||
|
|
5ed9aea410 | ||
|
|
e2f64f89c9 | ||
|
|
0daf37ab8a | ||
|
|
a6a3e0048a | ||
|
|
f36a2874eb | ||
|
|
0cc5a80656 | ||
|
|
c951a5b1d3 | ||
|
|
026ee4e46b | ||
|
|
61d6765361 | ||
|
|
59f99b7f21 | ||
|
|
fe653d1489 | ||
|
|
5c248dbec9 | ||
|
|
6ed3d01f17 | ||
|
|
6c3903a303 | ||
|
|
8ee68d1b6f | ||
|
|
4251b7beaa | ||
|
|
fc169e739a | ||
|
|
028a8db6ba | ||
|
|
26cedb83b0 | ||
|
|
9c755d25c4 | ||
|
|
099459c8b9 | ||
|
|
8474d76fec | ||
|
|
08b5dc8598 | ||
|
|
17dee02287 | ||
|
|
44717682a7 | ||
|
|
a0bd96f75d | ||
|
|
d8c842263f | ||
|
|
5a874be7c1 | ||
|
|
402f515a5d | ||
|
|
2a91ffd73f | ||
|
|
0fc071d228 | ||
|
|
c19b763e03 | ||
|
|
be6d3bf9dc | ||
|
|
83c76be68a | ||
|
|
f2b4809b72 | ||
|
|
9fafca5c94 | ||
|
|
7d1e30766f | ||
|
|
5ddcf61ae4 | ||
|
|
713afb7070 | ||
|
|
4a16a920a3 | ||
|
|
a59645c839 | ||
|
|
f500bcf6e5 | ||
|
|
fc1a281381 | ||
|
|
007d469034 | ||
|
|
7a23b23728 | ||
|
|
f720261d46 | ||
|
|
17a2cdbe35 | ||
|
|
72474a2efa | ||
|
|
f8b1069a1c | ||
|
|
a153cc5b25 | ||
|
|
ef518fae3e | ||
|
|
e7f2ff189f | ||
|
|
88e330ef0e | ||
|
|
fc6c5ea0c7 | ||
|
|
c1dcb3f5b8 | ||
|
|
a010a8a7ca | ||
|
|
ef0a67eb94 | ||
|
|
67bbce43ab | ||
|
|
4fc4d94be4 | ||
|
|
b8f5c74f40 | ||
|
|
c13202a6f5 | ||
|
|
4a8c6d7f9c | ||
|
|
32837fb3a0 | ||
|
|
7810b757c9 | ||
|
|
896fc6fbb7 | ||
|
|
fa0a2129dc | ||
|
|
b0c471aed8 | ||
|
|
67099f80ba | ||
|
|
ad34c2bdd4 | ||
|
|
fbf5e5e732 | ||
|
|
ed0f93977d | ||
|
|
d948a4f2ee | ||
|
|
f2024a1f9e | ||
|
|
6fdec808b2 | ||
|
|
a1471becf3 | ||
|
|
16927d9a76 | ||
|
|
2b40c0beec | ||
|
|
d156bae156 | ||
|
|
85435a84f5 | ||
|
|
eb1081d37e | ||
|
|
e60b388a2e | ||
|
|
fc7e5f4a09 | ||
|
|
79b0866c62 | ||
|
|
6da62a62f0 | ||
|
|
059afb8ee8 | ||
|
|
5dd7378db4 | ||
|
|
dcb0e6c8a8 | ||
|
|
f569ae9b22 | ||
|
|
ec13ba2818 | ||
|
|
2020c23edf | ||
|
|
260a56c748 | ||
|
|
4258bb1f8a | ||
|
|
490404dbb2 | ||
|
|
513a214eca | ||
|
|
78a26cc5e4 | ||
|
|
bf493bb048 | ||
|
|
1b524c4617 | ||
|
|
cb10465127 | ||
|
|
92c025a7f6 | ||
|
|
4461af35c7 | ||
|
|
316b32c525 | ||
|
|
10e3f7bbf5 | ||
|
|
e1eac00ee0 | ||
|
|
111e152a5d | ||
|
|
ffed2ff823 | ||
|
|
a5275ea9e7 | ||
|
|
cdf0fedae2 | ||
|
|
d14b95f4bd | ||
|
|
eadc3b8d80 | ||
|
|
6db5307730 | ||
|
|
0a40e71652 | ||
|
|
b11c3f679e | ||
|
|
061d1969f8 | ||
|
|
f549d5a70a | ||
|
|
38c61493d2 | ||
|
|
29d2c936d1 | ||
|
|
cfa22ab1c4 | ||
|
|
3556f63a29 | ||
|
|
34de19ebf6 | ||
|
|
c1fce502f7 | ||
|
|
363f6659e4 | ||
|
|
6abeefb303 | ||
|
|
697a5f5d2a | ||
|
|
0a45dd384e | ||
|
|
27912f6e1a | ||
|
|
26acdebafa | ||
|
|
c7ea283f1f | ||
|
|
b881598166 | ||
|
|
a9c5f53562 | ||
|
|
15ce428672 | ||
|
|
b98e5f396a | ||
|
|
b7c92c5afd | ||
|
|
e7c4680b51 | ||
|
|
03a9f0bedf | ||
|
|
f8564398fc | ||
|
|
b96406669d | ||
|
|
e51a504550 | ||
|
|
eef604fd64 | ||
|
|
b803d51586 | ||
|
|
7e9786fccf | ||
|
|
f7aeeca884 | ||
|
|
16a84972f6 | ||
|
|
4dbe2634aa | ||
|
|
233f0c4201 | ||
|
|
96cee4f9ac | ||
|
|
2d566710e5 | ||
|
|
a0f7d7ae0e | ||
|
|
38d81c14d0 | ||
|
|
3e99b90c0b | ||
|
|
c72c73a94f | ||
|
|
528eb1e7ad | ||
|
|
d1c56b8b28 | ||
|
|
7aa0f779de | ||
|
|
a1f27072c2 | ||
|
|
3acbef14b7 | ||
|
|
0600f551b3 | ||
|
|
9481762802 | ||
|
|
778264fbab | ||
|
|
1df3da0a88 | ||
|
|
7facb8207b | ||
|
|
dfd8ef0186 | ||
|
|
fb3b1ceba2 | ||
|
|
9e28dfac9c | ||
|
|
012f399639 | ||
|
|
a328f9ed3f | ||
|
|
e4ff972522 | ||
|
|
4862e8b650 | ||
|
|
078c3bd85c | ||
|
|
84749a4ced | ||
|
|
f1c58d0e2c | ||
|
|
76413e1d03 | ||
|
|
db70f1752a | ||
|
|
f3eaa33ce7 | ||
|
|
9e27a118ed | ||
|
|
34555c4934 | ||
|
|
9a3dd8815d | ||
|
|
c1809a23ba | ||
|
|
59ed2a0bea | ||
|
|
eecf351c64 | ||
|
|
abd4703c79 | ||
|
|
4b413a60e4 | ||
|
|
17b346dfe7 | ||
|
|
71e37816cc | ||
|
|
cce5fe2045 | ||
|
|
371e2a5cbc | ||
|
|
d4ce9f4a7c | ||
|
|
8fabf0be4a | ||
|
|
45d76d6234 | ||
|
|
da95bcfb4b | ||
|
|
64101d3af5 | ||
|
|
3c561bcdf2 | ||
|
|
ffef60912f | ||
|
|
bc88271520 | ||
|
|
5508e43466 | ||
|
|
79a5522931 | ||
|
|
f529d55380 | ||
|
|
f5f22fdbd0 | ||
|
|
5c0d077f74 | ||
|
|
131cfcdeae | ||
|
|
dc2e7d6e9b | ||
|
|
7bcd9e8089 | ||
|
|
fd0c501d68 | ||
|
|
14b410a12a | ||
|
|
ab96035bec | ||
|
|
9193a9517a | ||
|
|
8d7a3f26d3 | ||
|
|
3444a47cad | ||
|
|
89a59e7f99 | ||
|
|
f5dd74bcf0 | ||
|
|
78d930516d | ||
|
|
83b8eea611 | ||
|
|
1bebe78c56 | ||
|
|
b75a209374 | ||
|
|
e90263c23f | ||
|
|
f414c28589 | ||
|
|
7e5e84fbb7 | ||
|
|
37b007603a | ||
|
|
c25dc51935 | ||
|
|
34daf240f9 | ||
|
|
721d854095 | ||
|
|
d50803ff8e | ||
|
|
9e99cf937a | ||
|
|
245c5ce5ea | ||
|
|
4338e72a51 | ||
|
|
0fe2e19691 | ||
|
|
f505619c84 | ||
|
|
5fbeeb1cb4 | ||
|
|
017c3a9649 | ||
|
|
bcbcad98d0 | ||
|
|
fd3014016b | ||
|
|
ac33bafb91 | ||
|
|
9a19c740ee | ||
|
|
f79557d2aa | ||
|
|
22de3c56bd | ||
|
|
10f9b49313 | ||
|
|
2490977f89 | ||
|
|
a35f1ab784 | ||
|
|
4d4275d1b8 | ||
|
|
3c45a555e9 | ||
|
|
f39df0906e | ||
|
|
005c092943 | ||
|
|
908aec27fe | ||
|
|
aed2068342 | ||
|
|
afaa291eab | ||
|
|
7b611b49f2 | ||
|
|
f81b4b45bf | ||
|
|
043617168e | ||
|
|
64001a480a | ||
|
|
04499d1c7d | ||
|
|
7a19047329 | ||
|
|
df8528df73 | ||
|
|
f0742c22f4 | ||
|
|
35f9cdb70a | ||
|
|
9fb135e020 | ||
|
|
df66226f7d | ||
|
|
3c25d81759 | ||
|
|
4f0cee9330 | ||
|
|
56c0d2898d | ||
|
|
b2cd3bdb3f | ||
|
|
5fe685427a | ||
|
|
eec906aa05 | ||
|
|
9325075f80 | ||
|
|
a9acdd25de | ||
|
|
f028f67c68 | ||
|
|
a10f3aea5e | ||
|
|
8bb6a6c201 | ||
|
|
4528f73479 | ||
|
|
d87573ea75 | ||
|
|
cc6db61c93 | ||
|
|
f605a5b686 | ||
|
|
1534df3e9f | ||
|
|
672cb850f9 | ||
|
|
a5b93cf095 | ||
|
|
75deee9adb | ||
|
|
2eb83b9f2a | ||
|
|
906699e8e9 | ||
|
|
ea66669cef | ||
|
|
088afada49 | ||
|
|
b4d82ea289 | ||
|
|
12f943e966 | ||
|
|
40c78d2f78 | ||
|
|
5d346e13d7 | ||
|
|
4eefd386d0 | ||
|
|
3c2aa299d8 | ||
|
|
f9deb87d20 | ||
|
|
cc7675d432 | ||
|
|
0493e6eb07 | ||
|
|
a49a1dcdf4 | ||
|
|
d5d72f0361 | ||
|
|
050e7f076e | ||
|
|
31b20f093a | ||
|
|
8f3abb37ca | ||
|
|
4219c0e2e7 | ||
|
|
ce7be1db48 | ||
|
|
cca9e6ce81 | ||
|
|
25297786db | ||
|
|
fd47088f2b | ||
|
|
6277eac9cc | ||
|
|
aa706ab1ff | ||
|
|
17fc9e3e58 | ||
|
|
7c67262a13 | ||
|
|
42bcb814b3 | ||
|
|
4392bf26e0 | ||
|
|
34f2ec2b33 | ||
|
|
1d29e4696c | ||
|
|
507753a37c | ||
|
|
d90d003a1d | ||
|
|
5f3d739205 | ||
|
|
b4cf12e1bd | ||
|
|
21a5709b07 | ||
|
|
4426640f44 | ||
|
|
6711bddc4c | ||
|
|
7f979c8258 | ||
|
|
99c106e6b5 | ||
|
|
9611c4081a | ||
|
|
17cb4a86d1 | ||
|
|
0d1edaf029 | ||
|
|
dc80d1e578 | ||
|
|
e86c63750d | ||
|
|
f47e698193 | ||
|
|
84905aa281 | ||
|
|
ecf014f03b | ||
|
|
e6e724d2dc | ||
|
|
06a833e652 | ||
|
|
045f6e6cdc | ||
|
|
0f046cf905 | ||
|
|
655372dbfa | ||
|
|
aa33419c6e | ||
|
|
79843c269e | ||
|
|
9013a089bd | ||
|
|
3076e0bf26 | ||
|
|
1fa67a585c | ||
|
|
cf4eb530ce | ||
|
|
21a3244645 | ||
|
|
0458c9b4e6 | ||
|
|
4b9a345aee | ||
|
|
6f038c136b | ||
|
|
86e862df7e | ||
|
|
358ff2a477 | ||
|
|
891ddafc33 | ||
|
|
8f99dca70f | ||
|
|
f0735efa7d | ||
|
|
c953b321b7 | ||
|
|
0ad1472b62 | ||
|
|
c4d23512e4 | ||
|
|
b6e38d69ed | ||
|
|
707b91a24f | ||
|
|
a69d23ecc4 | ||
|
|
b9fd0c25b2 | ||
|
|
85e34598f9 | ||
|
|
9f1cbad4f1 | ||
|
|
202805637b | ||
|
|
2832fad965 | ||
|
|
6a309e2ac8 | ||
|
|
94969a4199 | ||
|
|
1a2a9791bd | ||
|
|
8d80f7963e | ||
|
|
1723f82aaa | ||
|
|
3bdc87ff4a | ||
|
|
5e5a235639 | ||
|
|
4521c71b4e | ||
|
|
2f1c995739 | ||
|
|
84e08858a8 | ||
|
|
6fd6369ab3 | ||
|
|
54bc61e280 | ||
|
|
320eda9685 | ||
|
|
f578fa6cdf | ||
|
|
17d3e4976c | ||
|
|
7ec522dfb0 | ||
|
|
7ae6bfc928 | ||
|
|
987546c63b | ||
|
|
d55cbbee32 | ||
|
|
0bc2274869 | ||
|
|
33c22be2aa | ||
|
|
27a8b020c3 | ||
|
|
36f7fb5848 | ||
|
|
b43eec0e2c | ||
|
|
a63093554f | ||
|
|
a9668eb2e4 | ||
|
|
2c0ee50dce | ||
|
|
4e55940edf | ||
|
|
4d855afe97 | ||
|
|
af6fe5fbb5 | ||
|
|
ca8baa294b | ||
|
|
889c8d1758 | ||
|
|
108d950874 | ||
|
|
0f2bb506a8 | ||
|
|
c449b71b56 | ||
|
|
cbdcde8b75 | ||
|
|
3d2db76070 | ||
|
|
55f96aacc6 | ||
|
|
e56f977b67 | ||
|
|
e285ce91da | ||
|
|
19d6460282 | ||
|
|
83ad6b42c4 | ||
|
|
3ed6d176a5 | ||
|
|
91a32c0e84 | ||
|
|
39acbc8378 | ||
|
|
b9e2553995 | ||
|
|
09a143228c | ||
|
|
fc1af4a234 | ||
|
|
6d03b3e500 | ||
|
|
397f3ba2d7 | ||
|
|
0e866a0e8f | ||
|
|
9100b2ef6f | ||
|
|
14f4b522d5 | ||
|
|
545c23b4bd | ||
|
|
b3f84c56e7 | ||
|
|
41f640577c | ||
|
|
6431d46776 | ||
|
|
2befff83d6 | ||
|
|
3d10110314 | ||
|
|
8aba2c9009 | ||
|
|
58f0fcab57 | ||
|
|
b4dbbd1485 | ||
|
|
5f0aaf8bdb | ||
|
|
4974ae917c | ||
|
|
63849d9afc | ||
|
|
2d02c65177 | ||
|
|
1e74171a7b | ||
|
|
cfd70b69fc | ||
|
|
306105e62f | ||
|
|
89e277bb3c | ||
|
|
f543affa9a | ||
|
|
6c8669cad3 | ||
|
|
0c0a4f2c22 | ||
|
|
6656f0f41e | ||
|
|
bb2b82e1b9 | ||
|
|
c77ab849c0 | ||
|
|
1c4a244291 | ||
|
|
936dcd2bfc | ||
|
|
15f1fe5445 | ||
|
|
ee4186d579 | ||
|
|
6200900677 | ||
|
|
4963db8f43 | ||
|
|
0efdbfcffe | ||
|
|
315a1f2aa2 | ||
|
|
ae8eb297ac | ||
|
|
1f749d7633 | ||
|
|
33557b1f39 | ||
|
|
64b409e0b8 | ||
|
|
e59946f05d | ||
|
|
b72b409d40 | ||
|
|
59cae1132c | ||
|
|
a0dae86a95 |
@@ -11,8 +11,10 @@ workflows:
|
||||
base-revision: main
|
||||
config-path: .circleci/continue_config.yml
|
||||
mapping: |
|
||||
.circleci/.* run-all-workflows true
|
||||
gpt4all-backend/.* run-all-workflows true
|
||||
gpt4all-bindings/python/.* run-python-workflow true
|
||||
gpt4all-bindings/typescript/.* run-ts-workflow true
|
||||
gpt4all-bindings/csharp/.* run-csharp-workflow true
|
||||
gpt4all-backend/.* run-chat-workflow true
|
||||
gpt4all-chat/.* run-chat-workflow true
|
||||
.* run-default-workflow true
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,3 @@
|
||||
[codespell]
|
||||
ignore-words-list = blong, belong, afterall
|
||||
ignore-words-list = blong, afterall, som, assistent, crasher
|
||||
skip = .git,*.pdf,*.svg,*.lock
|
||||
|
||||
35
.github/ISSUE_TEMPLATE/bindings-bug.md
vendored
Normal file
35
.github/ISSUE_TEMPLATE/bindings-bug.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: "\U0001F6E0 Bindings Bug Report"
|
||||
about: A bug report for the GPT4All Bindings
|
||||
labels: ["bindings", "bug-unconfirmed"]
|
||||
---
|
||||
|
||||
<!-- Before creating a new issue, please make sure to take a few moments to check the issue tracker for existing issues about the bug. -->
|
||||
|
||||
### Bug Report
|
||||
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
### Example Code
|
||||
|
||||
<!-- Please provide a minimal code example that can be used to experience this issue. Delete this section if it does not apply. -->
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
<!-- List the steps that should be taken to experience this issue. -->
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### Expected Behavior
|
||||
|
||||
<!-- In a few words, what did you expect to happen? -->
|
||||
|
||||
### Your Environment
|
||||
|
||||
- Bindings version (e.g. "Version" from `pip show gpt4all`):
|
||||
- Operating System:
|
||||
- Chat model used (if applicable):
|
||||
|
||||
<!-- You can freely edit this text, please remove all the lines you believe are unnecessary. -->
|
||||
70
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
70
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -1,70 +0,0 @@
|
||||
name: "\U0001F41B Bug Report"
|
||||
description: Submit a bug report to help us improve GPT4All
|
||||
labels: ["02 Bug Report"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
Thank you for taking the time to file a bug report. Before creating a new
|
||||
issue, please make sure to take a few moments to check the issue tracker
|
||||
for existing issues about the bug.
|
||||
|
||||
- type: textarea
|
||||
id: system-info
|
||||
attributes:
|
||||
label: System Info
|
||||
description: Please share your system info with us.
|
||||
placeholder: GPT4All version, platform, python version, etc...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
id: information-scripts-examples
|
||||
attributes:
|
||||
label: Information
|
||||
description: "The problem arises when using:"
|
||||
options:
|
||||
- label: "The official example notebooks/scripts"
|
||||
- label: "My own modified scripts"
|
||||
|
||||
- type: checkboxes
|
||||
id: related-components
|
||||
attributes:
|
||||
label: Related Components
|
||||
description: "Select the components related to the issue (if applicable):"
|
||||
options:
|
||||
- label: "backend"
|
||||
- label: "bindings"
|
||||
- label: "python-bindings"
|
||||
- label: "chat-ui"
|
||||
- label: "models"
|
||||
- label: "circleci"
|
||||
- label: "docker"
|
||||
- label: "api"
|
||||
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: |
|
||||
Please provide a [code sample](https://stackoverflow.com/help/minimal-reproducible-example) that reproduces the problem you ran into. It can be a Colab link or just a code snippet.
|
||||
If you have code snippets, error messages, stack traces please provide them here as well.
|
||||
Important! Use code tags to correctly format your code. See https://help.github.com/en/github/writing-on-github/creating-and-highlighting-code-blocks#syntax-highlighting
|
||||
Avoid screenshots when possible, as they are hard to read and (more importantly) don't allow others to copy-and-paste your code.
|
||||
|
||||
placeholder: |
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: "A clear and concise description of what you would expect to happen."
|
||||
31
.github/ISSUE_TEMPLATE/chat-bug.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/chat-bug.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: "\U0001F4AC GPT4All Bug Report"
|
||||
about: A bug report for GPT4All Chat
|
||||
labels: ["chat", "bug-unconfirmed"]
|
||||
---
|
||||
|
||||
<!-- Before creating a new issue, please make sure to take a few moments to check the issue tracker for existing issues about the bug. -->
|
||||
|
||||
### Bug Report
|
||||
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
<!-- List the steps that should be taken to experience this issue. Provide any relevant information about your configuration, and describe anything that was unexpected. -->
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### Expected Behavior
|
||||
|
||||
<!-- In a few words, what did you expect to happen? -->
|
||||
|
||||
### Your Environment
|
||||
|
||||
- GPT4All version:
|
||||
- Operating System:
|
||||
- Chat model used (if applicable):
|
||||
|
||||
<!-- You can freely edit this text, please remove all the lines you believe are unnecessary. -->
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
1
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,2 +1 @@
|
||||
blank_issues_enabled: false
|
||||
version: 2.1
|
||||
9
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal file
9
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
name: "\U0001F4C4 Documentation"
|
||||
about: An issue related to the GPT4All documentation
|
||||
labels: ["documentation"]
|
||||
---
|
||||
|
||||
### Documentation
|
||||
|
||||
<!-- Please describe the issue with the documentation as clearly as possible. -->
|
||||
19
.github/ISSUE_TEMPLATE/documentation.yml
vendored
19
.github/ISSUE_TEMPLATE/documentation.yml
vendored
@@ -1,19 +0,0 @@
|
||||
name: Documentation
|
||||
description: Report an issue related to the GPT4All documentation.
|
||||
title: "DOC: <Please write a comprehensive title after the 'DOC: ' prefix>"
|
||||
labels: [03 - Documentation]
|
||||
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Issue with current documentation:"
|
||||
description: >
|
||||
Please make sure to leave a reference to the document/code you're
|
||||
referring to.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Idea or request for content:"
|
||||
description: >
|
||||
Please describe as clearly as possible what topics you think are missing
|
||||
from the current documentation.
|
||||
10
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: "\U0001F680 Feature Request"
|
||||
about: Submit a proposal/request for a new GPT4All feature
|
||||
title: "[Feature] Feature request title..."
|
||||
labels: ["enhancement"]
|
||||
---
|
||||
|
||||
### Feature Request
|
||||
|
||||
<!-- A clear and concise description of the feature proposal. -->
|
||||
30
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
30
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -1,30 +0,0 @@
|
||||
name: "\U0001F680 Feature Request"
|
||||
description: Submit a proposal/request for a new GPT4All feature
|
||||
labels: ["02 Feature Request"]
|
||||
body:
|
||||
- type: textarea
|
||||
id: feature-request
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Feature request
|
||||
description: |
|
||||
A clear and concise description of the feature proposal. Please provide links to any relevant GitHub repos, papers, or other resources if relevant.
|
||||
|
||||
- type: textarea
|
||||
id: motivation
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Motivation
|
||||
description: |
|
||||
Please outline the motivation for the proposal. Is your feature request related to a problem? e.g., I'm always frustrated when [...]. If this is related to another GitHub issue, please link here too.
|
||||
|
||||
- type: textarea
|
||||
id: contribution
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Your contribution
|
||||
description: |
|
||||
Is there any way that you could help, e.g. by submitting a PR? Make sure to read the CONTRIBUTING.MD [readme](https://github.com/nomic-ai/gpt4all/blob/main/CONTRIBUTING.md)
|
||||
32
.github/ISSUE_TEMPLATE/other-bug.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE/other-bug.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: "\U0001F41B Other Bug Report"
|
||||
about: A bug in another component of GPT4All
|
||||
labels: ["bug-unconfirmed"]
|
||||
---
|
||||
|
||||
<!-- Before creating a new issue, please make sure to take a few moments to check the issue tracker for existing issues about the bug. -->
|
||||
|
||||
### Bug Report
|
||||
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
<!-- List the steps that should be taken to experience this issue. Provide any relevant information about your configuration, and describe anything that was unexpected. If this bug involves original code, please provide a minimal version that can reproduce the issue. -->
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### Expected Behavior
|
||||
|
||||
<!-- In a few words, what did you expect to happen? -->
|
||||
|
||||
### Your Environment
|
||||
|
||||
- GPT4All version (if applicable):
|
||||
- Operating System:
|
||||
- Chat model used (if applicable):
|
||||
|
||||
<!-- You can freely edit this text, please remove all the lines you believe are unnecessary. -->
|
||||
|
||||
18
.github/ISSUE_TEMPLATE/other.yml
vendored
18
.github/ISSUE_TEMPLATE/other.yml
vendored
@@ -1,18 +0,0 @@
|
||||
name: Other Issue
|
||||
description: Raise an issue that wouldn't be covered by the other templates.
|
||||
title: "Issue: <Please write a comprehensive title after the 'Issue: ' prefix>"
|
||||
labels: [04 - Other]
|
||||
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Issue you'd like to raise."
|
||||
description: >
|
||||
Please describe the issue you'd like to raise as clearly as possible.
|
||||
Make sure to include any relevant links or references.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Suggestion:"
|
||||
description: >
|
||||
Please outline a suggestion to improve the issue here.
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -184,3 +184,6 @@ build-*
|
||||
|
||||
# IntelliJ
|
||||
.idea/
|
||||
|
||||
# LLM models
|
||||
*.gguf
|
||||
|
||||
7
.gitmodules
vendored
7
.gitmodules
vendored
@@ -1,9 +1,4 @@
|
||||
[submodule "llama.cpp-230519"]
|
||||
path = gpt4all-backend/llama.cpp-230519
|
||||
url = https://github.com/ggerganov/llama.cpp.git
|
||||
[submodule "llama.cpp-230511"]
|
||||
path = gpt4all-backend/llama.cpp-230511
|
||||
url = https://github.com/nomic-ai/llama.cpp
|
||||
[submodule "llama.cpp-mainline"]
|
||||
path = gpt4all-backend/llama.cpp-mainline
|
||||
url = https://github.com/nomic-ai/llama.cpp.git
|
||||
branch = master
|
||||
|
||||
30
LICENSE_SOM.txt
Normal file
30
LICENSE_SOM.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
Software for Open Models License (SOM)
|
||||
Version 1.0 dated August 30th, 2023
|
||||
|
||||
This license governs use of the accompanying Software. If you use the Software, you accept this license. If you do not accept the license, do not use the Software.
|
||||
|
||||
This license is intended to encourage open release of models created, modified, processed, or otherwise used via the Software under open licensing terms, and should be interpreted in light of that intent.
|
||||
|
||||
1. Definitions
|
||||
The “Licensor” is the person or entity who is making the Software available under this license. “Software” is the software made available by Licensor under this license.
|
||||
A “Model” is the output of a machine learning algorithm, and excludes the Software.
|
||||
“Model Source Materials” must include the Model and model weights, and may include any input data, input data descriptions, documentation or training descriptions for the Model.
|
||||
“Open Licensing Terms” means: (a) any open source license approved by the Open Source Initiative, or (b) any other terms that make the Model Source Materials publicly available free of charge, and allow recipients to use, modify and distribute the Model Source Materials. Terms described in (b) may include reasonable restrictions such as non-commercial or non-production limitations, or require use in compliance with law.
|
||||
|
||||
2. Grant of Rights. Subject to the conditions and limitations in section 3:
|
||||
(A) Copyright Grant. Licensor grants you a non-exclusive, worldwide, royalty-free copyright license to copy, modify, and distribute the Software and any modifications of the Software you create under this license. The foregoing license includes without limitation the right to create, modify, and use Models using this Software.
|
||||
|
||||
(B) Patent Grant. Licensor grants you a non-exclusive, worldwide, royalty-free license, under any patents owned or controlled by Licensor, to make, have made, use, sell, offer for sale, import, or otherwise exploit the Software. No license is granted to patent rights that are not embodied in the operation of the Software in the form provided by Licensor.
|
||||
|
||||
3. Conditions and Limitations
|
||||
(A) Model Licensing and Access. If you use the Software to create, modify, process, or otherwise use any Model, including usage to create inferences with a Model, whether or not you make the Model available to others, you must make that Model Source Materials publicly available under Open Licensing Terms.
|
||||
|
||||
(B) No Re-Licensing. If you redistribute the Software, or modifications to the Software made under the license granted above, you must make it available only under the terms of this license. You may offer additional terms such as warranties, maintenance and support, but You, and not Licensor, are responsible for performing such terms.
|
||||
|
||||
(C) No Trademark License. This license does not grant you rights to use the Licensor’s name, logo, or trademarks.
|
||||
|
||||
(D) If you assert in writing a claim against any person or entity alleging that the use of the Software infringes any patent, all of your licenses to the Software under Section 2 end automatically as of the date you asserted the claim.
|
||||
|
||||
(E) If you distribute any portion of the Software, you must retain all copyright, patent, trademark, and attribution notices that are present in the Software, and you must include a copy of this license.
|
||||
|
||||
(F) The Software is licensed “as-is.” You bear the entire risk of using it. Licensor gives You no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws that this license cannot change. To the extent permitted under your local laws, the Licensor disclaims and excludes the implied warranties of merchantability, fitness for a particular purpose and non-infringement. To the extent this disclaimer is unlawful, you, and not Licensor, are responsible for any liability.
|
||||
31
README.md
31
README.md
@@ -1,8 +1,9 @@
|
||||
<h1 align="center">GPT4All</h1>
|
||||
<p align="center">Open-source assistant-style large language models that run locally on your CPU</p>
|
||||
|
||||
<p align="center">Open-source large language models that run locally on your CPU and nearly any GPU</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://gpt4all.io">GPT4All Website</a>
|
||||
<a href="https://gpt4all.io">GPT4All Website and Models</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -21,6 +22,10 @@
|
||||
GPT4All is made possible by our compute partner <a href="https://www.paperspace.com/">Paperspace</a>.
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.phorm.ai/query?projectId=755eecd3-24ad-49cc-abf4-0ab84caacf63"><img src="https://img.shields.io/badge/Phorm-Ask_AI-%23F2777A.svg?&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNSIgaGVpZ2h0PSI0IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGQ9Ik00LjQzIDEuODgyYTEuNDQgMS40NCAwIDAgMS0uMDk4LjQyNmMtLjA1LjEyMy0uMTE1LjIzLS4xOTIuMzIyLS4wNzUuMDktLjE2LjE2NS0uMjU1LjIyNmExLjM1MyAxLjM1MyAwIDAgMS0uNTk1LjIxMmMtLjA5OS4wMTItLjE5Mi4wMTQtLjI3OS4wMDZsLTEuNTkzLS4xNHYtLjQwNmgxLjY1OGMuMDkuMDAxLjE3LS4xNjkuMjQ2LS4xOTFhLjYwMy42MDMgMCAwIDAgLjItLjEwNi41MjkuNTI5IDAgMCAwIC4xMzgtLjE3LjY1NC42NTQgMCAwIDAgLjA2NS0uMjRsLjAyOC0uMzJhLjkzLjkzIDAgMCAwLS4wMzYtLjI0OS41NjcuNTY3IDAgMCAwLS4xMDMtLjIuNTAyLjUwMiAwIDAgMC0uMTY4LS4xMzguNjA4LjYwOCAwIDAgMC0uMjQtLjA2N0wyLjQzNy43MjkgMS42MjUuNjcxYS4zMjIuMzIyIDAgMCAwLS4yMzIuMDU4LjM3NS4zNzUgMCAwIDAtLjExNi4yMzJsLS4xMTYgMS40NS0uMDU4LjY5Ny0uMDU4Ljc1NEwuNzA1IDRsLS4zNTctLjA3OUwuNjAyLjkwNkMuNjE3LjcyNi42NjMuNTc0LjczOS40NTRhLjk1OC45NTggMCAwIDEgLjI3NC0uMjg1Ljk3MS45NzEgMCAwIDEgLjMzNy0uMTRjLjExOS0uMDI2LjIyNy0uMDM0LjMyNS0uMDI2TDMuMjMyLjE2Yy4xNTkuMDE0LjMzNi4wMy40NTkuMDgyYTEuMTczIDEuMTczIDAgMCAxIC41NDUuNDQ3Yy4wNi4wOTQuMTA5LjE5Mi4xNDQuMjkzYTEuMzkyIDEuMzkyIDAgMCAxIC4wNzguNThsLS4wMjkuMzJaIiBmaWxsPSIjRjI3NzdBIi8+CiAgPHBhdGggZD0iTTQuMDgyIDIuMDA3YTEuNDU1IDEuNDU1IDAgMCAxLS4wOTguNDI3Yy0uMDUuMTI0LS4xMTQuMjMyLS4xOTIuMzI0YTEuMTMgMS4xMyAwIDAgMS0uMjU0LjIyNyAxLjM1MyAxLjM1MyAwIDAgMS0uNTk1LjIxNGMtLjEuMDEyLS4xOTMuMDE0LS4yOC4wMDZsLTEuNTYtLjEwOC4wMzQtLjQwNi4wMy0uMzQ4IDEuNTU5LjE1NGMuMDkgMCAuMTczLS4wMS4yNDgtLjAzM2EuNjAzLjYwMyAwIDAgMCAuMi0uMTA2LjUzMi41MzIgMCAwIDAgLjEzOS0uMTcyLjY2LjY2IDAgMCAwIC4wNjQtLjI0MWwuMDI5LS4zMjFhLjk0Ljk0IDAgMCAwLS4wMzYtLjI1LjU3LjU3IDAgMCAwLS4xMDMtLjIwMi41MDIuNTAyIDAgMCAwLS4xNjgtLjEzOC42MDUuNjA1IDAgMCAwLS4yNC0uMDY3TDEuMjczLjgyN2MtLjA5NC0uMDA4LS4xNjguMDEtLjIyMS4wNTUtLjA1My4wNDUtLjA4NC4xMTQtLjA5Mi4yMDZMLjcwNSA0IDAgMy45MzhsLjI1NS0yLjkxMUExLjAxIDEuMDEgMCAwIDEgLjM5My41NzIuOTYyLjk2MiAwIDAgMSAuNjY2LjI4NmEuOTcuOTcgMCAwIDEgLjMzOC0uMTRDMS4xMjIuMTIgMS4yMy4xMSAxLjMyOC4xMTlsMS41OTMuMTRjLjE2LjAxNC4zLjA0Ny40MjMuMWExLjE3IDEuMTcgMCAwIDEgLjU0NS40NDhjLjA2MS4wOTUuMTA5LjE5My4xNDQuMjk1YTEuNDA2IDEuNDA2IDAgMCAxIC4wNzcuNTgzbC0uMDI4LjMyMloiIGZpbGw9IndoaXRlIi8+CiAgPHBhdGggZD0iTTQuMDgyIDIuMDA3YTEuNDU1IDEuNDU1IDAgMCAxLS4wOTguNDI3Yy0uMDUuMTI0LS4xMTQuMjMyLS4xOTIuMzI0YTEuMTMgMS4xMyAwIDAgMS0uMjU0LjIyNyAxLjM1MyAxLjM1MyAwIDAgMS0uNTk1LjIxNGMtLjEuMDEyLS4xOTMuMDE0LS4yOC4wMDZsLTEuNTYtLjEwOC4wMzQtLjQwNi4wMy0uMzQ4IDEuNTU5LjE1NGMuMDkgMCAuMTczLS4wMS4yNDgtLjAzM2EuNjAzLjYwMyAwIDAgMCAuMi0uMTA2LjUzMi41MzIgMCAwIDAgLjEzOS0uMTcyLjY2LjY2IDAgMCAwIC4wNjQtLjI0MWwuMDI5LS4zMjFhLjk0Ljk0IDAgMCAwLS4wMzYtLjI1LjU3LjU3IDAgMCAwLS4xMDMtLjIwMi41MDIuNTAyIDAgMCAwLS4xNjgtLjEzOC42MDUuNjA1IDAgMCAwLS4yNC0uMDY3TDEuMjczLjgyN2MtLjA5NC0uMDA4LS4xNjguMDEtLjIyMS4wNTUtLjA1My4wNDUtLjA4NC4xMTQtLjA5Mi4yMDZMLjcwNSA0IDAgMy45MzhsLjI1NS0yLjkxMUExLjAxIDEuMDEgMCAwIDEgLjM5My41NzIuOTYyLjk2MiAwIDAgMSAuNjY2LjI4NmEuOTcuOTcgMCAwIDEgLjMzOC0uMTRDMS4xMjIuMTIgMS4yMy4xMSAxLjMyOC4xMTlsMS41OTMuMTRjLjE2LjAxNC4zLjA0Ny40MjMuMWExLjE3IDEuMTcgMCAwIDEgLjU0NS40NDhjLjA2MS4wOTUuMTA5LjE5My4xNDQuMjk1YTEuNDA2IDEuNDA2IDAgMCAxIC4wNzcuNTgzbC0uMDI4LjMyMloiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=" alt="phorm.ai"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img width="600" height="365" src="https://user-images.githubusercontent.com/13879686/231876409-e3de1934-93bb-4b4b-9013-b491a969ebbc.gif">
|
||||
</p>
|
||||
@@ -29,14 +34,25 @@ Run on an M1 macOS Device (not sped up!)
|
||||
</p>
|
||||
|
||||
## GPT4All: An ecosystem of open-source on-edge large language models.
|
||||
GPT4All is an ecosystem to train and deploy **powerful** and **customized** large language models that run locally on consumer grade CPUs.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> GPT4All v2.5.0 and newer only supports models in GGUF format (.gguf). Models used with a previous version of GPT4All (.bin extension) will no longer work.
|
||||
|
||||
GPT4All is an ecosystem to run **powerful** and **customized** large language models that work locally on consumer grade CPUs and any GPU. Note that your CPU needs to support [AVX or AVX2 instructions](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions).
|
||||
|
||||
Learn more in the [documentation](https://docs.gpt4all.io).
|
||||
|
||||
The goal is simple - be the best instruction tuned assistant-style language model that any person or enterprise can freely use, distribute and build on.
|
||||
|
||||
A GPT4All model is a 3GB - 8GB file that you can download and plug into the GPT4All open-source ecosystem software. **Nomic AI** supports and maintains this software ecosystem to enforce quality and security alongside spearheading the effort to allow any person or enterprise to easily train and deploy their own on-edge large language models.
|
||||
|
||||
### What's New ([Issue Tracker](https://github.com/orgs/nomic-ai/projects/2))
|
||||
- **October 19th, 2023**: GGUF Support Launches with Support for:
|
||||
- Mistral 7b base model, an updated model gallery on [gpt4all.io](https://gpt4all.io), several new local code models including Rift Coder v1.5
|
||||
- [Nomic Vulkan](https://blog.nomic.ai/posts/gpt4all-gpu-inference-with-vulkan) support for Q4\_0 and Q4\_1 quantizations in GGUF.
|
||||
- Offline build support for running old versions of the GPT4All Local LLM Chat Client.
|
||||
- **September 18th, 2023**: [Nomic Vulkan](https://blog.nomic.ai/posts/gpt4all-gpu-inference-with-vulkan) launches supporting local LLM inference on AMD, Intel, Samsung, Qualcomm and NVIDIA GPUs.
|
||||
- **August 15th, 2023**: GPT4All API launches allowing inference of local LLMs from docker containers.
|
||||
- **July 2023**: Stable support for LocalDocs, a GPT4All Plugin that allows you to privately and locally chat with your data.
|
||||
|
||||
|
||||
### Chat Client
|
||||
Run any GPT4All model natively on your home desktop with the auto-updating desktop chat client. See <a href="https://gpt4all.io">GPT4All Website</a> for a full list of open-source models you can run with this powerful desktop application.
|
||||
@@ -57,12 +73,15 @@ Find the most up-to-date information on the [GPT4All Website](https://gpt4all.io
|
||||
|
||||
### Bindings
|
||||
|
||||
* <a href="https://github.com/nomic-ai/gpt4all/tree/main/gpt4all-bindings/python/README.md">:snake: Official Python Bindings</a>
|
||||
* <a href="https://github.com/nomic-ai/gpt4all/tree/main/gpt4all-bindings/python/README.md">:snake: Official Python Bindings</a> [](https://pepy.tech/project/gpt4all)
|
||||
* <a href="https://github.com/nomic-ai/gpt4all/tree/main/gpt4all-bindings/typescript">:computer: Official Typescript Bindings</a>
|
||||
* <a href="https://github.com/nomic-ai/gpt4all/tree/main/gpt4all-bindings/golang">:computer: Official GoLang Bindings</a>
|
||||
* <a href="https://github.com/nomic-ai/gpt4all/tree/main/gpt4all-bindings/csharp">:computer: Official C# Bindings</a>
|
||||
* <a href="https://github.com/nomic-ai/gpt4all/tree/main/gpt4all-bindings/java">:computer: Official Java Bindings</a>
|
||||
|
||||
### Integrations
|
||||
|
||||
* 🗃️ [Weaviate Vector Database](https://github.com/weaviate/weaviate) - [module docs](https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-gpt4all)
|
||||
|
||||
## Contributing
|
||||
GPT4All welcomes contributions, involvement, and discussion from the open source community!
|
||||
|
||||
7
gpt4all-api/.isort.cfg
Normal file
7
gpt4all-api/.isort.cfg
Normal file
@@ -0,0 +1,7 @@
|
||||
[settings]
|
||||
known_third_party=geopy,nltk,np,numpy,pandas,pysbd,fire,torch
|
||||
|
||||
line_length=120
|
||||
include_trailing_comma=True
|
||||
multi_line_output=3
|
||||
use_parentheses=True
|
||||
@@ -1,12 +1,19 @@
|
||||
# GPT4All REST API
|
||||
|
||||
NOTICE: We are considering to deprecate this API as it has become challenging to maintain and test. If you have any interest in maintaining this or would like to takeover and adopt or discuss the future of this API please speak up in the discord channel.
|
||||
|
||||
This directory contains the source code to run and build docker images that run a FastAPI app
|
||||
for serving inference from GPT4All models. The API matches the OpenAI API spec.
|
||||
|
||||
## Tutorial
|
||||
|
||||
The following tutorial assumes that you have checked out this repo and cd'd into it.
|
||||
|
||||
### Starting the app
|
||||
|
||||
First build the FastAPI docker image. You only have to do this on initial build or when you add new dependencies to the requirements.txt file:
|
||||
First change your working directory to `gpt4all/gpt4all-api`.
|
||||
|
||||
Now you can build the FastAPI docker image. You only have to do this on initial build or when you add new dependencies to the requirements.txt file:
|
||||
```bash
|
||||
DOCKER_BUILDKIT=1 docker build -t gpt4all_api --progress plain -f gpt4all_api/Dockerfile.buildkit .
|
||||
```
|
||||
@@ -17,6 +24,18 @@ Then, start the backend with:
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
This will run both the API and locally hosted GPU inference server. If you want to run the API without the GPU inference server, you can run:
|
||||
|
||||
```bash
|
||||
docker compose up --build gpt4all_api
|
||||
```
|
||||
|
||||
To run the API with the GPU inference server, you will need to include environment variables (like the `MODEL_ID`). Edit the `.env` file and run
|
||||
```bash
|
||||
docker compose --env-file .env up --build
|
||||
```
|
||||
|
||||
|
||||
#### Spinning up your app
|
||||
Run `docker compose up` to spin up the backend. Monitor the logs for errors in-case you forgot to set an environment variable above.
|
||||
|
||||
@@ -27,7 +46,7 @@ Run
|
||||
```bash
|
||||
docker compose up --build
|
||||
```
|
||||
and edit files in the `api` directory. The api will hot-reload on changes.
|
||||
and edit files in the `app` directory. The api will hot-reload on changes.
|
||||
|
||||
You can run the unit tests with
|
||||
|
||||
|
||||
24
gpt4all-api/docker-compose.gpu.yaml
Normal file
24
gpt4all-api/docker-compose.gpu.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
gpt4all_gpu:
|
||||
image: ghcr.io/huggingface/text-generation-inference:0.9.3
|
||||
container_name: gpt4all_gpu
|
||||
restart: always #restart on error (usually code compilation from save during bad state)
|
||||
environment:
|
||||
- HUGGING_FACE_HUB_TOKEN=token
|
||||
- USE_FLASH_ATTENTION=false
|
||||
- MODEL_ID=''
|
||||
- NUM_SHARD=1
|
||||
command: --model-id $MODEL_ID --num-shard $NUM_SHARD
|
||||
volumes:
|
||||
- ./:/data
|
||||
ports:
|
||||
- "8080:80"
|
||||
shm_size: 1g
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
capabilities: [gpu]
|
||||
@@ -1,4 +1,4 @@
|
||||
version: "3.5"
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
gpt4all_api:
|
||||
@@ -7,12 +7,16 @@ services:
|
||||
restart: always #restart on error (usually code compilation from save during bad state)
|
||||
ports:
|
||||
- "4891:4891"
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- APP_ENVIRONMENT=dev
|
||||
- WEB_CONCURRENCY=2
|
||||
- LOGLEVEL=debug
|
||||
- PORT=4891
|
||||
- model=ggml-mpt-7b-chat.bin
|
||||
- model=${MODEL_BIN} # using variable from .env file
|
||||
- inference_mode=cpu
|
||||
volumes:
|
||||
- './gpt4all_api/app:/app'
|
||||
- './gpt4all_api/models:/models' # models are mounted in the container
|
||||
command: ["/start-reload.sh"]
|
||||
@@ -1,8 +1,6 @@
|
||||
# syntax=docker/dockerfile:1.0.0-experimental
|
||||
FROM tiangolo/uvicorn-gunicorn:python3.11
|
||||
|
||||
ARG MODEL_BIN=ggml-mpt-7b-chat.bin
|
||||
|
||||
# Put first so anytime this file changes other cached layers are invalidated.
|
||||
COPY gpt4all_api/requirements.txt /requirements.txt
|
||||
|
||||
@@ -17,7 +15,3 @@ COPY gpt4all_api/app /app
|
||||
|
||||
RUN mkdir -p /models
|
||||
|
||||
# Include the following line to bake a model into the image and not have to download it on API start.
|
||||
RUN wget -q --show-progress=off https://gpt4all.io/models/${MODEL_BIN} -P /models \
|
||||
&& md5sum /models/${MODEL_BIN}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from api_v1.routes import chat, completions, engines
|
||||
from api_v1.routes import chat, completions, engines, health
|
||||
from fastapi import APIRouter
|
||||
|
||||
router = APIRouter()
|
||||
@@ -6,3 +6,4 @@ router = APIRouter()
|
||||
router.include_router(chat.router)
|
||||
router.include_router(completions.router)
|
||||
router.include_router(engines.router)
|
||||
router.include_router(health.router)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import logging
|
||||
|
||||
from api_v1.settings import settings
|
||||
from fastapi import HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from starlette.requests import Request
|
||||
from api_v1.settings import settings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -19,6 +21,7 @@ async def on_startup(app):
|
||||
startup_msg = startup_msg_fmt.format(settings=settings)
|
||||
log.info(startup_msg)
|
||||
|
||||
|
||||
def startup_event_handler(app):
|
||||
async def start_app() -> None:
|
||||
await on_startup(app)
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
from fastapi import APIRouter, Depends, Response, Security, status
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List, Dict
|
||||
import logging
|
||||
import time
|
||||
from typing import List
|
||||
from uuid import uuid4
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from gpt4all import GPT4All
|
||||
from pydantic import BaseModel, Field
|
||||
from api_v1.settings import settings
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
### This should follow https://github.com/openai/openai-openapi/blob/master/openapi.yaml
|
||||
|
||||
|
||||
|
||||
class ChatCompletionMessage(BaseModel):
|
||||
role: str
|
||||
content: str
|
||||
|
||||
class ChatCompletionRequest(BaseModel):
|
||||
model: str = Field(..., description='The model to generate a completion from.')
|
||||
messages: List[ChatCompletionMessage] = Field(..., description='The model to generate a completion from.')
|
||||
|
||||
model: str = Field(settings.model, description='The model to generate a completion from.')
|
||||
messages: List[ChatCompletionMessage] = Field(..., description='Messages for the chat completion.')
|
||||
temperature: float = Field(settings.temp, description='Model temperature')
|
||||
|
||||
class ChatCompletionChoice(BaseModel):
|
||||
message: ChatCompletionMessage
|
||||
index: int
|
||||
logprobs: float
|
||||
finish_reason: str
|
||||
|
||||
class ChatCompletionUsage(BaseModel):
|
||||
@@ -39,25 +40,64 @@ class ChatCompletionResponse(BaseModel):
|
||||
choices: List[ChatCompletionChoice]
|
||||
usage: ChatCompletionUsage
|
||||
|
||||
|
||||
router = APIRouter(prefix="/chat", tags=["Completions Endpoints"])
|
||||
|
||||
@router.post("/completions", response_model=ChatCompletionResponse)
|
||||
async def chat_completion(request: ChatCompletionRequest):
|
||||
'''
|
||||
Completes a GPT4All model response.
|
||||
Completes a GPT4All model response based on the last message in the chat.
|
||||
'''
|
||||
# GPU is not implemented yet
|
||||
if settings.inference_mode == "gpu":
|
||||
raise HTTPException(status_code=400,
|
||||
detail=f"Not implemented yet: Can only infer in CPU mode.")
|
||||
|
||||
return ChatCompletionResponse(
|
||||
id='asdf',
|
||||
created=time.time(),
|
||||
model=request.model,
|
||||
choices=[{}],
|
||||
usage={
|
||||
'prompt_tokens': 0,
|
||||
'completion_tokens': 0,
|
||||
'total_tokens': 0
|
||||
}
|
||||
# we only support the configured model
|
||||
if request.model != settings.model:
|
||||
raise HTTPException(status_code=400,
|
||||
detail=f"The GPT4All inference server is booted to only infer: `{settings.model}`")
|
||||
|
||||
# run only of we have a message
|
||||
if request.messages:
|
||||
model = GPT4All(model_name=settings.model, model_path=settings.gpt4all_path)
|
||||
|
||||
# format system message and conversation history correctly
|
||||
formatted_messages = ""
|
||||
for message in request.messages:
|
||||
formatted_messages += f"<|im_start|>{message.role}\n{message.content}<|im_end|>\n"
|
||||
|
||||
# the LLM will complete the response of the assistant
|
||||
formatted_messages += "<|im_start|>assistant\n"
|
||||
response = model.generate(
|
||||
prompt=formatted_messages,
|
||||
temp=request.temperature
|
||||
)
|
||||
|
||||
# the LLM may continue to hallucinate the conversation, but we want only the first response
|
||||
# so, cut off everything after first <|im_end|>
|
||||
index = response.find("<|im_end|>")
|
||||
response_content = response[:index].strip()
|
||||
else:
|
||||
response_content = "No messages received."
|
||||
|
||||
# Create a chat message for the response
|
||||
response_message = ChatCompletionMessage(role="assistant", content=response_content)
|
||||
|
||||
# Create a choice object with the response message
|
||||
response_choice = ChatCompletionChoice(
|
||||
message=response_message,
|
||||
index=0,
|
||||
logprobs=-1.0, # Placeholder value
|
||||
finish_reason="length" # Placeholder value
|
||||
)
|
||||
|
||||
# Create the response object
|
||||
chat_response = ChatCompletionResponse(
|
||||
id=str(uuid4()),
|
||||
created=int(time.time()),
|
||||
model=request.model,
|
||||
choices=[response_choice],
|
||||
usage=ChatCompletionUsage(prompt_tokens=0, completion_tokens=0, total_tokens=0), # Placeholder values
|
||||
)
|
||||
|
||||
return chat_response
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import json
|
||||
|
||||
from fastapi import APIRouter, Depends, Response, Security, status
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List, Dict, Iterable, AsyncIterable
|
||||
import logging
|
||||
from uuid import uuid4
|
||||
from api_v1.settings import settings
|
||||
from gpt4all import GPT4All
|
||||
import time
|
||||
from typing import Dict, List, Union, Optional
|
||||
from uuid import uuid4
|
||||
import aiohttp
|
||||
import asyncio
|
||||
from api_v1.settings import settings
|
||||
from fastapi import APIRouter, Depends, Response, Security, status, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
from gpt4all import GPT4All
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
@@ -16,14 +18,17 @@ logger.setLevel(logging.DEBUG)
|
||||
|
||||
### This should follow https://github.com/openai/openai-openapi/blob/master/openapi.yaml
|
||||
|
||||
|
||||
class CompletionRequest(BaseModel):
|
||||
model: str = Field(..., description='The model to generate a completion from.')
|
||||
prompt: str = Field(..., description='The prompt to begin completing from.')
|
||||
max_tokens: int = Field(7, description='Max tokens to generate')
|
||||
temperature: float = Field(0, description='Model temperature')
|
||||
top_p: float = Field(1.0, description='top_p')
|
||||
n: int = Field(1, description='')
|
||||
model: str = Field(settings.model, description='The model to generate a completion from.')
|
||||
prompt: Union[List[str], str] = Field(..., description='The prompt to begin completing from.')
|
||||
max_tokens: int = Field(None, description='Max tokens to generate')
|
||||
temperature: float = Field(settings.temp, description='Model temperature')
|
||||
top_p: Optional[float] = Field(settings.top_p, description='top_p')
|
||||
top_k: Optional[int] = Field(settings.top_k, description='top_k')
|
||||
n: int = Field(1, description='How many completions to generate for each prompt')
|
||||
stream: bool = Field(False, description='Stream responses')
|
||||
repeat_penalty: float = Field(settings.repeat_penalty, description='Repeat penalty')
|
||||
|
||||
|
||||
class CompletionChoice(BaseModel):
|
||||
@@ -58,7 +63,6 @@ class CompletionStreamResponse(BaseModel):
|
||||
|
||||
router = APIRouter(prefix="/completions", tags=["Completion Endpoints"])
|
||||
|
||||
|
||||
def stream_completion(output: Iterable, base_response: CompletionStreamResponse):
|
||||
"""
|
||||
Streams a GPT4All output to the client.
|
||||
@@ -80,49 +84,132 @@ def stream_completion(output: Iterable, base_response: CompletionStreamResponse)
|
||||
))]
|
||||
yield f"data: {json.dumps(dict(chunk))}\n\n"
|
||||
|
||||
async def gpu_infer(payload, header):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
try:
|
||||
async with session.post(
|
||||
settings.hf_inference_server_host, headers=header, data=json.dumps(payload)
|
||||
) as response:
|
||||
resp = await response.json()
|
||||
return resp
|
||||
|
||||
except aiohttp.ClientError as e:
|
||||
# Handle client-side errors (e.g., connection error, invalid URL)
|
||||
logger.error(f"Client error: {e}")
|
||||
except aiohttp.ServerError as e:
|
||||
# Handle server-side errors (e.g., internal server error)
|
||||
logger.error(f"Server error: {e}")
|
||||
except json.JSONDecodeError as e:
|
||||
# Handle JSON decoding errors
|
||||
logger.error(f"JSON decoding error: {e}")
|
||||
except Exception as e:
|
||||
# Handle other unexpected exceptions
|
||||
logger.error(f"Unexpected error: {e}")
|
||||
|
||||
@router.post("/", response_model=CompletionResponse)
|
||||
async def completions(request: CompletionRequest):
|
||||
'''
|
||||
Completes a GPT4All model response.
|
||||
'''
|
||||
if settings.inference_mode == "gpu":
|
||||
params = request.dict(exclude={'model', 'prompt', 'max_tokens', 'n'})
|
||||
params["max_new_tokens"] = request.max_tokens
|
||||
params["num_return_sequences"] = request.n
|
||||
|
||||
model = GPT4All(model_name=settings.model, model_path=settings.gpt4all_path)
|
||||
header = {"Content-Type": "application/json"}
|
||||
if isinstance(request.prompt, list):
|
||||
tasks = []
|
||||
for prompt in request.prompt:
|
||||
payload = {"parameters": params}
|
||||
payload["inputs"] = prompt
|
||||
task = gpu_infer(payload, header)
|
||||
tasks.append(task)
|
||||
results = await asyncio.gather(*tasks)
|
||||
|
||||
output = model.generate(prompt=request.prompt,
|
||||
n_predict=request.max_tokens,
|
||||
streaming=request.stream,
|
||||
top_k=20,
|
||||
top_p=request.top_p,
|
||||
temp=request.temperature,
|
||||
n_batch=1024,
|
||||
repeat_penalty=1.2,
|
||||
repeat_last_n=10)
|
||||
choices = []
|
||||
for response in results:
|
||||
scores = response["scores"] if "scores" in response else -1.0
|
||||
choices.append(
|
||||
dict(
|
||||
CompletionChoice(
|
||||
text=response["generated_text"], index=0, logprobs=scores, finish_reason='stop'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
return CompletionResponse(
|
||||
id=str(uuid4()),
|
||||
created=time.time(),
|
||||
model=request.model,
|
||||
choices=choices,
|
||||
usage={'prompt_tokens': 0, 'completion_tokens': 0, 'total_tokens': 0},
|
||||
)
|
||||
|
||||
else:
|
||||
payload = {"parameters": params}
|
||||
# If streaming, we need to return a StreamingResponse
|
||||
payload["inputs"] = request.prompt
|
||||
|
||||
resp = await gpu_infer(payload, header)
|
||||
|
||||
output = resp["generated_text"]
|
||||
# this returns all logprobs
|
||||
scores = resp["scores"] if "scores" in resp else -1.0
|
||||
|
||||
return CompletionResponse(
|
||||
id=str(uuid4()),
|
||||
created=time.time(),
|
||||
model=request.model,
|
||||
choices=[dict(CompletionChoice(text=output, index=0, logprobs=scores, finish_reason='stop'))],
|
||||
usage={'prompt_tokens': 0, 'completion_tokens': 0, 'total_tokens': 0},
|
||||
)
|
||||
|
||||
# If streaming, we need to return a StreamingResponse
|
||||
if request.stream:
|
||||
base_chunk = CompletionStreamResponse(
|
||||
id=str(uuid4()),
|
||||
created=time.time(),
|
||||
model=request.model,
|
||||
choices=[]
|
||||
)
|
||||
return StreamingResponse((response for response in stream_completion(output, base_chunk)),
|
||||
media_type="text/event-stream")
|
||||
else:
|
||||
return CompletionResponse(
|
||||
id=str(uuid4()),
|
||||
created=time.time(),
|
||||
model=request.model,
|
||||
choices=[dict(CompletionChoice(
|
||||
text=output,
|
||||
index=0,
|
||||
logprobs=-1,
|
||||
finish_reason='stop'
|
||||
))],
|
||||
usage={
|
||||
'prompt_tokens': 0, #TODO how to compute this?
|
||||
'completion_tokens': 0,
|
||||
'total_tokens': 0
|
||||
}
|
||||
)
|
||||
|
||||
if request.model != settings.model:
|
||||
raise HTTPException(status_code=400,
|
||||
detail=f"The GPT4All inference server is booted to only infer: `{settings.model}`")
|
||||
|
||||
if isinstance(request.prompt, list):
|
||||
if len(request.prompt) > 1:
|
||||
raise HTTPException(status_code=400, detail="Can only infer one inference per request in CPU mode.")
|
||||
else:
|
||||
request.prompt = request.prompt[0]
|
||||
|
||||
model = GPT4All(model_name=settings.model, model_path=settings.gpt4all_path)
|
||||
|
||||
output = model.generate(prompt=request.prompt,
|
||||
max_tokens=request.max_tokens,
|
||||
streaming=request.stream,
|
||||
top_k=request.top_k,
|
||||
top_p=request.top_p,
|
||||
temp=request.temperature,
|
||||
)
|
||||
|
||||
# If streaming, we need to return a StreamingResponse
|
||||
if request.stream:
|
||||
base_chunk = CompletionStreamResponse(
|
||||
id=str(uuid4()),
|
||||
created=time.time(),
|
||||
model=request.model,
|
||||
choices=[]
|
||||
)
|
||||
return StreamingResponse((response for response in stream_completion(output, base_chunk)),
|
||||
media_type="text/event-stream")
|
||||
else:
|
||||
return CompletionResponse(
|
||||
id=str(uuid4()),
|
||||
created=time.time(),
|
||||
model=request.model,
|
||||
choices=[dict(CompletionChoice(
|
||||
text=output,
|
||||
index=0,
|
||||
logprobs=-1,
|
||||
finish_reason='stop'
|
||||
))],
|
||||
usage={
|
||||
'prompt_tokens': 0, # TODO how to compute this?
|
||||
'completion_tokens': 0,
|
||||
'total_tokens': 0
|
||||
}
|
||||
)
|
||||
|
||||
65
gpt4all-api/gpt4all_api/app/api_v1/routes/embeddings.py
Normal file
65
gpt4all-api/gpt4all_api/app/api_v1/routes/embeddings.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from typing import List, Union
|
||||
from fastapi import APIRouter
|
||||
from api_v1.settings import settings
|
||||
from gpt4all import Embed4All
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
### This should follow https://github.com/openai/openai-openapi/blob/master/openapi.yaml
|
||||
|
||||
|
||||
class EmbeddingRequest(BaseModel):
|
||||
model: str = Field(
|
||||
settings.model, description="The model to generate an embedding from."
|
||||
)
|
||||
input: Union[str, List[str], List[int], List[List[int]]] = Field(
|
||||
..., description="Input text to embed, encoded as a string or array of tokens."
|
||||
)
|
||||
|
||||
|
||||
class EmbeddingUsage(BaseModel):
|
||||
prompt_tokens: int = 0
|
||||
total_tokens: int = 0
|
||||
|
||||
|
||||
class Embedding(BaseModel):
|
||||
index: int = 0
|
||||
object: str = "embedding"
|
||||
embedding: List[float]
|
||||
|
||||
|
||||
class EmbeddingResponse(BaseModel):
|
||||
object: str = "list"
|
||||
model: str
|
||||
data: List[Embedding]
|
||||
usage: EmbeddingUsage
|
||||
|
||||
|
||||
router = APIRouter(prefix="/embeddings", tags=["Embedding Endpoints"])
|
||||
|
||||
embedder = Embed4All()
|
||||
|
||||
|
||||
def get_embedding(data: EmbeddingRequest) -> EmbeddingResponse:
|
||||
"""
|
||||
Calculates the embedding for the given input using a specified model.
|
||||
|
||||
Args:
|
||||
data (EmbeddingRequest): An EmbeddingRequest object containing the input data
|
||||
and model name.
|
||||
|
||||
Returns:
|
||||
EmbeddingResponse: An EmbeddingResponse object encapsulating the calculated embedding,
|
||||
usage info, and the model name.
|
||||
"""
|
||||
embedding = embedder.embed(data.input)
|
||||
return EmbeddingResponse(
|
||||
data=[Embedding(embedding=embedding)], usage=EmbeddingUsage(), model=data.model
|
||||
)
|
||||
|
||||
|
||||
@router.post("/", response_model=EmbeddingResponse)
|
||||
def embeddings(data: EmbeddingRequest):
|
||||
"""
|
||||
Creates a GPT4All embedding
|
||||
"""
|
||||
return get_embedding(data)
|
||||
@@ -1,38 +1,39 @@
|
||||
from fastapi import APIRouter, Depends, Response, Security, status
|
||||
import requests
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List, Dict
|
||||
import logging
|
||||
from api_v1.settings import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
### This should follow https://github.com/openai/openai-openapi/blob/master/openapi.yaml
|
||||
# Define the router for the engines module
|
||||
router = APIRouter(prefix="/engines", tags=["Search Endpoints"])
|
||||
|
||||
# Define the models for the engines module
|
||||
class ListEnginesResponse(BaseModel):
|
||||
data: List[Dict] = Field(..., description="All available models.")
|
||||
|
||||
class EngineResponse(BaseModel):
|
||||
data: List[Dict] = Field(..., description="All available models.")
|
||||
|
||||
router = APIRouter(prefix="/engines", tags=["Search Endpoints"])
|
||||
|
||||
# Define the routes for the engines module
|
||||
@router.get("/", response_model=ListEnginesResponse)
|
||||
async def list_engines():
|
||||
'''
|
||||
List all available GPT4All models from
|
||||
https://raw.githubusercontent.com/nomic-ai/gpt4all/main/gpt4all-chat/metadata/models.json
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
return ListEnginesResponse(data=[])
|
||||
|
||||
try:
|
||||
response = requests.get('https://raw.githubusercontent.com/nomic-ai/gpt4all/main/gpt4all-chat/metadata/models2.json')
|
||||
response.raise_for_status() # This will raise an HTTPError if the HTTP request returned an unsuccessful status code
|
||||
engines = response.json()
|
||||
return ListEnginesResponse(data=engines)
|
||||
except requests.RequestException as e:
|
||||
logger.error(f"Error fetching engine list: {e}")
|
||||
raise HTTPException(status_code=500, detail="Error fetching engine list")
|
||||
|
||||
# Define the routes for the engines module
|
||||
@router.get("/{engine_id}", response_model=EngineResponse)
|
||||
async def retrieve_engine(engine_id: str):
|
||||
'''
|
||||
|
||||
'''
|
||||
|
||||
raise NotImplementedError()
|
||||
return EngineResponse()
|
||||
|
||||
try:
|
||||
# Implement logic to fetch a specific engine's details
|
||||
# This is a placeholder, replace with your actual data retrieval logic
|
||||
engine_details = {"id": engine_id, "name": "Engine Name", "description": "Engine Description"}
|
||||
return EngineResponse(data=[engine_details])
|
||||
except Exception as e:
|
||||
logger.error(f"Error fetching engine details: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Error fetching details for engine {engine_id}")
|
||||
@@ -1,6 +1,7 @@
|
||||
import logging
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/health", tags=["Health"])
|
||||
|
||||
@@ -5,6 +5,15 @@ class Settings(BaseSettings):
|
||||
app_environment = 'dev'
|
||||
model: str = 'ggml-mpt-7b-chat.bin'
|
||||
gpt4all_path: str = '/models'
|
||||
inference_mode: str = "cpu"
|
||||
hf_inference_server_host: str = "http://gpt4all_gpu:80/generate"
|
||||
sentry_dns: str = None
|
||||
|
||||
temp: float = 0.18
|
||||
top_p: float = 1.0
|
||||
top_k: int = 50
|
||||
repeat_penalty: float = 1.18
|
||||
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import os
|
||||
import docs
|
||||
import logging
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
from starlette.middleware.cors import CORSMiddleware
|
||||
from fastapi.logger import logger as fastapi_logger
|
||||
from api_v1.settings import settings
|
||||
from api_v1.api import router as v1_router
|
||||
from api_v1 import events
|
||||
import os
|
||||
|
||||
import docs
|
||||
from api_v1 import events
|
||||
from api_v1.api import router as v1_router
|
||||
from api_v1.settings import settings
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
from fastapi.logger import logger as fastapi_logger
|
||||
from starlette.middleware.cors import CORSMiddleware
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
app = FastAPI(title='GPT4All API', description=docs.desc)
|
||||
|
||||
#CORS Configuration (in-case you want to deploy)
|
||||
# CORS Configuration (in-case you want to deploy)
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
@@ -29,20 +29,41 @@ app.include_router(v1_router, prefix='/v1')
|
||||
app.add_event_handler('startup', events.startup_event_handler(app))
|
||||
app.add_exception_handler(HTTPException, events.on_http_error)
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup():
|
||||
global model
|
||||
logger.info(f"Downloading/fetching model: {os.path.join(settings.gpt4all_path, settings.model)}")
|
||||
from gpt4all import GPT4All
|
||||
model = GPT4All(model_name=settings.model, model_path=settings.gpt4all_path)
|
||||
if settings.inference_mode == "cpu":
|
||||
logger.info(f"Downloading/fetching model: {os.path.join(settings.gpt4all_path, settings.model)}")
|
||||
from gpt4all import GPT4All
|
||||
|
||||
model = GPT4All(model_name=settings.model, model_path=settings.gpt4all_path)
|
||||
|
||||
logger.info(f"GPT4All API is ready to infer from {settings.model} on CPU.")
|
||||
|
||||
else:
|
||||
# is it possible to do this once the server is up?
|
||||
## TODO block until HF inference server is up.
|
||||
logger.info(f"GPT4All API is ready to infer from {settings.model} on CPU.")
|
||||
|
||||
|
||||
logger.info("GPT4All API is ready.")
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown():
|
||||
logger.info("Shutting down API")
|
||||
|
||||
|
||||
if settings.sentry_dns is not None:
|
||||
import sentry_sdk
|
||||
|
||||
def traces_sampler(sampling_context):
|
||||
if 'health' in sampling_context['transaction_context']['name']:
|
||||
return False
|
||||
|
||||
sentry_sdk.init(
|
||||
dsn=settings.sentry_dns, traces_sample_rate=0.1, traces_sampler=traces_sampler, send_default_pii=False
|
||||
)
|
||||
|
||||
# This is needed to get logs to show up in the app
|
||||
if "gunicorn" in os.environ.get("SERVER_SOFTWARE", ""):
|
||||
gunicorn_error_logger = logging.getLogger("gunicorn.error")
|
||||
@@ -57,5 +78,7 @@ if "gunicorn" in os.environ.get("SERVER_SOFTWARE", ""):
|
||||
uvicorn_logger.handlers = gunicorn_error_logger.handlers
|
||||
else:
|
||||
# https://github.com/tiangolo/fastapi/issues/2019
|
||||
LOG_FORMAT2 = "[%(asctime)s %(process)d:%(threadName)s] %(name)s - %(levelname)s - %(message)s | %(filename)s:%(lineno)d"
|
||||
LOG_FORMAT2 = (
|
||||
"[%(asctime)s %(process)d:%(threadName)s] %(name)s - %(levelname)s - %(message)s | %(filename)s:%(lineno)d"
|
||||
)
|
||||
logging.basicConfig(level=logging.INFO, format=LOG_FORMAT2)
|
||||
@@ -1,31 +1,35 @@
|
||||
"""
|
||||
Use the OpenAI python API to test gpt4all models.
|
||||
"""
|
||||
import openai
|
||||
openai.api_base = "http://localhost:4891/v1"
|
||||
from typing import List, get_args
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
import openai
|
||||
|
||||
openai.api_base = "http://localhost:4891/v1"
|
||||
openai.api_key = "not needed for a local LLM"
|
||||
|
||||
# Load the .env file
|
||||
env_path = 'gpt4all-api/gpt4all_api/.env'
|
||||
load_dotenv(dotenv_path=env_path)
|
||||
|
||||
# Fetch MODEL_ID from .env file
|
||||
model_id = os.getenv('MODEL_BIN', 'default_model_id')
|
||||
embedding = os.getenv('EMBEDDING', 'default_embedding_model_id')
|
||||
print (model_id)
|
||||
print (embedding)
|
||||
|
||||
def test_completion():
|
||||
model = "gpt4all-j-v1.3-groovy"
|
||||
model = model_id
|
||||
prompt = "Who is Michael Jordan?"
|
||||
response = openai.Completion.create(
|
||||
model=model,
|
||||
prompt=prompt,
|
||||
max_tokens=50,
|
||||
temperature=0.28,
|
||||
top_p=0.95,
|
||||
n=1,
|
||||
echo=True,
|
||||
stream=False
|
||||
model=model, prompt=prompt, max_tokens=50, temperature=0.28, top_p=0.95, n=1, echo=True, stream=False
|
||||
)
|
||||
assert len(response['choices'][0]['text']) > len(prompt)
|
||||
print(response)
|
||||
|
||||
|
||||
def test_streaming_completion():
|
||||
model = "gpt4all-j-v1.3-groovy"
|
||||
model = model_id
|
||||
prompt = "Who is Michael Jordan?"
|
||||
tokens = []
|
||||
for resp in openai.Completion.create(
|
||||
@@ -42,10 +46,48 @@ def test_streaming_completion():
|
||||
assert (len(tokens) > 0)
|
||||
assert (len("".join(tokens)) > len(prompt))
|
||||
|
||||
# def test_chat_completions():
|
||||
# model = "gpt4all-j-v1.3-groovy"
|
||||
# prompt = "Who is Michael Jordan?"
|
||||
# response = openai.ChatCompletion.create(
|
||||
# model=model,
|
||||
# messages=[]
|
||||
# )
|
||||
# Modified test batch, problems with keyerror in response
|
||||
def test_batched_completion():
|
||||
model = model_id # replace with your specific model ID
|
||||
prompt = "Who is Michael Jordan?"
|
||||
responses = []
|
||||
|
||||
# Loop to create completions one at a time
|
||||
for _ in range(3):
|
||||
response = openai.Completion.create(
|
||||
model=model, prompt=prompt, max_tokens=50, temperature=0.28, top_p=0.95, n=1, echo=True, stream=False
|
||||
)
|
||||
responses.append(response)
|
||||
|
||||
# Assertions to check the responses
|
||||
for response in responses:
|
||||
assert len(response['choices'][0]['text']) > len(prompt)
|
||||
|
||||
assert len(responses) == 3
|
||||
|
||||
def test_embedding():
|
||||
model = embedding
|
||||
prompt = "Who is Michael Jordan?"
|
||||
response = openai.Embedding.create(model=model, input=prompt)
|
||||
output = response["data"][0]["embedding"]
|
||||
args = get_args(List[float])
|
||||
|
||||
assert response["model"] == model
|
||||
assert isinstance(output, list)
|
||||
assert all(isinstance(x, args) for x in output)
|
||||
|
||||
def test_chat_completion():
|
||||
model = model_id
|
||||
|
||||
response = openai.ChatCompletion.create(
|
||||
model=model,
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "Knock knock."},
|
||||
{"role": "assistant", "content": "Who's there?"},
|
||||
{"role": "user", "content": "Orange."},
|
||||
]
|
||||
)
|
||||
|
||||
assert response.choices[0].message.role == "assistant"
|
||||
assert len(response.choices[0].message.content) > 0
|
||||
|
||||
3
gpt4all-api/gpt4all_api/env
Normal file
3
gpt4all-api/gpt4all_api/env
Normal file
@@ -0,0 +1,3 @@
|
||||
# Add your GGUF compatible model LLM here. ie: MODEL_BIN="mistral-7b-instruct-v0.1.Q4_0", rename file ".env"
|
||||
# Make sure this LLM matches the model you placed inside the models folder
|
||||
MODEL_BIN=""
|
||||
1
gpt4all-api/gpt4all_api/models/README.md
Normal file
1
gpt4all-api/gpt4all_api/models/README.md
Normal file
@@ -0,0 +1 @@
|
||||
### Drop GGUF compatible models here, make sure it matches MODEL_BIN on your .env file
|
||||
@@ -1,10 +1,13 @@
|
||||
aiohttp>=3.6.2
|
||||
aiofiles
|
||||
pydantic>=1.4.0
|
||||
pydantic>=1.4.0,<2.0.0
|
||||
requests>=2.24.0
|
||||
ujson>=2.0.2
|
||||
fastapi>=0.95.0
|
||||
Jinja2>=3.0
|
||||
gpt4all==1.0.1
|
||||
gpt4all>=1.0.0
|
||||
pytest
|
||||
openai
|
||||
openai==0.28.0
|
||||
black
|
||||
isort
|
||||
python-dotenv
|
||||
@@ -1,22 +1,26 @@
|
||||
ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
APP_NAME:=gpt4all_api
|
||||
PYTHON:=python3.8
|
||||
SHELL := /bin/bash
|
||||
|
||||
all: dependencies
|
||||
|
||||
fresh: clean dependencies
|
||||
|
||||
testenv: clean_testenv test_build
|
||||
docker compose up --build
|
||||
docker compose -f docker-compose.yaml up --build
|
||||
|
||||
testenv_gpu: clean_testenv test_build
|
||||
docker compose -f docker-compose.yaml -f docker-compose.gpu.yaml up --build
|
||||
|
||||
testenv_d: clean_testenv test_build
|
||||
docker compose up --build -d
|
||||
docker compose env up --build -d
|
||||
|
||||
test:
|
||||
docker compose exec gpt4all_api pytest -svv --disable-warnings -p no:cacheprovider /app/tests
|
||||
docker compose exec $(APP_NAME) pytest -svv --disable-warnings -p no:cacheprovider /app/tests
|
||||
|
||||
test_build:
|
||||
DOCKER_BUILDKIT=1 docker build -t gpt4all_api --progress plain -f gpt4all_api/Dockerfile.buildkit .
|
||||
DOCKER_BUILDKIT=1 docker build -t $(APP_NAME) --progress plain -f $(APP_NAME)/Dockerfile.buildkit .
|
||||
|
||||
clean_testenv:
|
||||
docker compose down -v
|
||||
@@ -24,14 +28,19 @@ clean_testenv:
|
||||
fresh_testenv: clean_testenv testenv
|
||||
|
||||
venv:
|
||||
if [ ! -d $(ROOT_DIR)/env ]; then $(PYTHON) -m venv $(ROOT_DIR)/env; fi
|
||||
if [ ! -d $(ROOT_DIR)/venv ]; then $(PYTHON) -m venv $(ROOT_DIR)/venv; fi
|
||||
|
||||
dependencies: venv
|
||||
source $(ROOT_DIR)/env/bin/activate; yes w | python -m pip install -r $(ROOT_DIR)/gpt4all_api/requirements.txt
|
||||
source $(ROOT_DIR)/venv/bin/activate; $(PYTHON) -m pip install -r $(ROOT_DIR)/$(APP_NAME)/requirements.txt
|
||||
|
||||
clean: clean_testenv
|
||||
# Remove existing environment
|
||||
rm -rf $(ROOT_DIR)/env;
|
||||
rm -rf $(ROOT_DIR)/venv;
|
||||
rm -rf $(ROOT_DIR)/$(APP_NAME)/*.pyc;
|
||||
|
||||
|
||||
black:
|
||||
source $(ROOT_DIR)/venv/bin/activate; black -l 120 -S --target-version py38 $(APP_NAME)
|
||||
|
||||
isort:
|
||||
source $(ROOT_DIR)/venv/bin/activate; isort --ignore-whitespace --atomic -w 120 $(APP_NAME)
|
||||
@@ -20,7 +20,7 @@ endif()
|
||||
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
|
||||
|
||||
set(LLMODEL_VERSION_MAJOR 0)
|
||||
set(LLMODEL_VERSION_MINOR 2)
|
||||
set(LLMODEL_VERSION_MINOR 5)
|
||||
set(LLMODEL_VERSION_PATCH 0)
|
||||
set(LLMODEL_VERSION "${LLMODEL_VERSION_MAJOR}.${LLMODEL_VERSION_MINOR}.${LLMODEL_VERSION_PATCH}")
|
||||
project(llmodel VERSION ${LLMODEL_VERSION} LANGUAGES CXX C)
|
||||
@@ -69,11 +69,6 @@ foreach(BUILD_VARIANT IN LISTS BUILD_VARIANTS)
|
||||
# Include GGML
|
||||
set(LLAMA_K_QUANTS YES)
|
||||
include_ggml(llama.cpp-mainline -mainline-${BUILD_VARIANT} ON)
|
||||
if (NOT LLAMA_METAL)
|
||||
set(LLAMA_K_QUANTS NO)
|
||||
include_ggml(llama.cpp-230511 -230511-${BUILD_VARIANT} ON)
|
||||
include_ggml(llama.cpp-230519 -230519-${BUILD_VARIANT} ON)
|
||||
endif()
|
||||
|
||||
# Function for preparing individual implementations
|
||||
function(prepare_target TARGET_NAME BASE_LIB)
|
||||
@@ -98,33 +93,10 @@ foreach(BUILD_VARIANT IN LISTS BUILD_VARIANTS)
|
||||
LLAMA_VERSIONS=>=3 LLAMA_DATE=999999)
|
||||
prepare_target(llamamodel-mainline llama-mainline)
|
||||
|
||||
add_library(replit-mainline-${BUILD_VARIANT} SHARED
|
||||
replit.cpp utils.h utils.cpp llmodel_shared.cpp llmodel_shared.h)
|
||||
prepare_target(replit-mainline llama-mainline)
|
||||
|
||||
if (NOT LLAMA_METAL)
|
||||
add_library(llamamodel-230519-${BUILD_VARIANT} SHARED
|
||||
llamamodel.cpp llmodel_shared.cpp)
|
||||
target_compile_definitions(llamamodel-230519-${BUILD_VARIANT} PRIVATE
|
||||
LLAMA_VERSIONS===2 LLAMA_DATE=230519)
|
||||
prepare_target(llamamodel-230519 llama-230519)
|
||||
add_library(llamamodel-230511-${BUILD_VARIANT} SHARED
|
||||
llamamodel.cpp llmodel_shared.cpp)
|
||||
target_compile_definitions(llamamodel-230511-${BUILD_VARIANT} PRIVATE
|
||||
LLAMA_VERSIONS=<=1 LLAMA_DATE=230511)
|
||||
prepare_target(llamamodel-230511 llama-230511)
|
||||
|
||||
add_library(gptj-${BUILD_VARIANT} SHARED
|
||||
gptj.cpp utils.h utils.cpp llmodel_shared.cpp llmodel_shared.h)
|
||||
prepare_target(gptj ggml-230511)
|
||||
|
||||
add_library(falcon-${BUILD_VARIANT} SHARED
|
||||
falcon.cpp utils.h utils.cpp llmodel_shared.cpp llmodel_shared.h)
|
||||
prepare_target(falcon llama-mainline)
|
||||
|
||||
add_library(mpt-${BUILD_VARIANT} SHARED
|
||||
mpt.cpp utils.h utils.cpp llmodel_shared.cpp llmodel_shared.h)
|
||||
prepare_target(mpt ggml-230511)
|
||||
prepare_target(gptj llama-mainline)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ public:
|
||||
}
|
||||
};
|
||||
#else
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
@@ -75,7 +77,9 @@ public:
|
||||
|
||||
Dlhandle() : chandle(nullptr) {}
|
||||
Dlhandle(const std::string& fpath) {
|
||||
chandle = LoadLibraryA(fpath.c_str());
|
||||
std::string afpath = std::filesystem::absolute(fpath).string();
|
||||
std::replace(afpath.begin(), afpath.end(), '/', '\\');
|
||||
chandle = LoadLibraryExA(afpath.c_str(), NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
|
||||
if (!chandle) {
|
||||
throw Exception("dlopen(\""+fpath+"\"): Error");
|
||||
}
|
||||
|
||||
@@ -1,983 +0,0 @@
|
||||
#define FALCON_H_I_KNOW_WHAT_I_AM_DOING_WHEN_INCLUDING_THIS_FILE
|
||||
#include "falcon_impl.h"
|
||||
#include "llama.h"
|
||||
#include "llama-util.h"
|
||||
#include "utils.h"
|
||||
#include "llmodel_shared.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cinttypes>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
const char *modelType_ = "Falcon";
|
||||
}
|
||||
|
||||
// commented out 40B support as it presently would require forking ggml/llama.cpp
|
||||
// can re-add once mainline ggml supports it
|
||||
|
||||
#define FALCON_MAGIC 0x67676a74
|
||||
|
||||
// default hparams (Falcon 7B)
|
||||
struct falcon_hparams {
|
||||
int32_t n_vocab = 65024;
|
||||
int32_t n_embd = 4544;
|
||||
int32_t n_head = 71;
|
||||
int32_t n_head_kv = 1;
|
||||
int32_t n_layer = 32;
|
||||
int32_t falcon_version = 7; // 7 for Falcon-7B, 40 for Falcon-40B
|
||||
int32_t ftype = 1;
|
||||
int32_t n_ctx = 2048;
|
||||
};
|
||||
|
||||
struct falcon_layer {
|
||||
// normalization
|
||||
struct ggml_tensor* input_layernorm;
|
||||
struct ggml_tensor* input_layernorm_b;
|
||||
//struct ggml_tensor* attention_norm; // Falcon-40B only
|
||||
//struct ggml_tensor* attention_norm_b; // Falcon-40B only
|
||||
|
||||
// attention
|
||||
struct ggml_tensor* query_key_value;
|
||||
struct ggml_tensor* wo;
|
||||
|
||||
// ff
|
||||
struct ggml_tensor* ffn_up;
|
||||
struct ggml_tensor* ffn_down;
|
||||
};
|
||||
|
||||
struct falcon_model {
|
||||
falcon_hparams hparams;
|
||||
|
||||
struct ggml_tensor* tok_embeddings;
|
||||
struct ggml_tensor* output_norm;
|
||||
struct ggml_tensor* output_norm_b;
|
||||
struct ggml_tensor* lm_head;
|
||||
|
||||
std::vector<falcon_layer> layers;
|
||||
|
||||
// key + value memory
|
||||
llm_kv_cache kv_self;
|
||||
|
||||
struct ggml_context* ctx;
|
||||
std::map<std::string, struct ggml_tensor*> tensors;
|
||||
|
||||
llm_buffer eval_buf;
|
||||
llm_buffer scr0_buf;
|
||||
llm_buffer scr1_buf;
|
||||
};
|
||||
|
||||
static bool kv_cache_init(
|
||||
const struct falcon_hparams & hparams,
|
||||
struct llm_kv_cache & cache,
|
||||
ggml_type wtype,
|
||||
int n_ctx) {
|
||||
const int n_embd = hparams.n_embd;
|
||||
const int dim_head = n_embd / hparams.n_head;
|
||||
const int dim_kv = dim_head * hparams.n_head_kv;
|
||||
const int n_layer = hparams.n_layer;
|
||||
|
||||
const int64_t n_mem = (int64_t)n_layer*n_ctx;
|
||||
const int64_t n_elements = dim_kv * n_mem;
|
||||
cache.buf.resize(2u*n_elements*ggml_type_size(wtype) + 2_MiB);
|
||||
struct ggml_init_params params;
|
||||
params.mem_size = cache.buf.size;
|
||||
params.mem_buffer = cache.buf.addr;
|
||||
params.no_alloc = false;
|
||||
|
||||
cache.ctx = ggml_init(params);
|
||||
if (!cache.ctx) {
|
||||
fprintf(stderr, "%s: failed to allocate memory for kv cache\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
cache.k = ggml_new_tensor_1d(cache.ctx, wtype, n_elements);
|
||||
cache.v = ggml_new_tensor_1d(cache.ctx, wtype, n_elements);
|
||||
return true;
|
||||
}
|
||||
|
||||
// load the model's weights from a file
|
||||
bool falcon_model_load(const std::string & fname, falcon_model & model, gpt_vocab & vocab, size_t *mem_req) {
|
||||
printf("%s: loading model from '%s' - please wait ...\n", __func__, fname.c_str());
|
||||
if (mem_req) {
|
||||
*mem_req = 0;
|
||||
}
|
||||
|
||||
auto fin = std::ifstream(fname, std::ios::binary);
|
||||
if (!fin) {
|
||||
fprintf(stderr, "%s: failed to open '%s'\n", __func__, fname.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// verify magic
|
||||
{
|
||||
uint32_t magic;
|
||||
fin.read((char *) &magic, sizeof(magic));
|
||||
if (magic != FALCON_MAGIC) {
|
||||
fprintf(stderr, "%s: invalid model file '%s' (bad magic)\n", __func__, fname.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t format_version;
|
||||
fin.read((char *) &format_version, sizeof(format_version));
|
||||
|
||||
// load hparams
|
||||
{
|
||||
auto & hparams = model.hparams;
|
||||
|
||||
fin.read((char *) &hparams.n_vocab, sizeof(hparams.n_vocab));
|
||||
fin.read((char *) &hparams.n_embd, sizeof(hparams.n_embd));
|
||||
fin.read((char *) &hparams.n_head, sizeof(hparams.n_head));
|
||||
fin.read((char *) &hparams.n_head_kv, sizeof(hparams.n_head_kv));
|
||||
fin.read((char *) &hparams.n_layer, sizeof(hparams.n_layer));
|
||||
fin.read((char *) &hparams.falcon_version, sizeof(hparams.falcon_version));
|
||||
fin.read((char *) &hparams.ftype, sizeof(hparams.ftype));
|
||||
|
||||
if (hparams.falcon_version != 7) { // && hparams.falcon_version != 40) {
|
||||
fprintf(stderr, "%s: invalid model file '%s' (bad Falcon version: %d)\n", __func__, fname.c_str(), hparams.falcon_version);
|
||||
return false;
|
||||
}
|
||||
|
||||
const int32_t qntvr = hparams.ftype / GGML_QNT_VERSION_FACTOR;
|
||||
|
||||
printf("%s: n_vocab = %d\n", __func__, hparams.n_vocab);
|
||||
printf("%s: n_embd = %d\n", __func__, hparams.n_embd);
|
||||
printf("%s: n_head = %d\n", __func__, hparams.n_head);
|
||||
printf("%s: n_head_kv = %d\n", __func__, hparams.n_head_kv);
|
||||
printf("%s: n_layer = %d\n", __func__, hparams.n_layer);
|
||||
printf("%s: ftype = %d\n", __func__, hparams.ftype);
|
||||
printf("%s: qntvr = %d\n", __func__, qntvr);
|
||||
|
||||
hparams.ftype %= GGML_QNT_VERSION_FACTOR;
|
||||
}
|
||||
|
||||
// load vocab
|
||||
{
|
||||
const int32_t n_vocab = model.hparams.n_vocab;
|
||||
|
||||
std::string word;
|
||||
std::vector<char> buf(128);
|
||||
|
||||
for (int i = 0; i < n_vocab; i++) {
|
||||
uint32_t len;
|
||||
fin.read((char *) &len, sizeof(len));
|
||||
|
||||
buf.resize(len);
|
||||
fin.read((char *) buf.data(), len);
|
||||
word.assign(buf.data(), len);
|
||||
|
||||
uint32_t dummy;
|
||||
fin.read((char *) &dummy, sizeof(dummy));
|
||||
|
||||
vocab.token_to_id[word] = i;
|
||||
vocab.id_to_token[i] = word;
|
||||
}
|
||||
}
|
||||
|
||||
// for the big tensors, we have the option to store the data in 16-bit floats or quantized
|
||||
// in order to save memory and also to speed up the computation
|
||||
ggml_type wtype = ggml_ftype_to_ggml_type((ggml_ftype) (model.hparams.ftype));
|
||||
if (wtype == GGML_TYPE_COUNT) {
|
||||
fprintf(stderr, "%s: invalid model file '%s' (bad ftype value %d)\n",
|
||||
__func__, fname.c_str(), model.hparams.ftype);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto & ctx = model.ctx;
|
||||
|
||||
size_t ctx_size = 0;
|
||||
|
||||
{
|
||||
const auto& hparams = model.hparams;
|
||||
|
||||
const int n_embd = hparams.n_embd;
|
||||
const int n_head = hparams.n_head;
|
||||
const int n_head_kv = hparams.n_head_kv;
|
||||
const int n_layer = hparams.n_layer;
|
||||
const int n_ctx = hparams.n_ctx;
|
||||
const int n_ff = 4 * model.hparams.n_embd;
|
||||
const int n_vocab = hparams.n_vocab;
|
||||
const int head_dim = hparams.n_embd / hparams.n_head;
|
||||
|
||||
ctx_size += ggml_tensor_overhead() + ggml_type_sizef(wtype) * n_embd * n_vocab; // tok_embeddings
|
||||
ctx_size += ggml_tensor_overhead() + ggml_type_sizef(GGML_TYPE_F32) * n_embd; // output_norm
|
||||
ctx_size += ggml_tensor_overhead() + ggml_type_sizef(GGML_TYPE_F32) * n_embd; // output_norm_b
|
||||
ctx_size += ggml_tensor_overhead() + ggml_type_sizef(wtype) * n_embd * n_vocab; // lm_head
|
||||
|
||||
// if (hparams.version == 40) { // Falcon-40B
|
||||
// ctx_size += n_layer * ggml_sizeof_tensor_1d(GGML_TYPE_F32, n_embd); // attention_norm
|
||||
// ctx_size += n_layer * ggml_sizeof_tensor_1d(GGML_TYPE_F32, n_embd); // attention_norm_b
|
||||
// }
|
||||
ctx_size += n_layer * (ggml_tensor_overhead() + ggml_type_sizef(GGML_TYPE_F32) * n_embd); // input_layernorm
|
||||
ctx_size += n_layer * (ggml_tensor_overhead() + ggml_type_sizef(GGML_TYPE_F32) * n_embd); // input_layernorm_b
|
||||
ctx_size += n_layer * (ggml_tensor_overhead() + ggml_type_sizef(wtype) * n_embd * (n_head_kv * 2 + n_head) * head_dim); // query_key_value
|
||||
ctx_size += n_layer * (ggml_tensor_overhead() + ggml_type_sizef(wtype) * n_embd * n_embd); // wo
|
||||
ctx_size += n_layer * (ggml_tensor_overhead() + ggml_type_sizef(wtype) * n_embd * n_ff); // ffn_up
|
||||
ctx_size += n_layer * (ggml_tensor_overhead() + ggml_type_sizef(wtype) * n_ff * n_embd); // ffn_down
|
||||
|
||||
printf("%s: ggml ctx size = %6.2f MB\n", __func__, ctx_size/(1024.0*1024.0));
|
||||
}
|
||||
|
||||
if (mem_req) {
|
||||
const int n_embd = model.hparams.n_embd;
|
||||
const int dim_head = n_embd / model.hparams.n_head;
|
||||
const int dim_kv = dim_head * model.hparams.n_head_kv;
|
||||
const int n_layer = model.hparams.n_layer;
|
||||
|
||||
const int64_t n_mem = (int64_t)n_layer*model.hparams.n_ctx;
|
||||
const int64_t n_elements = dim_kv * n_mem;
|
||||
size_t kv_cache_size = 2u*n_elements*ggml_type_size(wtype) + 2_MiB;
|
||||
*mem_req = ctx_size + kv_cache_size;
|
||||
return false;
|
||||
}
|
||||
|
||||
// create the ggml context
|
||||
{
|
||||
struct ggml_init_params params = {
|
||||
.mem_size = ctx_size,
|
||||
.mem_buffer = NULL,
|
||||
.no_alloc = false,
|
||||
};
|
||||
|
||||
model.ctx = ggml_init(params);
|
||||
if (!model.ctx) {
|
||||
fprintf(stderr, "%s: ggml_init() failed\n", __func__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// prepare memory for the weights
|
||||
{
|
||||
const auto& hparams = model.hparams;
|
||||
|
||||
const int n_embd = hparams.n_embd;
|
||||
const int n_head = hparams.n_head;
|
||||
const int n_head_kv = hparams.n_head_kv;
|
||||
const int n_layer = hparams.n_layer;
|
||||
const int n_ff = 4 * model.hparams.n_embd;
|
||||
const int n_vocab = hparams.n_vocab;
|
||||
const int head_dim = hparams.n_embd / hparams.n_head;
|
||||
|
||||
model.layers.resize(n_layer);
|
||||
|
||||
model.tok_embeddings = ggml_new_tensor_2d(ctx, wtype, n_embd, n_vocab);
|
||||
|
||||
model.output_norm = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_embd);
|
||||
model.output_norm_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_embd);
|
||||
model.lm_head = ggml_new_tensor_2d(ctx, wtype, n_embd, n_vocab);
|
||||
|
||||
// map by name
|
||||
model.tensors["transformer.word_embeddings.weight"] =
|
||||
model.tok_embeddings;
|
||||
|
||||
model.tensors["transformer.ln_f.weight"] = model.output_norm;
|
||||
model.tensors["transformer.ln_f.bias"] = model.output_norm_b;
|
||||
model.tensors["lm_head.weight"] = model.lm_head;
|
||||
|
||||
for (int i = 0; i < n_layer; ++i) {
|
||||
auto& layer = model.layers[i];
|
||||
|
||||
layer.input_layernorm =
|
||||
ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_embd);
|
||||
layer.input_layernorm_b =
|
||||
ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_embd);
|
||||
|
||||
// if (hparams.version == 40) { // for Falcon-40B only
|
||||
// layer.attention_norm =
|
||||
// ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_embd);
|
||||
// layer.attention_norm_b =
|
||||
// ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_embd);
|
||||
// }
|
||||
|
||||
// query_key_value shape for config.multi_query == True:
|
||||
layer.query_key_value = ggml_new_tensor_2d(
|
||||
ctx, wtype, n_embd, (n_head_kv * 2 + n_head) * head_dim);
|
||||
layer.wo = ggml_new_tensor_2d(ctx, wtype, n_embd, n_embd);
|
||||
|
||||
layer.ffn_up = ggml_new_tensor_2d(ctx, wtype, n_embd, n_ff);
|
||||
layer.ffn_down = ggml_new_tensor_2d(ctx, wtype, n_ff, n_embd);
|
||||
|
||||
// map by name
|
||||
// if (hparams.version == 40) {
|
||||
// // Falcon-40B:
|
||||
// model.tensors["transformer.h." + std::to_string(i) +
|
||||
// ".ln_mlp.weight"] = layer.input_layernorm;
|
||||
// model.tensors["transformer.h." + std::to_string(i) +
|
||||
// ".ln_mlp.bias"] = layer.input_layernorm_b;
|
||||
// model.tensors["transformer.h." + std::to_string(i) +
|
||||
// ".ln_attn.weight"] = layer.attention_norm;
|
||||
// model.tensors["transformer.h." + std::to_string(i) +
|
||||
// ".ln_attn.bias"] = layer.attention_norm_b;
|
||||
// } else {
|
||||
// Falcon-7B:
|
||||
model.tensors["transformer.h." + std::to_string(i) +
|
||||
".input_layernorm.weight"] = layer.input_layernorm;
|
||||
model.tensors["transformer.h." + std::to_string(i) +
|
||||
".input_layernorm.bias"] = layer.input_layernorm_b;
|
||||
//}
|
||||
|
||||
model.tensors["transformer.h." + std::to_string(i) +
|
||||
".self_attention.query_key_value.weight"] =
|
||||
layer.query_key_value;
|
||||
model.tensors["transformer.h." + std::to_string(i) +
|
||||
".self_attention.dense.weight"] = layer.wo;
|
||||
|
||||
model.tensors["transformer.h." + std::to_string(i) +
|
||||
".mlp.dense_h_to_4h.weight"] = layer.ffn_up;
|
||||
model.tensors["transformer.h." + std::to_string(i) +
|
||||
".mlp.dense_4h_to_h.weight"] = layer.ffn_down;
|
||||
}
|
||||
}
|
||||
|
||||
// key + value memory
|
||||
{
|
||||
const auto & hparams = model.hparams;
|
||||
|
||||
const int n_layer = hparams.n_layer;
|
||||
const int n_ctx = hparams.n_ctx;
|
||||
const int n_head_kv = hparams.n_head_kv;
|
||||
const int head_dim = hparams.n_embd / hparams.n_head;
|
||||
|
||||
const int64_t n_mem = n_layer*n_ctx;
|
||||
const int64_t n_elements = head_dim*n_mem;
|
||||
|
||||
if (!kv_cache_init(hparams, model.kv_self, GGML_TYPE_F32, model.hparams.n_ctx)) {
|
||||
fprintf(stderr, "%s: kv_cache_init() failed for self-attention cache\n", __func__);
|
||||
ggml_free(ctx);
|
||||
return false;
|
||||
}
|
||||
const size_t memory_size = ggml_nbytes(model.kv_self.k) + ggml_nbytes(model.kv_self.v);
|
||||
|
||||
printf("%s: memory_size = %8.2f MB, n_mem = %" PRId64 "\n", __func__, memory_size/1024.0/1024.0, n_mem);
|
||||
}
|
||||
|
||||
// load weights
|
||||
{
|
||||
int n_tensors = 0;
|
||||
size_t total_size = 0;
|
||||
|
||||
printf("%s: ", __func__);
|
||||
|
||||
while (true) {
|
||||
int32_t n_dims;
|
||||
int32_t length;
|
||||
int32_t ttype;
|
||||
|
||||
fin.read(reinterpret_cast<char *>(&n_dims), sizeof(n_dims));
|
||||
fin.read(reinterpret_cast<char *>(&length), sizeof(length));
|
||||
fin.read(reinterpret_cast<char *>(&ttype), sizeof(ttype));
|
||||
|
||||
if (fin.eof()) {
|
||||
break;
|
||||
}
|
||||
|
||||
int32_t nelements = 1;
|
||||
int32_t ne[2] = { 1, 1 };
|
||||
for (int i = 0; i < n_dims; ++i) {
|
||||
fin.read(reinterpret_cast<char *>(&ne[i]), sizeof(ne[i]));
|
||||
nelements *= ne[i];
|
||||
}
|
||||
|
||||
std::string name(length, 0);
|
||||
fin.read(&name[0], length);
|
||||
fin.seekg(-static_cast<ptrdiff_t>(fin.tellg()) & 31, std::ios_base::cur);
|
||||
|
||||
if (model.tensors.find(name.data()) == model.tensors.end()) {
|
||||
fprintf(stderr, "%s: unknown tensor '%s' in model file\n", __func__, name.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto tensor = model.tensors[name.data()];
|
||||
if (ggml_nelements(tensor) != nelements) {
|
||||
fprintf(stderr, "%s: tensor '%s' has wrong size in model file\n", __func__, name.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tensor->ne[0] != ne[0] || tensor->ne[1] != ne[1]) {
|
||||
fprintf(stderr, "%s: tensor '%s' has wrong shape in model file: got [%5d, %5d], expected [%5d, %5d]\n",
|
||||
__func__, name.data(), (int) tensor->ne[0], (int) tensor->ne[1], ne[0], ne[1]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// for debugging
|
||||
if (0) {
|
||||
printf("%24s - [%5d, %5d], type = %6s, %6.2f MB, %9zu bytes\n", name.data(), ne[0], ne[1], ggml_type_name(ggml_type(ttype)), ggml_nbytes(tensor)/1024.0/1024.0, ggml_nbytes(tensor));
|
||||
}
|
||||
|
||||
const size_t bpe = ggml_type_size(ggml_type(ttype));
|
||||
|
||||
if ((nelements*bpe)/ggml_blck_size(tensor->type) != ggml_nbytes(tensor)) {
|
||||
fprintf(stderr, "%s: tensor '%s' has wrong size in model file: got %zu, expected %zu\n",
|
||||
__func__, name.data(), ggml_nbytes(tensor), nelements*bpe);
|
||||
return false;
|
||||
}
|
||||
|
||||
fin.read(reinterpret_cast<char *>(tensor->data), ggml_nbytes(tensor));
|
||||
|
||||
total_size += ggml_nbytes(tensor);
|
||||
if (++n_tensors % 8 == 0) {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
printf(" done\n");
|
||||
|
||||
printf("%s: model size = %8.2f MB / num tensors = %d\n", __func__, total_size/1024.0/1024.0, n_tensors);
|
||||
}
|
||||
|
||||
fin.close();
|
||||
|
||||
model.eval_buf.resize(1280u * 1024 * 1024);
|
||||
model.scr0_buf.resize(256u * 1024 * 1024);
|
||||
model.scr1_buf.resize(256u * 1024 * 1024);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// evaluate the transformer
|
||||
//
|
||||
// - model: the model
|
||||
// - n_threads: number of threads to use
|
||||
// - n_past: the context size so far
|
||||
// - embd_inp: the embeddings of the tokens in the context
|
||||
// - embd_w: the predicted logits for the next token
|
||||
//
|
||||
bool falcon_eval(
|
||||
const falcon_model & model,
|
||||
const int n_threads,
|
||||
const int n_past,
|
||||
const std::vector<gpt_vocab::id> & embd_inp,
|
||||
std::vector<float> & embd_w,
|
||||
size_t & mem_per_token) {
|
||||
const int N = embd_inp.size();
|
||||
|
||||
const auto & hparams = model.hparams;
|
||||
|
||||
const int n_embd = hparams.n_embd;
|
||||
const int n_layer = hparams.n_layer;
|
||||
const int n_ctx = hparams.n_ctx;
|
||||
const int n_head = hparams.n_head;
|
||||
const int n_head_kv = hparams.n_head_kv;
|
||||
const int n_vocab = hparams.n_vocab;
|
||||
const int version = hparams.falcon_version;
|
||||
const size_t head_dim = n_embd / n_head;
|
||||
|
||||
struct ggml_init_params eval_ctx_params = {
|
||||
.mem_size = model.eval_buf.size,
|
||||
.mem_buffer = model.eval_buf.addr,
|
||||
.no_alloc = false,
|
||||
};
|
||||
|
||||
struct ggml_context * ctx0 = ggml_init(eval_ctx_params);
|
||||
struct ggml_cgraph gf = {};
|
||||
gf.n_threads = n_threads;
|
||||
|
||||
struct ggml_tensor * embd = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, N);
|
||||
memcpy(embd->data, embd_inp.data(), N*ggml_element_size(embd));
|
||||
|
||||
// wte
|
||||
struct ggml_tensor * inpL = ggml_get_rows(ctx0, model.tok_embeddings, embd);
|
||||
struct ggml_tensor* repeat_dummy = ggml_new_tensor_3d(ctx0, inpL->type, head_dim, N + n_past, n_head);
|
||||
|
||||
ggml_type wtype = GGML_TYPE_F32;
|
||||
const int sizeof_wtype = ggml_type_sizef(wtype);
|
||||
|
||||
for (int il = 0; il < n_layer; ++il) {
|
||||
struct ggml_tensor * cur;
|
||||
struct ggml_tensor * layernorm_output;
|
||||
|
||||
ggml_set_scratch(ctx0, {0, model.scr0_buf.size, model.scr0_buf.addr, });
|
||||
|
||||
// self-attention
|
||||
{
|
||||
layernorm_output = ggml_norm(ctx0, inpL);
|
||||
|
||||
layernorm_output = ggml_add(ctx0,
|
||||
ggml_mul(ctx0,
|
||||
ggml_repeat(ctx0, model.layers[il].input_layernorm, layernorm_output),
|
||||
layernorm_output),
|
||||
ggml_repeat(ctx0, model.layers[il].input_layernorm_b, layernorm_output));
|
||||
|
||||
// if (version == 40) { // Falcon-40B only
|
||||
// cur = ggml_norm(ctx0, inpL);
|
||||
|
||||
// cur = ggml_add(ctx0,
|
||||
// ggml_mul(ctx0,
|
||||
// ggml_repeat(ctx0, model.layers[il].attention_norm, cur),
|
||||
// cur),
|
||||
// ggml_repeat(ctx0, model.layers[il].attention_norm_b, cur));
|
||||
// }
|
||||
// else {
|
||||
cur = layernorm_output;
|
||||
// }
|
||||
|
||||
// compute QKV
|
||||
|
||||
cur = ggml_mul_mat(ctx0, model.layers[il].query_key_value, cur);
|
||||
|
||||
// Note that the strides for Kcur, Vcur are set up so that the
|
||||
// resulting views are misaligned with the tensor's storage
|
||||
// (by applying the K/V offset we shift the tensor's original
|
||||
// view to stick out behind the viewed QKV tensor's allocated
|
||||
// memory, so to say). This is ok because no actual accesses
|
||||
// happen to that out-of-range memory, but it can require some
|
||||
// trickery when trying to accurately dump these views for
|
||||
// debugging.
|
||||
|
||||
struct ggml_tensor * Qcur = ggml_view_3d(
|
||||
ctx0, cur, head_dim, n_head, N,
|
||||
head_dim * sizeof_wtype,
|
||||
head_dim * (n_head + 2 * n_head_kv) * sizeof_wtype,
|
||||
0);
|
||||
|
||||
struct ggml_tensor * Kcur = ggml_view_3d(
|
||||
ctx0, cur, head_dim, n_head_kv, N,
|
||||
head_dim * sizeof_wtype,
|
||||
head_dim * (n_head + 2 * n_head_kv) * sizeof_wtype,
|
||||
head_dim * n_head * sizeof_wtype);
|
||||
|
||||
struct ggml_tensor * Vcur = ggml_view_3d(
|
||||
ctx0, cur, head_dim, n_head_kv, N,
|
||||
head_dim * sizeof_wtype,
|
||||
head_dim * (n_head + 2 * n_head_kv) * sizeof_wtype,
|
||||
head_dim * (n_head + n_head_kv) * sizeof_wtype);
|
||||
|
||||
// using mode = 2 for neox mode
|
||||
Qcur = ggml_rope_inplace(ctx0, Qcur, n_past, head_dim, 2);
|
||||
Kcur = ggml_rope_inplace(ctx0, Kcur, n_past, head_dim, 2);
|
||||
|
||||
// store key and value to memory
|
||||
{
|
||||
struct ggml_tensor* k = ggml_view_1d(
|
||||
ctx0, model.kv_self.k, N * n_head_kv * head_dim,
|
||||
(ggml_element_size(model.kv_self.k) * n_head_kv * head_dim) *
|
||||
(il * n_ctx + n_past));
|
||||
struct ggml_tensor* v = ggml_view_1d(
|
||||
ctx0, model.kv_self.v, N * n_head_kv * head_dim,
|
||||
(ggml_element_size(model.kv_self.v) * n_head_kv * head_dim) *
|
||||
(il * n_ctx + n_past));
|
||||
|
||||
ggml_build_forward_expand(&gf, ggml_cpy(ctx0, Kcur, k));
|
||||
ggml_build_forward_expand(&gf, ggml_cpy(ctx0, Vcur, v));
|
||||
}
|
||||
|
||||
struct ggml_tensor * K = ggml_permute(
|
||||
ctx0,
|
||||
ggml_view_3d(
|
||||
ctx0,
|
||||
model.kv_self.k,
|
||||
head_dim, n_head_kv, n_past + N,
|
||||
head_dim * sizeof_wtype,
|
||||
head_dim * n_head_kv * sizeof_wtype,
|
||||
il * n_ctx * ggml_element_size(model.kv_self.k) * n_head_kv * head_dim),
|
||||
0, 2, 1, 3);
|
||||
|
||||
// K * Q
|
||||
|
||||
// changed from repeat2 back to repeat, will not support 40B!
|
||||
K = ggml_cont(ctx0, ggml_repeat(ctx0, K, repeat_dummy));
|
||||
|
||||
struct ggml_tensor * Q = ggml_permute(ctx0, Qcur, 0, 2, 1, 3);
|
||||
struct ggml_tensor * KQ = ggml_mul_mat(ctx0, K, Q);
|
||||
|
||||
// KQ_scaled = KQ / sqrt(n_embd/n_head)
|
||||
struct ggml_tensor * KQ_scaled =
|
||||
ggml_scale_inplace(ctx0,
|
||||
KQ,
|
||||
ggml_new_f32(ctx0, 1.0f/sqrt(float(head_dim)))
|
||||
);
|
||||
|
||||
// KQ_masked = mask_past(KQ_scaled)
|
||||
struct ggml_tensor * KQ_masked = ggml_diag_mask_inf_inplace(ctx0, KQ_scaled, n_past);
|
||||
|
||||
// KQ = soft_max(KQ_masked)
|
||||
struct ggml_tensor * KQ_soft_max = ggml_soft_max_inplace(ctx0, KQ_masked);
|
||||
|
||||
// V_trans = Vmem.view(n_embd/n_head, n_head, n_past + N).permute(1, 2, 0, 3).contiguous()
|
||||
struct ggml_tensor* V = ggml_permute(
|
||||
ctx0,
|
||||
ggml_view_3d(
|
||||
ctx0,
|
||||
model.kv_self.v,
|
||||
head_dim, n_head_kv, n_past + N,
|
||||
head_dim * sizeof_wtype,
|
||||
head_dim * n_head_kv * sizeof_wtype,
|
||||
il * n_ctx * ggml_element_size(model.kv_self.v) * n_head_kv * head_dim),
|
||||
0, 2, 1, 3);
|
||||
|
||||
// changed from repeat2 back to repeat, will not support 40B!
|
||||
V = ggml_cont(ctx0, ggml_transpose(ctx0, ggml_repeat(ctx0, V, repeat_dummy)));
|
||||
|
||||
// KQV = transpose(V) * KQ_soft_max
|
||||
struct ggml_tensor * KQV = ggml_mul_mat(ctx0, V, KQ_soft_max);
|
||||
|
||||
// KQV_merged = KQV.permute(0, 2, 1, 3)
|
||||
struct ggml_tensor * KQV_merged = ggml_permute(ctx0, KQV, 0, 2, 1, 3);
|
||||
|
||||
// cur = KQV_merged.contiguous().view(n_embd, N)
|
||||
cur = ggml_cpy(ctx0,
|
||||
KQV_merged,
|
||||
ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, n_embd, N));
|
||||
|
||||
// projection
|
||||
{
|
||||
cur = ggml_mul_mat(ctx0,
|
||||
model.layers[il].wo,
|
||||
cur);
|
||||
}
|
||||
}
|
||||
|
||||
ggml_set_scratch(ctx0, {0, model.scr1_buf.size, model.scr1_buf.addr, });
|
||||
|
||||
struct ggml_tensor* inpFF = layernorm_output;
|
||||
struct ggml_tensor* attn_out = ggml_cpy(
|
||||
ctx0, cur, ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, n_embd, N));
|
||||
|
||||
{
|
||||
cur = ggml_mul_mat(ctx0, model.layers[il].ffn_up, inpFF);
|
||||
cur = ggml_gelu(ctx0, cur);
|
||||
cur = ggml_mul_mat(ctx0, model.layers[il].ffn_down, cur);
|
||||
}
|
||||
|
||||
cur = ggml_add(ctx0, cur, attn_out);
|
||||
cur = ggml_add(ctx0, cur, inpL);
|
||||
// input for next layer
|
||||
inpL = cur;
|
||||
}
|
||||
|
||||
ggml_set_scratch(ctx0, {0, model.scr0_buf.size, model.scr0_buf.addr, });
|
||||
|
||||
// norm
|
||||
{
|
||||
inpL = ggml_norm(ctx0, inpL);
|
||||
|
||||
// inpL = ln_f_g*inpL + ln_f_b
|
||||
inpL = ggml_add(ctx0,
|
||||
ggml_mul(ctx0,
|
||||
ggml_repeat(ctx0, model.output_norm, inpL),
|
||||
inpL),
|
||||
ggml_repeat(ctx0, model.output_norm_b, inpL));
|
||||
}
|
||||
|
||||
ggml_set_scratch(ctx0, { 0, 0, nullptr, });
|
||||
|
||||
// lm_head
|
||||
{
|
||||
inpL = ggml_mul_mat(ctx0, model.lm_head, inpL);
|
||||
|
||||
//inpL = ggml_add(ctx0,
|
||||
// ggml_repeat(ctx0, model.lmh_b, inpL),
|
||||
// inpL);
|
||||
}
|
||||
|
||||
// logits -> probs
|
||||
//inpL = ggml_soft_max_inplace(ctx0, inpL);
|
||||
|
||||
// run the computation
|
||||
ggml_build_forward_expand(&gf, inpL);
|
||||
ggml_graph_compute (ctx0, &gf);
|
||||
|
||||
//if (n_past%100 == 0) {
|
||||
// ggml_graph_print (&gf);
|
||||
// ggml_graph_dump_dot(&gf, NULL, "gpt-2.dot");
|
||||
//}
|
||||
|
||||
//embd_w.resize(n_vocab*N);
|
||||
//memcpy(embd_w.data(), ggml_get_data(inpL), sizeof(float)*n_vocab*N);
|
||||
|
||||
// return result for just the last token
|
||||
embd_w.resize(n_vocab);
|
||||
memcpy(embd_w.data(), (float *) ggml_get_data(inpL) + (n_vocab*(N-1)), sizeof(float)*n_vocab);
|
||||
|
||||
if (mem_per_token == 0) {
|
||||
mem_per_token = ggml_used_mem(ctx0)/N;
|
||||
}
|
||||
//printf("used_mem = %zu\n", ggml_used_mem(ctx0));
|
||||
|
||||
ggml_free(ctx0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#define MAX_RNG_STATE 64*1024
|
||||
size_t falcon_get_state_size(const falcon_model &model) {
|
||||
const size_t s_rng_size = sizeof(size_t);
|
||||
const size_t s_rng = MAX_RNG_STATE;
|
||||
const size_t s_kv_size = sizeof(size_t);
|
||||
const size_t s_kv_ntok = sizeof(int);
|
||||
const size_t s_kv = model.kv_self.buf.size;
|
||||
const size_t s_total = (
|
||||
+ s_rng_size
|
||||
+ s_rng
|
||||
+ s_kv_size
|
||||
+ s_kv_ntok
|
||||
+ s_kv
|
||||
);
|
||||
return s_total;
|
||||
}
|
||||
|
||||
size_t falcon_copy_state_data(const falcon_model &model, const std::mt19937 &rng, uint8_t *dest)
|
||||
{
|
||||
uint8_t * out = dest;
|
||||
// copy rng
|
||||
{
|
||||
std::stringstream rng_ss;
|
||||
rng_ss << rng;
|
||||
|
||||
const size_t rng_size = rng_ss.str().size();
|
||||
char rng_buf[MAX_RNG_STATE];
|
||||
|
||||
memset(&rng_buf[0], 0, MAX_RNG_STATE);
|
||||
memcpy(&rng_buf[0], rng_ss.str().data(), rng_ss.str().size());
|
||||
|
||||
memcpy(out, &rng_size, sizeof(rng_size)); out += sizeof(rng_size);
|
||||
memcpy(out, &rng_buf[0], MAX_RNG_STATE); out += MAX_RNG_STATE;
|
||||
}
|
||||
|
||||
// copy kv cache
|
||||
{
|
||||
const size_t kv_size = model.kv_self.buf.size;
|
||||
const int kv_ntok = model.kv_self.n;
|
||||
|
||||
memcpy(out, &kv_size, sizeof(kv_size)); out += sizeof(kv_size);
|
||||
memcpy(out, &kv_ntok, sizeof(kv_ntok)); out += sizeof(kv_ntok);
|
||||
|
||||
if (kv_size) {
|
||||
memcpy(out, model.kv_self.buf.addr, kv_size); out += kv_size;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t written = out - dest;
|
||||
assert(written == falcon_get_state_size(model));
|
||||
fflush(stdout);
|
||||
return written;
|
||||
}
|
||||
|
||||
size_t falcon_set_state_data(falcon_model *model, std::mt19937 *rng, const uint8_t *src)
|
||||
{
|
||||
const uint8_t * in = src;
|
||||
|
||||
// set rng
|
||||
{
|
||||
size_t rng_size;
|
||||
char rng_buf[MAX_RNG_STATE];
|
||||
|
||||
memcpy(&rng_size, in, sizeof(rng_size)); in += sizeof(rng_size);
|
||||
memcpy(&rng_buf[0], in, MAX_RNG_STATE); in += MAX_RNG_STATE;
|
||||
|
||||
std::stringstream rng_ss;
|
||||
rng_ss.str(std::string(&rng_buf[0], rng_size));
|
||||
rng_ss >> *rng;
|
||||
|
||||
assert(rng_ss.fail() == false);
|
||||
}
|
||||
|
||||
// set kv cache
|
||||
{
|
||||
size_t kv_size;
|
||||
int kv_ntok;
|
||||
|
||||
memcpy(&kv_size, in, sizeof(kv_size)); in += sizeof(kv_size);
|
||||
memcpy(&kv_ntok, in, sizeof(kv_ntok)); in += sizeof(kv_ntok);
|
||||
|
||||
if (kv_size) {
|
||||
assert(model->kv_self.buf.size == kv_size);
|
||||
|
||||
void * k_data = model->kv_self.k->data; // remember data pointers
|
||||
void * v_data = model->kv_self.v->data; // because their value is stored in buf and overwritten by memcpy
|
||||
|
||||
memcpy(model->kv_self.buf.addr, in, kv_size); in += kv_size;
|
||||
|
||||
model->kv_self.k->data = k_data; // restore correct data pointers
|
||||
model->kv_self.v->data = v_data;
|
||||
|
||||
}
|
||||
|
||||
model->kv_self.n = kv_ntok;
|
||||
}
|
||||
|
||||
const size_t nread = in - src;
|
||||
assert(nread == falcon_get_state_size(*model));
|
||||
fflush(stdout);
|
||||
return nread;
|
||||
}
|
||||
|
||||
struct FalconPrivate {
|
||||
const std::string modelPath;
|
||||
bool modelLoaded;
|
||||
gpt_vocab vocab;
|
||||
falcon_model *model = nullptr;
|
||||
int64_t n_threads = 0;
|
||||
size_t mem_per_token = 0;
|
||||
std::mt19937 rng;
|
||||
};
|
||||
|
||||
Falcon::Falcon() : d_ptr(new FalconPrivate) {
|
||||
d_ptr->model = new falcon_model;
|
||||
d_ptr->model->ctx = nullptr;
|
||||
d_ptr->modelLoaded = false;
|
||||
}
|
||||
|
||||
Falcon::~Falcon() {
|
||||
if(d_ptr->model->ctx) {
|
||||
ggml_free(d_ptr->model->ctx);
|
||||
d_ptr->model->ctx = nullptr;
|
||||
}
|
||||
delete d_ptr->model;
|
||||
}
|
||||
|
||||
bool Falcon::loadModel(const std::string &modelPath)
|
||||
{
|
||||
std::mt19937 rng(time(NULL));
|
||||
d_ptr->rng = rng;
|
||||
|
||||
// load the model
|
||||
if (!falcon_model_load(modelPath, *d_ptr->model, d_ptr->vocab, nullptr)) {
|
||||
std::cerr << "FALCON ERROR: failed to load model from " << modelPath;
|
||||
return false;
|
||||
}
|
||||
|
||||
d_ptr->n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency());
|
||||
d_ptr->modelLoaded = true;
|
||||
fflush(stdout);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Falcon::isModelLoaded() const
|
||||
{
|
||||
return d_ptr -> modelLoaded;
|
||||
}
|
||||
|
||||
size_t Falcon::requiredMem(const std::string &modelPath)
|
||||
{
|
||||
falcon_model dummy_model;
|
||||
gpt_vocab dummy_vocab;
|
||||
size_t mem_req;
|
||||
auto fin = std::ifstream(modelPath, std::ios::binary);
|
||||
falcon_model_load(modelPath, dummy_model, dummy_vocab, &mem_req);
|
||||
return mem_req;
|
||||
}
|
||||
|
||||
size_t Falcon::stateSize() const
|
||||
{
|
||||
return falcon_get_state_size(*d_ptr->model);
|
||||
}
|
||||
|
||||
size_t Falcon::saveState(uint8_t *dest) const
|
||||
{
|
||||
return falcon_copy_state_data(*d_ptr->model, d_ptr->rng, dest);
|
||||
}
|
||||
|
||||
size_t Falcon::restoreState(const uint8_t *src)
|
||||
{
|
||||
return falcon_set_state_data(d_ptr->model, &d_ptr->rng, src);
|
||||
}
|
||||
|
||||
void Falcon::setThreadCount(int32_t n_threads)
|
||||
{
|
||||
d_ptr->n_threads = n_threads;
|
||||
}
|
||||
|
||||
int32_t Falcon::threadCount() const
|
||||
{
|
||||
return d_ptr->n_threads;
|
||||
}
|
||||
|
||||
std::vector<LLModel::Token> Falcon::tokenize(PromptContext &, const std::string &str) const
|
||||
{
|
||||
return ::gpt_tokenize(d_ptr->vocab, str);
|
||||
}
|
||||
|
||||
LLModel::Token Falcon::sampleToken(PromptContext &promptCtx) const
|
||||
{
|
||||
const size_t n_prev_toks = std::min((size_t) promptCtx.repeat_last_n, promptCtx.tokens.size());
|
||||
return gpt_sample_top_k_top_p(d_ptr->model->hparams.n_vocab,
|
||||
promptCtx.tokens.data() + promptCtx.tokens.size() - n_prev_toks,
|
||||
n_prev_toks,
|
||||
promptCtx.logits,
|
||||
promptCtx.top_k, promptCtx.top_p, promptCtx.temp,
|
||||
promptCtx.repeat_penalty,
|
||||
d_ptr->rng);
|
||||
}
|
||||
|
||||
std::string Falcon::tokenToString(Token id) const
|
||||
{
|
||||
return d_ptr->vocab.id_to_token[id];
|
||||
}
|
||||
|
||||
bool Falcon::evalTokens(PromptContext &ctx, const std::vector<int32_t> &tokens) const
|
||||
{
|
||||
// determine the required inference memory per token:
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
falcon_eval(*d_ptr->model, d_ptr->n_threads, 0, { 0, 1, 2, 3 }, ctx.logits,
|
||||
d_ptr->mem_per_token);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
return falcon_eval(*d_ptr->model, d_ptr->n_threads, ctx.n_past, tokens, ctx.logits, d_ptr->mem_per_token);
|
||||
}
|
||||
|
||||
int32_t Falcon::contextLength() const
|
||||
{
|
||||
return d_ptr->model->hparams.n_ctx;
|
||||
}
|
||||
|
||||
const std::vector<LLModel::Token> &Falcon::endTokens() const
|
||||
{
|
||||
static const std::vector<LLModel::Token> out = { 11 };
|
||||
return out;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define DLL_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLL_EXPORT __attribute__ ((visibility ("default")))
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
DLL_EXPORT bool is_g4a_backend_model_implementation() {
|
||||
return true;
|
||||
}
|
||||
|
||||
DLL_EXPORT const char *get_model_type() {
|
||||
return modelType_;
|
||||
}
|
||||
|
||||
DLL_EXPORT const char *get_build_variant() {
|
||||
return GGML_BUILD_VARIANT;
|
||||
}
|
||||
|
||||
DLL_EXPORT bool magic_match(std::istream& f) {
|
||||
uint32_t magic = 0;
|
||||
f.read(reinterpret_cast<char*>(&magic), sizeof(magic));
|
||||
uint32_t version = 0;
|
||||
f.read(reinterpret_cast<char*>(&version), sizeof(version));
|
||||
if (magic != FALCON_MAGIC) {
|
||||
return false;
|
||||
}
|
||||
falcon_hparams hparams;
|
||||
f.read(reinterpret_cast<char*>(&hparams), sizeof(hparams));
|
||||
// we're matching the file format of existing pre-converted models
|
||||
// compatible with ctransformers llama.cpp based format, which also
|
||||
// unfortunately shares its magic number what llama uses, so we now
|
||||
// differentiate by n_vocab
|
||||
// give some wiggle room over the max to allow for finetunes that expand the
|
||||
// vocabulary
|
||||
if (!(hparams.n_vocab >= 65024 && hparams.n_vocab <= 65100)) {
|
||||
return false;
|
||||
}
|
||||
if (hparams.falcon_version != 7) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DLL_EXPORT LLModel *construct() {
|
||||
return new Falcon;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
#ifndef FALCON_H_I_KNOW_WHAT_I_AM_DOING_WHEN_INCLUDING_THIS_FILE
|
||||
#error This file is NOT meant to be included outside of falcon.cpp. Doing so is DANGEROUS. Be sure to know what you are doing before proceeding to #define FALCON_H_I_KNOW_WHAT_I_AM_DOING_WHEN_INCLUDING_THIS_FILE
|
||||
#endif
|
||||
#ifndef FALCON_H
|
||||
#define FALCON_H
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "llmodel.h"
|
||||
|
||||
struct FalconPrivate;
|
||||
class Falcon : public LLModel {
|
||||
public:
|
||||
Falcon();
|
||||
~Falcon();
|
||||
|
||||
bool loadModel(const std::string &modelPath) override;
|
||||
bool isModelLoaded() const override;
|
||||
size_t requiredMem(const std::string &modelPath) override;
|
||||
size_t stateSize() const override;
|
||||
size_t saveState(uint8_t *dest) const override;
|
||||
size_t restoreState(const uint8_t *src) override;
|
||||
void setThreadCount(int32_t n_threads) override;
|
||||
int32_t threadCount() const override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FalconPrivate> d_ptr;
|
||||
|
||||
protected:
|
||||
std::vector<Token> tokenize(PromptContext &, const std::string&) const override;
|
||||
Token sampleToken(PromptContext &ctx) const override;
|
||||
std::string tokenToString(Token) const override;
|
||||
bool evalTokens(PromptContext &ctx, const std::vector<int32_t> &tokens) const override;
|
||||
int32_t contextLength() const override;
|
||||
const std::vector<Token>& endTokens() const override;
|
||||
};
|
||||
|
||||
#endif // Falcon_H
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -42,7 +41,7 @@ struct gptj_hparams {
|
||||
int32_t n_head = 16;
|
||||
int32_t n_layer = 28;
|
||||
int32_t n_rot = 64;
|
||||
int32_t f16 = 1;
|
||||
float norm_eps = 1e-5;
|
||||
};
|
||||
|
||||
struct gptj_layer {
|
||||
@@ -128,216 +127,149 @@ static bool kv_cache_init(
|
||||
return true;
|
||||
}
|
||||
|
||||
// load the model's weights from a stream
|
||||
bool gptj_model_load(const std::string &fname, std::istream &fin, gptj_model & model, gpt_vocab & vocab, size_t * mem_req = nullptr) {
|
||||
// load the model's weights from a file path
|
||||
bool gptj_model_load(const std::string &fname, gptj_model & model, gpt_vocab & vocab, size_t * mem_req = nullptr) {
|
||||
printf("%s: loading model from '%s' - please wait ...\n", __func__, fname.c_str());
|
||||
if(mem_req != nullptr) {
|
||||
*mem_req = 0;
|
||||
}
|
||||
|
||||
// verify magic
|
||||
{
|
||||
uint32_t magic;
|
||||
fin.read((char *) &magic, sizeof(magic));
|
||||
if (magic != 0x67676d6c) {
|
||||
fprintf(stderr, "%s: invalid model file '%s' (bad magic)\n", __func__, fname.c_str());
|
||||
return false;
|
||||
}
|
||||
// create the ggml context
|
||||
struct gguf_init_params params = {
|
||||
/*.no_alloc = */ false,
|
||||
/*.ctx = */ &model.ctx,
|
||||
};
|
||||
|
||||
gguf_context *ggufctx = gguf_init_from_file(fname.c_str(), params);
|
||||
if (!ggufctx) {
|
||||
fprintf(stderr, "%s: gguf_init_from_file() failed\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
// load hparams
|
||||
{
|
||||
auto & hparams = model.hparams;
|
||||
|
||||
fin.read((char *) &hparams.n_vocab, sizeof(hparams.n_vocab));
|
||||
fin.read((char *) &hparams.n_ctx, sizeof(hparams.n_ctx));
|
||||
fin.read((char *) &hparams.n_embd, sizeof(hparams.n_embd));
|
||||
fin.read((char *) &hparams.n_head, sizeof(hparams.n_head));
|
||||
fin.read((char *) &hparams.n_layer, sizeof(hparams.n_layer));
|
||||
fin.read((char *) &hparams.n_rot, sizeof(hparams.n_rot));
|
||||
fin.read((char *) &hparams.f16, sizeof(hparams.f16));
|
||||
bool ok = false;
|
||||
int keyidx;
|
||||
|
||||
do {
|
||||
keyidx = gguf_find_key(ggufctx, "gptj.context_length");
|
||||
if (keyidx == -1) { break; }
|
||||
hparams.n_ctx = gguf_get_val_u32(ggufctx, keyidx);
|
||||
|
||||
keyidx = gguf_find_key(ggufctx, "gptj.embedding_length");
|
||||
if (keyidx == -1) { break; }
|
||||
hparams.n_embd = gguf_get_val_u32(ggufctx, keyidx);
|
||||
|
||||
keyidx = gguf_find_key(ggufctx, "gptj.attention.head_count");
|
||||
if (keyidx == -1) { break; }
|
||||
hparams.n_head = gguf_get_val_u32(ggufctx, keyidx);
|
||||
|
||||
keyidx = gguf_find_key(ggufctx, "gptj.block_count");
|
||||
if (keyidx == -1) { break; }
|
||||
hparams.n_layer = gguf_get_val_u32(ggufctx, keyidx);
|
||||
|
||||
keyidx = gguf_find_key(ggufctx, "gptj.rope.dimension_count");
|
||||
if (keyidx == -1) { break; }
|
||||
hparams.n_rot = gguf_get_val_u32(ggufctx, keyidx);
|
||||
|
||||
keyidx = gguf_find_key(ggufctx, "gptj.attention.layer_norm_epsilon");
|
||||
if (keyidx == -1) { break; }
|
||||
hparams.norm_eps = gguf_get_val_f32(ggufctx, keyidx);
|
||||
|
||||
ok = true;
|
||||
} while (false);
|
||||
|
||||
if (!ok) {
|
||||
fprintf(stderr, "%s: required hparam missing!\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("%s: n_vocab = %d\n", __func__, hparams.n_vocab);
|
||||
printf("%s: n_ctx = %d\n", __func__, hparams.n_ctx);
|
||||
printf("%s: n_embd = %d\n", __func__, hparams.n_embd);
|
||||
printf("%s: n_head = %d\n", __func__, hparams.n_head);
|
||||
printf("%s: n_layer = %d\n", __func__, hparams.n_layer);
|
||||
printf("%s: n_rot = %d\n", __func__, hparams.n_rot);
|
||||
printf("%s: f16 = %d\n", __func__, hparams.f16);
|
||||
}
|
||||
|
||||
// load vocab
|
||||
{
|
||||
int32_t n_vocab = 0;
|
||||
fin.read((char *) &n_vocab, sizeof(n_vocab));
|
||||
auto & hparams = model.hparams;
|
||||
|
||||
if (n_vocab != model.hparams.n_vocab) {
|
||||
fprintf(stderr, "%s: invalid model file '%s' (bad vocab size %d != %d)\n",
|
||||
__func__, fname.c_str(), n_vocab, model.hparams.n_vocab);
|
||||
int keyidx = gguf_find_key(ggufctx, "tokenizer.ggml.model");
|
||||
if (keyidx == -1) {
|
||||
fprintf(stderr, "%s: tokenizer model not found!\n", __func__);
|
||||
return false;
|
||||
}
|
||||
if (strcmp(gguf_get_val_str(ggufctx, keyidx), "gpt2") != 0) {
|
||||
fprintf(stderr, "%s: tokenizer model not supported!\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string word;
|
||||
for (int i = 0; i < n_vocab; i++) {
|
||||
uint32_t len;
|
||||
fin.read((char *) &len, sizeof(len));
|
||||
int tokens_keyidx = gguf_find_key(ggufctx, "tokenizer.ggml.tokens");
|
||||
if (tokens_keyidx == -1) {
|
||||
fprintf(stderr, "%s: gpt2 tokenizer vocab not found!\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
word.resize(len);
|
||||
fin.read((char *) word.data(), len);
|
||||
hparams.n_vocab = gguf_get_arr_n(ggufctx, tokens_keyidx);
|
||||
printf("%s: gpt2 tokenizer vocab = %d\n", __func__, int(hparams.n_vocab));
|
||||
|
||||
for (int i = 0; i < hparams.n_vocab; i++) {
|
||||
std::string word = gguf_get_arr_str(ggufctx, tokens_keyidx, i);
|
||||
vocab.token_to_id[word] = i;
|
||||
vocab.id_to_token[i] = word;
|
||||
}
|
||||
}
|
||||
|
||||
// for the big tensors, we have the option to store the data in 16-bit floats or quantized
|
||||
// in order to save memory and also to speed up the computation
|
||||
ggml_type wtype = GGML_TYPE_COUNT;
|
||||
switch (model.hparams.f16) {
|
||||
case 0: wtype = GGML_TYPE_F32; break;
|
||||
case 1: wtype = GGML_TYPE_F16; break;
|
||||
case 2: wtype = GGML_TYPE_Q4_0; break;
|
||||
case 3: wtype = GGML_TYPE_Q4_1; break;
|
||||
case 5: wtype = GGML_TYPE_Q4_2; break;
|
||||
default:
|
||||
{
|
||||
fprintf(stderr, "%s: invalid model file '%s' (bad f16 value %d)\n",
|
||||
__func__, fname.c_str(), model.hparams.f16);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto & ctx = model.ctx;
|
||||
|
||||
size_t ctx_size = 0;
|
||||
|
||||
{
|
||||
const auto & hparams = model.hparams;
|
||||
|
||||
const int n_embd = hparams.n_embd;
|
||||
const int n_layer = hparams.n_layer;
|
||||
const int n_ctx = hparams.n_ctx;
|
||||
const int n_vocab = hparams.n_vocab;
|
||||
|
||||
ctx_size += n_embd*ggml_type_sizef(GGML_TYPE_F32); // ln_f_g
|
||||
ctx_size += n_embd*ggml_type_sizef(GGML_TYPE_F32); // ln_f_b
|
||||
|
||||
ctx_size += n_embd*n_vocab*ggml_type_sizef(wtype); // wte
|
||||
|
||||
ctx_size += n_embd*n_vocab*ggml_type_sizef(wtype); // lmh_g
|
||||
ctx_size += n_vocab*ggml_type_sizef(GGML_TYPE_F32); // lmh_b
|
||||
|
||||
ctx_size += n_layer*(n_embd*ggml_type_sizef(GGML_TYPE_F32)); // ln_1_g
|
||||
ctx_size += n_layer*(n_embd*ggml_type_sizef(GGML_TYPE_F32)); // ln_1_b
|
||||
|
||||
ctx_size += n_layer*(n_embd*n_embd*ggml_type_sizef(wtype)); // c_attn_q_proj_w
|
||||
ctx_size += n_layer*(n_embd*n_embd*ggml_type_sizef(wtype)); // c_attn_k_proj_w
|
||||
ctx_size += n_layer*(n_embd*n_embd*ggml_type_sizef(wtype)); // c_attn_v_proj_w
|
||||
|
||||
ctx_size += n_layer*(n_embd*n_embd*ggml_type_sizef(wtype)); // c_attn_proj_w
|
||||
|
||||
ctx_size += n_layer*(4*n_embd*n_embd*ggml_type_sizef(wtype)); // c_mlp_fc_w
|
||||
ctx_size += n_layer*( 4*n_embd*ggml_type_sizef(GGML_TYPE_F32)); // c_mlp_fc_b
|
||||
|
||||
ctx_size += n_layer*(4*n_embd*n_embd*ggml_type_sizef(wtype)); // c_mlp_proj_w
|
||||
ctx_size += n_layer*( n_embd*ggml_type_sizef(GGML_TYPE_F32)); // c_mlp_proj_b
|
||||
|
||||
ctx_size += n_ctx*n_layer*n_embd*ggml_type_sizef(GGML_TYPE_F32); // memory_k
|
||||
ctx_size += n_ctx*n_layer*n_embd*ggml_type_sizef(GGML_TYPE_F32); // memory_v
|
||||
|
||||
ctx_size += (5 + 10*n_layer)*256; // object overhead
|
||||
|
||||
printf("%s: ggml ctx size = %6.2f MB\n", __func__, ctx_size/(1024.0*1024.0));
|
||||
}
|
||||
size_t ctx_size = ggml_get_mem_size(ctx);
|
||||
printf("%s: ggml ctx size = %6.2f MB\n", __func__, ctx_size / (1024.0 * 1024.0));
|
||||
|
||||
if (mem_req != nullptr) {
|
||||
*mem_req += ctx_size;
|
||||
const int n_embd = model.hparams.n_embd;
|
||||
const int n_layer = model.hparams.n_layer;
|
||||
|
||||
const int64_t n_mem = (int64_t)n_layer*model.hparams.n_ctx;
|
||||
const int64_t n_elements = n_embd*n_mem;
|
||||
|
||||
*mem_req += (2u*n_elements*ggml_type_size(wtype) + 2_MiB);
|
||||
*mem_req = ctx_size;
|
||||
gguf_free(ggufctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// create the ggml context
|
||||
{
|
||||
struct ggml_init_params params = {
|
||||
.mem_size = ctx_size,
|
||||
.mem_buffer = NULL,
|
||||
.no_alloc = false
|
||||
};
|
||||
|
||||
model.ctx = ggml_init(params);
|
||||
if (!model.ctx) {
|
||||
fprintf(stderr, "%s: ggml_init() failed\n", __func__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// prepare memory for the weights
|
||||
{
|
||||
const auto & hparams = model.hparams;
|
||||
model.layers.resize(hparams.n_layer);
|
||||
|
||||
const int n_embd = hparams.n_embd;
|
||||
const int n_layer = hparams.n_layer;
|
||||
const int n_vocab = hparams.n_vocab;
|
||||
model.wte = ggml_get_tensor(ctx, "token_embd.weight");
|
||||
|
||||
model.layers.resize(n_layer);
|
||||
model.ln_f_g = ggml_get_tensor(ctx, "output_norm.weight");
|
||||
model.ln_f_b = ggml_get_tensor(ctx, "output_norm.bias");
|
||||
|
||||
model.wte = ggml_new_tensor_2d(ctx, wtype, n_embd, n_vocab);
|
||||
model.lmh_g = ggml_get_tensor(ctx, "output.weight");
|
||||
model.lmh_b = ggml_get_tensor(ctx, "output.bias");
|
||||
|
||||
model.ln_f_g = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_embd);
|
||||
model.ln_f_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_embd);
|
||||
auto name = [](int i, std::string n) {
|
||||
static std::string key;
|
||||
key = "blk." + std::to_string(i) + "." + n;
|
||||
return key.c_str();
|
||||
};
|
||||
|
||||
model.lmh_g = ggml_new_tensor_2d(ctx, wtype, n_embd, n_vocab);
|
||||
model.lmh_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_vocab);
|
||||
|
||||
// map by name
|
||||
model.tensors["transformer.wte.weight"] = model.wte;
|
||||
|
||||
model.tensors["transformer.ln_f.weight"] = model.ln_f_g;
|
||||
model.tensors["transformer.ln_f.bias"] = model.ln_f_b;
|
||||
|
||||
model.tensors["lm_head.weight"] = model.lmh_g;
|
||||
model.tensors["lm_head.bias"] = model.lmh_b;
|
||||
|
||||
for (int i = 0; i < n_layer; ++i) {
|
||||
for (int i = 0; i < hparams.n_layer; ++i) {
|
||||
auto & layer = model.layers[i];
|
||||
|
||||
layer.ln_1_g = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_embd);
|
||||
layer.ln_1_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_embd);
|
||||
layer.ln_1_g = ggml_get_tensor(ctx, name(i, "attn_norm.weight"));
|
||||
layer.ln_1_b = ggml_get_tensor(ctx, name(i, "attn_norm.bias"));
|
||||
|
||||
layer.c_attn_q_proj_w = ggml_new_tensor_2d(ctx, wtype, n_embd, n_embd);
|
||||
layer.c_attn_k_proj_w = ggml_new_tensor_2d(ctx, wtype, n_embd, n_embd);
|
||||
layer.c_attn_v_proj_w = ggml_new_tensor_2d(ctx, wtype, n_embd, n_embd);
|
||||
layer.c_attn_q_proj_w = ggml_get_tensor(ctx, name(i, "attn_q.weight"));
|
||||
layer.c_attn_k_proj_w = ggml_get_tensor(ctx, name(i, "attn_k.weight"));
|
||||
layer.c_attn_v_proj_w = ggml_get_tensor(ctx, name(i, "attn_v.weight"));
|
||||
|
||||
layer.c_attn_proj_w = ggml_new_tensor_2d(ctx, wtype, n_embd, n_embd);
|
||||
layer.c_attn_proj_w = ggml_get_tensor(ctx, name(i, "attn_output.weight"));
|
||||
|
||||
layer.c_mlp_fc_w = ggml_new_tensor_2d(ctx, wtype, n_embd, 4*n_embd);
|
||||
layer.c_mlp_fc_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 4*n_embd);
|
||||
layer.c_mlp_fc_w = ggml_get_tensor(ctx, name(i, "ffn_up.weight"));
|
||||
layer.c_mlp_fc_b = ggml_get_tensor(ctx, name(i, "ffn_up.bias"));
|
||||
|
||||
layer.c_mlp_proj_w = ggml_new_tensor_2d(ctx, wtype, 4*n_embd, n_embd);
|
||||
layer.c_mlp_proj_b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_embd);
|
||||
|
||||
// map by name
|
||||
model.tensors["transformer.h." + std::to_string(i) + ".ln_1.weight"] = layer.ln_1_g;
|
||||
model.tensors["transformer.h." + std::to_string(i) + ".ln_1.bias"] = layer.ln_1_b;
|
||||
|
||||
model.tensors["transformer.h." + std::to_string(i) + ".attn.q_proj.weight"] = layer.c_attn_q_proj_w;
|
||||
model.tensors["transformer.h." + std::to_string(i) + ".attn.k_proj.weight"] = layer.c_attn_k_proj_w;
|
||||
model.tensors["transformer.h." + std::to_string(i) + ".attn.v_proj.weight"] = layer.c_attn_v_proj_w;
|
||||
|
||||
model.tensors["transformer.h." + std::to_string(i) + ".attn.out_proj.weight"] = layer.c_attn_proj_w;
|
||||
|
||||
model.tensors["transformer.h." + std::to_string(i) + ".mlp.fc_in.weight"] = layer.c_mlp_fc_w;
|
||||
model.tensors["transformer.h." + std::to_string(i) + ".mlp.fc_in.bias"] = layer.c_mlp_fc_b;
|
||||
|
||||
model.tensors["transformer.h." + std::to_string(i) + ".mlp.fc_out.weight"] = layer.c_mlp_proj_w;
|
||||
model.tensors["transformer.h." + std::to_string(i) + ".mlp.fc_out.bias"] = layer.c_mlp_proj_b;
|
||||
layer.c_mlp_proj_w = ggml_get_tensor(ctx, name(i, "ffn_down.weight"));
|
||||
layer.c_mlp_proj_b = ggml_get_tensor(ctx, name(i, "ffn_down.bias"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,113 +286,12 @@ bool gptj_model_load(const std::string &fname, std::istream &fin, gptj_model & m
|
||||
printf("%s: kv self size = %7.2f MB\n", __func__, memory_size / 1024.0 / 1024.0);
|
||||
}
|
||||
|
||||
// load weights
|
||||
{
|
||||
int n_tensors = 0;
|
||||
size_t total_size = 0;
|
||||
|
||||
printf("%s: ", __func__);
|
||||
|
||||
while (true) {
|
||||
int32_t n_dims;
|
||||
int32_t length;
|
||||
int32_t ftype;
|
||||
|
||||
fin.read(reinterpret_cast<char *>(&n_dims), sizeof(n_dims));
|
||||
fin.read(reinterpret_cast<char *>(&length), sizeof(length));
|
||||
fin.read(reinterpret_cast<char *>(&ftype), sizeof(ftype));
|
||||
|
||||
if (fin.eof()) {
|
||||
break;
|
||||
}
|
||||
|
||||
int32_t nelements = 1;
|
||||
int32_t ne[2] = { 1, 1 };
|
||||
for (int i = 0; i < n_dims; ++i) {
|
||||
fin.read(reinterpret_cast<char *>(&ne[i]), sizeof(ne[i]));
|
||||
nelements *= ne[i];
|
||||
}
|
||||
|
||||
std::string name(length, 0);
|
||||
fin.read(&name[0], length);
|
||||
|
||||
if (model.tensors.find(name.data()) == model.tensors.end()) {
|
||||
fprintf(stderr, "%s: unknown tensor '%s' in model file\n", __func__, name.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto tensor = model.tensors[name.data()];
|
||||
if (ggml_nelements(tensor) != nelements) {
|
||||
fprintf(stderr, "%s: tensor '%s' has wrong size in model file\n", __func__, name.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tensor->ne[0] != ne[0] || tensor->ne[1] != ne[1]) {
|
||||
fprintf(stderr, "%s: tensor '%s' has wrong shape in model file: got [%" PRId64 ", %" PRId64 "], expected [%d, %d]\n",
|
||||
__func__, name.data(), tensor->ne[0], tensor->ne[1], ne[0], ne[1]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (0) {
|
||||
static const char * ftype_str[] = { "f32", "f16", "q4_0", "q4_1", };
|
||||
printf("%24s - [%5d, %5d], type = %6s, %6.2f MB, %9zu bytes\n", name.data(), ne[0], ne[1], ftype_str[ftype], ggml_nbytes(tensor)/1024.0/1024.0, ggml_nbytes(tensor));
|
||||
}
|
||||
|
||||
size_t bpe = 0;
|
||||
|
||||
switch (ftype) {
|
||||
case 0: bpe = ggml_type_size(GGML_TYPE_F32); break;
|
||||
case 1: bpe = ggml_type_size(GGML_TYPE_F16); break;
|
||||
case 2: bpe = ggml_type_size(GGML_TYPE_Q4_0); assert(ne[0] % 64 == 0); break;
|
||||
case 3: bpe = ggml_type_size(GGML_TYPE_Q4_1); assert(ne[0] % 64 == 0); break;
|
||||
default:
|
||||
{
|
||||
fprintf(stderr, "%s: unknown ftype %d in model file\n", __func__, ftype);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
if ((nelements*bpe)/ggml_blck_size(tensor->type) != ggml_nbytes(tensor)) {
|
||||
fprintf(stderr, "%s: tensor '%s' has wrong size in model file: got %zu, expected %zu\n",
|
||||
__func__, name.data(), ggml_nbytes(tensor), nelements*bpe);
|
||||
return false;
|
||||
}
|
||||
|
||||
fin.read(reinterpret_cast<char *>(tensor->data), ggml_nbytes(tensor));
|
||||
|
||||
//printf("%42s - [%5d, %5d], type = %6s, %6.2f MB\n", name.data(), ne[0], ne[1], ftype == 0 ? "float" : "f16", ggml_nbytes(tensor)/1024.0/1024.0);
|
||||
total_size += ggml_nbytes(tensor);
|
||||
if (++n_tensors % 8 == 0) {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
printf(" done\n");
|
||||
|
||||
printf("%s: model size = %8.2f MB / num tensors = %d\n", __func__, total_size/1024.0/1024.0, n_tensors);
|
||||
}
|
||||
|
||||
model.scr0_buf.resize(256u * 1024 * 1024);
|
||||
model.scr1_buf.resize(256u * 1024 * 1024);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// load the model's weights from a file path
|
||||
bool gptj_model_load(const std::string & fname, gptj_model & model, gpt_vocab & vocab) {
|
||||
|
||||
auto fin = std::ifstream(fname, std::ios::binary);
|
||||
if (!fin) {
|
||||
fprintf(stderr, "%s: failed to open '%s'\n", __func__, fname.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool loaded = gptj_model_load(fname, fin, model, vocab);
|
||||
fin.close();
|
||||
return loaded;
|
||||
}
|
||||
|
||||
// evaluate the transformer
|
||||
//
|
||||
// - model: the model
|
||||
@@ -512,8 +343,14 @@ bool gptj_eval(
|
||||
};
|
||||
|
||||
struct ggml_context * ctx0 = ggml_init(params);
|
||||
struct ggml_cgraph gf = {};
|
||||
gf.n_threads = n_threads;
|
||||
struct ggml_cgraph * gf = ggml_new_graph(ctx0);
|
||||
|
||||
// KQ_pos - contains the positions
|
||||
struct ggml_tensor * KQ_pos = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, N);
|
||||
int * data = (int *) KQ_pos->data;
|
||||
for (int i = 0; i < N; ++i) {
|
||||
data[i] = n_past + i;
|
||||
}
|
||||
|
||||
struct ggml_tensor * embd = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, N);
|
||||
memcpy(embd->data, embd_inp.data(), N*ggml_element_size(embd));
|
||||
@@ -526,7 +363,7 @@ bool gptj_eval(
|
||||
ggml_set_scratch(ctx0, {0, model.scr0_buf.size, model.scr0_buf.addr, });
|
||||
// norm
|
||||
{
|
||||
cur = ggml_norm(ctx0, inpL);
|
||||
cur = ggml_norm(ctx0, inpL, model.hparams.norm_eps);
|
||||
|
||||
// cur = ln_1_g*cur + ln_1_b
|
||||
cur = ggml_add(ctx0,
|
||||
@@ -540,48 +377,44 @@ bool gptj_eval(
|
||||
|
||||
// self-attention
|
||||
{
|
||||
struct ggml_tensor * Qcur = ggml_mul_mat(ctx0, model.layers[il].c_attn_q_proj_w, cur);
|
||||
struct ggml_tensor * Kcur = ggml_mul_mat(ctx0, model.layers[il].c_attn_k_proj_w, cur);
|
||||
struct ggml_tensor * Vcur = ggml_mul_mat(ctx0, model.layers[il].c_attn_v_proj_w, cur);
|
||||
struct ggml_tensor * Qcur = ggml_rope(
|
||||
ctx0, ggml_reshape_3d(ctx0, ggml_mul_mat(ctx0, model.layers[il].c_attn_q_proj_w, cur), n_embd/n_head, n_head, N),
|
||||
KQ_pos, n_rot, 0, 0
|
||||
);
|
||||
struct ggml_tensor * Kcur = ggml_rope(
|
||||
ctx0, ggml_reshape_3d(ctx0, ggml_mul_mat(ctx0, model.layers[il].c_attn_k_proj_w, cur), n_embd/n_head, n_head, N),
|
||||
KQ_pos, n_rot, 0, 0
|
||||
);
|
||||
|
||||
// store key and value to memory
|
||||
{
|
||||
struct ggml_tensor * k = ggml_view_1d(ctx0, model.kv_self.k, N*n_embd, (ggml_element_size(model.kv_self.k)*n_embd)*(il*n_ctx + n_past));
|
||||
struct ggml_tensor * v = ggml_view_1d(ctx0, model.kv_self.v, N*n_embd, (ggml_element_size(model.kv_self.v)*n_embd)*(il*n_ctx + n_past));
|
||||
struct ggml_tensor * Vcur = ggml_transpose(ctx0, ggml_mul_mat(ctx0, model.layers[il].c_attn_v_proj_w, cur));
|
||||
|
||||
ggml_build_forward_expand(&gf, ggml_cpy(ctx0, Kcur, k));
|
||||
ggml_build_forward_expand(&gf, ggml_cpy(ctx0, Vcur, v));
|
||||
struct ggml_tensor * k = ggml_view_1d(ctx0, model.kv_self.k, N*n_embd, (ggml_element_size(model.kv_self.k)*n_embd)*(il*n_ctx + n_past));
|
||||
struct ggml_tensor * v = ggml_view_2d(ctx0, model.kv_self.v, N, n_embd,
|
||||
( n_ctx)*ggml_element_size(model.kv_self.v),
|
||||
(il*n_ctx)*ggml_element_size(model.kv_self.v)*n_embd + n_past*ggml_element_size(model.kv_self.v));
|
||||
|
||||
ggml_build_forward_expand(gf, ggml_cpy(ctx0, Kcur, k));
|
||||
ggml_build_forward_expand(gf, ggml_cpy(ctx0, Vcur, v));
|
||||
}
|
||||
|
||||
// Q = Qcur.contiguous().view(n_embd/n_head, n_head, N).permute(0, 2, 1, 3)
|
||||
struct ggml_tensor * Q =
|
||||
ggml_permute(ctx0,
|
||||
ggml_rope(ctx0,
|
||||
ggml_cpy(ctx0,
|
||||
Qcur,
|
||||
ggml_new_tensor_3d(ctx0, GGML_TYPE_F32, n_embd/n_head, n_head, N)),
|
||||
n_past, n_rot, 0),
|
||||
0, 2, 1, 3);
|
||||
struct ggml_tensor * Q = ggml_permute(ctx0, Qcur, 0, 2, 1, 3);
|
||||
|
||||
// K = Kmem.view(n_embd/n_head, n_head, n_past + N).permute(0, 2, 1, 3)
|
||||
struct ggml_tensor * K =
|
||||
ggml_permute(ctx0,
|
||||
ggml_rope(ctx0,
|
||||
ggml_reshape_3d(ctx0,
|
||||
ggml_view_1d(ctx0, model.kv_self.k, (n_past + N)*n_embd, il*n_ctx*ggml_element_size(model.kv_self.k)*n_embd),
|
||||
n_embd/n_head, n_head, n_past + N),
|
||||
n_past, n_rot, 1),
|
||||
ggml_reshape_3d(ctx0,
|
||||
ggml_view_1d(ctx0, model.kv_self.k, (n_past + N)*n_embd, il*n_ctx*ggml_element_size(model.kv_self.k)*n_embd),
|
||||
n_embd/n_head, n_head, n_past + N),
|
||||
0, 2, 1, 3);
|
||||
|
||||
// K * Q
|
||||
struct ggml_tensor * KQ = ggml_mul_mat(ctx0, K, Q);
|
||||
|
||||
// KQ_scaled = KQ / sqrt(n_embd/n_head)
|
||||
struct ggml_tensor * KQ_scaled =
|
||||
ggml_scale(ctx0,
|
||||
KQ,
|
||||
ggml_new_f32(ctx0, 1.0f/sqrt(float(n_embd)/n_head))
|
||||
);
|
||||
struct ggml_tensor * KQ_scaled = ggml_scale(ctx0, KQ, 1.0f/sqrt(float(n_embd)/n_head));
|
||||
|
||||
// KQ_masked = mask_past(KQ_scaled)
|
||||
struct ggml_tensor * KQ_masked = ggml_diag_mask_inf(ctx0, KQ_scaled, n_past);
|
||||
@@ -590,17 +423,15 @@ bool gptj_eval(
|
||||
struct ggml_tensor * KQ_soft_max = ggml_soft_max(ctx0, KQ_masked);
|
||||
|
||||
// V_trans = Vmem.view(n_embd/n_head, n_head, n_past + N).permute(1, 2, 0, 3).contiguous()
|
||||
struct ggml_tensor * V_trans =
|
||||
ggml_cpy(ctx0,
|
||||
ggml_permute(ctx0,
|
||||
ggml_reshape_3d(ctx0,
|
||||
ggml_view_1d(ctx0, model.kv_self.v, (n_past + N)*n_embd, il*n_ctx*ggml_element_size(model.kv_self.v)*n_embd),
|
||||
n_embd/n_head, n_head, n_past + N),
|
||||
1, 2, 0, 3),
|
||||
ggml_new_tensor_3d(ctx0, model.kv_self.v->type, n_past + N, n_embd/n_head, n_head));
|
||||
struct ggml_tensor * V =
|
||||
ggml_view_3d(ctx0, model.kv_self.v,
|
||||
n_past + N, n_embd/n_head, n_head,
|
||||
n_ctx*ggml_element_size(model.kv_self.v),
|
||||
n_ctx*ggml_element_size(model.kv_self.v)*n_embd/n_head,
|
||||
il*n_ctx*ggml_element_size(model.kv_self.v)*n_embd);
|
||||
|
||||
// KQV = transpose(V) * KQ_soft_max
|
||||
struct ggml_tensor * KQV = ggml_mul_mat(ctx0, V_trans, KQ_soft_max);
|
||||
struct ggml_tensor * KQV = ggml_mul_mat(ctx0, V, KQ_soft_max);
|
||||
|
||||
// KQV_merged = KQV.permute(0, 2, 1, 3)
|
||||
struct ggml_tensor * KQV_merged = ggml_permute(ctx0, KQV, 0, 2, 1, 3);
|
||||
@@ -656,7 +487,7 @@ bool gptj_eval(
|
||||
|
||||
// norm
|
||||
{
|
||||
inpL = ggml_norm(ctx0, inpL);
|
||||
inpL = ggml_norm(ctx0, inpL, model.hparams.norm_eps);
|
||||
|
||||
// inpL = ln_f_g*inpL + ln_f_b
|
||||
inpL = ggml_add(ctx0,
|
||||
@@ -680,13 +511,22 @@ bool gptj_eval(
|
||||
// logits -> probs
|
||||
//inpL = ggml_soft_max(ctx0, inpL);
|
||||
|
||||
ggml_build_forward_expand(gf, inpL);
|
||||
|
||||
// run the computation
|
||||
ggml_build_forward_expand(&gf, inpL);
|
||||
ggml_graph_compute (ctx0, &gf);
|
||||
{
|
||||
std::unique_ptr<uint8_t []> data;
|
||||
auto plan = ggml_graph_plan(gf, n_threads);
|
||||
if (plan.work_size > 0) {
|
||||
data.reset(new uint8_t[plan.work_size]);
|
||||
plan.work_data = data.get();
|
||||
}
|
||||
ggml_graph_compute(gf, &plan);
|
||||
}
|
||||
|
||||
//if (n_past%100 == 0) {
|
||||
// ggml_graph_print (&gf);
|
||||
// ggml_graph_dump_dot(&gf, NULL, "gpt-2.dot");
|
||||
// ggml_graph_print (gf);
|
||||
// ggml_graph_dump_dot(gf, NULL, "gpt-2.dot");
|
||||
//}
|
||||
|
||||
//embd_w.resize(n_vocab*N);
|
||||
@@ -832,30 +672,34 @@ GPTJ::GPTJ()
|
||||
d_ptr->modelLoaded = false;
|
||||
}
|
||||
|
||||
size_t GPTJ::requiredMem(const std::string &modelPath) {
|
||||
size_t GPTJ::requiredMem(const std::string &modelPath, int n_ctx, int ngl) {
|
||||
(void)n_ctx;
|
||||
(void)ngl;
|
||||
gptj_model dummy_model;
|
||||
gpt_vocab dummy_vocab;
|
||||
size_t mem_req;
|
||||
auto fin = std::ifstream(modelPath, std::ios::binary);
|
||||
gptj_model_load(modelPath, fin, dummy_model, dummy_vocab, &mem_req);
|
||||
gptj_model_load(modelPath, dummy_model, dummy_vocab, &mem_req);
|
||||
return mem_req;
|
||||
}
|
||||
|
||||
bool GPTJ::loadModel(const std::string &modelPath) {
|
||||
bool GPTJ::loadModel(const std::string &modelPath, int n_ctx, int ngl) {
|
||||
(void)n_ctx;
|
||||
(void)ngl;
|
||||
d_ptr->modelLoaded = false;
|
||||
|
||||
std::mt19937 rng(time(NULL));
|
||||
d_ptr->rng = rng;
|
||||
|
||||
auto fin = std::ifstream(modelPath, std::ios::binary);
|
||||
|
||||
// load the model
|
||||
if (!gptj_model_load(modelPath, fin, *d_ptr->model, d_ptr->vocab)) {
|
||||
bool ok = gptj_model_load(modelPath, *d_ptr->model, d_ptr->vocab);
|
||||
fflush(stdout);
|
||||
if (!ok) {
|
||||
std::cerr << "GPT-J ERROR: failed to load model from " << modelPath;
|
||||
return false;
|
||||
}
|
||||
|
||||
d_ptr->n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency());
|
||||
d_ptr->modelLoaded = true;
|
||||
fflush(stdout);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -893,8 +737,10 @@ size_t GPTJ::restoreState(const uint8_t *src)
|
||||
return gptj_set_state_data(d_ptr->model, &d_ptr->rng, src);
|
||||
}
|
||||
|
||||
std::vector<LLModel::Token> GPTJ::tokenize(PromptContext &, const std::string &str) const
|
||||
std::vector<LLModel::Token> GPTJ::tokenize(PromptContext &ctx, const std::string &str, bool special) const
|
||||
{
|
||||
(void)ctx;
|
||||
(void)special;
|
||||
return ::gpt_tokenize(d_ptr->vocab, str);
|
||||
}
|
||||
|
||||
@@ -939,6 +785,16 @@ const std::vector<LLModel::Token> &GPTJ::endTokens() const
|
||||
return fres;
|
||||
}
|
||||
|
||||
std::string get_arch_name(gguf_context *ctx_gguf) {
|
||||
std::string arch_name;
|
||||
const int kid = gguf_find_key(ctx_gguf, "general.architecture");
|
||||
enum gguf_type ktype = gguf_get_kv_type(ctx_gguf, kid);
|
||||
if (ktype != GGUF_TYPE_STRING) {
|
||||
throw std::runtime_error("ERROR: Can't get general architecture from gguf file.");
|
||||
}
|
||||
return gguf_get_val_str(ctx_gguf, kid);
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define DLL_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
@@ -958,10 +814,21 @@ DLL_EXPORT const char *get_build_variant() {
|
||||
return GGML_BUILD_VARIANT;
|
||||
}
|
||||
|
||||
DLL_EXPORT bool magic_match(std::istream& f) {
|
||||
uint32_t magic = 0;
|
||||
f.read(reinterpret_cast<char*>(&magic), sizeof(magic));
|
||||
return magic == 0x67676d6c;
|
||||
DLL_EXPORT bool magic_match(const char * fname) {
|
||||
struct ggml_context * ctx_meta = NULL;
|
||||
struct gguf_init_params params = {
|
||||
/*.no_alloc = */ true,
|
||||
/*.ctx = */ &ctx_meta,
|
||||
};
|
||||
gguf_context *ctx_gguf = gguf_init_from_file(fname, params);
|
||||
if (!ctx_gguf)
|
||||
return false;
|
||||
|
||||
bool isValid = gguf_get_version(ctx_gguf) <= 3;
|
||||
isValid = isValid && get_arch_name(ctx_gguf) == "gptj";
|
||||
|
||||
gguf_free(ctx_gguf);
|
||||
return isValid;
|
||||
}
|
||||
|
||||
DLL_EXPORT LLModel *construct() {
|
||||
|
||||
@@ -15,9 +15,11 @@ public:
|
||||
GPTJ();
|
||||
~GPTJ();
|
||||
|
||||
bool loadModel(const std::string &modelPath) override;
|
||||
bool supportsEmbedding() const override { return false; }
|
||||
bool supportsCompletion() const override { return true; }
|
||||
bool loadModel(const std::string &modelPath, int n_ctx, int ngl) override;
|
||||
bool isModelLoaded() const override;
|
||||
size_t requiredMem(const std::string &modelPath) override;
|
||||
size_t requiredMem(const std::string &modelPath, int n_ctx, int ngl) override;
|
||||
size_t stateSize() const override;
|
||||
size_t saveState(uint8_t *dest) const override;
|
||||
size_t restoreState(const uint8_t *src) override;
|
||||
@@ -28,12 +30,13 @@ private:
|
||||
GPTJPrivate *d_ptr;
|
||||
|
||||
protected:
|
||||
std::vector<Token> tokenize(PromptContext &, const std::string&) const override;
|
||||
std::vector<Token> tokenize(PromptContext &ctx, const std::string &str, bool special) const override;
|
||||
Token sampleToken(PromptContext &ctx) const override;
|
||||
std::string tokenToString(Token) const override;
|
||||
std::string tokenToString(Token id) const override;
|
||||
bool evalTokens(PromptContext &ctx, const std::vector<int32_t> &tokens) const override;
|
||||
int32_t contextLength() const override;
|
||||
const std::vector<Token>& endTokens() const override;
|
||||
const std::vector<Token> &endTokens() const override;
|
||||
bool shouldAddBOS() const override { return false; }
|
||||
};
|
||||
|
||||
#endif // GPTJ_H
|
||||
|
||||
Submodule gpt4all-backend/llama.cpp-230511 deleted from f826aac617
Submodule gpt4all-backend/llama.cpp-230519 deleted from 5ea4339273
Submodule gpt4all-backend/llama.cpp-mainline updated: da760ac382...e3c4f65d78
@@ -1,3 +1,11 @@
|
||||
#
|
||||
# Copyright (c) 2023 Nomic, Inc. All rights reserved.
|
||||
#
|
||||
# This software is licensed under the terms of the Software for Open Models License (SOM),
|
||||
# version 1.0, as detailed in the LICENSE_SOM.txt file. A copy of this license should accompany
|
||||
# this software. Except as expressly granted in the SOM license, all rights are reserved by Nomic, Inc.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.12) # Don't bump this version for no reason
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
@@ -30,6 +38,12 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set(LLAMA_KOMPUTE_DEFAULT OFF)
|
||||
else()
|
||||
set(LLAMA_KOMPUTE_DEFAULT ON)
|
||||
endif()
|
||||
|
||||
|
||||
#
|
||||
# Option list
|
||||
@@ -69,7 +83,7 @@ option(LLAMA_OPENBLAS "llama: use OpenBLAS"
|
||||
#option(LLAMA_CUBLAS "llama: use cuBLAS" OFF)
|
||||
#option(LLAMA_CLBLAST "llama: use CLBlast" OFF)
|
||||
#option(LLAMA_METAL "llama: use Metal" OFF)
|
||||
#option(LLAMA_K_QUANTS "llama: use k-quants" ON)
|
||||
option(LLAMA_KOMPUTE "llama: use Kompute" ${LLAMA_KOMPUTE_DEFAULT})
|
||||
set(LLAMA_BLAS_VENDOR "Generic" CACHE STRING "llama: BLAS library vendor")
|
||||
set(LLAMA_CUDA_DMMV_X "32" CACHE STRING "llama: x stride for dmmv CUDA kernels")
|
||||
set(LLAMA_CUDA_DMMV_Y "1" CACHE STRING "llama: y block size for dmmv CUDA kernels")
|
||||
@@ -145,6 +159,160 @@ if (LLAMA_OPENBLAS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (LLAMA_KOMPUTE)
|
||||
set(LLAMA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/llama.cpp-mainline)
|
||||
if (NOT EXISTS "${LLAMA_DIR}/kompute/CMakeLists.txt")
|
||||
message(FATAL_ERROR "Kompute not found")
|
||||
endif()
|
||||
message(STATUS "Kompute found")
|
||||
|
||||
add_compile_definitions(VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1)
|
||||
find_package(Vulkan COMPONENTS glslc REQUIRED)
|
||||
find_program(glslc_executable NAMES glslc HINTS Vulkan::glslc)
|
||||
if (NOT glslc_executable)
|
||||
message(FATAL_ERROR "glslc not found")
|
||||
endif()
|
||||
|
||||
function(compile_shader)
|
||||
set(options)
|
||||
set(oneValueArgs)
|
||||
set(multiValueArgs SOURCES)
|
||||
cmake_parse_arguments(compile_shader "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
foreach(source ${compile_shader_SOURCES})
|
||||
get_filename_component(OP_FILE ${source} NAME)
|
||||
set(spv_file ${CMAKE_CURRENT_BINARY_DIR}/${OP_FILE}.spv)
|
||||
add_custom_command(
|
||||
OUTPUT ${spv_file}
|
||||
DEPENDS ${LLAMA_DIR}/${source}
|
||||
${LLAMA_DIR}/kompute-shaders/common.comp
|
||||
${LLAMA_DIR}/kompute-shaders/op_getrows.comp
|
||||
${LLAMA_DIR}/kompute-shaders/op_mul_mv_q_n_pre.comp
|
||||
${LLAMA_DIR}/kompute-shaders/op_mul_mv_q_n.comp
|
||||
COMMAND ${glslc_executable} --target-env=vulkan1.2 -o ${spv_file} ${LLAMA_DIR}/${source}
|
||||
COMMENT "Compiling ${source} to ${source}.spv"
|
||||
)
|
||||
|
||||
get_filename_component(RAW_FILE_NAME ${spv_file} NAME)
|
||||
set(FILE_NAME "shader${RAW_FILE_NAME}")
|
||||
string(REPLACE ".comp.spv" ".h" HEADER_FILE ${FILE_NAME})
|
||||
string(TOUPPER ${HEADER_FILE} HEADER_FILE_DEFINE)
|
||||
string(REPLACE "." "_" HEADER_FILE_DEFINE "${HEADER_FILE_DEFINE}")
|
||||
set(OUTPUT_HEADER_FILE "${HEADER_FILE}")
|
||||
message(STATUS "${HEADER_FILE} generating ${HEADER_FILE_DEFINE}")
|
||||
if(CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
add_custom_command(
|
||||
OUTPUT ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "/*THIS FILE HAS BEEN AUTOMATICALLY GENERATED - DO NOT EDIT*/" > ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo \"\#ifndef ${HEADER_FILE_DEFINE}\" >> ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo \"\#define ${HEADER_FILE_DEFINE}\" >> ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "namespace kp {" >> ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "namespace shader_data {" >> ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_BINARY_DIR}/bin/$<CONFIG>/xxd -i ${RAW_FILE_NAME} >> ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "}}" >> ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo \"\#endif // define ${HEADER_FILE_DEFINE}\" >> ${OUTPUT_HEADER_FILE}
|
||||
DEPENDS ${spv_file} xxd
|
||||
COMMENT "Converting to hpp: ${FILE_NAME} ${CMAKE_BINARY_DIR}/bin/$<CONFIG>/xxd"
|
||||
)
|
||||
else()
|
||||
add_custom_command(
|
||||
OUTPUT ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "/*THIS FILE HAS BEEN AUTOMATICALLY GENERATED - DO NOT EDIT*/" > ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo \"\#ifndef ${HEADER_FILE_DEFINE}\" >> ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo \"\#define ${HEADER_FILE_DEFINE}\" >> ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "namespace kp {" >> ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "namespace shader_data {" >> ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_BINARY_DIR}/bin/xxd -i ${RAW_FILE_NAME} >> ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "}}" >> ${OUTPUT_HEADER_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo \"\#endif // define ${HEADER_FILE_DEFINE}\" >> ${OUTPUT_HEADER_FILE}
|
||||
DEPENDS ${spv_file} xxd
|
||||
COMMENT "Converting to hpp: ${FILE_NAME} ${CMAKE_BINARY_DIR}/bin/xxd"
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
set(KOMPUTE_OPT_LOG_LEVEL Critical CACHE STRING "Kompute log level")
|
||||
add_subdirectory(${LLAMA_DIR}/kompute)
|
||||
|
||||
# Compile our shaders
|
||||
compile_shader(SOURCES
|
||||
kompute-shaders/op_scale.comp
|
||||
kompute-shaders/op_scale_8.comp
|
||||
kompute-shaders/op_add.comp
|
||||
kompute-shaders/op_addrow.comp
|
||||
kompute-shaders/op_mul.comp
|
||||
kompute-shaders/op_silu.comp
|
||||
kompute-shaders/op_relu.comp
|
||||
kompute-shaders/op_gelu.comp
|
||||
kompute-shaders/op_softmax.comp
|
||||
kompute-shaders/op_norm.comp
|
||||
kompute-shaders/op_rmsnorm.comp
|
||||
kompute-shaders/op_diagmask.comp
|
||||
kompute-shaders/op_mul_mat_mat_f32.comp
|
||||
kompute-shaders/op_mul_mat_f16.comp
|
||||
kompute-shaders/op_mul_mat_q8_0.comp
|
||||
kompute-shaders/op_mul_mat_q4_0.comp
|
||||
kompute-shaders/op_mul_mat_q4_1.comp
|
||||
kompute-shaders/op_mul_mat_q6_k.comp
|
||||
kompute-shaders/op_getrows_f16.comp
|
||||
kompute-shaders/op_getrows_q4_0.comp
|
||||
kompute-shaders/op_getrows_q4_1.comp
|
||||
kompute-shaders/op_getrows_q6_k.comp
|
||||
kompute-shaders/op_rope_f16.comp
|
||||
kompute-shaders/op_rope_f32.comp
|
||||
kompute-shaders/op_cpy_f16_f16.comp
|
||||
kompute-shaders/op_cpy_f16_f32.comp
|
||||
kompute-shaders/op_cpy_f32_f16.comp
|
||||
kompute-shaders/op_cpy_f32_f32.comp
|
||||
)
|
||||
|
||||
# Create a custom target for our generated shaders
|
||||
add_custom_target(generated_shaders DEPENDS
|
||||
shaderop_scale.h
|
||||
shaderop_scale_8.h
|
||||
shaderop_add.h
|
||||
shaderop_addrow.h
|
||||
shaderop_mul.h
|
||||
shaderop_silu.h
|
||||
shaderop_relu.h
|
||||
shaderop_gelu.h
|
||||
shaderop_softmax.h
|
||||
shaderop_norm.h
|
||||
shaderop_rmsnorm.h
|
||||
shaderop_diagmask.h
|
||||
shaderop_mul_mat_mat_f32.h
|
||||
shaderop_mul_mat_f16.h
|
||||
shaderop_mul_mat_q8_0.h
|
||||
shaderop_mul_mat_q4_0.h
|
||||
shaderop_mul_mat_q4_1.h
|
||||
shaderop_mul_mat_q6_k.h
|
||||
shaderop_getrows_f16.h
|
||||
shaderop_getrows_q4_0.h
|
||||
shaderop_getrows_q4_1.h
|
||||
shaderop_getrows_q6_k.h
|
||||
shaderop_rope_f16.h
|
||||
shaderop_rope_f32.h
|
||||
shaderop_cpy_f16_f16.h
|
||||
shaderop_cpy_f16_f32.h
|
||||
shaderop_cpy_f32_f16.h
|
||||
shaderop_cpy_f32_f32.h
|
||||
)
|
||||
|
||||
# Create a custom command that depends on the generated_shaders
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ggml-kompute.stamp
|
||||
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/ggml-kompute.stamp
|
||||
DEPENDS generated_shaders
|
||||
COMMENT "Ensuring shaders are generated before compiling ggml-kompute.cpp"
|
||||
)
|
||||
|
||||
# Add the stamp to the main sources to ensure dependency tracking
|
||||
set(GGML_SOURCES_KOMPUTE ${LLAMA_DIR}/ggml-kompute.cpp ${LLAMA_DIR}/ggml-kompute.h ${CMAKE_CURRENT_BINARY_DIR}/ggml-kompute.stamp)
|
||||
add_compile_definitions(GGML_USE_KOMPUTE)
|
||||
set(LLAMA_EXTRA_LIBS ${LLAMA_EXTRA_LIBS} kompute)
|
||||
set(LLAMA_EXTRA_INCLUDES ${LLAMA_EXTRA_INCLUDES} ${CMAKE_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
if (LLAMA_ALL_WARNINGS)
|
||||
if (NOT MSVC)
|
||||
set(c_flags
|
||||
@@ -198,6 +366,13 @@ endif()
|
||||
# TODO: probably these flags need to be tweaked on some architectures
|
||||
# feel free to update the Makefile for your architecture and send a pull request or issue
|
||||
message(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
if (MSVC)
|
||||
string(TOLOWER "${CMAKE_GENERATOR_PLATFORM}" CMAKE_GENERATOR_PLATFORM_LWR)
|
||||
message(STATUS "CMAKE_GENERATOR_PLATFORM: ${CMAKE_GENERATOR_PLATFORM}")
|
||||
else ()
|
||||
set(CMAKE_GENERATOR_PLATFORM_LWR "")
|
||||
endif ()
|
||||
|
||||
if (NOT MSVC)
|
||||
if (LLAMA_STATIC)
|
||||
add_link_options(-static)
|
||||
@@ -213,6 +388,139 @@ if (NOT MSVC)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if ((${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") OR (${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") OR ("${CMAKE_GENERATOR_PLATFORM_LWR}" MATCHES "arm64"))
|
||||
message(STATUS "ARM detected")
|
||||
if (MSVC)
|
||||
add_compile_definitions(__ARM_NEON)
|
||||
add_compile_definitions(__ARM_FEATURE_FMA)
|
||||
add_compile_definitions(__ARM_FEATURE_DOTPROD)
|
||||
# add_compile_definitions(__ARM_FEATURE_FP16_VECTOR_ARITHMETIC) # MSVC doesn't support vdupq_n_f16, vld1q_f16, vst1q_f16
|
||||
add_compile_definitions(__aarch64__) # MSVC defines _M_ARM64 instead
|
||||
else()
|
||||
include(CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag(-mfp16-format=ieee COMPILER_SUPPORTS_FP16_FORMAT_I3E)
|
||||
if (NOT "${COMPILER_SUPPORTS_FP16_FORMAT_I3E}" STREQUAL "")
|
||||
add_compile_options(-mfp16-format=ieee)
|
||||
endif()
|
||||
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "armv6")
|
||||
# Raspberry Pi 1, Zero
|
||||
add_compile_options(-mfpu=neon-fp-armv8 -mno-unaligned-access)
|
||||
endif()
|
||||
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "armv7")
|
||||
# Raspberry Pi 2
|
||||
add_compile_options(-mfpu=neon-fp-armv8 -mno-unaligned-access -funsafe-math-optimizations)
|
||||
endif()
|
||||
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "armv8")
|
||||
# Raspberry Pi 3, 4, Zero 2 (32-bit)
|
||||
add_compile_options(-mno-unaligned-access)
|
||||
endif()
|
||||
endif()
|
||||
elseif (${CMAKE_SYSTEM_PROCESSOR} MATCHES "^(x86_64|i686|AMD64)$" OR "${CMAKE_GENERATOR_PLATFORM_LWR}" MATCHES "^(x86_64|i686|amd64|x64)$" )
|
||||
message(STATUS "x86 detected")
|
||||
if (MSVC)
|
||||
if (LLAMA_AVX512)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C>:/arch:AVX512>)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/arch:AVX512>)
|
||||
# MSVC has no compile-time flags enabling specific
|
||||
# AVX512 extensions, neither it defines the
|
||||
# macros corresponding to the extensions.
|
||||
# Do it manually.
|
||||
if (LLAMA_AVX512_VBMI)
|
||||
add_compile_definitions($<$<COMPILE_LANGUAGE:C>:__AVX512VBMI__>)
|
||||
add_compile_definitions($<$<COMPILE_LANGUAGE:CXX>:__AVX512VBMI__>)
|
||||
endif()
|
||||
if (LLAMA_AVX512_VNNI)
|
||||
add_compile_definitions($<$<COMPILE_LANGUAGE:C>:__AVX512VNNI__>)
|
||||
add_compile_definitions($<$<COMPILE_LANGUAGE:CXX>:__AVX512VNNI__>)
|
||||
endif()
|
||||
elseif (LLAMA_AVX2)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C>:/arch:AVX2>)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/arch:AVX2>)
|
||||
elseif (LLAMA_AVX)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C>:/arch:AVX>)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/arch:AVX>)
|
||||
endif()
|
||||
else()
|
||||
if (LLAMA_F16C)
|
||||
add_compile_options(-mf16c)
|
||||
endif()
|
||||
if (LLAMA_FMA)
|
||||
add_compile_options(-mfma)
|
||||
endif()
|
||||
if (LLAMA_AVX)
|
||||
add_compile_options(-mavx)
|
||||
endif()
|
||||
if (LLAMA_AVX2)
|
||||
add_compile_options(-mavx2)
|
||||
endif()
|
||||
if (LLAMA_AVX512)
|
||||
add_compile_options(-mavx512f)
|
||||
add_compile_options(-mavx512bw)
|
||||
endif()
|
||||
if (LLAMA_AVX512_VBMI)
|
||||
add_compile_options(-mavx512vbmi)
|
||||
endif()
|
||||
if (LLAMA_AVX512_VNNI)
|
||||
add_compile_options(-mavx512vnni)
|
||||
endif()
|
||||
endif()
|
||||
elseif (${CMAKE_SYSTEM_PROCESSOR} MATCHES "ppc64")
|
||||
message(STATUS "PowerPC detected")
|
||||
add_compile_options(-mcpu=native -mtune=native)
|
||||
#TODO: Add targets for Power8/Power9 (Altivec/VSX) and Power10(MMA) and query for big endian systems (ppc64/le/be)
|
||||
else()
|
||||
message(STATUS "Unknown architecture")
|
||||
endif()
|
||||
|
||||
#
|
||||
# POSIX conformance
|
||||
#
|
||||
|
||||
# clock_gettime came in POSIX.1b (1993)
|
||||
# CLOCK_MONOTONIC came in POSIX.1-2001 / SUSv3 as optional
|
||||
# posix_memalign came in POSIX.1-2001 / SUSv3
|
||||
# M_PI is an XSI extension since POSIX.1-2001 / SUSv3, came in XPG1 (1985)
|
||||
add_compile_definitions(_XOPEN_SOURCE=600)
|
||||
|
||||
# Somehow in OpenBSD whenever POSIX conformance is specified
|
||||
# some string functions rely on locale_t availability,
|
||||
# which was introduced in POSIX.1-2008, forcing us to go higher
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
|
||||
remove_definitions(-D_XOPEN_SOURCE=600)
|
||||
add_compile_definitions(_XOPEN_SOURCE=700)
|
||||
endif()
|
||||
|
||||
# Data types, macros and functions related to controlling CPU affinity and
|
||||
# some memory allocation are available on Linux through GNU extensions in libc
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
add_compile_definitions(_GNU_SOURCE)
|
||||
endif()
|
||||
|
||||
# RLIMIT_MEMLOCK came in BSD, is not specified in POSIX.1,
|
||||
# and on macOS its availability depends on enabling Darwin extensions
|
||||
# similarly on DragonFly, enabling BSD extensions is necessary
|
||||
if (
|
||||
CMAKE_SYSTEM_NAME MATCHES "Darwin" OR
|
||||
CMAKE_SYSTEM_NAME MATCHES "iOS" OR
|
||||
CMAKE_SYSTEM_NAME MATCHES "tvOS" OR
|
||||
CMAKE_SYSTEM_NAME MATCHES "DragonFly"
|
||||
)
|
||||
add_compile_definitions(_DARWIN_C_SOURCE)
|
||||
endif()
|
||||
|
||||
# alloca is a non-standard interface that is not visible on BSDs when
|
||||
# POSIX conformance is specified, but not all of them provide a clean way
|
||||
# to enable it in such cases
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
add_compile_definitions(__BSD_VISIBLE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "NetBSD")
|
||||
add_compile_definitions(_NETBSD_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
|
||||
add_compile_definitions(_BSD_SOURCE)
|
||||
endif()
|
||||
|
||||
function(include_ggml DIRECTORY SUFFIX WITH_LLAMA)
|
||||
message(STATUS "Configuring ggml implementation target llama${SUFFIX} in ${CMAKE_CURRENT_SOURCE_DIR}/${DIRECTORY}")
|
||||
|
||||
@@ -264,46 +572,41 @@ function(include_ggml DIRECTORY SUFFIX WITH_LLAMA)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(GGML_SOURCES_QUANT_K )
|
||||
set(GGML_METAL_SOURCES )
|
||||
if (LLAMA_K_QUANTS)
|
||||
set(GGML_SOURCES_QUANT_K
|
||||
${DIRECTORY}/k_quants.h
|
||||
${DIRECTORY}/k_quants.c)
|
||||
set(GGML_METAL_SOURCES)
|
||||
if (LLAMA_METAL)
|
||||
find_library(FOUNDATION_LIBRARY Foundation REQUIRED)
|
||||
find_library(METAL_FRAMEWORK Metal REQUIRED)
|
||||
find_library(METALKIT_FRAMEWORK MetalKit REQUIRED)
|
||||
find_library(METALPERFORMANCE_FRAMEWORK MetalPerformanceShaders REQUIRED)
|
||||
|
||||
if (LLAMA_METAL)
|
||||
find_library(FOUNDATION_LIBRARY Foundation REQUIRED)
|
||||
find_library(METAL_FRAMEWORK Metal REQUIRED)
|
||||
find_library(METALKIT_FRAMEWORK MetalKit REQUIRED)
|
||||
find_library(METALPERFORMANCE_FRAMEWORK MetalPerformanceShaders REQUIRED)
|
||||
set(GGML_METAL_SOURCES ${DIRECTORY}/ggml-metal.m ${DIRECTORY}/ggml-metal.h)
|
||||
# get full path to the file
|
||||
#add_compile_definitions(GGML_METAL_DIR_KERNELS="${CMAKE_CURRENT_SOURCE_DIR}/")
|
||||
|
||||
set(GGML_METAL_SOURCES ${DIRECTORY}/ggml-metal.m ${DIRECTORY}/ggml-metal.h)
|
||||
# get full path to the file
|
||||
#add_compile_definitions(GGML_METAL_DIR_KERNELS="${CMAKE_CURRENT_SOURCE_DIR}/")
|
||||
# copy ggml-metal.metal to bin directory
|
||||
configure_file(${DIRECTORY}/ggml-metal.metal bin/ggml-metal.metal COPYONLY)
|
||||
|
||||
# copy ggml-metal.metal to bin directory
|
||||
configure_file(${DIRECTORY}/ggml-metal.metal bin/ggml-metal.metal COPYONLY)
|
||||
|
||||
set(LLAMA_EXTRA_LIBS ${LLAMA_EXTRA_LIBS}
|
||||
${FOUNDATION_LIBRARY}
|
||||
${METAL_FRAMEWORK}
|
||||
${METALKIT_FRAMEWORK}
|
||||
${METALPERFORMANCE_FRAMEWORK}
|
||||
)
|
||||
endif()
|
||||
set(LLAMA_EXTRA_LIBS ${LLAMA_EXTRA_LIBS}
|
||||
${FOUNDATION_LIBRARY}
|
||||
${METAL_FRAMEWORK}
|
||||
${METALKIT_FRAMEWORK}
|
||||
${METALPERFORMANCE_FRAMEWORK}
|
||||
)
|
||||
endif()
|
||||
|
||||
add_library(ggml${SUFFIX} OBJECT
|
||||
${DIRECTORY}/ggml.c
|
||||
${DIRECTORY}/ggml.h
|
||||
${GGML_SOURCES_QUANT_K}
|
||||
${DIRECTORY}/ggml-alloc.c
|
||||
${DIRECTORY}/ggml-alloc.h
|
||||
${DIRECTORY}/ggml-backend.c
|
||||
${DIRECTORY}/ggml-backend.h
|
||||
${DIRECTORY}/ggml-quants.h
|
||||
${DIRECTORY}/ggml-quants.c
|
||||
${GGML_SOURCES_CUDA}
|
||||
${GGML_METAL_SOURCES}
|
||||
${GGML_OPENCL_SOURCES})
|
||||
|
||||
if (LLAMA_K_QUANTS)
|
||||
target_compile_definitions(ggml${SUFFIX} PUBLIC GGML_USE_K_QUANTS)
|
||||
endif()
|
||||
${GGML_OPENCL_SOURCES}
|
||||
${GGML_SOURCES_KOMPUTE})
|
||||
|
||||
if (LLAMA_METAL AND GGML_METAL_SOURCES)
|
||||
target_compile_definitions(ggml${SUFFIX} PUBLIC GGML_USE_METAL GGML_METAL_NDEBUG)
|
||||
@@ -317,15 +620,14 @@ function(include_ggml DIRECTORY SUFFIX WITH_LLAMA)
|
||||
|
||||
if (WITH_LLAMA)
|
||||
# Backwards compatibility with old llama.cpp versions
|
||||
set(LLAMA_UTIL_SOURCE_FILE llama-util.h)
|
||||
# set(LLAMA_UTIL_SOURCE_FILE llama-util.h)
|
||||
if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${DIRECTORY}/${LLAMA_UTIL_SOURCE_FILE})
|
||||
set(LLAMA_UTIL_SOURCE_FILE llama_util.h)
|
||||
endif()
|
||||
|
||||
add_library(llama${SUFFIX} STATIC
|
||||
${DIRECTORY}/llama.cpp
|
||||
${DIRECTORY}/llama.h
|
||||
${DIRECTORY}/${LLAMA_UTIL_SOURCE_FILE})
|
||||
${DIRECTORY}/llama.h)
|
||||
|
||||
if (LLAMA_METAL AND GGML_METAL_SOURCES)
|
||||
target_compile_definitions(llama${SUFFIX} PUBLIC GGML_USE_METAL GGML_METAL_NDEBUG)
|
||||
|
||||
@@ -6,65 +6,85 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <initializer_list>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#if defined(_WIN32) && defined(_MSC_VER)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <llama.h>
|
||||
#include <ggml.h>
|
||||
#ifdef GGML_USE_KOMPUTE
|
||||
#include <ggml-kompute.h>
|
||||
#endif
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace {
|
||||
const char *modelType_ = "LLaMA";
|
||||
// Maximum supported GGUF version
|
||||
static constexpr int GGUF_VER_MAX = 3;
|
||||
|
||||
static const char * const modelType_ = "LLaMA";
|
||||
|
||||
static const std::vector<const char *> KNOWN_ARCHES {
|
||||
"baichuan", "bert", "bloom", "codeshell", "falcon", "gemma", "gpt2", "llama", "mpt", "nomic-bert", "orion",
|
||||
"persimmon", "phi2", "plamo", "qwen", "qwen2", "refact", "stablelm", "starcoder"
|
||||
};
|
||||
|
||||
static const std::vector<const char *> EMBEDDING_ARCHES {
|
||||
"bert", "nomic-bert"
|
||||
};
|
||||
|
||||
static bool is_embedding_arch(const std::string &arch) {
|
||||
return std::find(EMBEDDING_ARCHES.begin(), EMBEDDING_ARCHES.end(), arch) < EMBEDDING_ARCHES.end();
|
||||
}
|
||||
|
||||
static bool llama_verbose() {
|
||||
const char* var = getenv("GPT4ALL_VERBOSE_LLAMACPP");
|
||||
return var && *var;
|
||||
}
|
||||
|
||||
static void llama_log_callback(enum ggml_log_level level, const char *text, void *userdata) {
|
||||
(void)userdata;
|
||||
if (llama_verbose() || level <= GGML_LOG_LEVEL_ERROR) {
|
||||
fputs(text, stderr);
|
||||
}
|
||||
}
|
||||
|
||||
struct gpt_params {
|
||||
int32_t seed = -1; // RNG seed
|
||||
int32_t n_keep = 0; // number of tokens to keep from initial prompt
|
||||
#if LLAMA_DATE <= 230511
|
||||
int32_t n_parts = -1; // amount of model parts (-1 = determine from model dimensions)
|
||||
#endif
|
||||
|
||||
#if LLAMA_DATE >= 230519
|
||||
// sampling parameters
|
||||
float tfs_z = 1.0f; // 1.0 = disabled
|
||||
float typical_p = 1.0f; // 1.0 = disabled
|
||||
#endif
|
||||
|
||||
std::string prompt = "";
|
||||
|
||||
bool memory_f16 = true; // use f16 instead of f32 for memory kv
|
||||
enum ggml_type kv_type = GGML_TYPE_F16; // use f16 instead of f32 for memory kv
|
||||
|
||||
bool use_mmap = true; // use mmap for faster loads
|
||||
bool use_mlock = false; // use mlock to keep model in memory
|
||||
};
|
||||
|
||||
#if LLAMA_DATE >= 230519
|
||||
static int llama_sample_top_p_top_k(
|
||||
llama_context *ctx,
|
||||
const llama_token *last_n_tokens_data,
|
||||
int last_n_tokens_size,
|
||||
int top_k,
|
||||
float top_p,
|
||||
float min_p,
|
||||
float temp,
|
||||
float repeat_penalty) {
|
||||
auto logits = llama_get_logits(ctx);
|
||||
auto n_vocab = llama_n_vocab(ctx);
|
||||
float repeat_penalty,
|
||||
int32_t pos) {
|
||||
auto logits = llama_get_logits_ith(ctx, pos);
|
||||
auto n_vocab = llama_n_vocab(llama_get_model(ctx));
|
||||
// Populate initial list of all candidates
|
||||
std::vector<llama_token_data> candidates;
|
||||
candidates.reserve(n_vocab);
|
||||
@@ -73,23 +93,79 @@ static int llama_sample_top_p_top_k(
|
||||
}
|
||||
llama_token_data_array candidates_p = {candidates.data(), candidates.size(), false};
|
||||
// Sample repeat penalty
|
||||
llama_sample_repetition_penalty(nullptr, &candidates_p, last_n_tokens_data, last_n_tokens_size, repeat_penalty);
|
||||
llama_sample_repetition_penalties(nullptr, &candidates_p, last_n_tokens_data, last_n_tokens_size, repeat_penalty, 0.0f, 0.0f);
|
||||
// Temperature sampling
|
||||
llama_sample_top_k(ctx, &candidates_p, top_k, 1);
|
||||
llama_sample_tail_free(ctx, &candidates_p, 1.0f, 1);
|
||||
llama_sample_typical(ctx, &candidates_p, 1.0f, 1);
|
||||
llama_sample_top_p(ctx, &candidates_p, top_p, 1);
|
||||
llama_sample_temperature(ctx, &candidates_p, temp);
|
||||
llama_sample_min_p(ctx, &candidates_p, min_p, 1);
|
||||
llama_sample_temp(ctx, &candidates_p, temp);
|
||||
return llama_sample_token(ctx, &candidates_p);
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string get_arch_name(gguf_context *ctx_gguf) {
|
||||
std::string arch_name;
|
||||
const int kid = gguf_find_key(ctx_gguf, "general.architecture");
|
||||
enum gguf_type ktype = gguf_get_kv_type(ctx_gguf, kid);
|
||||
if (ktype != (GGUF_TYPE_STRING)) {
|
||||
throw std::runtime_error("ERROR: Can't get general architecture from gguf file.");
|
||||
}
|
||||
return gguf_get_val_str(ctx_gguf, kid);
|
||||
}
|
||||
|
||||
static gguf_context *load_gguf(const char *fname) {
|
||||
struct gguf_init_params params = {
|
||||
/*.no_alloc = */ true,
|
||||
/*.ctx = */ nullptr,
|
||||
};
|
||||
gguf_context *ctx = gguf_init_from_file(fname, params);
|
||||
if (!ctx) {
|
||||
std::cerr << __func__ << ": gguf_init_from_file failed\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int gguf_ver = gguf_get_version(ctx);
|
||||
if (gguf_ver > GGUF_VER_MAX) {
|
||||
std::cerr << __func__ << ": unsupported gguf version: " << gguf_ver << "\n";
|
||||
gguf_free(ctx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static int32_t get_arch_key_u32(std::string const &modelPath, std::string const &archKey) {
|
||||
auto * ctx = load_gguf(modelPath.c_str());
|
||||
if (!ctx)
|
||||
return -1;
|
||||
std::string arch = get_arch_name(ctx);
|
||||
|
||||
int32_t value = -1;
|
||||
if (ctx) {
|
||||
auto key = arch + "." + archKey;
|
||||
int keyidx = gguf_find_key(ctx, key.c_str());
|
||||
if (keyidx != -1) {
|
||||
value = gguf_get_val_u32(ctx, keyidx);
|
||||
} else {
|
||||
std::cerr << __func__ << ": " << key << "not found in " << modelPath << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
gguf_free(ctx);
|
||||
return value;
|
||||
}
|
||||
|
||||
struct LLamaPrivate {
|
||||
const std::string modelPath;
|
||||
bool modelLoaded;
|
||||
int device = -1;
|
||||
llama_model *model = nullptr;
|
||||
llama_context *ctx = nullptr;
|
||||
llama_context_params params;
|
||||
llama_model_params model_params;
|
||||
llama_context_params ctx_params;
|
||||
int64_t n_threads = 0;
|
||||
std::vector<LLModel::Token> end_tokens;
|
||||
};
|
||||
|
||||
LLamaModel::LLamaModel()
|
||||
@@ -108,7 +184,9 @@ struct llama_file_hparams {
|
||||
enum llama_ftype ftype = LLAMA_FTYPE_MOSTLY_F16;
|
||||
};
|
||||
|
||||
size_t LLamaModel::requiredMem(const std::string &modelPath) {
|
||||
size_t LLamaModel::requiredMem(const std::string &modelPath, int n_ctx, int ngl) {
|
||||
// TODO(cebtenzzre): update to GGUF
|
||||
(void)ngl; // FIXME(cetenzzre): use this value
|
||||
auto fin = std::ifstream(modelPath, std::ios::binary);
|
||||
fin.seekg(0, std::ios_base::end);
|
||||
size_t filesize = fin.tellg();
|
||||
@@ -125,51 +203,181 @@ size_t LLamaModel::requiredMem(const std::string &modelPath) {
|
||||
fin.read(reinterpret_cast<char*>(&hparams.n_layer), sizeof(hparams.n_layer));
|
||||
fin.read(reinterpret_cast<char*>(&hparams.n_rot), sizeof(hparams.n_rot));
|
||||
fin.read(reinterpret_cast<char*>(&hparams.ftype), sizeof(hparams.ftype));
|
||||
const size_t n_ctx = 2048;
|
||||
const size_t kvcache_element_size = 2; // fp16
|
||||
const size_t est_kvcache_size = hparams.n_embd * hparams.n_layer * 2u * n_ctx * kvcache_element_size;
|
||||
return filesize + est_kvcache_size;
|
||||
}
|
||||
|
||||
bool LLamaModel::loadModel(const std::string &modelPath)
|
||||
{
|
||||
// load the model
|
||||
d_ptr->params = llama_context_default_params();
|
||||
|
||||
gpt_params params;
|
||||
d_ptr->params.n_ctx = 2048;
|
||||
d_ptr->params.seed = params.seed;
|
||||
d_ptr->params.f16_kv = params.memory_f16;
|
||||
d_ptr->params.use_mmap = params.use_mmap;
|
||||
#if defined (__APPLE__)
|
||||
d_ptr->params.use_mlock = true;
|
||||
#else
|
||||
d_ptr->params.use_mlock = params.use_mlock;
|
||||
#endif
|
||||
#if LLAMA_DATE <= 230511
|
||||
d_ptr->params.n_parts = params.n_parts;
|
||||
#endif
|
||||
#ifdef GGML_USE_METAL
|
||||
std::cerr << "llama.cpp: using Metal" << std::endl;
|
||||
// metal always runs the whole model if n_gpu_layers is not 0, at least
|
||||
// currently
|
||||
d_ptr->params.n_gpu_layers = 1;
|
||||
#endif
|
||||
|
||||
d_ptr->ctx = llama_init_from_file(modelPath.c_str(), d_ptr->params);
|
||||
if (!d_ptr->ctx) {
|
||||
std::cerr << "LLAMA ERROR: failed to load model from " << modelPath << std::endl;
|
||||
bool LLamaModel::isModelBlacklisted(const std::string &modelPath) const {
|
||||
auto * ctx = load_gguf(modelPath.c_str());
|
||||
if (!ctx) {
|
||||
std::cerr << __func__ << ": failed to load " << modelPath << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto get_key = [ctx, &modelPath](const char *name) {
|
||||
int keyidx = gguf_find_key(ctx, name);
|
||||
if (keyidx == -1) {
|
||||
throw std::logic_error(name + " not found in "s + modelPath);
|
||||
}
|
||||
return keyidx;
|
||||
};
|
||||
|
||||
bool res = false;
|
||||
try {
|
||||
std::string name(gguf_get_val_str(ctx, get_key("general.name")));
|
||||
int token_idx = get_key("tokenizer.ggml.tokens");
|
||||
int n_vocab = gguf_get_arr_n(ctx, token_idx);
|
||||
|
||||
// check for known bad models
|
||||
if (name == "open-orca_mistral-7b-openorca"
|
||||
&& n_vocab == 32002
|
||||
&& gguf_get_arr_str(ctx, token_idx, 32000) == "<dummy32000>"s // should be <|im_end|>
|
||||
) {
|
||||
res = true;
|
||||
}
|
||||
} catch (const std::logic_error &e) {
|
||||
std::cerr << __func__ << ": " << e.what() << "\n";
|
||||
}
|
||||
|
||||
gguf_free(ctx);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool LLamaModel::isEmbeddingModel(const std::string &modelPath) const {
|
||||
auto *ctx_gguf = load_gguf(modelPath.c_str());
|
||||
if (!ctx_gguf) {
|
||||
std::cerr << __func__ << ": failed to load GGUF from " << modelPath << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string arch = get_arch_name(ctx_gguf);
|
||||
gguf_free(ctx_gguf);
|
||||
return is_embedding_arch(arch);
|
||||
}
|
||||
|
||||
bool LLamaModel::loadModel(const std::string &modelPath, int n_ctx, int ngl)
|
||||
{
|
||||
d_ptr->modelLoaded = false;
|
||||
|
||||
// clean up after previous loadModel()
|
||||
if (d_ptr->model) {
|
||||
llama_free_model(d_ptr->model);
|
||||
d_ptr->model = nullptr;
|
||||
}
|
||||
if (d_ptr->ctx) {
|
||||
llama_free(d_ptr->ctx);
|
||||
d_ptr->ctx = nullptr;
|
||||
}
|
||||
|
||||
if (n_ctx < 8) {
|
||||
std::cerr << "warning: minimum context size is 8, using minimum size.\n";
|
||||
n_ctx = 8;
|
||||
}
|
||||
|
||||
// -- load the model --
|
||||
|
||||
gpt_params params;
|
||||
|
||||
d_ptr->model_params = llama_model_default_params();
|
||||
|
||||
d_ptr->model_params.use_mmap = params.use_mmap;
|
||||
#if defined (__APPLE__)
|
||||
d_ptr->model_params.use_mlock = true;
|
||||
#else
|
||||
d_ptr->model_params.use_mlock = params.use_mlock;
|
||||
#endif
|
||||
|
||||
d_ptr->model_params.progress_callback = &LLModel::staticProgressCallback;
|
||||
d_ptr->model_params.progress_callback_user_data = this;
|
||||
|
||||
#ifdef GGML_USE_KOMPUTE
|
||||
if (d_ptr->device != -1) {
|
||||
d_ptr->model_params.main_gpu = d_ptr->device;
|
||||
d_ptr->model_params.n_gpu_layers = ngl;
|
||||
}
|
||||
#elif defined(GGML_USE_METAL)
|
||||
(void)ngl;
|
||||
|
||||
if (llama_verbose()) {
|
||||
std::cerr << "llama.cpp: using Metal" << std::endl;
|
||||
}
|
||||
|
||||
// always fully offload on Metal
|
||||
// TODO(cebtenzzre): use this parameter to allow using more than 53% of system RAM to load a model
|
||||
d_ptr->model_params.n_gpu_layers = 100;
|
||||
#else
|
||||
(void)ngl;
|
||||
#endif
|
||||
|
||||
d_ptr->model = llama_load_model_from_file_gpt4all(modelPath.c_str(), &d_ptr->model_params);
|
||||
if (!d_ptr->model) {
|
||||
fflush(stdout);
|
||||
d_ptr->device = -1;
|
||||
std::cerr << "LLAMA ERROR: failed to load model from " << modelPath << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// -- initialize the context --
|
||||
|
||||
d_ptr->ctx_params = llama_context_default_params();
|
||||
|
||||
bool isEmbedding = is_embedding_arch(llama_model_arch(d_ptr->model));
|
||||
const int n_ctx_train = llama_n_ctx_train(d_ptr->model);
|
||||
if (isEmbedding) {
|
||||
d_ptr->ctx_params.n_batch = n_ctx_train;
|
||||
} else {
|
||||
if (n_ctx > n_ctx_train) {
|
||||
std::cerr << "warning: model was trained on only " << n_ctx_train << " context tokens ("
|
||||
<< n_ctx << " specified)\n";
|
||||
}
|
||||
}
|
||||
|
||||
d_ptr->ctx_params.n_ctx = n_ctx;
|
||||
d_ptr->ctx_params.seed = params.seed;
|
||||
d_ptr->ctx_params.type_k = params.kv_type;
|
||||
d_ptr->ctx_params.type_v = params.kv_type;
|
||||
|
||||
// The new batch API provides space for n_vocab*n_tokens logits. Tell llama.cpp early
|
||||
// that we want this many logits so the state serializes consistently.
|
||||
d_ptr->ctx_params.logits_all = true;
|
||||
|
||||
d_ptr->n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency());
|
||||
d_ptr->ctx_params.n_threads = d_ptr->n_threads;
|
||||
d_ptr->ctx_params.n_threads_batch = d_ptr->n_threads;
|
||||
|
||||
if (isEmbedding)
|
||||
d_ptr->ctx_params.embeddings = true;
|
||||
|
||||
d_ptr->ctx = llama_new_context_with_model(d_ptr->model, d_ptr->ctx_params);
|
||||
if (!d_ptr->ctx) {
|
||||
fflush(stdout);
|
||||
std::cerr << "LLAMA ERROR: failed to init context for model " << modelPath << std::endl;
|
||||
llama_free_model(d_ptr->model);
|
||||
d_ptr->model = nullptr;
|
||||
d_ptr->device = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
d_ptr->end_tokens = {llama_token_eos(d_ptr->model)};
|
||||
|
||||
#ifdef GGML_USE_KOMPUTE
|
||||
if (usingGPUDevice() && ggml_vk_has_device()) {
|
||||
std::cerr << "llama.cpp: using Vulkan on " << ggml_vk_current_device().name << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_supportsEmbedding = isEmbedding;
|
||||
m_supportsCompletion = !isEmbedding;
|
||||
|
||||
fflush(stdout);
|
||||
d_ptr->modelLoaded = true;
|
||||
fflush(stderr);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LLamaModel::setThreadCount(int32_t n_threads) {
|
||||
d_ptr->n_threads = n_threads;
|
||||
llama_set_n_threads(d_ptr->ctx, n_threads, n_threads);
|
||||
}
|
||||
|
||||
int32_t LLamaModel::threadCount() const {
|
||||
@@ -178,9 +386,10 @@ int32_t LLamaModel::threadCount() const {
|
||||
|
||||
LLamaModel::~LLamaModel()
|
||||
{
|
||||
if(d_ptr->ctx) {
|
||||
if (d_ptr->ctx) {
|
||||
llama_free(d_ptr->ctx);
|
||||
}
|
||||
llama_free_model(d_ptr->model);
|
||||
}
|
||||
|
||||
bool LLamaModel::isModelLoaded() const
|
||||
@@ -204,18 +413,20 @@ size_t LLamaModel::restoreState(const uint8_t *src)
|
||||
return llama_set_state_data(d_ptr->ctx, const_cast<uint8_t*>(src));
|
||||
}
|
||||
|
||||
std::vector<LLModel::Token> LLamaModel::tokenize(PromptContext &ctx, const std::string &str) const
|
||||
std::vector<LLModel::Token> LLamaModel::tokenize(PromptContext &ctx, const std::string &str, bool special) const
|
||||
{
|
||||
const bool useBOS = ctx.n_past == 0 && (ctx.tokens.empty() || ctx.tokens.front() != llama_token_bos());
|
||||
std::vector<LLModel::Token> fres(str.size()+4);
|
||||
auto fres_len = llama_tokenize(d_ptr->ctx, str.c_str(), fres.data(), fres.size(), useBOS);
|
||||
const bool wantBOS = ctx.n_past == 0 && ctx.tokens.empty();
|
||||
const bool useBOS = wantBOS && shouldAddBOS();
|
||||
auto strCat = wantBOS && !special ? " " + str : str; // insert leading space ourselves, llama.cpp fork doesn't anymore
|
||||
std::vector<LLModel::Token> fres(strCat.size()+4);
|
||||
auto fres_len = llama_tokenize(d_ptr->model, strCat.c_str(), strCat.length(), fres.data(), fres.size(), useBOS, special);
|
||||
fres.resize(fres_len);
|
||||
return fres;
|
||||
}
|
||||
|
||||
std::string LLamaModel::tokenToString(Token id) const
|
||||
{
|
||||
return llama_token_to_str(d_ptr->ctx, id);
|
||||
return llama_token_to_piece(d_ptr->ctx, id);
|
||||
}
|
||||
|
||||
LLModel::Token LLamaModel::sampleToken(PromptContext &promptCtx) const
|
||||
@@ -223,22 +434,33 @@ LLModel::Token LLamaModel::sampleToken(PromptContext &promptCtx) const
|
||||
const size_t n_prev_toks = std::min((size_t) promptCtx.repeat_last_n, promptCtx.tokens.size());
|
||||
return llama_sample_top_p_top_k(d_ptr->ctx,
|
||||
promptCtx.tokens.data() + promptCtx.tokens.size() - n_prev_toks,
|
||||
n_prev_toks, promptCtx.top_k, promptCtx.top_p, promptCtx.temp,
|
||||
promptCtx.repeat_penalty);
|
||||
n_prev_toks, promptCtx.top_k, promptCtx.top_p, promptCtx.min_p, promptCtx.temp,
|
||||
promptCtx.repeat_penalty, promptCtx.n_last_batch_tokens - 1);
|
||||
}
|
||||
|
||||
bool LLamaModel::evalTokens(PromptContext &ctx, const std::vector<int32_t> &tokens) const
|
||||
{
|
||||
// When we recalculate context we could have erased the original BOS token... we need to replace it
|
||||
const bool useBOS = ctx.n_past == 0 && (ctx.tokens.empty() || ctx.tokens.front() != llama_token_bos());
|
||||
if (useBOS) {
|
||||
std::vector<int32_t> myTokens;
|
||||
myTokens.push_back(llama_token_bos());
|
||||
myTokens.insert(myTokens.end(), tokens.begin(), tokens.end());
|
||||
ctx.n_past += 1;
|
||||
return llama_eval(d_ptr->ctx, myTokens.data(), myTokens.size(), ctx.n_past, d_ptr->n_threads) == 0;
|
||||
} else
|
||||
return llama_eval(d_ptr->ctx, tokens.data(), tokens.size(), ctx.n_past, d_ptr->n_threads) == 0;
|
||||
llama_kv_cache_seq_rm(d_ptr->ctx, 0, ctx.n_past, -1);
|
||||
|
||||
llama_batch batch = llama_batch_init(tokens.size(), 0, 1);
|
||||
|
||||
batch.n_tokens = tokens.size();
|
||||
ctx.n_last_batch_tokens = tokens.size();
|
||||
|
||||
for (int32_t i = 0; i < batch.n_tokens; i++) {
|
||||
batch.token [i] = tokens[i];
|
||||
batch.pos [i] = ctx.n_past + i;
|
||||
batch.n_seq_id[i] = 1;
|
||||
batch.seq_id [i][0] = 0;
|
||||
batch.logits [i] = false;
|
||||
}
|
||||
|
||||
// llama_decode will output logits only for the last token of the prompt
|
||||
batch.logits[batch.n_tokens - 1] = true;
|
||||
|
||||
int res = llama_decode(d_ptr->ctx, batch);
|
||||
llama_batch_free(batch);
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
int32_t LLamaModel::contextLength() const
|
||||
@@ -248,8 +470,429 @@ int32_t LLamaModel::contextLength() const
|
||||
|
||||
const std::vector<LLModel::Token> &LLamaModel::endTokens() const
|
||||
{
|
||||
static const std::vector<LLModel::Token> fres = {llama_token_eos()};
|
||||
return fres;
|
||||
return d_ptr->end_tokens;
|
||||
}
|
||||
|
||||
bool LLamaModel::shouldAddBOS() const
|
||||
{
|
||||
int add_bos = llama_add_bos_token(d_ptr->model);
|
||||
if (add_bos != -1) { return add_bos; }
|
||||
auto vocab_type = llama_vocab_type(d_ptr->model);
|
||||
return vocab_type == LLAMA_VOCAB_TYPE_SPM || vocab_type == LLAMA_VOCAB_TYPE_WPM;
|
||||
}
|
||||
|
||||
int32_t LLamaModel::maxContextLength(std::string const &modelPath) const
|
||||
{
|
||||
return get_arch_key_u32(modelPath, "context_length");
|
||||
}
|
||||
|
||||
int32_t LLamaModel::layerCount(std::string const &modelPath) const
|
||||
{
|
||||
return get_arch_key_u32(modelPath, "block_count");
|
||||
}
|
||||
|
||||
std::vector<LLModel::GPUDevice> LLamaModel::availableGPUDevices(size_t memoryRequired) const
|
||||
{
|
||||
#ifdef GGML_USE_KOMPUTE
|
||||
size_t count = 0;
|
||||
auto * vkDevices = ggml_vk_available_devices(memoryRequired, &count);
|
||||
|
||||
if (vkDevices) {
|
||||
std::vector<LLModel::GPUDevice> devices;
|
||||
devices.reserve(count);
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
auto & dev = vkDevices[i];
|
||||
devices.emplace_back(
|
||||
/* index = */ dev.index,
|
||||
/* type = */ dev.type,
|
||||
/* heapSize = */ dev.heapSize,
|
||||
/* name = */ dev.name,
|
||||
/* vendor = */ dev.vendor
|
||||
);
|
||||
ggml_vk_device_destroy(&dev);
|
||||
}
|
||||
|
||||
free(vkDevices);
|
||||
return devices;
|
||||
}
|
||||
#else
|
||||
(void)memoryRequired;
|
||||
std::cerr << __func__ << ": built without Kompute\n";
|
||||
#endif
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool LLamaModel::initializeGPUDevice(size_t memoryRequired, const std::string &name) const
|
||||
{
|
||||
#if defined(GGML_USE_KOMPUTE)
|
||||
ggml_vk_device device;
|
||||
bool ok = ggml_vk_get_device(&device, memoryRequired, name.c_str());
|
||||
if (ok) {
|
||||
d_ptr->device = device.index;
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
(void)memoryRequired;
|
||||
(void)name;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LLamaModel::initializeGPUDevice(int device, std::string *unavail_reason) const
|
||||
{
|
||||
#if defined(GGML_USE_KOMPUTE)
|
||||
(void)unavail_reason;
|
||||
d_ptr->device = device;
|
||||
return true;
|
||||
#else
|
||||
(void)device;
|
||||
if (unavail_reason) {
|
||||
*unavail_reason = "built without Kompute";
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool LLamaModel::hasGPUDevice()
|
||||
{
|
||||
#if defined(GGML_USE_KOMPUTE)
|
||||
return d_ptr->device != -1;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool LLamaModel::usingGPUDevice()
|
||||
{
|
||||
#if defined(GGML_USE_KOMPUTE)
|
||||
return hasGPUDevice() && d_ptr->model_params.n_gpu_layers > 0;
|
||||
#elif defined(GGML_USE_METAL)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void llama_batch_add(
|
||||
struct llama_batch & batch,
|
||||
llama_token id,
|
||||
llama_pos pos,
|
||||
const std::vector<llama_seq_id> & seq_ids,
|
||||
bool logits) {
|
||||
batch.token [batch.n_tokens] = id;
|
||||
batch.pos [batch.n_tokens] = pos;
|
||||
batch.n_seq_id[batch.n_tokens] = seq_ids.size();
|
||||
for (size_t i = 0; i < seq_ids.size(); ++i) {
|
||||
batch.seq_id[batch.n_tokens][i] = seq_ids[i];
|
||||
}
|
||||
batch.logits [batch.n_tokens] = logits;
|
||||
|
||||
batch.n_tokens++;
|
||||
}
|
||||
|
||||
static void batch_add_seq(llama_batch &batch, const std::vector<LLModel::Token> &tokens, int seq_id) {
|
||||
for (unsigned i = 0; i < tokens.size(); i++) {
|
||||
llama_batch_add(batch, tokens[i], i, { seq_id }, i == tokens.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t LLamaModel::embeddingSize() const {
|
||||
return llama_n_embd(d_ptr->model);
|
||||
}
|
||||
|
||||
struct EmbModelSpec {
|
||||
const char *docPrefix;
|
||||
const char *queryPrefix;
|
||||
std::vector<const char *> otherPrefixes = {};
|
||||
bool matryoshkaCapable = false;
|
||||
const char *recommendedDims = nullptr;
|
||||
};
|
||||
|
||||
struct EmbModelGroup {
|
||||
EmbModelSpec spec;
|
||||
std::vector<const char *> names;
|
||||
};
|
||||
|
||||
static const EmbModelSpec NOPREFIX_SPEC {"", ""};
|
||||
static const EmbModelSpec NOMIC_SPEC {"search_document", "search_query", {"clustering", "classification"}};
|
||||
static const EmbModelSpec E5_SPEC {"passage", "query"};
|
||||
|
||||
static const EmbModelSpec NOMIC_1_5_SPEC {
|
||||
"search_document", "search_query", {"clustering", "classification"}, true, "[768, 512, 384, 256, 128]",
|
||||
};
|
||||
static const EmbModelSpec LLM_EMBEDDER_SPEC {
|
||||
"Represent this document for retrieval",
|
||||
"Represent this query for retrieving relevant documents",
|
||||
};
|
||||
static const EmbModelSpec BGE_SPEC {
|
||||
"", "Represent this sentence for searching relevant passages",
|
||||
};
|
||||
static const EmbModelSpec E5_MISTRAL_SPEC {
|
||||
"", "Instruct: Given a query, retrieve relevant passages that answer the query\nQuery",
|
||||
};
|
||||
|
||||
static const EmbModelGroup EMBEDDING_MODEL_SPECS[] {
|
||||
{NOPREFIX_SPEC, {"all-MiniLM-L6-v1", "all-MiniLM-L12-v1", "all-MiniLM-L6-v2", "all-MiniLM-L12-v2"}},
|
||||
{NOMIC_SPEC, {"nomic-embed-text-v1", "nomic-embed-text-v1-ablated", "nomic-embed-text-v1-unsupervised"}},
|
||||
{NOMIC_1_5_SPEC, {"nomic-embed-text-v1.5"}},
|
||||
{LLM_EMBEDDER_SPEC, {"llm-embedder"}},
|
||||
{BGE_SPEC, {"bge-small-en", "bge-base-en", "bge-large-en",
|
||||
"bge-small-en-v1.5", "bge-base-en-v1.5", "bge-large-en-v1.5"}},
|
||||
// NOTE: E5 Mistral is not yet implemented in llama.cpp, so it's not in EMBEDDING_ARCHES
|
||||
{E5_SPEC, {"e5-small", "e5-base", "e5-large",
|
||||
"e5-small-unsupervised", "e5-base-unsupervised", "e5-large-unsupervised",
|
||||
"e5-small-v2", "e5-base-v2", "e5-large-v2"}},
|
||||
{E5_MISTRAL_SPEC, {"e5-mistral-7b-instruct",
|
||||
"multilingual-e5-small", "multilingual-e5-base", "multilingual-e5-large",
|
||||
"multilingual-e5-large-instruct"}},
|
||||
};
|
||||
|
||||
static const EmbModelSpec *getEmbedSpec(const std::string &modelName) {
|
||||
static const auto &specs = EMBEDDING_MODEL_SPECS;
|
||||
auto it = std::find_if(specs, std::end(specs),
|
||||
[&modelName](auto &spec) {
|
||||
auto &names = spec.names;
|
||||
return std::find(names.begin(), names.end(), modelName) < names.end();
|
||||
}
|
||||
);
|
||||
return it < std::end(specs) ? &it->spec : nullptr;
|
||||
}
|
||||
|
||||
void LLamaModel::embed(
|
||||
const std::vector<std::string> &texts, float *embeddings, bool isRetrieval, int dimensionality, size_t *tokenCount,
|
||||
bool doMean, bool atlas
|
||||
) {
|
||||
const EmbModelSpec *spec;
|
||||
std::optional<std::string> prefix;
|
||||
if (d_ptr->model && (spec = getEmbedSpec(llama_model_name(d_ptr->model))))
|
||||
prefix = isRetrieval ? spec->queryPrefix : spec->docPrefix;
|
||||
|
||||
embed(texts, embeddings, prefix, dimensionality, tokenCount, doMean, atlas);
|
||||
}
|
||||
|
||||
void LLamaModel::embed(
|
||||
const std::vector<std::string> &texts, float *embeddings, std::optional<std::string> prefix, int dimensionality,
|
||||
size_t *tokenCount, bool doMean, bool atlas
|
||||
) {
|
||||
if (!d_ptr->model)
|
||||
throw std::logic_error("no model is loaded");
|
||||
|
||||
const char *modelName = llama_model_name(d_ptr->model);
|
||||
if (!m_supportsEmbedding)
|
||||
throw std::logic_error("not an embedding model: "s + modelName);
|
||||
|
||||
auto *spec = getEmbedSpec(modelName);
|
||||
if (!spec)
|
||||
std::cerr << __func__ << ": warning: unknown model " << modelName << "\n";
|
||||
|
||||
const int32_t n_embd = llama_n_embd(d_ptr->model);
|
||||
if (dimensionality < 0) {
|
||||
dimensionality = n_embd;
|
||||
} else if (spec && dimensionality != n_embd) {
|
||||
auto msg = [dimensionality, modelName]() {
|
||||
return "unsupported dimensionality " + std::to_string(dimensionality) + " for model " + modelName;
|
||||
};
|
||||
if (!spec->matryoshkaCapable)
|
||||
throw std::out_of_range(msg() + " (supported: " + std::to_string(n_embd) + ")");
|
||||
if (dimensionality == 0 || dimensionality > n_embd)
|
||||
throw std::out_of_range(msg() + " (recommended: " + spec->recommendedDims + ")");
|
||||
}
|
||||
|
||||
if (!prefix) {
|
||||
if (!spec)
|
||||
throw std::invalid_argument("unknown model "s + modelName + ", specify a prefix if applicable or an empty string");
|
||||
prefix = spec->docPrefix;
|
||||
} else if (spec && prefix != spec->docPrefix && prefix != spec->queryPrefix &&
|
||||
std::find(spec->otherPrefixes.begin(), spec->otherPrefixes.end(), *prefix) == spec->otherPrefixes.end())
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::quoted(*prefix) << " is not a valid task type for model " << modelName;
|
||||
throw std::invalid_argument(ss.str());
|
||||
}
|
||||
|
||||
embedInternal(texts, embeddings, *prefix, dimensionality, tokenCount, doMean, atlas, spec);
|
||||
}
|
||||
|
||||
// MD5 hash of "nomic empty"
|
||||
static const char EMPTY_PLACEHOLDER[] = "24df574ea1c998de59d5be15e769658e";
|
||||
|
||||
auto product(double a) -> std::function<double(double)> {
|
||||
return [a](double b) { return a * b; };
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
double getL2NormScale(T *start, T *end) {
|
||||
double magnitude = std::sqrt(std::inner_product(start, end, start, 0.0));
|
||||
return 1.0 / std::max(magnitude, 1e-12);
|
||||
}
|
||||
|
||||
void LLamaModel::embedInternal(
|
||||
const std::vector<std::string> &texts, float *embeddings, std::string prefix, int dimensionality,
|
||||
size_t *tokenCount, bool doMean, bool atlas, const EmbModelSpec *spec
|
||||
) {
|
||||
typedef std::vector<LLModel::Token> TokenString;
|
||||
static constexpr int32_t atlasMaxLength = 8192;
|
||||
static constexpr int chunkOverlap = 8; // Atlas overlaps n_batch-sized chunks of input by 8 tokens
|
||||
|
||||
const llama_token bos_token = llama_token_bos(d_ptr->model);
|
||||
const llama_token eos_token = llama_token_eos(d_ptr->model);
|
||||
|
||||
bool useBOS = shouldAddBOS();
|
||||
bool useEOS = llama_vocab_type(d_ptr->model) == LLAMA_VOCAB_TYPE_WPM;
|
||||
|
||||
// no EOS, optional BOS
|
||||
auto tokenize = [this, useBOS, useEOS, eos_token](std::string text, TokenString &tokens, bool wantBOS) {
|
||||
if (!text.empty() && text[0] != ' ') {
|
||||
text = ' ' + text; // normalize for SPM - our fork of llama.cpp doesn't add a space prefix
|
||||
}
|
||||
wantBOS &= useBOS;
|
||||
|
||||
tokens.resize(text.length()+4);
|
||||
int32_t n_tokens = llama_tokenize(d_ptr->model, text.c_str(), text.length(), tokens.data(), tokens.size(), wantBOS, false);
|
||||
assert(useEOS == (eos_token != -1 && tokens[n_tokens - 1] == eos_token));
|
||||
tokens.resize(n_tokens - useEOS); // erase EOS/SEP
|
||||
};
|
||||
|
||||
// tokenize the texts
|
||||
std::vector<TokenString> inputs;
|
||||
for (unsigned i = 0; i < texts.size(); i++) {
|
||||
auto &text = texts[i];
|
||||
auto &inp = inputs.emplace_back();
|
||||
tokenize(text, inp, false);
|
||||
if (atlas && inp.size() > atlasMaxLength) {
|
||||
if (doMean) {
|
||||
throw std::length_error(
|
||||
"length of text at index " + std::to_string(i) + " is " + std::to_string(inp.size()) +
|
||||
" tokens which exceeds limit of " + std::to_string(atlasMaxLength)
|
||||
);
|
||||
}
|
||||
inp.resize(atlasMaxLength);
|
||||
} else if (inp.empty()) {
|
||||
if (!atlas || !text.empty()) {
|
||||
std::cerr << __func__ << ": warning: chunking tokenized text at index " << std::to_string(i)
|
||||
<< " into zero tokens\n";
|
||||
}
|
||||
tokenize(EMPTY_PLACEHOLDER, inp, false);
|
||||
}
|
||||
}
|
||||
|
||||
// tokenize the prefix
|
||||
TokenString prefixTokens;
|
||||
if (prefix.empty()) {
|
||||
prefixTokens.push_back(bos_token);
|
||||
} else {
|
||||
tokenize(prefix + ':', prefixTokens, true);
|
||||
}
|
||||
|
||||
const uint32_t n_batch = llama_n_batch(d_ptr->ctx);
|
||||
const uint32_t max_len = n_batch - (prefixTokens.size() + useEOS); // minus BOS/CLS and EOS/SEP
|
||||
if (chunkOverlap >= max_len) {
|
||||
throw std::logic_error("max chunk length of " + std::to_string(max_len) + " is smaller than overlap of " +
|
||||
std::to_string(chunkOverlap) + " tokens");
|
||||
}
|
||||
|
||||
// split into max_len-sized chunks
|
||||
struct split_batch { unsigned idx; TokenString batch; };
|
||||
std::vector<split_batch> batches;
|
||||
size_t totalTokens = 0;
|
||||
for (unsigned i = 0; i < inputs.size(); i++) {
|
||||
auto &input = inputs[i];
|
||||
for (auto it = input.begin(); it < input.end(); it += max_len) {
|
||||
if (it > input.begin()) { it -= chunkOverlap; }
|
||||
auto end = std::min(it + max_len, input.end());
|
||||
batches.push_back({ i, {} });
|
||||
auto &batch = batches.back().batch;
|
||||
batch = prefixTokens;
|
||||
batch.insert(batch.end(), it, end);
|
||||
totalTokens += end - it;
|
||||
batch.push_back(eos_token);
|
||||
if (!doMean) { break; /* limit text to one chunk */ }
|
||||
}
|
||||
}
|
||||
inputs.clear();
|
||||
|
||||
// initialize batch
|
||||
struct llama_batch batch = llama_batch_init(n_batch, 0, 1);
|
||||
|
||||
// n_texts x n_embd matrix
|
||||
const int32_t n_embd = llama_n_embd(d_ptr->model);
|
||||
std::vector<double> embeddingsSum(texts.size() * n_embd);
|
||||
std::vector<int> embeddingsSumTotal(texts.size());
|
||||
std::vector<int> queued_indices; // text indices of batches to be processed
|
||||
|
||||
auto decode = [this, &queued_indices, n_embd, &batch, &embeddingsSum, &embeddingsSumTotal, spec, dimensionality]() {
|
||||
if (llama_decode(d_ptr->ctx, batch) < 0)
|
||||
throw std::runtime_error("llama_decode failed");
|
||||
|
||||
for (int i = 0; i < batch.n_tokens; ++i) {
|
||||
if (!batch.logits[i]) { continue; }
|
||||
int i_prompt = queued_indices[batch.seq_id[i][0]];
|
||||
auto *out = &embeddingsSum[i_prompt * n_embd];
|
||||
|
||||
// sequence embeddings aren't available when pooling_type is NONE
|
||||
auto *embd = llama_get_embeddings_seq(d_ptr->ctx, batch.seq_id[i][0]);
|
||||
if (!embd) { embd = llama_get_embeddings_ith(d_ptr->ctx, i); }
|
||||
assert(embd);
|
||||
|
||||
auto *embd_end = embd + n_embd;
|
||||
|
||||
// layer normalization for nomic-embed-text-v1.5
|
||||
if (spec && spec->matryoshkaCapable) {
|
||||
// normalize mean
|
||||
double mean = std::accumulate(embd, embd_end, 0.0) / n_embd;
|
||||
std::transform(embd, embd_end, embd, [mean](double f){ return f - mean; });
|
||||
|
||||
// unbiased sample variance, with Bessel's correction
|
||||
double variance = std::inner_product(embd, embd_end, embd, 0.0) / (n_embd - 1);
|
||||
|
||||
// trim to matryoshka dim
|
||||
embd_end = embd + dimensionality;
|
||||
|
||||
// normalize variance
|
||||
std::transform(embd, embd_end, embd, product(1.0 / std::sqrt(variance + 1e-5)));
|
||||
}
|
||||
|
||||
// L2 norm
|
||||
auto scale = getL2NormScale(embd, embd_end);
|
||||
std::transform(embd, embd_end, out, out, [scale](double e, double o){ return o + scale * e; });
|
||||
embeddingsSumTotal[i_prompt]++;
|
||||
}
|
||||
};
|
||||
|
||||
// break into batches
|
||||
for (auto &inp: batches) {
|
||||
// encode if at capacity
|
||||
if (batch.n_tokens + inp.batch.size() > n_batch) {
|
||||
decode();
|
||||
batch.n_tokens = 0;
|
||||
queued_indices.clear();
|
||||
}
|
||||
|
||||
// add to batch
|
||||
batch_add_seq(batch, inp.batch, queued_indices.size());
|
||||
queued_indices.push_back(inp.idx);
|
||||
}
|
||||
|
||||
// final batch
|
||||
decode();
|
||||
|
||||
for (unsigned i = 0; i < texts.size(); i++) {
|
||||
auto *embd = &embeddingsSum[i * n_embd];
|
||||
auto *embd_end = embd + dimensionality;
|
||||
int total = embeddingsSumTotal[i];
|
||||
|
||||
// average over chunks
|
||||
std::transform(embd, embd_end, embd, product(1.0 / total));
|
||||
|
||||
// L2 norm and copy
|
||||
auto scale = getL2NormScale(embd, embd_end);
|
||||
std::transform(embd, embd_end, embeddings, product(scale));
|
||||
embeddings += dimensionality;
|
||||
}
|
||||
|
||||
if (tokenCount) { *tokenCount = totalTokens; }
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
@@ -271,42 +914,29 @@ DLL_EXPORT const char *get_build_variant() {
|
||||
return GGML_BUILD_VARIANT;
|
||||
}
|
||||
|
||||
DLL_EXPORT bool magic_match(std::istream& f) {
|
||||
// Check magic
|
||||
uint32_t magic = 0;
|
||||
f.read(reinterpret_cast<char*>(&magic), sizeof(magic));
|
||||
if (magic != 0x67676a74) return false;
|
||||
// Check version
|
||||
uint32_t version = 0;
|
||||
f.read(reinterpret_cast<char*>(&version), sizeof(version));
|
||||
if (!(version LLAMA_VERSIONS)) {
|
||||
return false;
|
||||
DLL_EXPORT bool magic_match(const char *fname) {
|
||||
auto * ctx = load_gguf(fname);
|
||||
std::string arch = get_arch_name(ctx);
|
||||
|
||||
bool valid = true;
|
||||
|
||||
if (std::find(KNOWN_ARCHES.begin(), KNOWN_ARCHES.end(), arch) == KNOWN_ARCHES.end()) {
|
||||
// not supported by this version of llama.cpp
|
||||
if (arch != "gptj") { // we support this via another module
|
||||
std::cerr << __func__ << ": unsupported model architecture: " << arch << "\n";
|
||||
}
|
||||
valid = false;
|
||||
}
|
||||
llama_file_hparams hparams;
|
||||
f.read(reinterpret_cast<char*>(&hparams), sizeof(hparams));
|
||||
if (!(hparams.n_vocab >= 32000 && hparams.n_vocab <= 32100)) {
|
||||
return false; // not a llama.
|
||||
}
|
||||
#ifdef GGML_USE_METAL
|
||||
// Check quant supported on metal
|
||||
// skip fields
|
||||
switch(hparams.ftype) {
|
||||
// currently supported on Metal https://github.com/ggerganov/llama.cpp/blob/ae9663f1887513e152839e91f61c513075a19422/ggml-metal.m#L51-L55
|
||||
case LLAMA_FTYPE_MOSTLY_F16:
|
||||
case LLAMA_FTYPE_MOSTLY_Q2_K:
|
||||
case LLAMA_FTYPE_MOSTLY_Q4_0:
|
||||
case LLAMA_FTYPE_MOSTLY_Q6_K:
|
||||
case LLAMA_FTYPE_MOSTLY_Q4_K_S:
|
||||
case LLAMA_FTYPE_MOSTLY_Q4_K_M:
|
||||
return true;
|
||||
default: // unsupported quant-type for Metal
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
|
||||
if (valid && is_embedding_arch(arch) && gguf_find_key(ctx, (arch + ".pooling_type").c_str()) < 0)
|
||||
valid = false; // old pre-llama.cpp embedding model, e.g. all-MiniLM-L6-v2-f16.gguf
|
||||
|
||||
gguf_free(ctx);
|
||||
return valid;
|
||||
}
|
||||
|
||||
DLL_EXPORT LLModel *construct() {
|
||||
llama_log_set(llama_log_callback, nullptr);
|
||||
return new LLamaModel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,36 +4,64 @@
|
||||
#ifndef LLAMAMODEL_H
|
||||
#define LLAMAMODEL_H
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "llmodel.h"
|
||||
|
||||
struct LLamaPrivate;
|
||||
struct EmbModelSpec;
|
||||
|
||||
class LLamaModel : public LLModel {
|
||||
public:
|
||||
LLamaModel();
|
||||
~LLamaModel();
|
||||
|
||||
bool loadModel(const std::string &modelPath) override;
|
||||
bool supportsEmbedding() const override { return m_supportsEmbedding; }
|
||||
bool supportsCompletion() const override { return m_supportsCompletion; }
|
||||
bool loadModel(const std::string &modelPath, int n_ctx, int ngl) override;
|
||||
bool isModelBlacklisted(const std::string &modelPath) const override;
|
||||
bool isEmbeddingModel(const std::string &modelPath) const override;
|
||||
bool isModelLoaded() const override;
|
||||
size_t requiredMem(const std::string &modelPath) override;
|
||||
size_t requiredMem(const std::string &modelPath, int n_ctx, int ngl) override;
|
||||
size_t stateSize() const override;
|
||||
size_t saveState(uint8_t *dest) const override;
|
||||
size_t restoreState(const uint8_t *src) override;
|
||||
void setThreadCount(int32_t n_threads) override;
|
||||
int32_t threadCount() const override;
|
||||
std::vector<GPUDevice> availableGPUDevices(size_t memoryRequired) const override;
|
||||
bool initializeGPUDevice(size_t memoryRequired, const std::string &name) const override;
|
||||
bool initializeGPUDevice(int device, std::string *unavail_reason = nullptr) const override;
|
||||
bool hasGPUDevice() override;
|
||||
bool usingGPUDevice() override;
|
||||
|
||||
size_t embeddingSize() const override;
|
||||
// user-specified prefix
|
||||
void embed(const std::vector<std::string> &texts, float *embeddings, std::optional<std::string> prefix,
|
||||
int dimensionality = -1, size_t *tokenCount = nullptr, bool doMean = true, bool atlas = false) override;
|
||||
// automatic prefix
|
||||
void embed(const std::vector<std::string> &texts, float *embeddings, bool isRetrieval, int dimensionality = -1,
|
||||
size_t *tokenCount = nullptr, bool doMean = true, bool atlas = false) override;
|
||||
|
||||
private:
|
||||
LLamaPrivate *d_ptr;
|
||||
std::unique_ptr<LLamaPrivate> d_ptr;
|
||||
bool m_supportsEmbedding = false;
|
||||
bool m_supportsCompletion = false;
|
||||
|
||||
protected:
|
||||
std::vector<Token> tokenize(PromptContext &, const std::string&) const override;
|
||||
std::string tokenToString(Token) const override;
|
||||
Token sampleToken(PromptContext& ctx) const override;
|
||||
bool evalTokens(PromptContext& ctx, const std::vector<int32_t> &tokens) const override;
|
||||
std::vector<Token> tokenize(PromptContext &ctx, const std::string &str, bool special) const override;
|
||||
std::string tokenToString(Token id) const override;
|
||||
Token sampleToken(PromptContext &ctx) const override;
|
||||
bool evalTokens(PromptContext &ctx, const std::vector<int32_t> &tokens) const override;
|
||||
int32_t contextLength() const override;
|
||||
const std::vector<Token>& endTokens() const override;
|
||||
const std::vector<Token> &endTokens() const override;
|
||||
bool shouldAddBOS() const override;
|
||||
int32_t maxContextLength(std::string const &modelPath) const override;
|
||||
int32_t layerCount(std::string const &modelPath) const override;
|
||||
|
||||
void embedInternal(const std::vector<std::string> &texts, float *embeddings, std::string prefix, int dimensionality,
|
||||
size_t *tokenCount, bool doMean, bool atlas, const EmbModelSpec *spec);
|
||||
};
|
||||
|
||||
#endif // LLAMAMODEL_H
|
||||
|
||||
@@ -2,81 +2,93 @@
|
||||
#include "dlhandle.h"
|
||||
#include "sysinfo.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <windows.h>
|
||||
#include <processthreadsapi.h>
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
std::string s_implementations_search_path = ".";
|
||||
|
||||
static bool has_at_least_minimal_hardware() {
|
||||
#ifdef __x86_64__
|
||||
#ifndef _MSC_VER
|
||||
return __builtin_cpu_supports("avx");
|
||||
#else
|
||||
return IsProcessorFeaturePresent(PF_AVX_INSTRUCTIONS_AVAILABLE);
|
||||
#endif
|
||||
#else
|
||||
return true; // Don't know how to handle non-x86_64
|
||||
#endif
|
||||
}
|
||||
#if !(defined(__x86_64__) || defined(_M_X64))
|
||||
// irrelevant on non-x86_64
|
||||
#define cpu_supports_avx() -1
|
||||
#define cpu_supports_avx2() -1
|
||||
#elif defined(_MSC_VER)
|
||||
// MSVC
|
||||
static int get_cpu_info(int func_id, int reg_id) {
|
||||
int info[4];
|
||||
__cpuid(info, func_id);
|
||||
return info[reg_id];
|
||||
}
|
||||
|
||||
static bool requires_avxonly() {
|
||||
#ifdef __x86_64__
|
||||
#ifndef _MSC_VER
|
||||
return !__builtin_cpu_supports("avx2");
|
||||
#else
|
||||
return !IsProcessorFeaturePresent(PF_AVX2_INSTRUCTIONS_AVAILABLE);
|
||||
#endif
|
||||
// AVX via EAX=1: Processor Info and Feature Bits, bit 28 of ECX
|
||||
#define cpu_supports_avx() (get_cpu_info(1, 2) & (1 << 28))
|
||||
// AVX2 via EAX=7, ECX=0: Extended Features, bit 5 of EBX
|
||||
#define cpu_supports_avx2() (get_cpu_info(7, 1) & (1 << 5))
|
||||
#else
|
||||
return false; // Don't know how to handle non-x86_64
|
||||
// gcc/clang
|
||||
#define cpu_supports_avx() __builtin_cpu_supports("avx")
|
||||
#define cpu_supports_avx2() __builtin_cpu_supports("avx2")
|
||||
#endif
|
||||
}
|
||||
|
||||
LLModel::Implementation::Implementation(Dlhandle &&dlhandle_) : dlhandle(new Dlhandle(std::move(dlhandle_))) {
|
||||
auto get_model_type = dlhandle->get<const char *()>("get_model_type");
|
||||
LLModel::Implementation::Implementation(Dlhandle &&dlhandle_)
|
||||
: m_dlhandle(new Dlhandle(std::move(dlhandle_))) {
|
||||
auto get_model_type = m_dlhandle->get<const char *()>("get_model_type");
|
||||
assert(get_model_type);
|
||||
modelType = get_model_type();
|
||||
auto get_build_variant = dlhandle->get<const char *()>("get_build_variant");
|
||||
m_modelType = get_model_type();
|
||||
auto get_build_variant = m_dlhandle->get<const char *()>("get_build_variant");
|
||||
assert(get_build_variant);
|
||||
buildVariant = get_build_variant();
|
||||
magicMatch = dlhandle->get<bool(std::ifstream&)>("magic_match");
|
||||
assert(magicMatch);
|
||||
construct_ = dlhandle->get<LLModel *()>("construct");
|
||||
assert(construct_);
|
||||
m_buildVariant = get_build_variant();
|
||||
m_magicMatch = m_dlhandle->get<bool(const char*)>("magic_match");
|
||||
assert(m_magicMatch);
|
||||
m_construct = m_dlhandle->get<LLModel *()>("construct");
|
||||
assert(m_construct);
|
||||
}
|
||||
|
||||
LLModel::Implementation::Implementation(Implementation &&o)
|
||||
: construct_(o.construct_)
|
||||
, modelType(o.modelType)
|
||||
, buildVariant(o.buildVariant)
|
||||
, magicMatch(o.magicMatch)
|
||||
, dlhandle(o.dlhandle) {
|
||||
o.dlhandle = nullptr;
|
||||
: m_magicMatch(o.m_magicMatch)
|
||||
, m_construct(o.m_construct)
|
||||
, m_modelType(o.m_modelType)
|
||||
, m_buildVariant(o.m_buildVariant)
|
||||
, m_dlhandle(o.m_dlhandle) {
|
||||
o.m_dlhandle = nullptr;
|
||||
}
|
||||
|
||||
LLModel::Implementation::~Implementation() {
|
||||
if (dlhandle) delete dlhandle;
|
||||
delete m_dlhandle;
|
||||
}
|
||||
|
||||
bool LLModel::Implementation::isImplementation(const Dlhandle &dl) {
|
||||
static bool isImplementation(const Dlhandle &dl) {
|
||||
return dl.get<bool(uint32_t)>("is_g4a_backend_model_implementation");
|
||||
}
|
||||
|
||||
const std::vector<LLModel::Implementation> &LLModel::implementationList() {
|
||||
const std::vector<LLModel::Implementation> &LLModel::Implementation::implementationList() {
|
||||
if (cpu_supports_avx() == 0) {
|
||||
throw std::runtime_error("CPU does not support AVX");
|
||||
}
|
||||
|
||||
// NOTE: allocated on heap so we leak intentionally on exit so we have a chance to clean up the
|
||||
// individual models without the cleanup of the static list interfering
|
||||
static auto* libs = new std::vector<LLModel::Implementation>([] () {
|
||||
std::vector<LLModel::Implementation> fres;
|
||||
static auto* libs = new std::vector<Implementation>([] () {
|
||||
std::vector<Implementation> fres;
|
||||
|
||||
std::string impl_name_re = "(gptj|llamamodel-mainline)";
|
||||
if (cpu_supports_avx2() == 0) {
|
||||
impl_name_re += "-avxonly";
|
||||
} else {
|
||||
impl_name_re += "-(default|metal)";
|
||||
}
|
||||
std::regex re(impl_name_re);
|
||||
auto search_in_directory = [&](const std::string& paths) {
|
||||
std::stringstream ss(paths);
|
||||
std::string path;
|
||||
@@ -86,13 +98,15 @@ const std::vector<LLModel::Implementation> &LLModel::implementationList() {
|
||||
// Iterate over all libraries
|
||||
for (const auto& f : std::filesystem::directory_iterator(fs_path)) {
|
||||
const std::filesystem::path& p = f.path();
|
||||
|
||||
if (p.extension() != LIB_FILE_EXT) continue;
|
||||
if (!std::regex_search(p.stem().string(), re)) continue;
|
||||
|
||||
// Add to list if model implementation
|
||||
try {
|
||||
Dlhandle dl(p.string());
|
||||
if (!Implementation::isImplementation(dl)) {
|
||||
if (!isImplementation(dl))
|
||||
continue;
|
||||
}
|
||||
fres.emplace_back(Implementation(std::move(dl)));
|
||||
} catch (...) {}
|
||||
}
|
||||
@@ -107,34 +121,38 @@ const std::vector<LLModel::Implementation> &LLModel::implementationList() {
|
||||
return *libs;
|
||||
}
|
||||
|
||||
const LLModel::Implementation* LLModel::implementation(std::ifstream& f, const std::string& buildVariant) {
|
||||
const LLModel::Implementation* LLModel::Implementation::implementation(const char *fname, const std::string& buildVariant) {
|
||||
bool buildVariantMatched = false;
|
||||
for (const auto& i : implementationList()) {
|
||||
f.seekg(0);
|
||||
if (!i.magicMatch(f)) continue;
|
||||
if (buildVariant != i.buildVariant) continue;
|
||||
if (buildVariant != i.m_buildVariant) continue;
|
||||
buildVariantMatched = true;
|
||||
|
||||
if (!i.m_magicMatch(fname)) continue;
|
||||
return &i;
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
if (!buildVariantMatched)
|
||||
throw std::runtime_error("Could not find any implementations for build variant: " + buildVariant);
|
||||
|
||||
return nullptr; // unsupported model format
|
||||
}
|
||||
|
||||
LLModel *LLModel::construct(const std::string &modelPath, std::string buildVariant) {
|
||||
|
||||
if (!has_at_least_minimal_hardware())
|
||||
return nullptr;
|
||||
|
||||
// Read magic
|
||||
std::ifstream f(modelPath, std::ios::binary);
|
||||
if (!f) return nullptr;
|
||||
LLModel *LLModel::Implementation::construct(const std::string &modelPath, std::string buildVariant, int n_ctx) {
|
||||
// Get correct implementation
|
||||
const LLModel::Implementation* impl = nullptr;
|
||||
const Implementation* impl = nullptr;
|
||||
|
||||
#if defined(__APPLE__) && defined(__arm64__) // FIXME: See if metal works for intel macs
|
||||
if (buildVariant == "auto") {
|
||||
size_t total_mem = getSystemTotalRAMInBytes();
|
||||
impl = implementation(f, "metal");
|
||||
impl = implementation(modelPath.c_str(), "metal");
|
||||
if(impl) {
|
||||
LLModel* metalimpl = impl->construct();
|
||||
size_t req_mem = metalimpl->requiredMem(modelPath);
|
||||
LLModel* metalimpl = impl->m_construct();
|
||||
metalimpl->m_implementation = impl;
|
||||
/* TODO(cebtenzzre): after we fix requiredMem, we should change this to happen at
|
||||
* load time, not construct time. right now n_ctx is incorrectly hardcoded 2048 in
|
||||
* most (all?) places where this is called, causing underestimation of required
|
||||
* memory. */
|
||||
size_t req_mem = metalimpl->requiredMem(modelPath, n_ctx, 100);
|
||||
float req_to_total = (float) req_mem / (float) total_mem;
|
||||
// on a 16GB M2 Mac a 13B q4_0 (0.52) works for me but a 13B q4_K_M (0.55) does not
|
||||
if (req_to_total >= 0.53) {
|
||||
@@ -145,29 +163,85 @@ LLModel *LLModel::construct(const std::string &modelPath, std::string buildVaria
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)n_ctx;
|
||||
#endif
|
||||
|
||||
if (!impl) {
|
||||
//TODO: Auto-detect CUDA/OpenCL
|
||||
if (buildVariant == "auto") {
|
||||
if (requires_avxonly()) {
|
||||
if (cpu_supports_avx2() == 0) {
|
||||
buildVariant = "avxonly";
|
||||
} else {
|
||||
buildVariant = "default";
|
||||
}
|
||||
}
|
||||
impl = implementation(f, buildVariant);
|
||||
impl = implementation(modelPath.c_str(), buildVariant);
|
||||
if (!impl) return nullptr;
|
||||
}
|
||||
f.close();
|
||||
|
||||
// Construct and return llmodel implementation
|
||||
return impl->construct();
|
||||
auto fres = impl->m_construct();
|
||||
fres->m_implementation = impl;
|
||||
return fres;
|
||||
}
|
||||
|
||||
void LLModel::setImplementationsSearchPath(const std::string& path) {
|
||||
LLModel *LLModel::Implementation::constructDefaultLlama() {
|
||||
static std::unique_ptr<LLModel> llama([]() -> LLModel * {
|
||||
const std::vector<LLModel::Implementation> *impls;
|
||||
try {
|
||||
impls = &implementationList();
|
||||
} catch (const std::runtime_error &e) {
|
||||
std::cerr << __func__ << ": implementationList failed: " << e.what() << "\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const LLModel::Implementation *impl = nullptr;
|
||||
for (const auto &i: *impls) {
|
||||
if (i.m_buildVariant == "metal" || i.m_modelType != "LLaMA") continue;
|
||||
impl = &i;
|
||||
}
|
||||
if (!impl) {
|
||||
std::cerr << __func__ << ": could not find llama.cpp implementation\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto fres = impl->m_construct();
|
||||
fres->m_implementation = impl;
|
||||
return fres;
|
||||
}());
|
||||
return llama.get();
|
||||
}
|
||||
|
||||
std::vector<LLModel::GPUDevice> LLModel::Implementation::availableGPUDevices() {
|
||||
auto *llama = constructDefaultLlama();
|
||||
if (llama) { return llama->availableGPUDevices(0); }
|
||||
return {};
|
||||
}
|
||||
|
||||
int32_t LLModel::Implementation::maxContextLength(const std::string &modelPath) {
|
||||
auto *llama = constructDefaultLlama();
|
||||
return llama ? llama->maxContextLength(modelPath) : -1;
|
||||
}
|
||||
|
||||
int32_t LLModel::Implementation::layerCount(const std::string &modelPath) {
|
||||
auto *llama = constructDefaultLlama();
|
||||
return llama ? llama->layerCount(modelPath) : -1;
|
||||
}
|
||||
|
||||
bool LLModel::Implementation::isEmbeddingModel(const std::string &modelPath) {
|
||||
auto *llama = constructDefaultLlama();
|
||||
return llama && llama->isEmbeddingModel(modelPath);
|
||||
}
|
||||
|
||||
void LLModel::Implementation::setImplementationsSearchPath(const std::string& path) {
|
||||
s_implementations_search_path = path;
|
||||
}
|
||||
|
||||
const std::string& LLModel::implementationsSearchPath() {
|
||||
const std::string& LLModel::Implementation::implementationsSearchPath() {
|
||||
return s_implementations_search_path;
|
||||
}
|
||||
|
||||
bool LLModel::Implementation::hasSupportedCPU() {
|
||||
return cpu_supports_avx() != 0;
|
||||
}
|
||||
|
||||
@@ -1,43 +1,64 @@
|
||||
#ifndef LLMODEL_H
|
||||
#define LLMODEL_H
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <string_view>
|
||||
#include <fstream>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#define LLMODEL_MAX_PROMPT_BATCH 128
|
||||
|
||||
class Dlhandle;
|
||||
|
||||
class LLModel {
|
||||
public:
|
||||
using Token = int32_t;
|
||||
|
||||
class Implementation {
|
||||
LLModel *(*construct_)();
|
||||
struct GPUDevice {
|
||||
int index;
|
||||
int type;
|
||||
size_t heapSize;
|
||||
std::string name;
|
||||
std::string vendor;
|
||||
|
||||
GPUDevice(int index, int type, size_t heapSize, std::string name, std::string vendor):
|
||||
index(index), type(type), heapSize(heapSize), name(std::move(name)), vendor(std::move(vendor)) {}
|
||||
};
|
||||
|
||||
class Implementation {
|
||||
public:
|
||||
Implementation(Dlhandle&&);
|
||||
Implementation(const Implementation&) = delete;
|
||||
Implementation(Implementation&&);
|
||||
Implementation(const Implementation &) = delete;
|
||||
Implementation(Implementation &&);
|
||||
~Implementation();
|
||||
|
||||
static bool isImplementation(const Dlhandle&);
|
||||
std::string_view modelType() const { return m_modelType; }
|
||||
std::string_view buildVariant() const { return m_buildVariant; }
|
||||
|
||||
std::string_view modelType, buildVariant;
|
||||
bool (*magicMatch)(std::ifstream& f);
|
||||
Dlhandle *dlhandle;
|
||||
static LLModel *construct(const std::string &modelPath, std::string buildVariant = "auto", int n_ctx = 2048);
|
||||
static std::vector<GPUDevice> availableGPUDevices();
|
||||
static int32_t maxContextLength(const std::string &modelPath);
|
||||
static int32_t layerCount(const std::string &modelPath);
|
||||
static bool isEmbeddingModel(const std::string &modelPath);
|
||||
static void setImplementationsSearchPath(const std::string &path);
|
||||
static const std::string &implementationsSearchPath();
|
||||
static bool hasSupportedCPU();
|
||||
|
||||
// The only way an implementation should be constructed
|
||||
LLModel *construct() const {
|
||||
auto fres = construct_();
|
||||
fres->m_implementation = this;
|
||||
return fres;
|
||||
}
|
||||
private:
|
||||
Implementation(Dlhandle &&);
|
||||
|
||||
static const std::vector<Implementation> &implementationList();
|
||||
static const Implementation *implementation(const char *fname, const std::string &buildVariant);
|
||||
static LLModel *constructDefaultLlama();
|
||||
|
||||
bool (*m_magicMatch)(const char *fname);
|
||||
LLModel *(*m_construct)();
|
||||
|
||||
std::string_view m_modelType;
|
||||
std::string_view m_buildVariant;
|
||||
Dlhandle *m_dlhandle;
|
||||
};
|
||||
|
||||
struct PromptContext {
|
||||
@@ -48,57 +69,132 @@ public:
|
||||
int32_t n_predict = 200;
|
||||
int32_t top_k = 40;
|
||||
float top_p = 0.9f;
|
||||
float min_p = 0.0f;
|
||||
float temp = 0.9f;
|
||||
int32_t n_batch = 9;
|
||||
float repeat_penalty = 1.10f;
|
||||
int32_t repeat_last_n = 64; // last n tokens to penalize
|
||||
float contextErase = 0.75f; // percent of context to erase if we exceed the context
|
||||
// window
|
||||
float contextErase = 0.75f; // percent of context to erase if we exceed the context window
|
||||
int32_t n_last_batch_tokens = 0;
|
||||
};
|
||||
|
||||
using ProgressCallback = std::function<bool(float progress)>;
|
||||
|
||||
explicit LLModel() {}
|
||||
virtual ~LLModel() {}
|
||||
|
||||
virtual bool loadModel(const std::string &modelPath) = 0;
|
||||
virtual bool supportsEmbedding() const = 0;
|
||||
virtual bool supportsCompletion() const = 0;
|
||||
virtual bool loadModel(const std::string &modelPath, int n_ctx, int ngl) = 0;
|
||||
virtual bool isModelBlacklisted(const std::string &modelPath) const { (void)modelPath; return false; };
|
||||
virtual bool isEmbeddingModel(const std::string &modelPath) const { (void)modelPath; return false; }
|
||||
virtual bool isModelLoaded() const = 0;
|
||||
virtual size_t requiredMem(const std::string &modelPath) = 0;
|
||||
virtual size_t requiredMem(const std::string &modelPath, int n_ctx, int ngl) = 0;
|
||||
virtual size_t stateSize() const { return 0; }
|
||||
virtual size_t saveState(uint8_t */*dest*/) const { return 0; }
|
||||
virtual size_t restoreState(const uint8_t */*src*/) { return 0; }
|
||||
virtual size_t saveState(uint8_t *dest) const { (void)dest; return 0; }
|
||||
virtual size_t restoreState(const uint8_t *src) { (void)src; return 0; }
|
||||
|
||||
// This method requires the model to return true from supportsCompletion otherwise it will throw
|
||||
// an error
|
||||
virtual void prompt(const std::string &prompt,
|
||||
const std::string &promptTemplate,
|
||||
std::function<bool(int32_t)> promptCallback,
|
||||
std::function<bool(int32_t, const std::string&)> responseCallback,
|
||||
std::function<bool(bool)> recalculateCallback,
|
||||
PromptContext &ctx);
|
||||
PromptContext &ctx,
|
||||
bool special = false,
|
||||
std::string *fakeReply = nullptr);
|
||||
|
||||
virtual void setThreadCount(int32_t /*n_threads*/) {}
|
||||
virtual size_t embeddingSize() const {
|
||||
throw std::logic_error(std::string(implementation().modelType()) + " does not support embeddings");
|
||||
}
|
||||
// user-specified prefix
|
||||
virtual void embed(const std::vector<std::string> &texts, float *embeddings, std::optional<std::string> prefix,
|
||||
int dimensionality = -1, size_t *tokenCount = nullptr, bool doMean = true, bool atlas = false);
|
||||
// automatic prefix
|
||||
virtual void embed(const std::vector<std::string> &texts, float *embeddings, bool isRetrieval,
|
||||
int dimensionality = -1, size_t *tokenCount = nullptr, bool doMean = true, bool atlas = false);
|
||||
|
||||
virtual void setThreadCount(int32_t n_threads) { (void)n_threads; }
|
||||
virtual int32_t threadCount() const { return 1; }
|
||||
|
||||
const Implementation& implementation() const {
|
||||
const Implementation &implementation() const {
|
||||
return *m_implementation;
|
||||
}
|
||||
|
||||
static const std::vector<Implementation>& implementationList();
|
||||
static const Implementation *implementation(std::ifstream& f, const std::string& buildVariant);
|
||||
static LLModel *construct(const std::string &modelPath, std::string buildVariant = "auto");
|
||||
virtual std::vector<GPUDevice> availableGPUDevices(size_t memoryRequired) const {
|
||||
(void)memoryRequired;
|
||||
return {};
|
||||
}
|
||||
|
||||
static void setImplementationsSearchPath(const std::string& path);
|
||||
static const std::string& implementationsSearchPath();
|
||||
virtual bool initializeGPUDevice(size_t memoryRequired, const std::string &name) const {
|
||||
(void)memoryRequired;
|
||||
(void)name;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool initializeGPUDevice(int device, std::string *unavail_reason = nullptr) const {
|
||||
(void)device;
|
||||
if (unavail_reason) {
|
||||
*unavail_reason = "model has no GPU support";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool hasGPUDevice() { return false; }
|
||||
virtual bool usingGPUDevice() { return false; }
|
||||
|
||||
void setProgressCallback(ProgressCallback callback) { m_progressCallback = callback; }
|
||||
|
||||
protected:
|
||||
// These are pure virtual because subclasses need to implement as the default implementation of
|
||||
// 'prompt' above calls these functions
|
||||
virtual std::vector<Token> tokenize(PromptContext &, const std::string&) const = 0;
|
||||
virtual std::string tokenToString(Token) const = 0;
|
||||
virtual std::vector<Token> tokenize(PromptContext &ctx, const std::string &str, bool special = false) const = 0;
|
||||
virtual std::string tokenToString(Token id) const = 0;
|
||||
virtual Token sampleToken(PromptContext &ctx) const = 0;
|
||||
virtual bool evalTokens(PromptContext &/*ctx*/, const std::vector<int32_t>& /*tokens*/) const = 0;
|
||||
virtual bool evalTokens(PromptContext &ctx, const std::vector<int32_t> &tokens) const = 0;
|
||||
virtual int32_t contextLength() const = 0;
|
||||
virtual const std::vector<Token>& endTokens() const = 0;
|
||||
virtual const std::vector<Token> &endTokens() const = 0;
|
||||
virtual bool shouldAddBOS() const = 0;
|
||||
|
||||
virtual int32_t maxContextLength(std::string const &modelPath) const
|
||||
{
|
||||
(void)modelPath;
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtual int32_t layerCount(std::string const &modelPath) const
|
||||
{
|
||||
(void)modelPath;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// This is a helper function called from the default implementation of 'prompt' but it can be
|
||||
// shared by all base classes so it isn't virtual
|
||||
void recalculateContext(PromptContext &promptCtx, std::function<bool(bool)> recalculate);
|
||||
|
||||
const Implementation *m_implementation = nullptr;
|
||||
|
||||
ProgressCallback m_progressCallback;
|
||||
static bool staticProgressCallback(float progress, void* ctx)
|
||||
{
|
||||
LLModel* model = static_cast<LLModel*>(ctx);
|
||||
if (model && model->m_progressCallback)
|
||||
return model->m_progressCallback(progress);
|
||||
return true;
|
||||
}
|
||||
|
||||
void decodePrompt(std::function<bool(int32_t)> promptCallback,
|
||||
std::function<bool(int32_t, const std::string&)> responseCallback,
|
||||
std::function<bool(bool)> recalculateCallback,
|
||||
PromptContext &promptCtx,
|
||||
std::vector<Token> embd_inp);
|
||||
void generateResponse(std::function<bool(int32_t, const std::string&)> responseCallback,
|
||||
std::function<bool(bool)> recalculateCallback,
|
||||
PromptContext &promptCtx);
|
||||
|
||||
private:
|
||||
friend class LLMImplementation;
|
||||
};
|
||||
|
||||
#endif // LLMODEL_H
|
||||
|
||||
@@ -1,132 +1,115 @@
|
||||
#include "llmodel_c.h"
|
||||
#include "llmodel.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
|
||||
struct LLModelWrapper {
|
||||
LLModel *llModel = nullptr;
|
||||
LLModel::PromptContext promptContext;
|
||||
~LLModelWrapper() { delete llModel; }
|
||||
};
|
||||
|
||||
|
||||
thread_local static std::string last_error_message;
|
||||
|
||||
|
||||
llmodel_model llmodel_model_create(const char *model_path) {
|
||||
auto fres = llmodel_model_create2(model_path, "auto", nullptr);
|
||||
const char *error;
|
||||
auto fres = llmodel_model_create2(model_path, "auto", &error);
|
||||
if (!fres) {
|
||||
fprintf(stderr, "Invalid model file\n");
|
||||
fprintf(stderr, "Unable to instantiate model: %s\n", error);
|
||||
}
|
||||
return fres;
|
||||
}
|
||||
|
||||
llmodel_model llmodel_model_create2(const char *model_path, const char *build_variant, llmodel_error *error) {
|
||||
auto wrapper = new LLModelWrapper;
|
||||
int error_code = 0;
|
||||
static void llmodel_set_error(const char **errptr, const char *message) {
|
||||
thread_local static std::string last_error_message;
|
||||
if (errptr) {
|
||||
last_error_message = message;
|
||||
*errptr = last_error_message.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
llmodel_model llmodel_model_create2(const char *model_path, const char *build_variant, const char **error) {
|
||||
LLModel *llModel;
|
||||
try {
|
||||
wrapper->llModel = LLModel::construct(model_path, build_variant);
|
||||
llModel = LLModel::Implementation::construct(model_path, build_variant);
|
||||
} catch (const std::exception& e) {
|
||||
error_code = EINVAL;
|
||||
last_error_message = e.what();
|
||||
llmodel_set_error(error, e.what());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!wrapper->llModel) {
|
||||
delete std::exchange(wrapper, nullptr);
|
||||
// Get errno and error message if none
|
||||
if (error_code == 0) {
|
||||
if (errno != 0) {
|
||||
error_code = errno;
|
||||
last_error_message = std::strerror(error_code);
|
||||
} else {
|
||||
error_code = ENOTSUP;
|
||||
last_error_message = "Model format not supported (no matching implementation found)";
|
||||
}
|
||||
}
|
||||
// Set error argument
|
||||
if (error) {
|
||||
error->message = last_error_message.c_str();
|
||||
error->code = error_code;
|
||||
}
|
||||
if (!llModel) {
|
||||
llmodel_set_error(error, "Model format not supported (no matching implementation found)");
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<llmodel_model*>(wrapper);
|
||||
|
||||
auto wrapper = new LLModelWrapper;
|
||||
wrapper->llModel = llModel;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
void llmodel_model_destroy(llmodel_model model) {
|
||||
delete reinterpret_cast<LLModelWrapper*>(model);
|
||||
delete static_cast<LLModelWrapper *>(model);
|
||||
}
|
||||
|
||||
size_t llmodel_required_mem(llmodel_model model, const char *model_path)
|
||||
size_t llmodel_required_mem(llmodel_model model, const char *model_path, int n_ctx, int ngl)
|
||||
{
|
||||
LLModelWrapper *wrapper = reinterpret_cast<LLModelWrapper*>(model);
|
||||
return wrapper->llModel->requiredMem(model_path);
|
||||
auto *wrapper = static_cast<LLModelWrapper *>(model);
|
||||
return wrapper->llModel->requiredMem(model_path, n_ctx, ngl);
|
||||
}
|
||||
|
||||
bool llmodel_loadModel(llmodel_model model, const char *model_path)
|
||||
bool llmodel_loadModel(llmodel_model model, const char *model_path, int n_ctx, int ngl)
|
||||
{
|
||||
LLModelWrapper *wrapper = reinterpret_cast<LLModelWrapper*>(model);
|
||||
return wrapper->llModel->loadModel(model_path);
|
||||
auto *wrapper = static_cast<LLModelWrapper *>(model);
|
||||
|
||||
std::string modelPath(model_path);
|
||||
if (wrapper->llModel->isModelBlacklisted(modelPath)) {
|
||||
size_t slash = modelPath.find_last_of("/\\");
|
||||
auto basename = slash == std::string::npos ? modelPath : modelPath.substr(slash + 1);
|
||||
std::cerr << "warning: model '" << basename << "' is out-of-date, please check for an updated version\n";
|
||||
}
|
||||
return wrapper->llModel->loadModel(modelPath, n_ctx, ngl);
|
||||
}
|
||||
|
||||
bool llmodel_isModelLoaded(llmodel_model model)
|
||||
{
|
||||
LLModelWrapper *wrapper = reinterpret_cast<LLModelWrapper*>(model);
|
||||
auto *wrapper = static_cast<LLModelWrapper *>(model);
|
||||
return wrapper->llModel->isModelLoaded();
|
||||
}
|
||||
|
||||
uint64_t llmodel_get_state_size(llmodel_model model)
|
||||
{
|
||||
LLModelWrapper *wrapper = reinterpret_cast<LLModelWrapper*>(model);
|
||||
auto *wrapper = static_cast<LLModelWrapper *>(model);
|
||||
return wrapper->llModel->stateSize();
|
||||
}
|
||||
|
||||
uint64_t llmodel_save_state_data(llmodel_model model, uint8_t *dest)
|
||||
{
|
||||
LLModelWrapper *wrapper = reinterpret_cast<LLModelWrapper*>(model);
|
||||
auto *wrapper = static_cast<LLModelWrapper *>(model);
|
||||
return wrapper->llModel->saveState(dest);
|
||||
}
|
||||
|
||||
uint64_t llmodel_restore_state_data(llmodel_model model, const uint8_t *src)
|
||||
{
|
||||
LLModelWrapper *wrapper = reinterpret_cast<LLModelWrapper*>(model);
|
||||
auto *wrapper = static_cast<LLModelWrapper *>(model);
|
||||
return wrapper->llModel->restoreState(src);
|
||||
}
|
||||
|
||||
// Wrapper functions for the C callbacks
|
||||
bool prompt_wrapper(int32_t token_id, void *user_data) {
|
||||
llmodel_prompt_callback callback = reinterpret_cast<llmodel_prompt_callback>(user_data);
|
||||
return callback(token_id);
|
||||
}
|
||||
|
||||
bool response_wrapper(int32_t token_id, const std::string &response, void *user_data) {
|
||||
llmodel_response_callback callback = reinterpret_cast<llmodel_response_callback>(user_data);
|
||||
return callback(token_id, response.c_str());
|
||||
}
|
||||
|
||||
bool recalculate_wrapper(bool is_recalculating, void *user_data) {
|
||||
llmodel_recalculate_callback callback = reinterpret_cast<llmodel_recalculate_callback>(user_data);
|
||||
return callback(is_recalculating);
|
||||
}
|
||||
|
||||
void llmodel_prompt(llmodel_model model, const char *prompt,
|
||||
const char *prompt_template,
|
||||
llmodel_prompt_callback prompt_callback,
|
||||
llmodel_response_callback response_callback,
|
||||
llmodel_recalculate_callback recalculate_callback,
|
||||
llmodel_prompt_context *ctx)
|
||||
llmodel_prompt_context *ctx,
|
||||
bool special,
|
||||
const char *fake_reply)
|
||||
{
|
||||
LLModelWrapper *wrapper = reinterpret_cast<LLModelWrapper*>(model);
|
||||
auto *wrapper = static_cast<LLModelWrapper *>(model);
|
||||
|
||||
// Create std::function wrappers that call the C function pointers
|
||||
std::function<bool(int32_t)> prompt_func =
|
||||
std::bind(&prompt_wrapper, std::placeholders::_1, reinterpret_cast<void*>(prompt_callback));
|
||||
std::function<bool(int32_t, const std::string&)> response_func =
|
||||
std::bind(&response_wrapper, std::placeholders::_1, std::placeholders::_2, reinterpret_cast<void*>(response_callback));
|
||||
std::function<bool(bool)> recalc_func =
|
||||
std::bind(&recalculate_wrapper, std::placeholders::_1, reinterpret_cast<void*>(recalculate_callback));
|
||||
auto response_func = [response_callback](int32_t token_id, const std::string &response) {
|
||||
return response_callback(token_id, response.c_str());
|
||||
};
|
||||
|
||||
if (size_t(ctx->n_past) < wrapper->promptContext.tokens.size())
|
||||
wrapper->promptContext.tokens.resize(ctx->n_past);
|
||||
@@ -137,14 +120,20 @@ void llmodel_prompt(llmodel_model model, const char *prompt,
|
||||
wrapper->promptContext.n_predict = ctx->n_predict;
|
||||
wrapper->promptContext.top_k = ctx->top_k;
|
||||
wrapper->promptContext.top_p = ctx->top_p;
|
||||
wrapper->promptContext.min_p = ctx->min_p;
|
||||
wrapper->promptContext.temp = ctx->temp;
|
||||
wrapper->promptContext.n_batch = ctx->n_batch;
|
||||
wrapper->promptContext.repeat_penalty = ctx->repeat_penalty;
|
||||
wrapper->promptContext.repeat_last_n = ctx->repeat_last_n;
|
||||
wrapper->promptContext.contextErase = ctx->context_erase;
|
||||
|
||||
std::string fake_reply_str;
|
||||
if (fake_reply) { fake_reply_str = fake_reply; }
|
||||
auto *fake_reply_p = fake_reply ? &fake_reply_str : nullptr;
|
||||
|
||||
// Call the C++ prompt method
|
||||
wrapper->llModel->prompt(prompt, prompt_func, response_func, recalc_func, wrapper->promptContext);
|
||||
wrapper->llModel->prompt(prompt, prompt_template, prompt_callback, response_func, recalculate_callback,
|
||||
wrapper->promptContext, special, fake_reply_p);
|
||||
|
||||
// Update the C context by giving access to the wrappers raw pointers to std::vector data
|
||||
// which involves no copies
|
||||
@@ -159,6 +148,7 @@ void llmodel_prompt(llmodel_model model, const char *prompt,
|
||||
ctx->n_predict = wrapper->promptContext.n_predict;
|
||||
ctx->top_k = wrapper->promptContext.top_k;
|
||||
ctx->top_p = wrapper->promptContext.top_p;
|
||||
ctx->min_p = wrapper->promptContext.min_p;
|
||||
ctx->temp = wrapper->promptContext.temp;
|
||||
ctx->n_batch = wrapper->promptContext.n_batch;
|
||||
ctx->repeat_penalty = wrapper->promptContext.repeat_penalty;
|
||||
@@ -166,24 +156,115 @@ void llmodel_prompt(llmodel_model model, const char *prompt,
|
||||
ctx->context_erase = wrapper->promptContext.contextErase;
|
||||
}
|
||||
|
||||
float *llmodel_embed(
|
||||
llmodel_model model, const char **texts, size_t *embedding_size, const char *prefix, int dimensionality,
|
||||
size_t *token_count, bool do_mean, bool atlas, const char **error
|
||||
) {
|
||||
auto *wrapper = static_cast<LLModelWrapper *>(model);
|
||||
|
||||
if (!texts || !*texts) {
|
||||
llmodel_set_error(error, "'texts' is NULL or empty");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::string> textsVec;
|
||||
while (*texts) { textsVec.emplace_back(*texts++); }
|
||||
|
||||
size_t embd_size;
|
||||
float *embedding;
|
||||
|
||||
try {
|
||||
embd_size = wrapper->llModel->embeddingSize();
|
||||
if (dimensionality > 0 && dimensionality < int(embd_size))
|
||||
embd_size = dimensionality;
|
||||
|
||||
embd_size *= textsVec.size();
|
||||
|
||||
std::optional<std::string> prefixStr;
|
||||
if (prefix) { prefixStr = prefix; }
|
||||
|
||||
embedding = new float[embd_size];
|
||||
wrapper->llModel->embed(textsVec, embedding, prefixStr, dimensionality, token_count, do_mean, atlas);
|
||||
} catch (std::exception const &e) {
|
||||
llmodel_set_error(error, e.what());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*embedding_size = embd_size;
|
||||
return embedding;
|
||||
}
|
||||
|
||||
void llmodel_free_embedding(float *ptr)
|
||||
{
|
||||
delete[] ptr;
|
||||
}
|
||||
|
||||
void llmodel_setThreadCount(llmodel_model model, int32_t n_threads)
|
||||
{
|
||||
LLModelWrapper *wrapper = reinterpret_cast<LLModelWrapper*>(model);
|
||||
auto *wrapper = static_cast<LLModelWrapper *>(model);
|
||||
wrapper->llModel->setThreadCount(n_threads);
|
||||
}
|
||||
|
||||
int32_t llmodel_threadCount(llmodel_model model)
|
||||
{
|
||||
LLModelWrapper *wrapper = reinterpret_cast<LLModelWrapper*>(model);
|
||||
auto *wrapper = static_cast<LLModelWrapper *>(model);
|
||||
return wrapper->llModel->threadCount();
|
||||
}
|
||||
|
||||
void llmodel_set_implementation_search_path(const char *path)
|
||||
{
|
||||
LLModel::setImplementationsSearchPath(path);
|
||||
LLModel::Implementation::setImplementationsSearchPath(path);
|
||||
}
|
||||
|
||||
const char *llmodel_get_implementation_search_path()
|
||||
{
|
||||
return LLModel::implementationsSearchPath().c_str();
|
||||
return LLModel::Implementation::implementationsSearchPath().c_str();
|
||||
}
|
||||
|
||||
struct llmodel_gpu_device* llmodel_available_gpu_devices(llmodel_model model, size_t memoryRequired, int* num_devices)
|
||||
{
|
||||
auto *wrapper = static_cast<LLModelWrapper *>(model);
|
||||
std::vector<LLModel::GPUDevice> devices = wrapper->llModel->availableGPUDevices(memoryRequired);
|
||||
|
||||
// Set the num_devices
|
||||
*num_devices = devices.size();
|
||||
|
||||
if (*num_devices == 0) return nullptr; // Return nullptr if no devices are found
|
||||
|
||||
// Allocate memory for the output array
|
||||
struct llmodel_gpu_device* output = (struct llmodel_gpu_device*) malloc(*num_devices * sizeof(struct llmodel_gpu_device));
|
||||
|
||||
for (int i = 0; i < *num_devices; i++) {
|
||||
output[i].index = devices[i].index;
|
||||
output[i].type = devices[i].type;
|
||||
output[i].heapSize = devices[i].heapSize;
|
||||
output[i].name = strdup(devices[i].name.c_str()); // Convert std::string to char* and allocate memory
|
||||
output[i].vendor = strdup(devices[i].vendor.c_str()); // Convert std::string to char* and allocate memory
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
bool llmodel_gpu_init_gpu_device_by_string(llmodel_model model, size_t memoryRequired, const char *device)
|
||||
{
|
||||
auto *wrapper = static_cast<LLModelWrapper *>(model);
|
||||
return wrapper->llModel->initializeGPUDevice(memoryRequired, std::string(device));
|
||||
}
|
||||
|
||||
bool llmodel_gpu_init_gpu_device_by_struct(llmodel_model model, const llmodel_gpu_device *device)
|
||||
{
|
||||
auto *wrapper = static_cast<LLModelWrapper *>(model);
|
||||
return wrapper->llModel->initializeGPUDevice(device->index);
|
||||
}
|
||||
|
||||
bool llmodel_gpu_init_gpu_device_by_int(llmodel_model model, int device)
|
||||
{
|
||||
auto *wrapper = static_cast<LLModelWrapper *>(model);
|
||||
return wrapper->llModel->initializeGPUDevice(device);
|
||||
}
|
||||
|
||||
bool llmodel_has_gpu_device(llmodel_model model)
|
||||
{
|
||||
auto *wrapper = static_cast<LLModelWrapper *>(model);
|
||||
return wrapper->llModel->hasGPUDevice();
|
||||
}
|
||||
|
||||
@@ -23,17 +23,6 @@ extern "C" {
|
||||
*/
|
||||
typedef void *llmodel_model;
|
||||
|
||||
/**
|
||||
* Structure containing any errors that may eventually occur
|
||||
*/
|
||||
struct llmodel_error {
|
||||
const char *message; // Human readable error description; Thread-local; guaranteed to survive until next llmodel C API call
|
||||
int code; // errno; 0 if none
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
typedef struct llmodel_error llmodel_error;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* llmodel_prompt_context structure for holding the prompt context.
|
||||
* NOTE: The implementation takes care of all the memory handling of the raw logits pointer and the
|
||||
@@ -50,14 +39,25 @@ struct llmodel_prompt_context {
|
||||
int32_t n_predict; // number of tokens to predict
|
||||
int32_t top_k; // top k logits to sample from
|
||||
float top_p; // nucleus sampling probability threshold
|
||||
float min_p; // Min P sampling
|
||||
float temp; // temperature to adjust model's output distribution
|
||||
int32_t n_batch; // number of predictions to generate in parallel
|
||||
float repeat_penalty; // penalty factor for repeated tokens
|
||||
int32_t repeat_last_n; // last n tokens to penalize
|
||||
float context_erase; // percent of context to erase if we exceed the context window
|
||||
};
|
||||
|
||||
struct llmodel_gpu_device {
|
||||
int index = 0;
|
||||
int type = 0; // same as VkPhysicalDeviceType
|
||||
size_t heapSize = 0;
|
||||
const char * name;
|
||||
const char * vendor;
|
||||
};
|
||||
|
||||
#ifndef __cplusplus
|
||||
typedef struct llmodel_prompt_context llmodel_prompt_context;
|
||||
typedef struct llmodel_gpu_device llmodel_gpu_device;
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -95,10 +95,10 @@ DEPRECATED llmodel_model llmodel_model_create(const char *model_path);
|
||||
* Recognises correct model type from file at model_path
|
||||
* @param model_path A string representing the path to the model file; will only be used to detect model type.
|
||||
* @param build_variant A string representing the implementation to use (auto, default, avxonly, ...),
|
||||
* @param error A pointer to a llmodel_error; will only be set on error.
|
||||
* @param error A pointer to a string; will only be set on error.
|
||||
* @return A pointer to the llmodel_model instance; NULL on error.
|
||||
*/
|
||||
llmodel_model llmodel_model_create2(const char *model_path, const char *build_variant, llmodel_error *error);
|
||||
llmodel_model llmodel_model_create2(const char *model_path, const char *build_variant, const char **error);
|
||||
|
||||
/**
|
||||
* Destroy a llmodel instance.
|
||||
@@ -111,17 +111,21 @@ void llmodel_model_destroy(llmodel_model model);
|
||||
* Estimate RAM requirement for a model file
|
||||
* @param model A pointer to the llmodel_model instance.
|
||||
* @param model_path A string representing the path to the model file.
|
||||
* @param n_ctx Maximum size of context window
|
||||
* @param ngl Number of GPU layers to use (Vulkan)
|
||||
* @return size greater than 0 if the model was parsed successfully, 0 if file could not be parsed.
|
||||
*/
|
||||
size_t llmodel_required_mem(llmodel_model model, const char *model_path);
|
||||
size_t llmodel_required_mem(llmodel_model model, const char *model_path, int n_ctx, int ngl);
|
||||
|
||||
/**
|
||||
* Load a model from a file.
|
||||
* @param model A pointer to the llmodel_model instance.
|
||||
* @param model_path A string representing the path to the model file.
|
||||
* @param n_ctx Maximum size of context window
|
||||
* @param ngl Number of GPU layers to use (Vulkan)
|
||||
* @return true if the model was loaded successfully, false otherwise.
|
||||
*/
|
||||
bool llmodel_loadModel(llmodel_model model, const char *model_path);
|
||||
bool llmodel_loadModel(llmodel_model model, const char *model_path, int n_ctx, int ngl);
|
||||
|
||||
/**
|
||||
* Check if a model is loaded.
|
||||
@@ -160,16 +164,52 @@ uint64_t llmodel_restore_state_data(llmodel_model model, const uint8_t *src);
|
||||
* Generate a response using the model.
|
||||
* @param model A pointer to the llmodel_model instance.
|
||||
* @param prompt A string representing the input prompt.
|
||||
* @param prompt_template A string representing the input prompt template.
|
||||
* @param prompt_callback A callback function for handling the processing of prompt.
|
||||
* @param response_callback A callback function for handling the generated response.
|
||||
* @param recalculate_callback A callback function for handling recalculation requests.
|
||||
* @param special True if special tokens in the prompt should be processed, false otherwise.
|
||||
* @param fake_reply A string to insert into context as the model's reply, or NULL to generate one.
|
||||
* @param ctx A pointer to the llmodel_prompt_context structure.
|
||||
*/
|
||||
void llmodel_prompt(llmodel_model model, const char *prompt,
|
||||
const char *prompt_template,
|
||||
llmodel_prompt_callback prompt_callback,
|
||||
llmodel_response_callback response_callback,
|
||||
llmodel_recalculate_callback recalculate_callback,
|
||||
llmodel_prompt_context *ctx);
|
||||
llmodel_prompt_context *ctx,
|
||||
bool special,
|
||||
const char *fake_reply);
|
||||
|
||||
/**
|
||||
* Generate an embedding using the model.
|
||||
* NOTE: If given NULL pointers for the model or text, or an empty text, a NULL pointer will be
|
||||
* returned. Bindings should signal an error when NULL is the return value.
|
||||
* @param model A pointer to the llmodel_model instance.
|
||||
* @param texts A pointer to a NULL-terminated array of strings representing the texts to generate an
|
||||
* embedding for.
|
||||
* @param embedding_size A pointer to a size_t type that will be set by the call indicating the length
|
||||
* of the returned floating point array.
|
||||
* @param prefix The model-specific prefix representing the embedding task, without the trailing colon. NULL for no
|
||||
* prefix.
|
||||
* @param dimensionality The embedding dimension, for use with Matryoshka-capable models. Set to -1 to for full-size.
|
||||
* @param token_count Return location for the number of prompt tokens processed, or NULL.
|
||||
* @param do_mean True to average multiple embeddings if the text is longer than the model can accept, False to
|
||||
* truncate.
|
||||
* @param atlas Try to be fully compatible with the Atlas API. Currently, this means texts longer than 8192 tokens with
|
||||
* long_text_mode="mean" will raise an error. Disabled by default.
|
||||
* @param error Return location for a malloc()ed string that will be set on error, or NULL.
|
||||
* @return A pointer to an array of floating point values passed to the calling method which then will
|
||||
* be responsible for lifetime of this memory. NULL if an error occurred.
|
||||
*/
|
||||
float *llmodel_embed(llmodel_model model, const char **texts, size_t *embedding_size, const char *prefix,
|
||||
int dimensionality, size_t *token_count, bool do_mean, bool atlas, const char **error);
|
||||
|
||||
/**
|
||||
* Frees the memory allocated by the llmodel_embedding function.
|
||||
* @param ptr A pointer to the embedding as returned from llmodel_embedding.
|
||||
*/
|
||||
void llmodel_free_embedding(float *ptr);
|
||||
|
||||
/**
|
||||
* Set the number of threads to be used by the model.
|
||||
@@ -199,6 +239,50 @@ void llmodel_set_implementation_search_path(const char *path);
|
||||
*/
|
||||
const char *llmodel_get_implementation_search_path();
|
||||
|
||||
/**
|
||||
* Get a list of available GPU devices given the memory required.
|
||||
* @return A pointer to an array of llmodel_gpu_device's whose number is given by num_devices.
|
||||
*/
|
||||
struct llmodel_gpu_device* llmodel_available_gpu_devices(llmodel_model model, size_t memoryRequired, int* num_devices);
|
||||
|
||||
/**
|
||||
* Initializes a GPU device based on a specified string criterion.
|
||||
*
|
||||
* This function initializes a GPU device based on a string identifier provided. The function
|
||||
* allows initialization based on general device type ("gpu"), vendor name ("amd", "nvidia", "intel"),
|
||||
* or any specific device name.
|
||||
*
|
||||
* @param memoryRequired The amount of memory (in bytes) required by the application or task
|
||||
* that will utilize the GPU device.
|
||||
* @param device A string specifying the desired criterion for GPU device selection. It can be:
|
||||
* - "gpu": To initialize the best available GPU.
|
||||
* - "amd", "nvidia", or "intel": To initialize the best available GPU from that vendor.
|
||||
* - A specific GPU device name: To initialize a GPU with that exact name.
|
||||
*
|
||||
* @return True if the GPU device is successfully initialized based on the provided string
|
||||
* criterion. Returns false if the desired GPU device could not be initialized.
|
||||
*/
|
||||
bool llmodel_gpu_init_gpu_device_by_string(llmodel_model model, size_t memoryRequired, const char *device);
|
||||
|
||||
/**
|
||||
* Initializes a GPU device by specifying a valid gpu device pointer.
|
||||
* @param device A gpu device pointer.
|
||||
* @return True if the GPU device is successfully initialized, false otherwise.
|
||||
*/
|
||||
bool llmodel_gpu_init_gpu_device_by_struct(llmodel_model model, const llmodel_gpu_device *device);
|
||||
|
||||
/**
|
||||
* Initializes a GPU device by its index.
|
||||
* @param device An integer representing the index of the GPU device to be initialized.
|
||||
* @return True if the GPU device is successfully initialized, false otherwise.
|
||||
*/
|
||||
bool llmodel_gpu_init_gpu_device_by_int(llmodel_model model, int device);
|
||||
|
||||
/**
|
||||
* @return True if a GPU device is successfully initialized, false otherwise.
|
||||
*/
|
||||
bool llmodel_has_gpu_device(llmodel_model model);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2,11 +2,21 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
// TODO(cebtenzzre): replace this with llama_kv_cache_seq_shift for llamamodel (GPT-J needs this as-is)
|
||||
void LLModel::recalculateContext(PromptContext &promptCtx, std::function<bool(bool)> recalculate) {
|
||||
size_t i = 0;
|
||||
promptCtx.n_past = 0;
|
||||
int n_keep = shouldAddBOS();
|
||||
const int32_t n_discard = (promptCtx.n_ctx - n_keep) * promptCtx.contextErase;
|
||||
|
||||
// Erase the first percentage of context from the tokens
|
||||
std::cerr << implementation().modelType() << ": reached the end of the context window so resizing\n";
|
||||
promptCtx.tokens.erase(promptCtx.tokens.begin() + n_keep, promptCtx.tokens.begin() + n_keep + n_discard);
|
||||
|
||||
size_t i = n_keep;
|
||||
promptCtx.n_past = n_keep;
|
||||
while (i < promptCtx.tokens.size()) {
|
||||
size_t batch_end = std::min(i + promptCtx.n_batch, promptCtx.tokens.size());
|
||||
std::vector<int32_t> batch(promptCtx.tokens.begin() + i, promptCtx.tokens.begin() + batch_end);
|
||||
@@ -26,27 +36,133 @@ stop_generating:
|
||||
recalculate(false);
|
||||
}
|
||||
|
||||
static bool parsePromptTemplate(const std::string &tmpl, std::vector<std::smatch> &placeholders, std::string &err) {
|
||||
static const std::regex placeholderRegex(R"(%[1-2](?![0-9]))");
|
||||
|
||||
auto it = std::sregex_iterator(tmpl.begin(), tmpl.end(), placeholderRegex);
|
||||
placeholders.clear();
|
||||
placeholders.insert(placeholders.end(), it, std::sregex_iterator());
|
||||
|
||||
if (placeholders.size() > 2) {
|
||||
err = "ERROR: expected at most two placeholders, got " + std::to_string(placeholders.size());
|
||||
return false;
|
||||
}
|
||||
if (placeholders.size() >= 1 && placeholders[0].str() != "%1") {
|
||||
err = "ERROR: first placeholder must be %1, got " + placeholders[0].str();
|
||||
return false;
|
||||
}
|
||||
if (placeholders.size() >= 2 && placeholders[1].str() != "%2") {
|
||||
err = "ERROR: second placeholder must be %2, got " + placeholders[1].str();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LLModel::prompt(const std::string &prompt,
|
||||
const std::string &promptTemplate,
|
||||
std::function<bool(int32_t)> promptCallback,
|
||||
std::function<bool(int32_t, const std::string&)> responseCallback,
|
||||
std::function<bool(bool)> recalculateCallback,
|
||||
PromptContext &promptCtx)
|
||||
PromptContext &promptCtx,
|
||||
bool special,
|
||||
std::string *fakeReply)
|
||||
{
|
||||
if (!isModelLoaded()) {
|
||||
std::cerr << implementation().modelType << " ERROR: prompt won't work with an unloaded model!\n";
|
||||
std::cerr << implementation().modelType() << " ERROR: prompt won't work with an unloaded model!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// tokenize the prompt
|
||||
std::vector<Token> embd_inp = tokenize(promptCtx, prompt);
|
||||
if (!supportsCompletion()) {
|
||||
std::string errorMessage = "ERROR: this model does not support text completion or chat!";
|
||||
responseCallback(-1, errorMessage);
|
||||
std::cerr << implementation().modelType() << " " << errorMessage << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// parse the prompt template
|
||||
std::vector<std::smatch> placeholders;
|
||||
{
|
||||
std::string err;
|
||||
if (!parsePromptTemplate(promptTemplate, placeholders, err)) {
|
||||
responseCallback(-1, err);
|
||||
std::cerr << err << "\n";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto old_n_past = promptCtx.n_past; // prepare to fake n_past for tokenize
|
||||
|
||||
// tokenize the user prompt
|
||||
std::vector<Token> embd_inp;
|
||||
if (placeholders.empty()) {
|
||||
// this is unusual, but well-defined
|
||||
std::cerr << __func__ << ": prompt template has no placeholder\n";
|
||||
embd_inp = tokenize(promptCtx, promptTemplate, true);
|
||||
} else {
|
||||
// template: beginning of user prompt
|
||||
const auto &phUser = placeholders[0];
|
||||
std::string userPrefix(phUser.prefix());
|
||||
if (!userPrefix.empty()) {
|
||||
embd_inp = tokenize(promptCtx, userPrefix, true);
|
||||
promptCtx.n_past += embd_inp.size();
|
||||
}
|
||||
|
||||
// user input (shouldn't have special token processing)
|
||||
auto tokens = tokenize(promptCtx, prompt, special);
|
||||
embd_inp.insert(embd_inp.end(), tokens.begin(), tokens.end());
|
||||
promptCtx.n_past += tokens.size();
|
||||
|
||||
// template: end of user prompt + start of assistant prompt
|
||||
size_t start = phUser.position() + phUser.length();
|
||||
size_t end = placeholders.size() >= 2 ? placeholders[1].position() : promptTemplate.length();
|
||||
auto userToAsst = promptTemplate.substr(start, end - start);
|
||||
if (!userToAsst.empty()) {
|
||||
tokens = tokenize(promptCtx, userToAsst, true);
|
||||
embd_inp.insert(embd_inp.end(), tokens.begin(), tokens.end());
|
||||
promptCtx.n_past += tokens.size();
|
||||
}
|
||||
}
|
||||
|
||||
promptCtx.n_past = old_n_past; // restore n_past so decodePrompt can increment it
|
||||
|
||||
// decode the user prompt
|
||||
decodePrompt(promptCallback, responseCallback, recalculateCallback, promptCtx, embd_inp);
|
||||
|
||||
// decode the assistant's reply, either generated or spoofed
|
||||
if (fakeReply == nullptr) {
|
||||
generateResponse(responseCallback, recalculateCallback, promptCtx);
|
||||
} else {
|
||||
embd_inp = tokenize(promptCtx, *fakeReply, false);
|
||||
decodePrompt(promptCallback, responseCallback, recalculateCallback, promptCtx, embd_inp);
|
||||
}
|
||||
|
||||
// decode the rest of the prompt template
|
||||
// template: end of assistant prompt
|
||||
std::string asstSuffix;
|
||||
if (placeholders.size() >= 2) {
|
||||
size_t start = placeholders[1].position() + placeholders[1].length();
|
||||
asstSuffix = promptTemplate.substr(start);
|
||||
} else {
|
||||
asstSuffix = "\n\n"; // default to a blank link, good for e.g. Alpaca
|
||||
}
|
||||
if (!asstSuffix.empty()) {
|
||||
embd_inp = tokenize(promptCtx, asstSuffix, true);
|
||||
decodePrompt(promptCallback, responseCallback, recalculateCallback, promptCtx, embd_inp);
|
||||
}
|
||||
}
|
||||
|
||||
void LLModel::decodePrompt(std::function<bool(int32_t)> promptCallback,
|
||||
std::function<bool(int32_t, const std::string&)> responseCallback,
|
||||
std::function<bool(bool)> recalculateCallback,
|
||||
PromptContext &promptCtx,
|
||||
std::vector<Token> embd_inp) {
|
||||
// save the context size
|
||||
promptCtx.n_ctx = contextLength();
|
||||
|
||||
if ((int) embd_inp.size() > promptCtx.n_ctx - 4) {
|
||||
responseCallback(-1, "ERROR: The prompt size exceeds the context window size and cannot be processed.");
|
||||
std::cerr << implementation().modelType << " ERROR: The prompt is" << embd_inp.size() <<
|
||||
"tokens and the context window is" << promptCtx.n_ctx << "!\n";
|
||||
std::cerr << implementation().modelType() << " ERROR: The prompt is " << embd_inp.size() <<
|
||||
" tokens and the context window is " << promptCtx.n_ctx << "!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -62,17 +178,12 @@ void LLModel::prompt(const std::string &prompt,
|
||||
|
||||
// Check if the context has run out...
|
||||
if (promptCtx.n_past + int32_t(batch.size()) > promptCtx.n_ctx) {
|
||||
const int32_t erasePoint = promptCtx.n_ctx * promptCtx.contextErase;
|
||||
// Erase the first percentage of context from the tokens...
|
||||
std::cerr << implementation().modelType << ": reached the end of the context window so resizing\n";
|
||||
promptCtx.tokens.erase(promptCtx.tokens.begin(), promptCtx.tokens.begin() + erasePoint);
|
||||
promptCtx.n_past = promptCtx.tokens.size();
|
||||
recalculateContext(promptCtx, recalculateCallback);
|
||||
assert(promptCtx.n_past + int32_t(batch.size()) <= promptCtx.n_ctx);
|
||||
}
|
||||
|
||||
if (!evalTokens(promptCtx, batch)) {
|
||||
std::cerr << implementation().modelType << " ERROR: Failed to process prompt\n";
|
||||
std::cerr << implementation().modelType() << " ERROR: Failed to process prompt\n";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -81,13 +192,17 @@ void LLModel::prompt(const std::string &prompt,
|
||||
if (int32_t(promptCtx.tokens.size()) == promptCtx.n_ctx)
|
||||
promptCtx.tokens.erase(promptCtx.tokens.begin());
|
||||
promptCtx.tokens.push_back(batch.at(t));
|
||||
promptCtx.n_past += 1;
|
||||
if (!promptCallback(batch.at(t)))
|
||||
return;
|
||||
}
|
||||
promptCtx.n_past += batch.size();
|
||||
i = batch_end;
|
||||
}
|
||||
}
|
||||
|
||||
void LLModel::generateResponse(std::function<bool(int32_t, const std::string&)> responseCallback,
|
||||
std::function<bool(bool)> recalculateCallback,
|
||||
PromptContext &promptCtx) {
|
||||
std::string cachedResponse;
|
||||
std::vector<Token> cachedTokens;
|
||||
std::unordered_set<std::string> reversePrompts
|
||||
@@ -101,22 +216,15 @@ void LLModel::prompt(const std::string &prompt,
|
||||
|
||||
// Check if the context has run out...
|
||||
if (promptCtx.n_past + 1 > promptCtx.n_ctx) {
|
||||
const int32_t erasePoint = promptCtx.n_ctx * promptCtx.contextErase;
|
||||
// Erase the first percentage of context from the tokens...
|
||||
std::cerr << implementation().modelType << ": reached the end of the context window so resizing\n";
|
||||
promptCtx.tokens.erase(promptCtx.tokens.begin(), promptCtx.tokens.begin() + erasePoint);
|
||||
promptCtx.n_past = promptCtx.tokens.size();
|
||||
recalculateContext(promptCtx, recalculateCallback);
|
||||
assert(promptCtx.n_past + 1 <= promptCtx.n_ctx);
|
||||
}
|
||||
|
||||
if (!evalTokens(promptCtx, { id })) {
|
||||
std::cerr << implementation().modelType << " ERROR: Failed to predict next token\n";
|
||||
std::cerr << implementation().modelType() << " ERROR: Failed to predict next token\n";
|
||||
return;
|
||||
}
|
||||
|
||||
promptCtx.n_past += 1;
|
||||
|
||||
// display text
|
||||
for (const auto token : endTokens()) {
|
||||
if (id == token) return;
|
||||
@@ -151,6 +259,7 @@ void LLModel::prompt(const std::string &prompt,
|
||||
if (int32_t(promptCtx.tokens.size()) == promptCtx.n_ctx)
|
||||
promptCtx.tokens.erase(promptCtx.tokens.begin());
|
||||
promptCtx.tokens.push_back(t);
|
||||
promptCtx.n_past += 1;
|
||||
//TODO: Conversion to std::string can be avoided here...
|
||||
if (!responseCallback(t, std::string(tokenToString(t))))
|
||||
return;
|
||||
@@ -158,3 +267,31 @@ void LLModel::prompt(const std::string &prompt,
|
||||
cachedTokens.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void LLModel::embed(
|
||||
const std::vector<std::string> &texts, float *embeddings, std::optional<std::string> prefix, int dimensionality,
|
||||
size_t *tokenCount, bool doMean, bool atlas
|
||||
) {
|
||||
(void)texts;
|
||||
(void)embeddings;
|
||||
(void)prefix;
|
||||
(void)dimensionality;
|
||||
(void)tokenCount;
|
||||
(void)doMean;
|
||||
(void)atlas;
|
||||
throw std::logic_error(std::string(implementation().modelType()) + " does not support embeddings");
|
||||
}
|
||||
|
||||
void LLModel::embed(
|
||||
const std::vector<std::string> &texts, float *embeddings, bool isRetrieval, int dimensionality, size_t *tokenCount,
|
||||
bool doMean, bool atlas
|
||||
) {
|
||||
(void)texts;
|
||||
(void)embeddings;
|
||||
(void)isRetrieval;
|
||||
(void)dimensionality;
|
||||
(void)tokenCount;
|
||||
(void)doMean;
|
||||
(void)atlas;
|
||||
throw std::logic_error(std::string(implementation().modelType()) + " does not support embeddings");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <ggml.h>
|
||||
|
||||
struct llm_buffer {
|
||||
@@ -34,3 +35,12 @@ struct llm_kv_cache {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline void ggml_graph_compute_g4a(llm_buffer& buf, ggml_cgraph * graph, int n_threads) {
|
||||
struct ggml_cplan plan = ggml_graph_plan(graph, n_threads);
|
||||
if (plan.work_size > 0) {
|
||||
buf.resize(plan.work_size);
|
||||
plan.work_data = buf.addr;
|
||||
}
|
||||
ggml_graph_compute(graph, &plan);
|
||||
}
|
||||
|
||||
@@ -1,893 +0,0 @@
|
||||
#define MPT_H_I_KNOW_WHAT_I_AM_DOING_WHEN_INCLUDING_THIS_FILE
|
||||
#include "mpt_impl.h"
|
||||
|
||||
#include "utils.h"
|
||||
#include "llmodel_shared.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#if defined(_WIN32) && defined(_MSC_VER)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <regex>
|
||||
#include <ggml.h>
|
||||
|
||||
|
||||
namespace {
|
||||
const char *modelType_ = "MPT";
|
||||
}
|
||||
|
||||
// default hparams (MPT 7B)
|
||||
struct mpt_hparams {
|
||||
int32_t n_vocab = 50432;
|
||||
int32_t n_ctx = 2048;
|
||||
int32_t n_embd = 4096;
|
||||
int32_t n_head = 32;
|
||||
int32_t n_layer = 32;
|
||||
float alibi_bias_max = 8;
|
||||
float clip_qkv = 0;
|
||||
int32_t expand = 4;
|
||||
int32_t f16 = 1;
|
||||
};
|
||||
|
||||
struct mpt_layer {
|
||||
// normalization
|
||||
struct ggml_tensor * norm_1_w;
|
||||
struct ggml_tensor * norm_2_w;
|
||||
|
||||
// attention
|
||||
struct ggml_tensor * attn_Wqkv_w;
|
||||
struct ggml_tensor * attn_out_proj_w;
|
||||
|
||||
// ff
|
||||
struct ggml_tensor * ffn_up_proj_w;
|
||||
struct ggml_tensor * ffn_down_proj_w;
|
||||
};
|
||||
|
||||
struct mpt_model {
|
||||
mpt_hparams hparams;
|
||||
|
||||
// normalization
|
||||
struct ggml_tensor * norm_f_w;
|
||||
|
||||
struct ggml_tensor * wte; // position embedding
|
||||
|
||||
// mpt does weight tying
|
||||
|
||||
std::vector<mpt_layer> layers;
|
||||
|
||||
struct llm_kv_cache kv_self;
|
||||
struct ggml_context * ctx;
|
||||
std::map<std::string, struct ggml_tensor *> tensors;
|
||||
|
||||
|
||||
llm_buffer eval_buf;
|
||||
llm_buffer scr0_buf;
|
||||
llm_buffer scr1_buf;
|
||||
|
||||
~mpt_model() {
|
||||
if (ctx) {
|
||||
ggml_free(ctx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static bool kv_cache_init(
|
||||
const struct mpt_hparams & hparams,
|
||||
struct llm_kv_cache & cache,
|
||||
ggml_type wtype,
|
||||
int n_ctx) {
|
||||
const int n_embd = hparams.n_embd;
|
||||
const int n_layer = hparams.n_layer;
|
||||
|
||||
const int64_t n_mem = (int64_t)n_layer*n_ctx;
|
||||
const int64_t n_elements = n_embd*n_mem;
|
||||
|
||||
cache.buf.resize(2u*n_elements*ggml_type_size(wtype) + 2_MiB);
|
||||
|
||||
struct ggml_init_params params;
|
||||
params.mem_size = cache.buf.size;
|
||||
params.mem_buffer = cache.buf.addr;
|
||||
params.no_alloc = false;
|
||||
|
||||
cache.ctx = ggml_init(params);
|
||||
|
||||
if (!cache.ctx) {
|
||||
fprintf(stderr, "%s: failed to allocate memory for kv cache\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
cache.k = ggml_new_tensor_1d(cache.ctx, wtype, n_elements);
|
||||
cache.v = ggml_new_tensor_1d(cache.ctx, wtype, n_elements);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// load the model's weights from a stream. if mem_req ptr is passed the model is
|
||||
// only partially parsed to estimate required memory
|
||||
bool mpt_model_load(const std::string &fname, std::istream &fin, mpt_model & model, gpt_vocab & vocab, size_t * mem_req) {
|
||||
printf("%s: loading model from '%s' - please wait ...\n", __func__, fname.c_str());
|
||||
if (mem_req != nullptr) {
|
||||
*mem_req = 0;
|
||||
}
|
||||
|
||||
// verify magic
|
||||
{
|
||||
uint32_t magic;
|
||||
fin.read((char *) &magic, sizeof(magic));
|
||||
if (magic != 0x67676d6d) {
|
||||
fprintf(stderr, "%s: invalid model file '%s' (bad magic)\n", __func__, fname.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// load hparams
|
||||
{
|
||||
auto & hparams = model.hparams;
|
||||
|
||||
fin.read((char *) &hparams.n_vocab, sizeof(hparams.n_vocab));
|
||||
fin.read((char *) &hparams.n_ctx, sizeof(hparams.n_ctx));
|
||||
fin.read((char *) &hparams.n_layer, sizeof(hparams.n_layer));
|
||||
fin.read((char *) &hparams.n_head, sizeof(hparams.n_head));
|
||||
fin.read((char *) &hparams.n_embd, sizeof(hparams.n_embd));
|
||||
fin.read((char *) &hparams.alibi_bias_max, sizeof(hparams.alibi_bias_max));
|
||||
fin.read((char *) &hparams.clip_qkv, sizeof(hparams.clip_qkv));
|
||||
fin.read((char *) &hparams.f16, sizeof(hparams.f16));
|
||||
|
||||
printf("%s: n_vocab = %d\n", __func__, hparams.n_vocab);
|
||||
printf("%s: n_ctx = %d\n", __func__, hparams.n_ctx);
|
||||
printf("%s: n_embd = %d\n", __func__, hparams.n_embd);
|
||||
printf("%s: n_head = %d\n", __func__, hparams.n_head);
|
||||
printf("%s: n_layer = %d\n", __func__, hparams.n_layer);
|
||||
printf("%s: alibi_bias_max = %f\n", __func__, hparams.alibi_bias_max);
|
||||
printf("%s: clip_qkv = %f\n", __func__, hparams.clip_qkv);
|
||||
printf("%s: ftype = %d\n", __func__, hparams.f16);
|
||||
}
|
||||
|
||||
// load vocab
|
||||
{
|
||||
int32_t n_vocab = model.hparams.n_vocab;
|
||||
fin.read((char *) &n_vocab, sizeof(n_vocab));
|
||||
|
||||
if (n_vocab != model.hparams.n_vocab) {
|
||||
fprintf(stderr, "%s: invalid model file '%s' (bad vocab size %d != %d)\n",
|
||||
__func__, fname.c_str(), n_vocab, model.hparams.n_vocab);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string word;
|
||||
for (int i = 0; i < n_vocab; i++) {
|
||||
uint32_t len;
|
||||
fin.read((char *) &len, sizeof(len));
|
||||
bool special = false;
|
||||
if (len & (1<<31)) {
|
||||
len = len &~ (1<<31);
|
||||
special = true;
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
word.resize(len);
|
||||
fin.read((char *) word.data(), len);
|
||||
vocab.token_to_id[word] = i;
|
||||
vocab.id_to_token[i] = word;
|
||||
}
|
||||
|
||||
if(special) {
|
||||
vocab.add_special_token(word);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for the big tensors, we have the option to store the data in 16-bit floats or quantized
|
||||
// in order to save memory and also to speed up the computation
|
||||
ggml_type wtype = GGML_TYPE_COUNT;
|
||||
switch (model.hparams.f16) {
|
||||
case 0: wtype = GGML_TYPE_F32; break;
|
||||
case 1: wtype = GGML_TYPE_F16; break;
|
||||
case 2: wtype = GGML_TYPE_Q4_0; break;
|
||||
case 3: wtype = GGML_TYPE_Q4_1; break;
|
||||
case 5: wtype = GGML_TYPE_Q4_2; break;
|
||||
default:
|
||||
{
|
||||
fprintf(stderr, "%s: invalid model file '%s' (bad f16 value %d)\n",
|
||||
__func__, fname.c_str(), model.hparams.f16);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto & ctx = model.ctx;
|
||||
|
||||
size_t ctx_size = 0;
|
||||
|
||||
{
|
||||
const auto & hparams = model.hparams;
|
||||
|
||||
const int n_embd = hparams.n_embd;
|
||||
const int n_layer = hparams.n_layer;
|
||||
const int n_ctx = hparams.n_ctx;
|
||||
const int n_vocab = hparams.n_vocab;
|
||||
const int expand = hparams.expand;
|
||||
|
||||
|
||||
ctx_size += n_embd*ggml_type_sizef(GGML_TYPE_F32); // ln_f_w
|
||||
|
||||
ctx_size += n_embd*n_vocab*ggml_type_sizef(GGML_TYPE_F32); // wte
|
||||
|
||||
ctx_size += n_layer*(n_embd*ggml_type_sizef(GGML_TYPE_F32)); // norm_1_w
|
||||
ctx_size += n_layer*(n_embd*ggml_type_sizef(GGML_TYPE_F32)); // norm_2_w
|
||||
|
||||
ctx_size += n_layer*(3*n_embd*n_embd*ggml_type_sizef(wtype)); // attn_Wqkv_w
|
||||
ctx_size += n_layer*(n_embd*n_embd*ggml_type_sizef(wtype)); // attn_out_proj_w
|
||||
|
||||
ctx_size += n_layer*(expand*n_embd*n_embd*ggml_type_sizef(wtype)); // ffn_up_proj_w
|
||||
ctx_size += n_layer*(expand*n_embd*n_embd*ggml_type_sizef(wtype)); // ffn_down_proj_w
|
||||
|
||||
ctx_size += n_ctx*n_layer*n_embd*ggml_type_sizef(GGML_TYPE_F16); // memory_k
|
||||
ctx_size += n_ctx*n_layer*n_embd*ggml_type_sizef(GGML_TYPE_F16); // memory_v
|
||||
|
||||
// TODO probably less now?
|
||||
ctx_size += (5 + 10*n_layer)*256; // object overhead
|
||||
|
||||
printf("%s: ggml ctx size = %6.2f MB\n", __func__, ctx_size/(1024.0*1024.0));
|
||||
}
|
||||
|
||||
if (mem_req != nullptr) {
|
||||
*mem_req += ctx_size;
|
||||
const int n_embd = model.hparams.n_embd;
|
||||
const int n_layer = model.hparams.n_layer;
|
||||
|
||||
const int64_t n_mem = (int64_t)n_layer*model.hparams.n_ctx;
|
||||
const int64_t n_elements = n_embd*n_mem;
|
||||
|
||||
*mem_req += (2u*n_elements*ggml_type_size(wtype) + 2_MiB);
|
||||
return false;
|
||||
}
|
||||
|
||||
// create the ggml context
|
||||
{
|
||||
struct ggml_init_params params = {
|
||||
.mem_size = ctx_size,
|
||||
.mem_buffer = NULL,
|
||||
.no_alloc = false,
|
||||
};
|
||||
|
||||
model.ctx = ggml_init(params);
|
||||
if (!model.ctx) {
|
||||
fprintf(stderr, "%s: ggml_init() failed\n", __func__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// prepare memory for the weights
|
||||
{
|
||||
const auto & hparams = model.hparams;
|
||||
|
||||
const int n_embd = hparams.n_embd;
|
||||
const int n_layer = hparams.n_layer;
|
||||
const int n_vocab = hparams.n_vocab;
|
||||
const int expand = hparams.expand;
|
||||
|
||||
model.layers.resize(n_layer);
|
||||
|
||||
model.wte = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, n_embd, n_vocab);
|
||||
model.norm_f_w = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_embd);
|
||||
|
||||
// map by name
|
||||
model.tensors["transformer.wte.weight"] = model.wte;
|
||||
model.tensors["transformer.norm_f.weight"] = model.norm_f_w;
|
||||
|
||||
for (int i = 0; i < n_layer; ++i) {
|
||||
auto & layer = model.layers[i];
|
||||
|
||||
layer.norm_1_w = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_embd);
|
||||
layer.norm_2_w = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, n_embd);
|
||||
|
||||
layer.attn_Wqkv_w = ggml_new_tensor_2d(ctx, wtype, n_embd, n_embd * 3);
|
||||
layer.attn_out_proj_w = ggml_new_tensor_2d(ctx, wtype, n_embd, n_embd);
|
||||
layer.ffn_up_proj_w = ggml_new_tensor_2d(ctx, wtype, n_embd, expand*n_embd);
|
||||
layer.ffn_down_proj_w = ggml_new_tensor_2d(ctx, wtype, expand*n_embd, n_embd);
|
||||
|
||||
// map by name
|
||||
model.tensors["transformer.blocks." + std::to_string(i) + ".norm_1.weight"] = layer.norm_1_w;
|
||||
model.tensors["transformer.blocks." + std::to_string(i) + ".norm_2.weight"] = layer.norm_2_w;
|
||||
model.tensors["transformer.blocks." + std::to_string(i) + ".attn.Wqkv.weight"] = layer.attn_Wqkv_w;
|
||||
model.tensors["transformer.blocks." + std::to_string(i) + ".attn.out_proj.weight"] = layer.attn_out_proj_w;
|
||||
|
||||
model.tensors["transformer.blocks." + std::to_string(i) + ".ffn.up_proj.weight"] = layer.ffn_up_proj_w;
|
||||
model.tensors["transformer.blocks." + std::to_string(i) + ".ffn.down_proj.weight"] = layer.ffn_down_proj_w;
|
||||
}
|
||||
}
|
||||
|
||||
// key + value memory
|
||||
{
|
||||
const auto & hparams = model.hparams;
|
||||
if (!kv_cache_init(hparams, model.kv_self, GGML_TYPE_F16, model.hparams.n_ctx)) {
|
||||
fprintf(stderr, "%s: kv_cache_init() failed for self-attention cache\n", __func__);
|
||||
ggml_free(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t memory_size = ggml_nbytes(model.kv_self.k) + ggml_nbytes(model.kv_self.v);
|
||||
printf("%s: kv self size = %7.2f MB\n", __func__, memory_size / 1024.0 / 1024.0);
|
||||
}
|
||||
|
||||
// load weights
|
||||
{
|
||||
int n_tensors = 0;
|
||||
size_t total_size = 0;
|
||||
|
||||
printf("%s: ", __func__);
|
||||
|
||||
while (true) {
|
||||
int32_t n_dims;
|
||||
int32_t length;
|
||||
int32_t ttype;
|
||||
|
||||
fin.read(reinterpret_cast<char *>(&n_dims), sizeof(n_dims));
|
||||
fin.read(reinterpret_cast<char *>(&length), sizeof(length));
|
||||
fin.read(reinterpret_cast<char *>(&ttype), sizeof(ttype));
|
||||
|
||||
if (fin.eof()) {
|
||||
break;
|
||||
}
|
||||
|
||||
int32_t nelements = 1;
|
||||
int32_t ne[2] = { 1, 1 };
|
||||
for (int i = 0; i < n_dims; ++i) {
|
||||
fin.read(reinterpret_cast<char *>(&ne[i]), sizeof(ne[i]));
|
||||
nelements *= ne[i];
|
||||
}
|
||||
|
||||
std::string name(length, 0);
|
||||
fin.read(&name[0], length);
|
||||
|
||||
if (model.tensors.find(name.data()) == model.tensors.end()) {
|
||||
fprintf(stderr, "%s: unknown tensor '%s' in model file\n", __func__, name.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto tensor = model.tensors[name.data()];
|
||||
if (ggml_nelements(tensor) != nelements) {
|
||||
fprintf(stderr, "%s: tensor '%s' has wrong size in model file\n", __func__, name.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tensor->ne[0] != ne[0] || tensor->ne[1] != ne[1]) {
|
||||
fprintf(stderr, "%s: tensor '%s' has wrong shape in model file: got [%" PRId64 ", %" PRId64 "], expected [%d, %d]\n",
|
||||
__func__, name.data(), tensor->ne[0], tensor->ne[1], ne[0], ne[1]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// for debugging
|
||||
if (0) {
|
||||
printf("%24s - [%5d, %5d], type = %6s, %6.2f MB, %9zu bytes\n", name.data(), ne[0], ne[1], ggml_type_name(ggml_type(ttype)), ggml_nbytes(tensor)/1024.0/1024.0, ggml_nbytes(tensor));
|
||||
}
|
||||
|
||||
const size_t bpe = ggml_type_size(ggml_type(ttype));
|
||||
|
||||
if ((nelements*bpe)/ggml_blck_size(tensor->type) != ggml_nbytes(tensor)) {
|
||||
fprintf(stderr, "%s: tensor '%s' has wrong size in model file: got %zu, expected %zu\n",
|
||||
__func__, name.data(), ggml_nbytes(tensor), nelements*bpe);
|
||||
return false;
|
||||
}
|
||||
|
||||
fin.read(reinterpret_cast<char *>(tensor->data), ggml_nbytes(tensor));
|
||||
|
||||
//printf("%42s - [%5d, %5d], type = %6s, %6.2f MB\n", name.data(), ne[0], ne[1], ttype == 0 ? "float" : "f16", ggml_nbytes(tensor)/1024.0/1024.0);
|
||||
total_size += ggml_nbytes(tensor);
|
||||
if (++n_tensors % 8 == 0) {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
printf(" done\n");
|
||||
|
||||
printf("%s: model size = %8.2f MB / num tensors = %d\n", __func__, total_size/1024.0/1024.0, n_tensors);
|
||||
}
|
||||
|
||||
model.scr0_buf.resize(256u * 1024 * 1024);
|
||||
model.scr1_buf.resize(256u * 1024 * 1024);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// load the model's weights from a file path
|
||||
bool mpt_model_load(const std::string & fname, mpt_model & model, gpt_vocab & vocab) {
|
||||
|
||||
auto fin = std::ifstream(fname, std::ios::binary);
|
||||
if (!fin) {
|
||||
fprintf(stderr, "%s: failed to open '%s'\n", __func__, fname.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool loaded = mpt_model_load(fname, fin, model, vocab, nullptr);
|
||||
fin.close();
|
||||
return loaded;
|
||||
}
|
||||
|
||||
bool mpt_eval(
|
||||
mpt_model & model,
|
||||
const int n_threads,
|
||||
const int n_past,
|
||||
const std::vector<int> & embd_inp,
|
||||
std::vector<float> & embd_w,
|
||||
size_t & mem_per_token) {
|
||||
const int N = embd_inp.size();
|
||||
|
||||
const auto & hparams = model.hparams;
|
||||
|
||||
const int n_embd = hparams.n_embd;
|
||||
const int n_layer = hparams.n_layer;
|
||||
const int n_ctx = hparams.n_ctx;
|
||||
const int n_head = hparams.n_head;
|
||||
const int n_vocab = hparams.n_vocab;
|
||||
|
||||
const size_t init_buf_size = 1024_MiB;
|
||||
if (!model.eval_buf.addr || model.eval_buf.size < init_buf_size)
|
||||
model.eval_buf.resize(init_buf_size);
|
||||
|
||||
if (mem_per_token > 0 && mem_per_token*N > model.eval_buf.size) {
|
||||
const size_t buf_size_new = 1.1*(mem_per_token*N); // add 10% to account for ggml object overhead
|
||||
// printf("\n%s: reallocating buffer from %zu to %zu bytes\n", __func__, model.buf.size, buf_size_new);
|
||||
|
||||
// reallocate
|
||||
model.eval_buf.resize(buf_size_new);
|
||||
if (model.eval_buf.addr == nullptr) {
|
||||
fprintf(stderr, "%s: failed to allocate %zu bytes\n", __func__, model.eval_buf.size);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct ggml_init_params params = {
|
||||
.mem_size = model.eval_buf.size,
|
||||
.mem_buffer = model.eval_buf.addr,
|
||||
.no_alloc = false
|
||||
};
|
||||
|
||||
struct ggml_context * ctx0 = ggml_init(params);
|
||||
struct ggml_cgraph gf = {};
|
||||
gf.n_threads = n_threads;
|
||||
|
||||
struct ggml_tensor * embd = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, N);
|
||||
memcpy(embd->data, embd_inp.data(), N*ggml_element_size(embd));
|
||||
|
||||
// wte
|
||||
struct ggml_tensor * inpL = ggml_get_rows(ctx0, model.wte, embd);
|
||||
|
||||
for (int il = 0; il < n_layer; ++il) {
|
||||
ggml_set_scratch(ctx0, {0, model.scr0_buf.size, model.scr0_buf.addr, });
|
||||
|
||||
struct ggml_tensor * inpSA = inpL;
|
||||
struct ggml_tensor * cur = inpSA;
|
||||
// self-attention
|
||||
{
|
||||
|
||||
// norm1
|
||||
cur = ggml_norm(ctx0, cur);
|
||||
cur = ggml_mul(ctx0,
|
||||
ggml_repeat(ctx0, model.layers[il].norm_1_w, cur),
|
||||
cur);
|
||||
// compute QKV
|
||||
cur = ggml_mul_mat(ctx0,
|
||||
model.layers[il].attn_Wqkv_w,
|
||||
cur);
|
||||
|
||||
// TODO: clip_qkv
|
||||
struct ggml_tensor * Qcur = ggml_cont(ctx0, ggml_view_2d(ctx0, cur, n_embd, N, cur->nb[1], 0*ggml_element_size(cur)*n_embd));
|
||||
struct ggml_tensor * Kcur = ggml_cont(ctx0, ggml_view_2d(ctx0, cur, n_embd, N, cur->nb[1], 1*ggml_element_size(cur)*n_embd));
|
||||
struct ggml_tensor * Vcur = ggml_cont(ctx0, ggml_view_2d(ctx0, cur, n_embd, N, cur->nb[1], 2*ggml_element_size(cur)*n_embd));
|
||||
|
||||
// TODO: qk_ln? (seems to be False in MPT-7B configs)
|
||||
{
|
||||
Vcur = ggml_transpose(ctx0, Vcur);
|
||||
|
||||
struct ggml_tensor * k = ggml_view_1d(ctx0, model.kv_self.k, N*n_embd, (ggml_element_size(model.kv_self.k)*n_embd)*(il*n_ctx + n_past));
|
||||
struct ggml_tensor * v = ggml_view_2d(ctx0, model.kv_self.v, N, n_embd,
|
||||
( n_ctx)*ggml_element_size(model.kv_self.v),
|
||||
(il*n_ctx)*ggml_element_size(model.kv_self.v)*n_embd + n_past*ggml_element_size(model.kv_self.v));
|
||||
|
||||
ggml_build_forward_expand(&gf, ggml_cpy(ctx0, Kcur, k));
|
||||
ggml_build_forward_expand(&gf, ggml_cpy(ctx0, Vcur, v));
|
||||
}
|
||||
// Q = Qcur.contiguous().view(n_embd/n_head, n_head, N).permute(0, 2, 1, 3)
|
||||
struct ggml_tensor * Q =
|
||||
ggml_permute(ctx0,
|
||||
ggml_reshape_3d(ctx0, Qcur, n_embd/n_head, n_head, N),
|
||||
0, 2, 1, 3);
|
||||
|
||||
struct ggml_tensor * K =
|
||||
ggml_permute(ctx0,
|
||||
ggml_reshape_3d(ctx0,
|
||||
ggml_view_1d(ctx0, model.kv_self.k, (n_past + N)*n_embd, il*n_ctx*ggml_element_size(model.kv_self.k)*n_embd),
|
||||
n_embd/n_head, n_head, n_past + N),
|
||||
0, 2, 1, 3);
|
||||
|
||||
// K * Q
|
||||
struct ggml_tensor * KQ = ggml_mul_mat(ctx0, K, Q);
|
||||
|
||||
// KQ_scaled = KQ / sqrt(n_embd/n_head)
|
||||
struct ggml_tensor * KQ_scaled =
|
||||
ggml_scale(ctx0,
|
||||
KQ,
|
||||
ggml_new_f32(ctx0, 1.0f/sqrt(float(n_embd)/n_head))
|
||||
);
|
||||
|
||||
|
||||
// Alibi
|
||||
struct ggml_tensor * KQ_scaled_biased = ggml_alibi(ctx0, ggml_cont(ctx0, KQ_scaled), n_past, n_head);
|
||||
|
||||
// KQ_masked = mask_past(KQ_scaled)
|
||||
struct ggml_tensor * KQ_masked = ggml_diag_mask_inf(ctx0, KQ_scaled_biased, n_past);
|
||||
|
||||
// KQ = soft_max(KQ_masked)
|
||||
struct ggml_tensor * KQ_soft_max = ggml_soft_max(ctx0, KQ_masked);
|
||||
|
||||
// V_trans = Vmem.view(n_embd/n_head, n_head, n_past + N).permute(1, 2, 0, 3).contiguous()
|
||||
struct ggml_tensor * V =
|
||||
ggml_view_3d(ctx0, model.kv_self.v,
|
||||
n_past + N, n_embd/n_head, n_head,
|
||||
n_ctx*ggml_element_size(model.kv_self.v),
|
||||
n_ctx*ggml_element_size(model.kv_self.v)*n_embd/n_head,
|
||||
il*n_ctx*ggml_element_size(model.kv_self.v)*n_embd);
|
||||
|
||||
// KQV = transpose(V) * KQ_soft_max
|
||||
struct ggml_tensor * KQV = ggml_mul_mat(ctx0, V, KQ_soft_max);
|
||||
|
||||
// KQV_merged = KQV.permute(0, 2, 1, 3)
|
||||
struct ggml_tensor * KQV_merged = ggml_permute(ctx0, KQV, 0, 2, 1, 3);
|
||||
|
||||
// cur = KQV_merged.contiguous().view(n_embd, N)
|
||||
cur = ggml_cpy(ctx0,
|
||||
KQV_merged,
|
||||
ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, n_embd, N));
|
||||
|
||||
// projection (no bias)
|
||||
cur = ggml_mul_mat(ctx0,
|
||||
model.layers[il].attn_out_proj_w,
|
||||
cur);
|
||||
}
|
||||
|
||||
ggml_set_scratch(ctx0, {0, model.scr1_buf.size, model.scr1_buf.addr, });
|
||||
// residual
|
||||
struct ggml_tensor * resSA = ggml_add(ctx0, cur, inpSA);
|
||||
// feed-forward network
|
||||
{
|
||||
cur = resSA;
|
||||
// norm2
|
||||
cur = ggml_norm(ctx0, cur);
|
||||
cur = ggml_mul(ctx0,
|
||||
ggml_repeat(ctx0, model.layers[il].norm_2_w, cur),
|
||||
cur);
|
||||
// ffn
|
||||
cur = ggml_mul_mat(ctx0,
|
||||
model.layers[il].ffn_up_proj_w,
|
||||
cur);
|
||||
cur = ggml_gelu(ctx0, cur);
|
||||
cur = ggml_mul_mat(ctx0,
|
||||
model.layers[il].ffn_down_proj_w,
|
||||
cur);
|
||||
|
||||
}
|
||||
|
||||
// self-attention + FF
|
||||
inpL = ggml_add(ctx0, cur, resSA);
|
||||
}
|
||||
ggml_set_scratch(ctx0, {0, model.scr0_buf.size, model.scr0_buf.addr, });
|
||||
|
||||
struct ggml_tensor * out = inpL;
|
||||
// -> logits
|
||||
{
|
||||
out = ggml_norm(ctx0, out);
|
||||
out = ggml_mul(ctx0,
|
||||
ggml_repeat(ctx0, model.norm_f_w, out),
|
||||
out);
|
||||
ggml_set_scratch(ctx0, { 0, 0, nullptr, });
|
||||
out = ggml_mul_mat(ctx0, model.wte, out);
|
||||
}
|
||||
|
||||
|
||||
// run the computation
|
||||
ggml_build_forward_expand(&gf, out);
|
||||
ggml_graph_compute (ctx0, &gf);
|
||||
|
||||
|
||||
// return result for just the last token
|
||||
embd_w.resize(n_vocab);
|
||||
memcpy(embd_w.data(), (float *) ggml_get_data(out) + (n_vocab*(N-1)), sizeof(float)*n_vocab);
|
||||
|
||||
if (mem_per_token == 0) {
|
||||
mem_per_token = ggml_used_mem(ctx0)/N;
|
||||
}
|
||||
//printf("used_mem = %zu\n", ggml_used_mem(ctx0));
|
||||
|
||||
ggml_free(ctx0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#define MPT_MAX_RNG_STATE 64*1024
|
||||
|
||||
size_t mpt_get_state_size(const mpt_model &model)
|
||||
{
|
||||
// we don't know size of rng until we actually serialize it. so reserve more than enough memory for its serialized state.
|
||||
// for reference, std::mt19937(1337) serializes to 6701 bytes.
|
||||
const size_t s_rng_size = sizeof(size_t);
|
||||
const size_t s_rng = MPT_MAX_RNG_STATE;
|
||||
const size_t s_kv_size = sizeof(size_t);
|
||||
const size_t s_kv_ntok = sizeof(int);
|
||||
const size_t s_kv = model.kv_self.buf.size;
|
||||
const size_t s_total = (
|
||||
+ s_rng_size
|
||||
+ s_rng
|
||||
+ s_kv_size
|
||||
+ s_kv_ntok
|
||||
+ s_kv
|
||||
);
|
||||
fflush(stdout);
|
||||
return s_total;
|
||||
}
|
||||
|
||||
size_t mpt_copy_state_data(const mpt_model &model, const std::mt19937 &rng, uint8_t *dest)
|
||||
{
|
||||
uint8_t * out = dest;
|
||||
fflush(stdout);
|
||||
// copy rng
|
||||
{
|
||||
std::stringstream rng_ss;
|
||||
rng_ss << rng;
|
||||
|
||||
const size_t rng_size = rng_ss.str().size();
|
||||
char rng_buf[MPT_MAX_RNG_STATE];
|
||||
|
||||
memset(&rng_buf[0], 0, MPT_MAX_RNG_STATE);
|
||||
memcpy(&rng_buf[0], rng_ss.str().data(), rng_ss.str().size());
|
||||
|
||||
memcpy(out, &rng_size, sizeof(rng_size)); out += sizeof(rng_size);
|
||||
memcpy(out, &rng_buf[0], MPT_MAX_RNG_STATE); out += MPT_MAX_RNG_STATE;
|
||||
}
|
||||
|
||||
// copy kv cache
|
||||
{
|
||||
const size_t kv_size = model.kv_self.buf.size;
|
||||
const int kv_ntok = model.kv_self.n;
|
||||
|
||||
memcpy(out, &kv_size, sizeof(kv_size)); out += sizeof(kv_size);
|
||||
memcpy(out, &kv_ntok, sizeof(kv_ntok)); out += sizeof(kv_ntok);
|
||||
|
||||
if (kv_size) {
|
||||
memcpy(out, model.kv_self.buf.addr, kv_size); out += kv_size;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t written = out - dest;
|
||||
assert(written == mpt_get_state_size(model));
|
||||
fflush(stdout);
|
||||
return written;
|
||||
}
|
||||
|
||||
size_t mpt_set_state_data(mpt_model *model, std::mt19937 *rng, const uint8_t *src)
|
||||
{
|
||||
const uint8_t * in = src;
|
||||
|
||||
// set rng
|
||||
{
|
||||
size_t rng_size;
|
||||
char rng_buf[MPT_MAX_RNG_STATE];
|
||||
|
||||
memcpy(&rng_size, in, sizeof(rng_size)); in += sizeof(rng_size);
|
||||
memcpy(&rng_buf[0], in, MPT_MAX_RNG_STATE); in += MPT_MAX_RNG_STATE;
|
||||
|
||||
std::stringstream rng_ss;
|
||||
rng_ss.str(std::string(&rng_buf[0], rng_size));
|
||||
rng_ss >> *rng;
|
||||
|
||||
assert(rng_ss.fail() == false);
|
||||
}
|
||||
|
||||
// set kv cache
|
||||
{
|
||||
size_t kv_size;
|
||||
int kv_ntok;
|
||||
|
||||
memcpy(&kv_size, in, sizeof(kv_size)); in += sizeof(kv_size);
|
||||
memcpy(&kv_ntok, in, sizeof(kv_ntok)); in += sizeof(kv_ntok);
|
||||
|
||||
if (kv_size) {
|
||||
assert(model->kv_self.buf.size == kv_size);
|
||||
|
||||
void * k_data = model->kv_self.k->data; // remember data pointers
|
||||
void * v_data = model->kv_self.v->data; // because their value is stored in buf and overwritten by memcpy
|
||||
|
||||
memcpy(model->kv_self.buf.addr, in, kv_size); in += kv_size;
|
||||
|
||||
model->kv_self.k->data = k_data; // restore correct data pointers
|
||||
model->kv_self.v->data = v_data;
|
||||
|
||||
}
|
||||
|
||||
model->kv_self.n = kv_ntok;
|
||||
}
|
||||
|
||||
const size_t nread = in - src;
|
||||
assert(nread == mpt_get_state_size(*model));
|
||||
fflush(stdout);
|
||||
return nread;
|
||||
}
|
||||
|
||||
struct MPTPrivate {
|
||||
const std::string modelPath;
|
||||
bool modelLoaded;
|
||||
gpt_vocab vocab;
|
||||
mpt_model *model = nullptr;
|
||||
int64_t n_threads = 0;
|
||||
size_t mem_per_token = 0;
|
||||
std::mt19937 rng;
|
||||
bool has_im_end = false;
|
||||
};
|
||||
|
||||
MPT::MPT()
|
||||
: d_ptr(new MPTPrivate) {
|
||||
d_ptr->model = new mpt_model;
|
||||
d_ptr->model->ctx = nullptr;
|
||||
d_ptr->modelLoaded = false;
|
||||
}
|
||||
|
||||
size_t MPT::requiredMem(const std::string &modelPath) {
|
||||
mpt_model dummy_model;
|
||||
gpt_vocab dummy_vocab;
|
||||
size_t mem_req;
|
||||
auto fin = std::ifstream(modelPath, std::ios::binary);
|
||||
mpt_model_load(modelPath, fin, dummy_model, dummy_vocab, &mem_req);
|
||||
return mem_req;
|
||||
}
|
||||
|
||||
bool MPT::loadModel(const std::string &modelPath) {
|
||||
std::mt19937 rng(time(NULL));
|
||||
d_ptr->rng = rng;
|
||||
|
||||
auto fin = std::ifstream(modelPath, std::ios::binary);
|
||||
|
||||
// load the model
|
||||
if (!mpt_model_load(modelPath, fin, *d_ptr->model, d_ptr->vocab, nullptr)) {
|
||||
std::cerr << "MPT ERROR: failed to load model from " << modelPath;
|
||||
return false;
|
||||
}
|
||||
|
||||
d_ptr->n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency());
|
||||
d_ptr->modelLoaded = true;
|
||||
d_ptr->has_im_end = d_ptr->vocab.token_to_id.find("<|im_end|>") != d_ptr->vocab.token_to_id.end();
|
||||
fflush(stdout);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MPT::setThreadCount(int32_t n_threads) {
|
||||
d_ptr->n_threads = n_threads;
|
||||
}
|
||||
|
||||
int32_t MPT::threadCount() const
|
||||
{
|
||||
return d_ptr->n_threads;
|
||||
}
|
||||
|
||||
MPT::~MPT()
|
||||
{
|
||||
delete d_ptr->model;
|
||||
}
|
||||
|
||||
bool MPT::isModelLoaded() const
|
||||
{
|
||||
return d_ptr->modelLoaded;
|
||||
}
|
||||
|
||||
size_t MPT::stateSize() const
|
||||
{
|
||||
return mpt_get_state_size(*d_ptr->model);
|
||||
}
|
||||
|
||||
size_t MPT::saveState(uint8_t *dest) const
|
||||
{
|
||||
return mpt_copy_state_data(*d_ptr->model, d_ptr->rng, dest);
|
||||
}
|
||||
|
||||
size_t MPT::restoreState(const uint8_t *src)
|
||||
{
|
||||
return mpt_set_state_data(d_ptr->model, &d_ptr->rng, src);
|
||||
}
|
||||
|
||||
std::vector<LLModel::Token> MPT::tokenize(PromptContext &, const std::string &str) const
|
||||
{
|
||||
return ::gpt_tokenize(d_ptr->vocab, str);
|
||||
}
|
||||
|
||||
std::string MPT::tokenToString(Token id) const
|
||||
{
|
||||
return d_ptr->vocab.id_to_token[id];
|
||||
}
|
||||
|
||||
LLModel::Token MPT::sampleToken(PromptContext &promptCtx) const
|
||||
{
|
||||
const size_t n_prev_toks = std::min((size_t) promptCtx.repeat_last_n, promptCtx.tokens.size());
|
||||
return gpt_sample_top_k_top_p(d_ptr->model->hparams.n_vocab,
|
||||
promptCtx.tokens.data() + promptCtx.tokens.size() - n_prev_toks,
|
||||
n_prev_toks,
|
||||
promptCtx.logits,
|
||||
promptCtx.top_k, promptCtx.top_p, promptCtx.temp,
|
||||
promptCtx.repeat_penalty,
|
||||
d_ptr->rng);
|
||||
}
|
||||
|
||||
bool MPT::evalTokens(PromptContext &ctx, const std::vector<int32_t> &tokens) const
|
||||
{
|
||||
// determine the required inference memory per token:
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
mpt_eval(*d_ptr->model, d_ptr->n_threads, 0, { 0, 1, 2, 3 }, ctx.logits,
|
||||
d_ptr->mem_per_token);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
return mpt_eval(*d_ptr->model, d_ptr->n_threads, ctx.n_past, tokens, ctx.logits, d_ptr->mem_per_token);
|
||||
}
|
||||
|
||||
int32_t MPT::contextLength() const
|
||||
{
|
||||
return d_ptr->model->hparams.n_ctx;
|
||||
}
|
||||
|
||||
const std::vector<LLModel::Token> &MPT::endTokens() const
|
||||
{
|
||||
static const std::vector<LLModel::Token> fres = {0, d_ptr->vocab.token_to_id["<|im_end|>"]};
|
||||
return fres;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define DLL_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLL_EXPORT __attribute__ ((visibility ("default")))
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
DLL_EXPORT bool is_g4a_backend_model_implementation() {
|
||||
return true;
|
||||
}
|
||||
|
||||
DLL_EXPORT const char *get_model_type() {
|
||||
return modelType_;
|
||||
}
|
||||
|
||||
DLL_EXPORT const char *get_build_variant() {
|
||||
return GGML_BUILD_VARIANT;
|
||||
}
|
||||
|
||||
DLL_EXPORT bool magic_match(std::istream& f) {
|
||||
uint32_t magic = 0;
|
||||
f.read(reinterpret_cast<char*>(&magic), sizeof(magic));
|
||||
return magic == 0x67676d6d;
|
||||
}
|
||||
|
||||
DLL_EXPORT LLModel *construct() {
|
||||
return new MPT;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#ifndef MPT_H_I_KNOW_WHAT_I_AM_DOING_WHEN_INCLUDING_THIS_FILE
|
||||
#error This file is NOT meant to be included outside of mpt.cpp. Doing so is DANGEROUS. Be sure to know what you are doing before proceeding to #define MPT_H_I_KNOW_WHAT_I_AM_DOING_WHEN_INCLUDING_THIS_FILE
|
||||
#endif
|
||||
#ifndef MPT_H
|
||||
#define MPT_H
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include "llmodel.h"
|
||||
|
||||
struct MPTPrivate;
|
||||
class MPT : public LLModel {
|
||||
public:
|
||||
MPT();
|
||||
~MPT();
|
||||
|
||||
bool loadModel(const std::string &modelPath) override;
|
||||
bool isModelLoaded() const override;
|
||||
size_t requiredMem(const std::string &modelPath) override;
|
||||
size_t stateSize() const override;
|
||||
size_t saveState(uint8_t *dest) const override;
|
||||
size_t restoreState(const uint8_t *src) override;
|
||||
void setThreadCount(int32_t n_threads) override;
|
||||
int32_t threadCount() const override;
|
||||
|
||||
private:
|
||||
MPTPrivate *d_ptr;
|
||||
|
||||
protected:
|
||||
std::vector<Token> tokenize(PromptContext &, const std::string&) const override;
|
||||
std::string tokenToString(Token) const override;
|
||||
Token sampleToken(PromptContext &ctx) const override;
|
||||
bool evalTokens(PromptContext &ctx, const std::vector<int32_t> &tokens) const override;
|
||||
int32_t contextLength() const override;
|
||||
const std::vector<Token>& endTokens() const override;
|
||||
};
|
||||
|
||||
#endif // MPT_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,41 +0,0 @@
|
||||
#ifndef REPLIT_H_I_KNOW_WHAT_I_AM_DOING_WHEN_INCLUDING_THIS_FILE
|
||||
#error This file is NOT meant to be included outside of replit.cpp. Doing so is DANGEROUS. Be sure to know what you are doing before proceeding to #define REPLIT_H_I_KNOW_WHAT_I_AM_DOING_WHEN_INCLUDING_THIS_FILE
|
||||
#endif
|
||||
#ifndef REPLIT_H
|
||||
#define REPLIT_H
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include "llmodel.h"
|
||||
|
||||
#define GGML_QNT_VERSION_FACTOR 1000 // do not change this
|
||||
|
||||
struct ReplitPrivate;
|
||||
class Replit : public LLModel {
|
||||
public:
|
||||
Replit();
|
||||
~Replit();
|
||||
|
||||
bool loadModel(const std::string &modelPath) override;
|
||||
bool isModelLoaded() const override;
|
||||
size_t requiredMem(const std::string & modelPath) override;
|
||||
size_t stateSize() const override;
|
||||
size_t saveState(uint8_t *dest) const override;
|
||||
size_t restoreState(const uint8_t *src) override;
|
||||
void setThreadCount(int32_t n_threads) override;
|
||||
int32_t threadCount() const override;
|
||||
|
||||
private:
|
||||
ReplitPrivate *d_ptr;
|
||||
|
||||
protected:
|
||||
std::vector<Token> tokenize(PromptContext &, const std::string&) const override;
|
||||
std::string tokenToString(Token) const override;
|
||||
Token sampleToken(PromptContext &ctx) const override;
|
||||
bool evalTokens(PromptContext &ctx, const std::vector<int32_t> &tokens) const override;
|
||||
int32_t contextLength() const override;
|
||||
const std::vector<Token>& endTokens() const override;
|
||||
};
|
||||
|
||||
#endif // REPLIT_H
|
||||
140
gpt4all-backend/scripts/convert_bert_hf_to_gguf.py
Executable file
140
gpt4all-backend/scripts/convert_bert_hf_to_gguf.py
Executable file
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import struct
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import gguf
|
||||
import numpy as np
|
||||
from transformers import AutoConfig, AutoModel, AutoTokenizer
|
||||
|
||||
|
||||
if not 2 <= len(sys.argv) < 4:
|
||||
print("Usage: {} dir-model [ftype]\n".format(Path(__file__).name))
|
||||
print(" ftype == 0 -> float32")
|
||||
print(" ftype == 1 -> float16")
|
||||
sys.exit(1)
|
||||
|
||||
# output in the same directory as the model
|
||||
dir_model = Path(sys.argv[1])
|
||||
|
||||
with open(dir_model / "vocab.txt", encoding="utf-8") as f:
|
||||
vocab = f.readlines()
|
||||
|
||||
# possible data types
|
||||
# ftype == 0 -> float32
|
||||
# ftype == 1 -> float16
|
||||
#
|
||||
# map from ftype to string
|
||||
ftype_str = ["f32", "f16"]
|
||||
|
||||
ftype = 1
|
||||
if len(sys.argv) > 2:
|
||||
ftype = int(sys.argv[2])
|
||||
if ftype < 0 or ftype > 1:
|
||||
print("Invalid ftype: " + str(ftype))
|
||||
sys.exit(1)
|
||||
|
||||
fname_out = dir_model / ("ggml-model-" + ftype_str[ftype] + ".gguf")
|
||||
|
||||
|
||||
ARCH = gguf.MODEL_ARCH.BERT
|
||||
gguf_writer = gguf.GGUFWriter(fname_out, gguf.MODEL_ARCH_NAMES[ARCH])
|
||||
|
||||
print("gguf: get model metadata")
|
||||
|
||||
config = AutoConfig.from_pretrained(dir_model)
|
||||
|
||||
block_count = config.num_hidden_layers
|
||||
gguf_writer.add_name("BERT")
|
||||
gguf_writer.add_context_length(config.max_position_embeddings)
|
||||
gguf_writer.add_embedding_length(config.hidden_size)
|
||||
gguf_writer.add_feed_forward_length(config.intermediate_size)
|
||||
gguf_writer.add_block_count(block_count)
|
||||
gguf_writer.add_head_count(config.num_attention_heads)
|
||||
gguf_writer.add_file_type(ftype)
|
||||
|
||||
print("gguf: get tokenizer metadata")
|
||||
|
||||
try:
|
||||
with open(dir_model / "tokenizer.json", encoding="utf-8") as f:
|
||||
tokenizer_json = json.load(f)
|
||||
except FileNotFoundError as e:
|
||||
print(f'Error: Missing {e.filename!r}', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print("gguf: get wordpiece tokenizer vocab")
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained(dir_model)
|
||||
print(tokenizer.encode('I believe the meaning of life is'))
|
||||
|
||||
tokens: list[bytearray] = []
|
||||
reverse_vocab = {id: encoded_tok for encoded_tok, id in tokenizer.vocab.items()}
|
||||
|
||||
# The number of tokens in tokenizer.json can differ from the expected vocab size.
|
||||
# This causes downstream issues with mismatched tensor sizes when running the inference
|
||||
for i in range(config.vocab_size):
|
||||
try:
|
||||
text = reverse_vocab[i]
|
||||
except KeyError:
|
||||
print(f"Key {i} not in tokenizer vocabulary. Padding with an arbitrary token.")
|
||||
pad_token = f"[PAD{i}]".encode("utf8")
|
||||
text = bytearray(pad_token)
|
||||
|
||||
tokens.append(text)
|
||||
|
||||
gguf_writer.add_tokenizer_model("bert") # wordpiece
|
||||
gguf_writer.add_token_list(tokens)
|
||||
|
||||
special_vocab = gguf.SpecialVocab(dir_model, load_merges=True)
|
||||
special_vocab.add_to_gguf(gguf_writer)
|
||||
|
||||
print("gguf: get tensor metadata")
|
||||
|
||||
model = AutoModel.from_pretrained(dir_model, config=config, low_cpu_mem_usage=True)
|
||||
print(model)
|
||||
|
||||
tensor_map = gguf.get_tensor_name_map(ARCH, block_count)
|
||||
|
||||
list_vars = model.state_dict()
|
||||
for name in list_vars.keys():
|
||||
print(name, list_vars[name].shape, list_vars[name].dtype)
|
||||
|
||||
for name in list_vars.keys():
|
||||
data = list_vars[name].squeeze().numpy()
|
||||
if name in ['embeddings.position_ids', 'pooler.dense.weight', 'pooler.dense.bias']:
|
||||
continue
|
||||
print("Processing variable:", name, "with shape:", data.shape)
|
||||
|
||||
n_dims = len(data.shape)
|
||||
|
||||
# ftype == 0 -> float32, ftype == 1 -> float16
|
||||
if ftype == 1 and name[-7:] == ".weight" and n_dims == 2:
|
||||
print(" Converting to float16")
|
||||
data = data.astype(np.float16)
|
||||
l_type = 1
|
||||
else:
|
||||
l_type = 0
|
||||
|
||||
# map tensor names
|
||||
new_name = tensor_map.get_name(name, try_suffixes=(".weight", ".bias"))
|
||||
if new_name is None:
|
||||
print("Can not map tensor '" + name + "'")
|
||||
sys.exit()
|
||||
|
||||
gguf_writer.add_tensor(new_name, data)
|
||||
|
||||
|
||||
print("gguf: write header")
|
||||
gguf_writer.write_header_to_file()
|
||||
print("gguf: write metadata")
|
||||
gguf_writer.write_kv_data_to_file()
|
||||
print("gguf: write tensors")
|
||||
gguf_writer.write_tensors_to_file()
|
||||
|
||||
gguf_writer.close()
|
||||
|
||||
print(f"gguf: model successfully exported to '{fname_out}'")
|
||||
print()
|
||||
@@ -1,143 +0,0 @@
|
||||
# Based on: https://github.com/KerfuffleV2/ggml-falcon/blob/feat-improve-falcon-convert-hf/examples/falcon/convert-hf-to-ggml.py
|
||||
# Convert Hugging Face fine-tuned bloom-like models to ggml format
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# python3 convert_falcon_hf_to_ggml.py model_directory output_directory [use-f32]
|
||||
#
|
||||
# This script is similar to "convert-pt-to-ggml.py"
|
||||
#
|
||||
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
import struct
|
||||
import json
|
||||
import code
|
||||
import torch
|
||||
import numpy as np
|
||||
import gc
|
||||
|
||||
from transformers import AutoTokenizer, AutoModelForCausalLM, AutoConfig
|
||||
|
||||
# ref: https://github.com/openai/gpt-2/blob/master/src/encoder.py
|
||||
def bytes_to_unicode():
|
||||
"""
|
||||
Returns list of utf-8 byte and a corresponding list of unicode strings.
|
||||
The reversible bpe codes work on unicode strings.
|
||||
This means you need a large # of unicode characters in your vocab if you want to avoid UNKs.
|
||||
When you're at something like a 10B token dataset you end up needing around 5K for decent coverage.
|
||||
This is a significant percentage of your normal, say, 32K bpe vocab.
|
||||
To avoid that, we want lookup tables between utf-8 bytes and unicode strings.
|
||||
And avoids mapping to whitespace/control characters the bpe code barfs on.
|
||||
"""
|
||||
bs = list(range(ord("!"), ord("~")+1))+list(range(ord("¡"), ord("¬")+1))+list(range(ord("®"), ord("ÿ")+1))
|
||||
cs = bs[:]
|
||||
n = 0
|
||||
for b in range(2**8):
|
||||
if b not in bs:
|
||||
bs.append(b)
|
||||
cs.append(2**8+n)
|
||||
n += 1
|
||||
cs = [chr(n) for n in cs]
|
||||
return dict(zip(bs, cs))
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print("INFO: GGML V1 files produced are meant to be finalized through examples/falcon_quantize which will bring them to latest version and precision of choice");
|
||||
print("Usage: python convert_falcon_hf_to_ggml.py model_directory output_directory [use-f32]")
|
||||
print(" model_directory: name of the directory and model you convert (it should be a subdirectory)")
|
||||
print(" output-directory: directory where the output file will be written")
|
||||
print(" use-f32: if present, use float32 instead of float16 (f32 is recommended)")
|
||||
sys.exit(1)
|
||||
|
||||
# num_parts = int(sys.argv[1])
|
||||
dir_model = sys.argv[1] # name and dir of model
|
||||
dir_out = sys.argv[2] # output directory
|
||||
|
||||
# make sure the output directory exists
|
||||
os.makedirs(dir_out, exist_ok=True)
|
||||
|
||||
|
||||
# possible data types
|
||||
# ftype == 0 -> float32
|
||||
# ftype == 1 -> float16
|
||||
#
|
||||
# map from ftype to string
|
||||
ftype_str = ["f32", "f16"]
|
||||
ftype = 1
|
||||
if len(sys.argv) > 3:
|
||||
ftype = 0
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained(dir_model)
|
||||
# print(tokenizer)
|
||||
config = AutoConfig.from_pretrained(dir_model, trust_remote_code=True)
|
||||
model = AutoModelForCausalLM.from_pretrained(dir_model, trust_remote_code=True, torch_dtype=torch.bfloat16, low_cpu_mem_usage=True)
|
||||
hparams = config.to_dict()
|
||||
|
||||
n_head = hparams["n_head"]
|
||||
n_head_kv = hparams["n_head_kv"] if "n_head_kv" in hparams else 1
|
||||
head_dim = hparams["hidden_size"] // n_head
|
||||
print("* Loading model from: ", dir_model)
|
||||
|
||||
fname_out = dir_out + f"/ggml-model-{dir_model.split('/')[-1]}-{ftype_str[ftype]}.bin"
|
||||
fout = open(fname_out, "wb")
|
||||
fout.write(struct.pack("i", 0x67676a74)) # magic: ggmf in hex (version 1) - possibly change to ggfc ?
|
||||
fout.write(struct.pack("i", 1)) # version
|
||||
fout.write(struct.pack("i", hparams["vocab_size"]))
|
||||
fout.write(struct.pack("i", hparams["hidden_size"]))
|
||||
fout.write(struct.pack("i", n_head))
|
||||
fout.write(struct.pack("i", n_head_kv))
|
||||
fout.write(struct.pack("i", hparams["n_layer"]))
|
||||
fout.write(struct.pack("i", 40 if "n_head_kv" in hparams else 7)) # obsolete field that breaks ggml compatibility - todo again remove one day
|
||||
fout.write(struct.pack("i", ftype))
|
||||
|
||||
reverse_vocab = {id: encoded_tok for encoded_tok, id in tokenizer.vocab.items()}
|
||||
byte_encoder = bytes_to_unicode()
|
||||
byte_decoder = {v:k for k, v in byte_encoder.items()}
|
||||
|
||||
for i in range(hparams["vocab_size"]):
|
||||
text = bytearray([byte_decoder[c] for c in reverse_vocab[i]])
|
||||
fout.write(struct.pack("i", len(text)))
|
||||
fout.write(text)
|
||||
fout.write(struct.pack("f", 0.0)) # falcon uses bpe on RefinedWeb - no probability scores used
|
||||
|
||||
model = model.state_dict()
|
||||
for name in model.keys():
|
||||
src = name
|
||||
# The original query_key_value tensor contains n_head_kv "kv groups",
|
||||
# each consisting of n_head/n_head_kv query weights followed by one key
|
||||
# and one value weight (shared by all query heads in the kv group).
|
||||
# This layout makes it a big pain to work with in GGML.
|
||||
# So we rearrange them here,, so that we have n_head query weights
|
||||
# followed by n_head_kv key weights followed by n_head_kv value weights,
|
||||
# in contiguous fashion.
|
||||
|
||||
if "query_key_value" in src:
|
||||
qkv = model[src].view(
|
||||
n_head_kv, n_head // n_head_kv + 2, head_dim, head_dim * n_head)
|
||||
|
||||
q = qkv[:, :-2 ].reshape(n_head * head_dim, head_dim * n_head)
|
||||
k = qkv[:, [-2]].reshape(n_head_kv * head_dim, head_dim * n_head)
|
||||
v = qkv[:, [-1]].reshape(n_head_kv * head_dim, head_dim * n_head)
|
||||
|
||||
model[src] = torch.cat((q,k,v)).reshape_as(model[src])
|
||||
data = model[src].squeeze()
|
||||
n_dims = len(data.shape)
|
||||
# default type is fp32
|
||||
ftype_cur = 1 if ftype == 1 and n_dims > 1 else 0
|
||||
data = data.to(dtype = torch.float16 if ftype_cur == 1 else torch.float32).numpy()
|
||||
print(f' |', name, data.shape, '->', data.dtype)
|
||||
# header
|
||||
str = name.encode('utf-8')
|
||||
fout.write(struct.pack("iii", n_dims, len(str), ftype_cur))
|
||||
for i in range(n_dims):
|
||||
fout.write(struct.pack("i", data.shape[n_dims - 1 - i]))
|
||||
fout.write(str)
|
||||
|
||||
# data
|
||||
data.tofile(fout)
|
||||
|
||||
fout.close()
|
||||
|
||||
print("Done. Output file: " + fname_out)
|
||||
print("")
|
||||
165
gpt4all-backend/scripts/convert_gptj_to_gguf.py
Executable file
165
gpt4all-backend/scripts/convert_gptj_to_gguf.py
Executable file
@@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env python3
|
||||
# Convert GPT-J-6B h5 transformer model to ggml format
|
||||
#
|
||||
# Load the model using GPTJForCausalLM.
|
||||
# Iterate over all variables and write them to a binary file.
|
||||
#
|
||||
# For each variable, write the following:
|
||||
# - Number of dimensions (int)
|
||||
# - Name length (int)
|
||||
# - Dimensions (int[n_dims])
|
||||
# - Name (char[name_length])
|
||||
# - Data (float[n_dims])
|
||||
#
|
||||
# By default, the bigger matrices are converted to 16-bit floats.
|
||||
# This can be disabled by adding the "ftype" CLI argument.
|
||||
#
|
||||
# At the start of the ggml file we write the model parameters
|
||||
# and vocabulary.
|
||||
#
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import struct
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import gguf
|
||||
import numpy as np
|
||||
from transformers import AutoConfig, AutoTokenizer, GPTJForCausalLM
|
||||
from transformers.models.gpt2 import tokenization_gpt2
|
||||
|
||||
|
||||
if not 2 <= len(sys.argv) < 4:
|
||||
print("Usage: python {} dir-model [ftype]\n".format(Path(__file__).name))
|
||||
print(" ftype == 0 -> float32")
|
||||
print(" ftype == 1 -> float16")
|
||||
sys.exit(1)
|
||||
|
||||
# output in the same directory as the model
|
||||
dir_model = Path(sys.argv[1])
|
||||
fname_out = dir_model / "ggml-model.gguf"
|
||||
|
||||
# possible data types
|
||||
# ftype == 0 -> float32
|
||||
# ftype == 1 -> float16
|
||||
#
|
||||
# map from ftype to string
|
||||
ftype_str = ["f32", "f16"]
|
||||
|
||||
ftype = 1
|
||||
if len(sys.argv) > 2:
|
||||
ftype = int(sys.argv[2])
|
||||
if ftype < 0 or ftype > 1:
|
||||
print("Invalid ftype: " + str(ftype))
|
||||
sys.exit(1)
|
||||
|
||||
fname_out = dir_model / ("ggml-model-" + ftype_str[ftype] + ".gguf")
|
||||
|
||||
|
||||
ARCH = gguf.MODEL_ARCH.GPTJ
|
||||
gguf_writer = gguf.GGUFWriter(fname_out, gguf.MODEL_ARCH_NAMES[ARCH])
|
||||
|
||||
print("gguf: get model metadata")
|
||||
|
||||
config = AutoConfig.from_pretrained(dir_model)
|
||||
|
||||
block_count = config.n_layer
|
||||
gguf_writer.add_name("GPT-J")
|
||||
gguf_writer.add_context_length(config.n_positions)
|
||||
gguf_writer.add_embedding_length(config.n_embd)
|
||||
gguf_writer.add_block_count(block_count)
|
||||
gguf_writer.add_feed_forward_length(4 * config.n_embd)
|
||||
gguf_writer.add_head_count(config.n_head)
|
||||
gguf_writer.add_rope_dimension_count(config.rotary_dim)
|
||||
gguf_writer.add_layer_norm_eps(config.layer_norm_epsilon)
|
||||
gguf_writer.add_file_type(ftype)
|
||||
|
||||
print("gguf: get gpt2 tokenizer vocab")
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained(dir_model)
|
||||
|
||||
reverse_vocab = {id: encoded_tok for encoded_tok, id in tokenizer.vocab.items()}
|
||||
byte_encoder = tokenization_gpt2.bytes_to_unicode()
|
||||
byte_decoder = {v: k for k, v in byte_encoder.items()}
|
||||
|
||||
tokens: list[bytearray] = []
|
||||
|
||||
for i in range(config.vocab_size):
|
||||
if i in reverse_vocab:
|
||||
try:
|
||||
text = bytearray([byte_decoder[c] for c in reverse_vocab[i]])
|
||||
except KeyError:
|
||||
text = bytearray()
|
||||
for c in reverse_vocab[i]:
|
||||
if ord(c) < 256: # single byte character
|
||||
text.append(byte_decoder[c])
|
||||
else: # multibyte special token character
|
||||
text.extend(c.encode('utf-8'))
|
||||
else:
|
||||
print(f"Key {i} not in tokenizer vocabulary. Padding with an arbitrary token.")
|
||||
pad_token = f"[PAD{i}]".encode("utf8")
|
||||
text = bytearray(pad_token)
|
||||
|
||||
tokens.append(text)
|
||||
|
||||
|
||||
gguf_writer.add_tokenizer_model("gpt2")
|
||||
gguf_writer.add_token_list(tokens)
|
||||
|
||||
special_vocab = gguf.SpecialVocab(dir_model, load_merges=True)
|
||||
special_vocab.add_to_gguf(gguf_writer)
|
||||
|
||||
print("gguf: get tensor metadata")
|
||||
|
||||
model = GPTJForCausalLM.from_pretrained(dir_model, config=config, low_cpu_mem_usage=True)
|
||||
#print (model)
|
||||
|
||||
tensor_map = gguf.get_tensor_name_map(ARCH, block_count)
|
||||
|
||||
list_vars = model.state_dict()
|
||||
#print (list_vars)
|
||||
|
||||
for name in list_vars.keys():
|
||||
data = list_vars[name].squeeze().numpy()
|
||||
print("Processing variable:", name, "with shape:", data.shape)
|
||||
|
||||
# we don't need these
|
||||
if name.endswith("attn.masked_bias") or name.endswith(".attn.bias"):
|
||||
print(" Skipping variable:", name)
|
||||
continue
|
||||
|
||||
n_dims = len(data.shape)
|
||||
|
||||
# ftype == 0 -> float32, ftype == 1 -> float16
|
||||
ftype_cur = 0
|
||||
if ftype == 1 and name[-7:] == ".weight" and n_dims == 2:
|
||||
print(" Converting to float16")
|
||||
data = data.astype(np.float16)
|
||||
ftype_cur = 1
|
||||
elif ftype == 1 or data.dtype != np.float32:
|
||||
print(" Converting to float32")
|
||||
data = data.astype(np.float32)
|
||||
ftype_cur = 0
|
||||
|
||||
# map tensor names
|
||||
new_name = tensor_map.get_name(name, try_suffixes=(".weight", ".bias"))
|
||||
if new_name is None:
|
||||
print("Can not map tensor '" + name + "'")
|
||||
sys.exit()
|
||||
|
||||
gguf_writer.add_tensor(new_name, data)
|
||||
|
||||
|
||||
print("gguf: write header")
|
||||
gguf_writer.write_header_to_file()
|
||||
print("gguf: write metadata")
|
||||
gguf_writer.write_kv_data_to_file()
|
||||
print("gguf: write tensors")
|
||||
gguf_writer.write_tensors_to_file()
|
||||
|
||||
gguf_writer.close()
|
||||
|
||||
print(f"gguf: model successfully exported to '{fname_out}'")
|
||||
print()
|
||||
@@ -1,145 +0,0 @@
|
||||
# Convert Hugging Face fine-tuned bloom-like models to ggml format
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# python3 models/convert-h5-to-ggml.py
|
||||
#
|
||||
# This script is similar to "convert-pt-to-ggml.py"
|
||||
#
|
||||
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
import struct
|
||||
import json
|
||||
import code
|
||||
import torch
|
||||
import numpy as np
|
||||
|
||||
from transformers import AutoTokenizer, AutoModelForCausalLM, AutoConfig, BloomForCausalLM
|
||||
|
||||
# ref: https://github.com/openai/gpt-2/blob/master/src/encoder.py
|
||||
def bytes_to_unicode():
|
||||
"""
|
||||
Returns list of utf-8 byte and a corresponding list of unicode strings.
|
||||
The reversible bpe codes work on unicode strings.
|
||||
This means you need a large # of unicode characters in your vocab if you want to avoid UNKs.
|
||||
When you're at something like a 10B token dataset you end up needing around 5K for decent coverage.
|
||||
This is a significant percentage of your normal, say, 32K bpe vocab.
|
||||
To avoid that, we want lookup tables between utf-8 bytes and unicode strings.
|
||||
And avoids mapping to whitespace/control characters the bpe code barfs on.
|
||||
"""
|
||||
bs = list(range(ord("!"), ord("~")+1))+list(range(ord("¡"), ord("¬")+1))+list(range(ord("®"), ord("ÿ")+1))
|
||||
cs = bs[:]
|
||||
n = 0
|
||||
for b in range(2**8):
|
||||
if b not in bs:
|
||||
bs.append(b)
|
||||
cs.append(2**8+n)
|
||||
n += 1
|
||||
cs = [chr(n) for n in cs]
|
||||
return dict(zip(bs, cs))
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: python convert-hf-to-ggml.py model_name dir-output [use-f32]")
|
||||
print(" model_name: name of the model to convert. Example: 'bigscience/bloomz-560m'")
|
||||
print(" dir-output: directory where the output file will be written")
|
||||
print(" use-f32: if present, use float32 instead of float16")
|
||||
sys.exit(1)
|
||||
|
||||
model_name = sys.argv[1]
|
||||
dir_out = sys.argv[2]
|
||||
|
||||
# make sure the output directory exists
|
||||
os.makedirs(dir_out, exist_ok=True)
|
||||
|
||||
# possible data types
|
||||
# ftype == 0 -> float32
|
||||
# ftype == 1 -> float16
|
||||
#
|
||||
# map from ftype to string
|
||||
ftype_str = ["f32", "f16"]
|
||||
ftype = 1
|
||||
if len(sys.argv) > 3:
|
||||
ftype = 0
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
|
||||
config = AutoConfig.from_pretrained(model_name, trust_remote_code=True)
|
||||
hparams = config.to_dict()
|
||||
print("Loading model: ", model_name)
|
||||
model = AutoModelForCausalLM.from_pretrained(model_name, trust_remote_code=True, config=config, torch_dtype=torch.float16 if ftype == 1 else torch.float32, low_cpu_mem_usage=True)
|
||||
print("Model loaded: ", model_name)
|
||||
|
||||
|
||||
fname_out = dir_out + f"/ggml-model-{model_name.split('/')[-1]}-{ftype_str[ftype]}.bin"
|
||||
fout = open(fname_out, "wb")
|
||||
vocab = tokenizer.vocab
|
||||
|
||||
hparams["multiple_of"] = 1
|
||||
fout.write(struct.pack("I", 0x67676d6d)) # magic: ggml in hex
|
||||
fout.write(struct.pack("I", model.config.vocab_size))
|
||||
fout.write(struct.pack("I", model.config.max_seq_len))
|
||||
fout.write(struct.pack("I", model.config.n_layers))
|
||||
fout.write(struct.pack("I", model.config.n_heads))
|
||||
fout.write(struct.pack("I", model.config.d_model))
|
||||
fout.write(struct.pack("f", model.config.attn_config['alibi_bias_max']))
|
||||
clip_qkv = model.config.attn_config['clip_qkv']
|
||||
fout.write(struct.pack("f", clip_qkv if clip_qkv is not None else 0))
|
||||
fout.write(struct.pack("I", ftype))
|
||||
|
||||
# # Is this correct??
|
||||
# dot_token = tokenizer.encode(".")[0]
|
||||
# write tokens to ggml file
|
||||
dot_token = tokenizer.encode('.')[0]
|
||||
fout.write(struct.pack("I", model.config.vocab_size))
|
||||
|
||||
for i in range(model.config.vocab_size):
|
||||
text = tokenizer.decode([dot_token, i]).encode('utf-8')
|
||||
# remove the first byte (it's always '.')
|
||||
text = text[1:]
|
||||
enclen = len(text)
|
||||
if i in tokenizer.all_special_ids:
|
||||
print(f"special token: {text}")
|
||||
enclen = enclen | 1<<31
|
||||
fout.write(struct.pack("I", enclen))
|
||||
fout.write(text)
|
||||
|
||||
list_vars = model.state_dict()
|
||||
for name in list_vars.keys():
|
||||
data = list_vars[name].squeeze().numpy()
|
||||
print("Processing variable: " + name + " with shape: ", data.shape)
|
||||
|
||||
n_dims = len(data.shape);
|
||||
|
||||
# ftype == 0 -> float32, ftype == 1 -> float16
|
||||
ftype_cur = 0;
|
||||
if ftype != 0:
|
||||
# Keep token embeddings in fp32
|
||||
if name[-7:] == ".weight" and n_dims == 2 and ".wte" not in name:
|
||||
print(" Converting to float16")
|
||||
data = data.astype(np.float16)
|
||||
ftype_cur = 1
|
||||
else:
|
||||
print(" Converting to float32")
|
||||
data = data.astype(np.float32)
|
||||
ftype_cur = 0
|
||||
else:
|
||||
if data.dtype != np.float32:
|
||||
print(" Converting to float32")
|
||||
data = data.astype(np.float32)
|
||||
ftype_cur = 0
|
||||
|
||||
# header
|
||||
str = name.encode('utf-8')
|
||||
fout.write(struct.pack("iii", n_dims, len(str), ftype_cur))
|
||||
for i in range(n_dims):
|
||||
fout.write(struct.pack("i", data.shape[n_dims - 1 - i]))
|
||||
fout.write(str);
|
||||
|
||||
# data
|
||||
data.tofile(fout)
|
||||
|
||||
fout.close()
|
||||
|
||||
print("Done. Output file: " + fname_out)
|
||||
print("")
|
||||
@@ -1,113 +0,0 @@
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import struct
|
||||
import json
|
||||
import numpy as np
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
import sentencepiece.sentencepiece_model_pb2 as model
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: convert-h5-to-ggml.py dir-model [use-f32]\n")
|
||||
print(" ftype == 0 -> float32")
|
||||
print(" ftype == 1 -> float16")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# output in the same directory as the model
|
||||
dir_model = sys.argv[1]
|
||||
fname_out = sys.argv[1] + "/ggml-replit-code-v1-3b.bin"
|
||||
|
||||
|
||||
with open(dir_model + "/config.json", "r", encoding="utf-8") as f:
|
||||
hparams = json.load(f)
|
||||
|
||||
sp_proto = model.ModelProto()
|
||||
sp_proto.ParseFromString(open(Path(sys.argv[1]) / "spiece.model", "rb").read())
|
||||
|
||||
|
||||
# possible data types
|
||||
# ftype == 0 -> float32
|
||||
# ftype == 1 -> float16
|
||||
#
|
||||
# map from ftype to string
|
||||
ftype_str = ["f32", "f16"]
|
||||
|
||||
ftype = 1
|
||||
if len(sys.argv) > 2:
|
||||
ftype = int(sys.argv[2])
|
||||
if ftype < 0 or ftype > 1:
|
||||
print("Invalid ftype: " + str(ftype))
|
||||
sys.exit(1)
|
||||
fname_out = sys.argv[1] + "/ggml-replit-code-v1-3b-" + ftype_str[ftype] + ".bin"
|
||||
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained(dir_model, trust_remote_code=True)
|
||||
model = AutoModelForCausalLM.from_pretrained(
|
||||
dir_model, low_cpu_mem_usage=True, trust_remote_code=True
|
||||
)
|
||||
# print (model)
|
||||
|
||||
# print(tokenizer.encode('I believe the meaning of life is'))
|
||||
|
||||
list_vars = model.state_dict()
|
||||
for name in list_vars.keys():
|
||||
print(name, list_vars[name].shape, list_vars[name].dtype)
|
||||
|
||||
fout = open(fname_out, "wb")
|
||||
|
||||
print(hparams)
|
||||
|
||||
fout.write(struct.pack("i", 0x7265706c)) # magic: repl in hex
|
||||
fout.write(struct.pack("i", hparams["vocab_size"]))
|
||||
fout.write(struct.pack("i", hparams["max_seq_len"]))
|
||||
fout.write(struct.pack("i", hparams["d_model"]))
|
||||
fout.write(struct.pack("i", hparams["n_heads"]))
|
||||
fout.write(struct.pack("i", hparams["n_layers"]))
|
||||
fout.write(struct.pack("i", ftype))
|
||||
|
||||
|
||||
# TODO: temporary hack to not deal with implementing the tokenizer
|
||||
for piece in sp_proto.pieces:
|
||||
encoded_piece = piece.piece.encode("utf-8")
|
||||
fout.write(struct.pack("i", len(encoded_piece)))
|
||||
fout.write(encoded_piece)
|
||||
fout.write(struct.pack("f", piece.score))
|
||||
|
||||
|
||||
for name in list_vars.keys():
|
||||
data = list_vars[name].squeeze().numpy()
|
||||
print("Processing variable: " + name + " with shape: ", data.shape)
|
||||
|
||||
n_dims = len(data.shape)
|
||||
|
||||
# ftype == 0 -> float32, ftype == 1 -> float16
|
||||
ftype_cur = 0
|
||||
if ftype != 0:
|
||||
if name[-7:] == ".weight" and n_dims == 2:
|
||||
print(" Converting to float16")
|
||||
data = data.astype(np.float16)
|
||||
ftype_cur = 1
|
||||
else:
|
||||
print(" Converting to float32")
|
||||
data = data.astype(np.float32)
|
||||
ftype_cur = 0
|
||||
else:
|
||||
if data.dtype != np.float32:
|
||||
print(" Converting to float32")
|
||||
data = data.astype(np.float32)
|
||||
ftype_cur = 0
|
||||
|
||||
# header
|
||||
str = name.encode("utf-8")
|
||||
fout.write(struct.pack("iii", n_dims, len(str), ftype_cur))
|
||||
for i in range(n_dims):
|
||||
fout.write(struct.pack("i", data.shape[n_dims - 1 - i]))
|
||||
fout.write(str)
|
||||
|
||||
# data
|
||||
data.tofile(fout)
|
||||
|
||||
fout.close()
|
||||
|
||||
print("Done. Output file: " + fname_out)
|
||||
print("")
|
||||
@@ -40,5 +40,5 @@ directory, if necessary.
|
||||
If you have already saved a model beforehand, specify its path with the `-m`/`--model` argument,
|
||||
for example:
|
||||
```shell
|
||||
python app.py repl --model /home/user/my-gpt4all-models/GPT4All-13B-snoozy.ggmlv3.q4_0.bin
|
||||
python app.py repl --model /home/user/my-gpt4all-models/gpt4all-13b-snoozy-q4_0.gguf
|
||||
```
|
||||
|
||||
19
gpt4all-bindings/cli/app.py
Normal file → Executable file
19
gpt4all-bindings/cli/app.py
Normal file → Executable file
@@ -1,16 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
"""GPT4All CLI
|
||||
|
||||
The GPT4All CLI is a self-contained script based on the `gpt4all` and `typer` packages. It offers a
|
||||
REPL to communicate with a language model similar to the chat GUI application, but more basic.
|
||||
"""
|
||||
|
||||
import importlib.metadata
|
||||
import io
|
||||
import pkg_resources # should be present as a dependency of gpt4all
|
||||
import sys
|
||||
import typer
|
||||
|
||||
from collections import namedtuple
|
||||
from typing_extensions import Annotated
|
||||
|
||||
import typer
|
||||
from gpt4all import GPT4All
|
||||
|
||||
|
||||
@@ -53,14 +54,18 @@ def repl(
|
||||
model: Annotated[
|
||||
str,
|
||||
typer.Option("--model", "-m", help="Model to use for chatbot"),
|
||||
] = "ggml-gpt4all-j-v1.3-groovy",
|
||||
] = "mistral-7b-instruct-v0.1.Q4_0.gguf",
|
||||
n_threads: Annotated[
|
||||
int,
|
||||
typer.Option("--n-threads", "-t", help="Number of threads to use for chatbot"),
|
||||
] = None,
|
||||
device: Annotated[
|
||||
str,
|
||||
typer.Option("--device", "-d", help="Device to use for chatbot, e.g. gpu, amd, nvidia, intel. Defaults to CPU."),
|
||||
] = None,
|
||||
):
|
||||
"""The CLI read-eval-print loop."""
|
||||
gpt4all_instance = GPT4All(model)
|
||||
gpt4all_instance = GPT4All(model, device=device)
|
||||
|
||||
# if threads are passed, set them
|
||||
if n_threads is not None:
|
||||
@@ -79,7 +84,7 @@ def repl(
|
||||
|
||||
use_new_loop = False
|
||||
try:
|
||||
version = pkg_resources.Environment()['gpt4all'][0].version
|
||||
version = importlib.metadata.version('gpt4all')
|
||||
version_major = int(version.split('.')[0])
|
||||
if version_major >= 1:
|
||||
use_new_loop = True
|
||||
@@ -115,6 +120,7 @@ def _old_loop(gpt4all_instance):
|
||||
n_predict=200,
|
||||
top_k=40,
|
||||
top_p=0.9,
|
||||
min_p=0.0,
|
||||
temp=0.9,
|
||||
n_batch=9,
|
||||
repeat_penalty=1.1,
|
||||
@@ -151,6 +157,7 @@ def _new_loop(gpt4all_instance):
|
||||
temp=0.9,
|
||||
top_k=40,
|
||||
top_p=0.9,
|
||||
min_p=0.0,
|
||||
repeat_penalty=1.1,
|
||||
repeat_last_n=64,
|
||||
n_batch=9,
|
||||
|
||||
@@ -41,6 +41,8 @@ insert_final_newline = true
|
||||
|
||||
# IDE0055: Fix formatting
|
||||
dotnet_diagnostic.IDE0055.severity = error
|
||||
dotnet_diagnostic.CS1573.severity = suggestion
|
||||
dotnet_diagnostic.CS1591.severity = suggestion
|
||||
|
||||
# Sort using and Import directives with System.* appearing first
|
||||
dotnet_sort_system_directives_first = true
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<Company></Company>
|
||||
<Copyright></Copyright>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
<Version>0.6.3-alpha</Version>
|
||||
<Version>0.6.4-alpha</Version>
|
||||
<VersionSuffix>$(VersionSuffix)</VersionSuffix>
|
||||
<Version Condition=" '$(VersionSuffix)' != '' ">$(Version)$(VersionSuffix)</Version>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
/// </summary>
|
||||
public interface ILLModel : IDisposable
|
||||
{
|
||||
ModelType ModelType { get; }
|
||||
|
||||
ulong GetStateSizeBytes();
|
||||
|
||||
int GetThreadCount();
|
||||
|
||||
@@ -42,16 +42,12 @@ public record ModelRecalculatingEventArgs(bool IsRecalculating);
|
||||
public class LLModel : ILLModel
|
||||
{
|
||||
protected readonly IntPtr _handle;
|
||||
private readonly ModelType _modelType;
|
||||
private readonly ILogger _logger;
|
||||
private bool _disposed;
|
||||
|
||||
public ModelType ModelType => _modelType;
|
||||
|
||||
internal LLModel(IntPtr handle, ModelType modelType, ILogger? logger = null)
|
||||
internal LLModel(IntPtr handle, ILogger? logger = null)
|
||||
{
|
||||
_handle = handle;
|
||||
_modelType = modelType;
|
||||
_logger = logger ?? NullLogger.Instance;
|
||||
}
|
||||
|
||||
@@ -59,10 +55,9 @@ public class LLModel : ILLModel
|
||||
/// Create a new model from a pointer
|
||||
/// </summary>
|
||||
/// <param name="handle">Pointer to underlying model</param>
|
||||
/// <param name="modelType">The model type</param>
|
||||
public static LLModel Create(IntPtr handle, ModelType modelType, ILogger? logger = null)
|
||||
public static LLModel Create(IntPtr handle, ILogger? logger = null)
|
||||
{
|
||||
return new LLModel(handle, modelType, logger: logger);
|
||||
return new LLModel(handle, logger: logger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -188,7 +183,7 @@ public class LLModel : ILLModel
|
||||
/// <returns>true if the model was loaded successfully, false otherwise.</returns>
|
||||
public bool Load(string modelPath)
|
||||
{
|
||||
return NativeMethods.llmodel_loadModel(_handle, modelPath);
|
||||
return NativeMethods.llmodel_loadModel(_handle, modelPath, 2048, 100);
|
||||
}
|
||||
|
||||
protected void Destroy()
|
||||
@@ -204,12 +199,7 @@ public class LLModel : ILLModel
|
||||
// dispose managed state
|
||||
}
|
||||
|
||||
switch (_modelType)
|
||||
{
|
||||
default:
|
||||
Destroy();
|
||||
break;
|
||||
}
|
||||
Destroy();
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,15 @@ public unsafe class LLModelPromptContext
|
||||
set => _ctx.top_p = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// min p sampling probability threshold
|
||||
/// </summary>
|
||||
public float MinP
|
||||
{
|
||||
get => _ctx.min_p;
|
||||
set => _ctx.min_p = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// temperature to adjust model's output distribution
|
||||
/// </summary>
|
||||
|
||||
@@ -29,6 +29,8 @@ public unsafe partial struct llmodel_prompt_context
|
||||
|
||||
public float top_p;
|
||||
|
||||
public float min_p;
|
||||
|
||||
public float temp;
|
||||
|
||||
[NativeTypeName("int32_t")]
|
||||
@@ -70,7 +72,9 @@ internal static unsafe partial class NativeMethods
|
||||
[return: MarshalAs(UnmanagedType.I1)]
|
||||
public static extern bool llmodel_loadModel(
|
||||
[NativeTypeName("llmodel_model")] IntPtr model,
|
||||
[NativeTypeName("const char *")][MarshalAs(UnmanagedType.LPUTF8Str)] string model_path);
|
||||
[NativeTypeName("const char *")][MarshalAs(UnmanagedType.LPUTF8Str)] string model_path,
|
||||
[NativeTypeName("int32_t")] int n_ctx,
|
||||
[NativeTypeName("int32_t")] int ngl);
|
||||
|
||||
[DllImport("libllmodel", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ internal static class LLPromptContextExtensions
|
||||
n_predict = {ctx.n_predict}
|
||||
top_k = {ctx.top_k}
|
||||
top_p = {ctx.top_p}
|
||||
min_p = {ctx.min_p}
|
||||
temp = {ctx.temp}
|
||||
n_batch = {ctx.n_batch}
|
||||
repeat_penalty = {ctx.repeat_penalty}
|
||||
|
||||
@@ -12,6 +12,7 @@ public static class PredictRequestOptionsExtensions
|
||||
TokensSize = opts.TokensSize,
|
||||
TopK = opts.TopK,
|
||||
TopP = opts.TopP,
|
||||
MinP = opts.MinP,
|
||||
PastNum = opts.PastConversationTokensNum,
|
||||
RepeatPenalty = opts.RepeatPenalty,
|
||||
Temperature = opts.Temperature,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- Windows -->
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Gpt4All.Bindings;
|
||||
using Gpt4All.LibraryLoader;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Gpt4All;
|
||||
|
||||
@@ -31,15 +32,18 @@ public class Gpt4AllModelFactory : IGpt4AllModelFactory
|
||||
}
|
||||
}
|
||||
|
||||
private IGpt4AllModel CreateModel(string modelPath)
|
||||
private Gpt4All CreateModel(string modelPath)
|
||||
{
|
||||
var modelType_ = ModelFileUtils.GetModelTypeFromModelFileHeader(modelPath);
|
||||
_logger.LogInformation("Creating model path={ModelPath} type={ModelType}", modelPath, modelType_);
|
||||
_logger.LogInformation("Creating model path={ModelPath}", modelPath);
|
||||
IntPtr error;
|
||||
var handle = NativeMethods.llmodel_model_create2(modelPath, "auto", out error);
|
||||
if (error != IntPtr.Zero)
|
||||
{
|
||||
throw new Exception(Marshal.PtrToStringAnsi(error));
|
||||
}
|
||||
_logger.LogDebug("Model created handle=0x{ModelHandle:X8}", handle);
|
||||
_logger.LogInformation("Model loading started");
|
||||
var loadedSuccessfully = NativeMethods.llmodel_loadModel(handle, modelPath);
|
||||
var loadedSuccessfully = NativeMethods.llmodel_loadModel(handle, modelPath, 2048, 100);
|
||||
_logger.LogInformation("Model loading completed success={ModelLoadSuccess}", loadedSuccessfully);
|
||||
if (!loadedSuccessfully)
|
||||
{
|
||||
@@ -47,7 +51,7 @@ public class Gpt4AllModelFactory : IGpt4AllModelFactory
|
||||
}
|
||||
|
||||
var logger = _loggerFactory.CreateLogger<LLModel>();
|
||||
var underlyingModel = LLModel.Create(handle, modelType_, logger: logger);
|
||||
var underlyingModel = LLModel.Create(handle, logger: logger);
|
||||
|
||||
Debug.Assert(underlyingModel.IsLoaded());
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
namespace Gpt4All;
|
||||
|
||||
public static class ModelFileUtils
|
||||
{
|
||||
private const uint GPTJ_MAGIC = 0x67676d6c;
|
||||
private const uint LLAMA_MAGIC = 0x67676a74;
|
||||
private const uint MPT_MAGIC = 0x67676d6d;
|
||||
|
||||
public static ModelType GetModelTypeFromModelFileHeader(string modelPath)
|
||||
{
|
||||
using var fileStream = new FileStream(modelPath, FileMode.Open);
|
||||
using var binReader = new BinaryReader(fileStream);
|
||||
|
||||
var magic = binReader.ReadUInt32();
|
||||
|
||||
return magic switch
|
||||
{
|
||||
GPTJ_MAGIC => ModelType.GPTJ,
|
||||
LLAMA_MAGIC => ModelType.LLAMA,
|
||||
MPT_MAGIC => ModelType.MPT,
|
||||
_ => throw new ArgumentOutOfRangeException($"Invalid model file. magic=0x{magic:X8}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,4 @@
|
||||
public record ModelOptions
|
||||
{
|
||||
public int Threads { get; init; } = 4;
|
||||
|
||||
public ModelType ModelType { get; init; } = ModelType.GPTJ;
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
namespace Gpt4All;
|
||||
|
||||
/// <summary>
|
||||
/// The supported model types
|
||||
/// </summary>
|
||||
public enum ModelType
|
||||
{
|
||||
LLAMA = 0,
|
||||
GPTJ,
|
||||
MPT
|
||||
}
|
||||
@@ -16,6 +16,8 @@ public record PredictRequestOptions
|
||||
|
||||
public float TopP { get; init; } = 0.9f;
|
||||
|
||||
public float MinP { get; init; } = 0.0f;
|
||||
|
||||
public float Temperature { get; init; } = 0.1f;
|
||||
|
||||
public int Batches { get; init; } = 8;
|
||||
|
||||
@@ -6,7 +6,10 @@ This package contains a set of C# bindings around the `llmodel` C-API.
|
||||
TBD
|
||||
|
||||
## Installation
|
||||
TBD NuGet
|
||||
|
||||
Windows and Linux builds are available on NuGet: https://www.nuget.org/packages/Gpt4All
|
||||
|
||||
macOS is WIP due to code signing issues, contributions are welcome.
|
||||
|
||||
## Project Structure
|
||||
```
|
||||
@@ -23,6 +26,12 @@ gpt4all-bindings/
|
||||
└── linux-x64
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
On Windows and Linux, building GPT4All requires the complete Vulkan SDK. You may download it from here: https://vulkan.lunarg.com/sdk/home
|
||||
|
||||
macOS users do not need Vulkan, as GPT4All will use Metal instead.
|
||||
|
||||
## Local Build Instructions
|
||||
> **Note**
|
||||
> Tested On:
|
||||
@@ -54,7 +63,7 @@ chmod +x ./build_linux.sh
|
||||
1. Setup
|
||||
```
|
||||
choco install mingw
|
||||
$env:Path += ";C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin"
|
||||
$env:Path += ";C:\ProgramData\mingw64\mingw64\bin"
|
||||
choco install -y cmake --installargs 'ADD_CMAKE_TO_PATH=System'
|
||||
```
|
||||
2. Run the `./build_win-mingw.ps1` build script
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/bin/sh
|
||||
mkdir -p runtimes
|
||||
rm -rf runtimes/linux-x64
|
||||
mkdir -p runtimes/linux-x64/native
|
||||
@@ -7,4 +8,3 @@ cmake --build runtimes/linux-x64/build --parallel --config Release
|
||||
cp runtimes/linux-x64/build/libllmodel.so runtimes/linux-x64/native/libllmodel.so
|
||||
cp runtimes/linux-x64/build/libgptj*.so runtimes/linux-x64/native/
|
||||
cp runtimes/linux-x64/build/libllama*.so runtimes/linux-x64/native/
|
||||
cp runtimes/linux-x64/build/libmpt*.so runtimes/linux-x64/native/
|
||||
|
||||
@@ -12,5 +12,5 @@ cmake -G "MinGW Makefiles" -S ..\..\gpt4all-backend -B $BUILD_DIR
|
||||
cmake --build $BUILD_DIR --parallel --config Release
|
||||
|
||||
# copy native dlls
|
||||
cp "C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin\*dll" $LIBS_DIR
|
||||
cp "C:\ProgramData\mingw64\mingw64\bin\*dll" $LIBS_DIR
|
||||
cp "$BUILD_DIR\bin\*.dll" $LIBS_DIR
|
||||
@@ -139,7 +139,7 @@ $(info I CXX: $(CXXV))
|
||||
$(info )
|
||||
|
||||
llmodel.o:
|
||||
mkdir buildllm
|
||||
[ -e buildllm ] || mkdir buildllm
|
||||
cd buildllm && cmake ../../../gpt4all-backend/ $(CMAKEFLAGS) && make
|
||||
cd buildllm && cp -rf CMakeFiles/llmodel.dir/llmodel_c.cpp.o ../llmodel_c.o
|
||||
cd buildllm && cp -rf CMakeFiles/llmodel.dir/llmodel.cpp.o ../llmodel.o
|
||||
@@ -150,7 +150,7 @@ clean:
|
||||
rm -rf buildllm
|
||||
rm -rf example/main
|
||||
|
||||
binding.o:
|
||||
binding.o: binding.cpp binding.h
|
||||
$(CXX) $(CXXFLAGS) binding.cpp -o binding.o -c $(LDFLAGS)
|
||||
|
||||
libgpt4all.a: binding.o llmodel.o
|
||||
|
||||
@@ -36,7 +36,7 @@ func main() {
|
||||
In order to use the bindings you will need to build `libgpt4all.a`:
|
||||
|
||||
```
|
||||
git clone https://github.com/nomic-ai/gpt4all
|
||||
git clone --recurse-submodules https://github.com/nomic-ai/gpt4all
|
||||
cd gpt4all/gpt4all-bindings/golang
|
||||
make libgpt4all.a
|
||||
```
|
||||
|
||||
@@ -17,14 +17,13 @@
|
||||
|
||||
void* load_model(const char *fname, int n_threads) {
|
||||
// load the model
|
||||
llmodel_error new_error{};
|
||||
const char *new_error;
|
||||
auto model = llmodel_model_create2(fname, "auto", &new_error);
|
||||
if (model == nullptr ){
|
||||
fprintf(stderr, "%s: error '%s'\n",
|
||||
__func__, new_error.message);
|
||||
if (model == nullptr) {
|
||||
fprintf(stderr, "%s: error '%s'\n", __func__, new_error);
|
||||
return nullptr;
|
||||
}
|
||||
if (!llmodel_loadModel(model, fname)) {
|
||||
if (!llmodel_loadModel(model, fname, 2048, 100)) {
|
||||
llmodel_model_destroy(model);
|
||||
return nullptr;
|
||||
}
|
||||
@@ -37,7 +36,7 @@ std::string res = "";
|
||||
void * mm;
|
||||
|
||||
void model_prompt( const char *prompt, void *m, char* result, int repeat_last_n, float repeat_penalty, int n_ctx, int tokens, int top_k,
|
||||
float top_p, float temp, int n_batch,float ctx_erase)
|
||||
float top_p, float min_p, float temp, int n_batch,float ctx_erase)
|
||||
{
|
||||
llmodel_model* model = (llmodel_model*) m;
|
||||
|
||||
@@ -70,6 +69,7 @@ void model_prompt( const char *prompt, void *m, char* result, int repeat_last_n,
|
||||
.n_predict = 50,
|
||||
.top_k = 10,
|
||||
.top_p = 0.9,
|
||||
.min_p = 0.0,
|
||||
.temp = 1.0,
|
||||
.n_batch = 1,
|
||||
.repeat_penalty = 1.2,
|
||||
@@ -84,6 +84,7 @@ void model_prompt( const char *prompt, void *m, char* result, int repeat_last_n,
|
||||
prompt_context->top_k = top_k;
|
||||
prompt_context->context_erase = ctx_erase;
|
||||
prompt_context->top_p = top_p;
|
||||
prompt_context->min_p = min_p;
|
||||
prompt_context->temp = temp;
|
||||
prompt_context->n_batch = n_batch;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ extern "C" {
|
||||
void* load_model(const char *fname, int n_threads);
|
||||
|
||||
void model_prompt( const char *prompt, void *m, char* result, int repeat_last_n, float repeat_penalty, int n_ctx, int tokens, int top_k,
|
||||
float top_p, float temp, int n_batch,float ctx_erase);
|
||||
float top_p, float min_p, float temp, int n_batch,float ctx_erase);
|
||||
|
||||
void free_model(void *state_ptr);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ package gpt4all
|
||||
// #cgo LDFLAGS: -lgpt4all -lm -lstdc++ -ldl
|
||||
// void* load_model(const char *fname, int n_threads);
|
||||
// void model_prompt( const char *prompt, void *m, char* result, int repeat_last_n, float repeat_penalty, int n_ctx, int tokens, int top_k,
|
||||
// float top_p, float temp, int n_batch,float ctx_erase);
|
||||
// float top_p, float min_p, float temp, int n_batch,float ctx_erase);
|
||||
// void free_model(void *state_ptr);
|
||||
// extern unsigned char getTokenCallback(void *, char *);
|
||||
// void llmodel_set_implementation_search_path(const char *path);
|
||||
@@ -58,7 +58,7 @@ func (l *Model) Predict(text string, opts ...PredictOption) (string, error) {
|
||||
out := make([]byte, po.Tokens)
|
||||
|
||||
C.model_prompt(input, l.state, (*C.char)(unsafe.Pointer(&out[0])), C.int(po.RepeatLastN), C.float(po.RepeatPenalty), C.int(po.ContextSize),
|
||||
C.int(po.Tokens), C.int(po.TopK), C.float(po.TopP), C.float(po.Temperature), C.int(po.Batch), C.float(po.ContextErase))
|
||||
C.int(po.Tokens), C.int(po.TopK), C.float(po.TopP), C.float(po.MinP), C.float(po.Temperature), C.int(po.Batch), C.float(po.ContextErase))
|
||||
|
||||
res := C.GoString((*C.char)(unsafe.Pointer(&out[0])))
|
||||
res = strings.TrimPrefix(res, " ")
|
||||
|
||||
@@ -2,7 +2,7 @@ package gpt4all
|
||||
|
||||
type PredictOptions struct {
|
||||
ContextSize, RepeatLastN, Tokens, TopK, Batch int
|
||||
TopP, Temperature, ContextErase, RepeatPenalty float64
|
||||
TopP, MinP, Temperature, ContextErase, RepeatPenalty float64
|
||||
}
|
||||
|
||||
type PredictOption func(p *PredictOptions)
|
||||
@@ -11,6 +11,7 @@ var DefaultOptions PredictOptions = PredictOptions{
|
||||
Tokens: 200,
|
||||
TopK: 10,
|
||||
TopP: 0.90,
|
||||
MinP: 0.0,
|
||||
Temperature: 0.96,
|
||||
Batch: 1,
|
||||
ContextErase: 0.55,
|
||||
@@ -50,6 +51,13 @@ func SetTopP(topp float64) PredictOption {
|
||||
}
|
||||
}
|
||||
|
||||
// SetMinP sets the value for min p sampling
|
||||
func SetMinP(minp float64) PredictOption {
|
||||
return func(p *PredictOptions) {
|
||||
p.MinP = minp
|
||||
}
|
||||
}
|
||||
|
||||
// SetRepeatPenalty sets the repeat penalty.
|
||||
func SetRepeatPenalty(ce float64) PredictOption {
|
||||
return func(p *PredictOptions) {
|
||||
|
||||
@@ -12,17 +12,17 @@ You can add Java bindings into your Java project by adding the following depende
|
||||
<dependency>
|
||||
<groupId>com.hexadevlabs</groupId>
|
||||
<artifactId>gpt4all-java-binding</artifactId>
|
||||
<version>1.1.3</version>
|
||||
<version>1.1.5</version>
|
||||
</dependency>
|
||||
```
|
||||
**Gradle**
|
||||
```
|
||||
implementation 'com.hexadevlabs:gpt4all-java-binding:1.1.3'
|
||||
implementation 'com.hexadevlabs:gpt4all-java-binding:1.1.5'
|
||||
```
|
||||
|
||||
To add the library dependency for another build system see [Maven Central Java bindings](https://central.sonatype.com/artifact/com.hexadevlabs/gpt4all-java-binding/).
|
||||
|
||||
To download model binary weights file use a URL such as [`https://gpt4all.io/models/ggml-gpt4all-j-v1.3-groovy.bin`](https://gpt4all.io/models/ggml-gpt4all-j-v1.3-groovy.bin).
|
||||
To download model binary weights file use a URL such as [`https://gpt4all.io/models/gguf/gpt4all-13b-snoozy-q4_0.gguf`](https://gpt4all.io/models/gguf/gpt4all-13b-snoozy-q4_0.gguf).
|
||||
|
||||
For information about other models available see the [model file list](https://github.com/nomic-ai/gpt4all/tree/main/gpt4all-chat#manual-download-of-models).
|
||||
|
||||
@@ -121,4 +121,6 @@ If this is the case you can easily download and install the latest x64 Microsoft
|
||||
3. Version **1.1.4**:
|
||||
- Java bindings is compatible with gpt4all version 2.4.11
|
||||
- Falcon model support included.
|
||||
4. Version **1.1.5**:
|
||||
- Add a check for model file readability before loading model.
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
## Needed
|
||||
1. Integrate with circleci build pipeline like the C# binding.
|
||||
|
||||
## These are just ideas
|
||||
1. Better Chat completions function.
|
||||
2. Chat completion that returns result in OpenAI compatible format.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>com.hexadevlabs</groupId>
|
||||
<artifactId>gpt4all-java-binding</artifactId>
|
||||
<version>1.1.4</version>
|
||||
<version>1.1.5</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.hexadevlabs.gpt4all;
|
||||
|
||||
import jnr.ffi.Pointer;
|
||||
import jnr.ffi.byref.PointerByReference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -8,9 +9,8 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class LLModel implements AutoCloseable {
|
||||
|
||||
@@ -32,6 +32,7 @@ public class LLModel implements AutoCloseable {
|
||||
n_predict.set(128);
|
||||
top_k.set(40);
|
||||
top_p.set(0.95);
|
||||
min_p.set(0.0);
|
||||
temp.set(0.28);
|
||||
n_batch.set(8);
|
||||
repeat_penalty.set(1.1);
|
||||
@@ -71,6 +72,11 @@ public class LLModel implements AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withMinP(float min_p) {
|
||||
configToBuild.min_p.set(min_p);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withTemp(float temp) {
|
||||
configToBuild.temp.set(temp);
|
||||
return this;
|
||||
@@ -177,20 +183,25 @@ public class LLModel implements AutoCloseable {
|
||||
modelName = modelPath.getFileName().toString();
|
||||
String modelPathAbs = modelPath.toAbsolutePath().toString();
|
||||
|
||||
LLModelLibrary.LLModelError error = new LLModelLibrary.LLModelError(jnr.ffi.Runtime.getSystemRuntime());
|
||||
PointerByReference error = new PointerByReference();
|
||||
|
||||
// Check if model file exists
|
||||
if(!Files.exists(modelPath)){
|
||||
throw new IllegalStateException("Model file does not exist: " + modelPathAbs);
|
||||
}
|
||||
|
||||
// Check if file is Readable
|
||||
if(!Files.isReadable(modelPath)){
|
||||
throw new IllegalStateException("Model file cannot be read: " + modelPathAbs);
|
||||
}
|
||||
|
||||
// Create Model Struct. Will load dynamically the correct backend based on model type
|
||||
model = library.llmodel_model_create2(modelPathAbs, "auto", error);
|
||||
|
||||
if(model == null) {
|
||||
throw new IllegalStateException("Could not load gpt4all backend :" + error.message);
|
||||
throw new IllegalStateException("Could not load, gpt4all backend returned error: " + error.getValue().getString(0));
|
||||
}
|
||||
library.llmodel_loadModel(model, modelPathAbs);
|
||||
library.llmodel_loadModel(model, modelPathAbs, 2048, 100);
|
||||
|
||||
if(!library.llmodel_isModelLoaded(model)){
|
||||
throw new IllegalStateException("The model " + modelName + " could not be loaded");
|
||||
@@ -301,6 +312,197 @@ public class LLModel implements AutoCloseable {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The array of messages for the conversation.
|
||||
*/
|
||||
public static class Messages {
|
||||
|
||||
private final List<PromptMessage> messages = new ArrayList<>();
|
||||
|
||||
public Messages(PromptMessage...messages) {
|
||||
this.messages.addAll(Arrays.asList(messages));
|
||||
}
|
||||
|
||||
public Messages(List<PromptMessage> messages) {
|
||||
this.messages.addAll(messages);
|
||||
}
|
||||
|
||||
public Messages addPromptMessage(PromptMessage promptMessage) {
|
||||
this.messages.add(promptMessage);
|
||||
return this;
|
||||
}
|
||||
|
||||
List<PromptMessage> toList() {
|
||||
return Collections.unmodifiableList(this.messages);
|
||||
}
|
||||
|
||||
List<Map<String, String>> toListMap() {
|
||||
return messages.stream()
|
||||
.map(PromptMessage::toMap).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A message in the conversation, identical to OpenAI's chat message.
|
||||
*/
|
||||
public static class PromptMessage {
|
||||
|
||||
private static final String ROLE = "role";
|
||||
private static final String CONTENT = "content";
|
||||
|
||||
private final Map<String, String> message = new HashMap<>();
|
||||
|
||||
public PromptMessage() {
|
||||
}
|
||||
|
||||
public PromptMessage(Role role, String content) {
|
||||
addRole(role);
|
||||
addContent(content);
|
||||
}
|
||||
|
||||
public PromptMessage addRole(Role role) {
|
||||
return this.addParameter(ROLE, role.type());
|
||||
}
|
||||
|
||||
public PromptMessage addContent(String content) {
|
||||
return this.addParameter(CONTENT, content);
|
||||
}
|
||||
|
||||
public PromptMessage addParameter(String key, String value) {
|
||||
this.message.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public String content() {
|
||||
return this.parameter(CONTENT);
|
||||
}
|
||||
|
||||
public Role role() {
|
||||
String role = this.parameter(ROLE);
|
||||
return Role.from(role);
|
||||
}
|
||||
|
||||
public String parameter(String key) {
|
||||
return this.message.get(key);
|
||||
}
|
||||
|
||||
Map<String, String> toMap() {
|
||||
return Collections.unmodifiableMap(this.message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum Role {
|
||||
|
||||
SYSTEM("system"), ASSISTANT("assistant"), USER("user");
|
||||
|
||||
private final String type;
|
||||
|
||||
String type() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
static Role from(String type) {
|
||||
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case "system": return SYSTEM;
|
||||
case "assistant": return ASSISTANT;
|
||||
case "user": return USER;
|
||||
default: throw new IllegalArgumentException(
|
||||
String.format("You passed %s type but only %s are supported",
|
||||
type, Arrays.toString(Role.values())
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Role(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return type();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of the completion, similar to OpenAI's format.
|
||||
*/
|
||||
public static class CompletionReturn {
|
||||
private String model;
|
||||
private Usage usage;
|
||||
private Choices choices;
|
||||
|
||||
public CompletionReturn(String model, Usage usage, Choices choices) {
|
||||
this.model = model;
|
||||
this.usage = usage;
|
||||
this.choices = choices;
|
||||
}
|
||||
|
||||
public Choices choices() {
|
||||
return choices;
|
||||
}
|
||||
|
||||
public String model() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public Usage usage() {
|
||||
return usage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The generated completions.
|
||||
*/
|
||||
public static class Choices {
|
||||
|
||||
private final List<CompletionChoice> choices = new ArrayList<>();
|
||||
|
||||
public Choices(List<CompletionChoice> choices) {
|
||||
this.choices.addAll(choices);
|
||||
}
|
||||
|
||||
public Choices(CompletionChoice...completionChoices){
|
||||
this.choices.addAll(Arrays.asList(completionChoices));
|
||||
}
|
||||
|
||||
public Choices addCompletionChoice(CompletionChoice completionChoice) {
|
||||
this.choices.add(completionChoice);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CompletionChoice first() {
|
||||
return this.choices.get(0);
|
||||
}
|
||||
|
||||
public int totalChoices() {
|
||||
return this.choices.size();
|
||||
}
|
||||
|
||||
public CompletionChoice get(int index) {
|
||||
return this.choices.get(index);
|
||||
}
|
||||
|
||||
public List<CompletionChoice> choices() {
|
||||
return Collections.unmodifiableList(choices);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A completion choice, similar to OpenAI's format.
|
||||
*/
|
||||
public static class CompletionChoice extends PromptMessage {
|
||||
public CompletionChoice(Role role, String content) {
|
||||
super(role, content);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ChatCompletionResponse {
|
||||
public String model;
|
||||
@@ -318,6 +520,41 @@ public class LLModel implements AutoCloseable {
|
||||
// Getters and setters
|
||||
}
|
||||
|
||||
public CompletionReturn chatCompletionResponse(Messages messages,
|
||||
GenerationConfig generationConfig) {
|
||||
return chatCompletion(messages, generationConfig, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* chatCompletion formats the existing chat conversation into a template to be
|
||||
* easier to process for chat UIs. It is not absolutely necessary as generate method
|
||||
* may be directly used to make generations with gpt models.
|
||||
*
|
||||
* @param messages object to create theMessages to send to GPT model
|
||||
* @param generationConfig How to decode/process the generation.
|
||||
* @param streamToStdOut Send tokens as they are calculated Standard output.
|
||||
* @param outputFullPromptToStdOut Should full prompt built out of messages be sent to Standard output.
|
||||
* @return CompletionReturn contains stats and generated Text.
|
||||
*/
|
||||
public CompletionReturn chatCompletion(Messages messages,
|
||||
GenerationConfig generationConfig, boolean streamToStdOut,
|
||||
boolean outputFullPromptToStdOut) {
|
||||
|
||||
String fullPrompt = buildPrompt(messages.toListMap());
|
||||
|
||||
if(outputFullPromptToStdOut)
|
||||
System.out.print(fullPrompt);
|
||||
|
||||
String generatedText = generate(fullPrompt, generationConfig, streamToStdOut);
|
||||
|
||||
final CompletionChoice promptMessage = new CompletionChoice(Role.ASSISTANT, generatedText);
|
||||
final Choices choices = new Choices(promptMessage);
|
||||
|
||||
final Usage usage = getUsage(fullPrompt, generatedText);
|
||||
return new CompletionReturn(this.modelName, usage, choices);
|
||||
|
||||
}
|
||||
|
||||
public ChatCompletionResponse chatCompletion(List<Map<String, String>> messages,
|
||||
GenerationConfig generationConfig) {
|
||||
return chatCompletion(messages, generationConfig, false, false);
|
||||
@@ -347,19 +584,23 @@ public class LLModel implements AutoCloseable {
|
||||
ChatCompletionResponse response = new ChatCompletionResponse();
|
||||
response.model = this.modelName;
|
||||
|
||||
Usage usage = new Usage();
|
||||
usage.promptTokens = fullPrompt.length();
|
||||
usage.completionTokens = generatedText.length();
|
||||
usage.totalTokens = fullPrompt.length() + generatedText.length();
|
||||
response.usage = usage;
|
||||
response.usage = getUsage(fullPrompt, generatedText);
|
||||
|
||||
Map<String, String> message = new HashMap<>();
|
||||
message.put("role", "assistant");
|
||||
message.put("content", generatedText);
|
||||
|
||||
response.choices = List.of(message);
|
||||
|
||||
return response;
|
||||
|
||||
}
|
||||
|
||||
private Usage getUsage(String fullPrompt, String generatedText) {
|
||||
Usage usage = new Usage();
|
||||
usage.promptTokens = fullPrompt.length();
|
||||
usage.completionTokens = generatedText.length();
|
||||
usage.totalTokens = fullPrompt.length() + generatedText.length();
|
||||
return usage;
|
||||
}
|
||||
|
||||
protected static String buildPrompt(List<Map<String, String>> messages) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user