mirror of
https://github.com/slhaf/Partner.git
synced 2026-05-12 16:53:04 +08:00
Compare commits
694 Commits
fix
...
release-co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a00e65868 | ||
| 6c3c08b5d5 | |||
| ef5a332f17 | |||
|
|
9acabca40e | ||
| b654090a6e | |||
|
|
c9a7343b30 | ||
| 6250b480e0 | |||
|
|
63e8cc314a | ||
| 451f83e14d | |||
| 2b7e6718d9 | |||
| ed806c668a | |||
| da381c3adf | |||
| 1cbff98b36 | |||
| 95a30f147c | |||
| 0ea8dfc7d9 | |||
| 8cb876b532 | |||
| d72b34acfd | |||
| 79dcda082f | |||
| fbd1d17fc4 | |||
| a8e3a84db8 | |||
| e6e0eef161 | |||
| 4c042769f2 | |||
| 7bee14452e | |||
| 442397c1f5 | |||
| 0b329aa370 | |||
| ffc96bbb64 | |||
| 8398c14794 | |||
| bd4d5164d3 | |||
| 9073f88117 | |||
| 0710f60ed6 | |||
| 1bf02f543e | |||
| 19f87c93e3 | |||
| 828ddff2c2 | |||
| 57b23e0dcb | |||
| 11f7200e23 | |||
| ee8f568006 | |||
| 393dcff6df | |||
| 0fdc0038a5 | |||
| 97bf0618f4 | |||
| 4200df6525 | |||
| 686dc76b55 | |||
| adab925e7d | |||
| 6c74de2126 | |||
| 7fcdb1c671 | |||
| 78877f14d5 | |||
| 729700ceb7 | |||
| 6f48c36f67 | |||
| 551a2a16d9 | |||
| 40af7a4de6 | |||
| cbdb33fefe | |||
| d2c3416f48 | |||
| c3dc8180f1 | |||
| 1564c7d5e1 | |||
| 9e33f16aae | |||
| c84d88eab7 | |||
| 692c601f17 | |||
| 2a8b2aff7b | |||
| afb896e6db | |||
| 2b575df3f9 | |||
| db18c818ac | |||
| 973875b2e7 | |||
| 018b524be3 | |||
| 843549dadf | |||
| 32f0c5c6a8 | |||
| dd31f6a20d | |||
| 178c5138f7 | |||
| f9cdf164f3 | |||
| 0c2843a889 | |||
| 572bedcd18 | |||
| 32ee6b5ed6 | |||
| 428f133ac3 | |||
| 7aab236221 | |||
| 0d456596a8 | |||
| cbd926bd41 | |||
| 4a00e5f320 | |||
| 690f258dc9 | |||
| d7b271bf71 | |||
| ff80d6bbc4 | |||
| 3e73d574df | |||
| 9ea475432f | |||
| e66ed9e9c0 | |||
| 73f6ff2745 | |||
| 3eac52f4e2 | |||
| cf61c171a5 | |||
| 822ea82593 | |||
| f957d7caa4 | |||
| 9be499eb64 | |||
| 184c396752 | |||
| 04ceccc46f | |||
| b0185c65c8 | |||
| d43476864c | |||
| e022f134ac | |||
| 9144cd90ce | |||
| c23bda430f | |||
| aa98c29744 | |||
| eade39328a | |||
| 94adf9a368 | |||
| dd5ab3aaf3 | |||
| 075a8ece3e | |||
| 4bc2e4adbf | |||
| 9240ed44c4 | |||
| 878fe4dc10 | |||
| 03bae6424e | |||
| a14e123ff6 | |||
| d4277aed33 | |||
| e37dc7bc9b | |||
| 51706eb55e | |||
| eb3e5f3f34 | |||
| 164078bf42 | |||
| 13d04f85e2 | |||
| 3a5d0f1cb4 | |||
| aa05d3817b | |||
| 45f7d1c51e | |||
| 3d56966381 | |||
| de340029c6 | |||
| 96101de227 | |||
| 2927cb2b6f | |||
| 4af8feb178 | |||
| 43c2c648ac | |||
| a64c8f6c97 | |||
| 406b7ba831 | |||
| 9c902e68d0 | |||
| 4e894384bf | |||
| a5cee79a8f | |||
| d1da99c44d | |||
| d90c514159 | |||
| ac715602a6 | |||
| e1a5a07e2a | |||
| 9108d1db46 | |||
| e9eaaa24db | |||
| 2ec2d8e096 | |||
| 95a3f782e6 | |||
| 6ec3bfcc9b | |||
| b4d6be849b | |||
| a9e41eb4c7 | |||
| eea72c747c | |||
| 137b1ee917 | |||
| c5aa558319 | |||
| 7ce7461f79 | |||
| 8c8b0883bb | |||
| dd8e20838d | |||
| 15d7eb6850 | |||
| 657023694c | |||
| 9b97fffc5c | |||
| 14df95fc59 | |||
| 864bda95e4 | |||
| bfa3562335 | |||
| 9b24b662da | |||
| 41c611cb70 | |||
| 6ca77bdb74 | |||
| e5ce6d6722 | |||
| a7ef9bff49 | |||
| 92c8e01000 | |||
| e0543a8966 | |||
| 0c079c127e | |||
| 96301dc64a | |||
| 3fd90c0f5b | |||
| 8928ec9e07 | |||
| 503afecbe2 | |||
| 281984bb05 | |||
| 062af4b7d2 | |||
| 380c674d06 | |||
| 347560d979 | |||
| 999a6a8d7e | |||
| f510dc5a42 | |||
| d8ff0b5ea4 | |||
| dc147000ba | |||
| 247057e100 | |||
| 33ffd782c4 | |||
| a847f3bff8 | |||
| 28d0a43ef3 | |||
| cb4380eb1e | |||
| 737f9d122a | |||
| e65d3302c6 | |||
| fece67135f | |||
| d30e58ff83 | |||
| 93304878ad | |||
| e37a282141 | |||
| 04d6ad6d80 | |||
| 9755672750 | |||
| e0f955694d | |||
| c14e6f84e7 | |||
| 19f56d11f0 | |||
| 94d91d9746 | |||
| fac6e24e49 | |||
| 2aae1b1f14 | |||
| 1b48e955bd | |||
| b8cb2afbcf | |||
| 3732555f02 | |||
| ec52d64e73 | |||
| 7b963df991 | |||
| 4876d621b2 | |||
| 663d66fdea | |||
| d29dad4691 | |||
| 291371f8da | |||
| 3e5d6ebeb4 | |||
| 56daaf0b60 | |||
| d6593c10f9 | |||
| 328befecca | |||
| 1e46149d0a | |||
| 427d224f65 | |||
| 0528890d60 | |||
| f233c5ce32 | |||
| 91a595d073 | |||
| 6d27e55a1e | |||
| 2935daeffa | |||
| e04b2c4fe8 | |||
| 21a9d2114f | |||
| 3640cc2108 | |||
| a114044c23 | |||
| 874488ea79 | |||
| b80ff8400c | |||
| 6fd12cd19f | |||
| 2cbaccedba | |||
| 006e7c61e5 | |||
| 2458ea4849 | |||
| eab8eec46e | |||
| a242723727 | |||
| 57bc63c57b | |||
| 9aa793df8e | |||
| 8c04566243 | |||
| b1ba86be57 | |||
| f79a0521b2 | |||
| 332792daa2 | |||
| 3b236286b9 | |||
| 50db3fa7b2 | |||
| 6503ec32b4 | |||
| 9771aa1de5 | |||
| 660bb01440 | |||
| 188b5e8b53 | |||
| db4dc6d040 | |||
| ef9d177adc | |||
| 5a41e02602 | |||
| f387c36b17 | |||
| 03532d3d97 | |||
| f37bef57ba | |||
| 29d6546b07 | |||
| b9fd9bcaac | |||
| 4ae65b885e | |||
| 632e47ec13 | |||
| 4f200cadfc | |||
| e4df68ea5d | |||
| 81aa4b7933 | |||
| b4c44c7d98 | |||
| 1301a0f8b1 | |||
| 7d82ec7238 | |||
| d70054cd9b | |||
| def48fd0ce | |||
| 0b750f8028 | |||
| 6e37ed032b | |||
| b6c86c6640 | |||
| 71956b4dce | |||
| c9466f4359 | |||
| cb09b86b23 | |||
| d4a5c5a0ed | |||
| 274d007ba1 | |||
| c7df35beb4 | |||
| 1c995923a1 | |||
| 247052e318 | |||
| eb1723de97 | |||
| c2fbfe751f | |||
| 4a1828ed94 | |||
| db20e0ca78 | |||
| 09f90d8ad5 | |||
| baa6870ccf | |||
| fd43466dd5 | |||
| 7628d40645 | |||
| d806693e08 | |||
| dbfd0b1fc3 | |||
| f5b9f8fc58 | |||
| 7bbb7745f4 | |||
| b65f5f37fb | |||
| fb9b3860af | |||
| c5256cbc90 | |||
| 1592c876c7 | |||
| 453a1cfe84 | |||
| 54320dbfde | |||
| 201addbc64 | |||
| 5219142b5c | |||
| 750bef0fd8 | |||
| a9b925c614 | |||
| 264a45c85f | |||
| 155d06df45 | |||
| 7879836b91 | |||
| 82db27484c | |||
| d11a431614 | |||
| 50b07488a6 | |||
| 313cea0d3b | |||
| 4494d58ff9 | |||
| d7179364a1 | |||
| b05ef8683d | |||
| 556b8a5348 | |||
| 027ebf860e | |||
| 8cd12f7379 | |||
| 617daea17c | |||
| 61d5270625 | |||
| 93b0199c9e | |||
| 977d92881c | |||
| 6aa96c33ac | |||
| e85094670b | |||
| 21ea6a25c8 | |||
| ad65cd4c09 | |||
| ff46d97eed | |||
| 1a83075031 | |||
| 809d38bd07 | |||
| f7d46c8ef1 | |||
| e1ee6589ef | |||
| 17108f3239 | |||
| 59a5e22f35 | |||
| 30373cbc02 | |||
| 0e164115c0 | |||
| 3cc6e8df99 | |||
| ccb7041093 | |||
| e0b20ce414 | |||
| 1029624dc7 | |||
| 5b9b9c3c09 | |||
| 67d7fd34f8 | |||
| 12368ded53 | |||
| 7d9ec976e3 | |||
| d8b19ebcea | |||
| 7f4b82204a | |||
| 664bd5a0fb | |||
| 6474eb8dc6 | |||
| 4439e5c04b | |||
| ef2eb909b7 | |||
| fd20af3e1c | |||
| d30ff322a2 | |||
| cb63bbf570 | |||
| 4da4e5f161 | |||
| 5a717dbdda | |||
| a6682a7719 | |||
| 1465d7687b | |||
| d31cac70a6 | |||
| 108cf9b071 | |||
| c4b8c2a858 | |||
| d55b849747 | |||
| a9993299a5 | |||
| 4c47cac3a5 | |||
| cba9ff4f0b | |||
| 603b0835c5 | |||
| fc0d4ef03b | |||
| 97bb897407 | |||
| 8463eb9dae | |||
| 9794b39572 | |||
| 0506149f5f | |||
| 9325b84d14 | |||
| 6c8a1b2636 | |||
| 3c550af33d | |||
| 229c7a0edb | |||
| a067e058fb | |||
| cdfae8ab1a | |||
| c1998f61b6 | |||
| 3f6283d12a | |||
| 3d1c258944 | |||
| 36dfd65046 | |||
| ee1a033c1b | |||
| 027e8bddc0 | |||
| 0903b8482b | |||
| f51401e2f2 | |||
| 5ad80d8b86 | |||
| 760ba8300b | |||
| 0f3d4659ae | |||
| 331d415925 | |||
| f5f64971f3 | |||
| 1cd6ba11bb | |||
| 5db533f823 | |||
| 1b2ccaee9c | |||
| 8dc7ed080b | |||
| 3348557352 | |||
| 4bb83f86a8 | |||
| b256af0f58 | |||
| ec429db4da | |||
| 145aeed600 | |||
| 5e8ef6d66f | |||
| 65690c65f8 | |||
| 7df0f208b5 | |||
| 4484d4a06b | |||
| 25ddc6f181 | |||
| d905c4ace1 | |||
| c3c4c88c9a | |||
| ae1b7fc033 | |||
| d9e384960f | |||
| 2baa3971a8 | |||
| 4ee7a52f42 | |||
| 28400545a7 | |||
| 1ce2038ab8 | |||
| 0b63ec8523 | |||
| 28a1bf8d1f | |||
| 77059f84c4 | |||
| d3eb5e8ee3 | |||
| 01cfc04dc7 | |||
| 6919fe656e | |||
| df25f488fa | |||
| 036fd9e051 | |||
| 383a49b855 | |||
| 7e88b8b926 | |||
| c6c8a83dad | |||
| 6635d7aca2 | |||
| facc49a799 | |||
| 3c6076ee0a | |||
| 40bd2deeba | |||
| 839f19f15b | |||
| da1abbdc88 | |||
| e6a071fc93 | |||
| 56688785ba | |||
| 8a9892f039 | |||
| 06f5ae9aac | |||
| f8d90fbcee | |||
| b02f29b1b1 | |||
| f1848fece4 | |||
| 85cc5cace8 | |||
| d462f02960 | |||
| 5ae8b713d7 | |||
| cf25fce09e | |||
| 868b17b56b | |||
| fe8031d9ac | |||
| 5847b38f2b | |||
| 6920bc6130 | |||
| fa9512db3b | |||
| 51d51937ed | |||
| 23026d6dc8 | |||
| 661dd625e3 | |||
| 33fdc61eff | |||
| 0870d7bc0e | |||
| baf0b05e60 | |||
| 51efb55259 | |||
| d5095359db | |||
| 1abfc729f8 | |||
| 528e88f613 | |||
| 333d087979 | |||
| a863b43563 | |||
| dde01a6253 | |||
| fa50f4aeb7 | |||
| b87ede0e8b | |||
| 010860de8d | |||
| 379cabe042 | |||
| 1b164cedf1 | |||
| 7ee698768c | |||
| 2e29e5ca7f | |||
| e0a62053b5 | |||
| f13e45327d | |||
| f56ff7d719 | |||
| fd9b376afa | |||
| 542de84640 | |||
| 907bb626f2 | |||
| 2cdeaa1c30 | |||
| 833fe4deb3 | |||
| 3c26e77b76 | |||
| 2825f7f1de | |||
| 86b7e5c492 | |||
| b1e4d3c2e4 | |||
| 997616e45f | |||
| 6733984843 | |||
| 0f2052c507 | |||
| 23bfb8bac1 | |||
| 15c11ac500 | |||
| deffc91dd1 | |||
| b2d44668da | |||
| f8399d2280 | |||
| 2870e79f79 | |||
| 3c9ace8e56 | |||
| e510725e71 | |||
| f963cae4ed | |||
| 45c4e8169a | |||
| 6bf4d95b05 | |||
| 00ef090d2f | |||
| e62cddfe44 | |||
| 115a8d5446 | |||
| ef5d5802a7 | |||
| 87c34cc699 | |||
| bbace28d7a | |||
| c47d2b2285 | |||
| 38c618a222 | |||
| e00441faa8 | |||
| c3b0a9dd25 | |||
| 6b7c9db5b1 | |||
| e2ef92ce43 | |||
| 051b6450e7 | |||
| 2a3d33a61e | |||
| e57c03e213 | |||
| 14e6d71ac9 | |||
| dc9f9417bc | |||
| 5051c2f662 | |||
| c30ec35f85 | |||
| c7f113b59a | |||
| 8735660830 | |||
| 18b2bb8812 | |||
| 7fccea5b91 | |||
| a9bf7ca1c2 | |||
| 1685d148c4 | |||
| 73ab40416d | |||
| 1244d59fa4 | |||
| 11ea1045f4 | |||
| a1bc784da5 | |||
| 747d3e47d6 | |||
| 5f0165fa3a | |||
| 2b0682b9e0 | |||
| 16a92de377 | |||
| cbba183b60 | |||
| 8e642b07d9 | |||
| 66d8a95c73 | |||
| 0850f8403d | |||
| 24c29a6dc6 | |||
| f703cc8157 | |||
| d52f48f132 | |||
| f6afe21b43 | |||
| d381a97731 | |||
| 940beb2587 | |||
| 69d9f04f11 | |||
| e2bd9eb0af | |||
| 9ec03c4c95 | |||
| ecbbbc9954 | |||
| a5d26769e8 | |||
| 2db1bdf3e9 | |||
| 656d6b65e3 | |||
| 7c46f1d1ff | |||
| 406b4250aa | |||
| eab3d00fe8 | |||
| d47e9fbf95 | |||
| 4b77f26e7b | |||
| 650f9b27a1 | |||
| 9f479c5f6f | |||
| 227c735667 | |||
| b05b665960 | |||
| 882ec43f2b | |||
| 7cb565fd1b | |||
| 84b96b6645 | |||
| 2169376062 | |||
| 9bff74c8c7 | |||
| 76c9c27532 | |||
| 8524ca6f9f | |||
| 7dd2104689 | |||
| 6ba5784a7f | |||
| cdea8d6322 | |||
| 8ca2b9998d | |||
| d098b28f31 | |||
| 98e4d4cf1b | |||
| 70489e57f7 | |||
| a43c87006e | |||
| be43b7eec6 | |||
| 3bc2ce839a | |||
| fe5a366527 | |||
| 9f724cee5d | |||
| ad58b83020 | |||
| c9b64fec2a | |||
| 0eb4765235 | |||
| 050c39cbc7 | |||
| 08100aea8a | |||
| 2cd0774834 | |||
| 12df938d85 | |||
| 277c0d437f | |||
| 6b861f4b77 | |||
| d33b6617c1 | |||
| a1dcf4a6fa | |||
| 9c38719514 | |||
| 33df0fa017 | |||
| 08bda84471 | |||
| 76da3c29f8 | |||
| 558b589830 | |||
| 80d7c283c5 | |||
| b0bb40c5f0 | |||
| eec8f71096 | |||
| fbd30d1a96 | |||
| 346f925b66 | |||
| 04e8d9e531 | |||
| 63d1552de2 | |||
| 77eb9b92a4 | |||
| a1b4743eeb | |||
| 0768cddd2d | |||
| 75145cc547 | |||
| d1ca1cda7d | |||
| fac6609d6b | |||
| dce8825e58 | |||
| cd641ac8dd | |||
| 5ffdab9e4a | |||
| 830503eee4 | |||
| 96e74ec877 | |||
| 420d51af15 | |||
| 8ead306b7b | |||
| c793851107 | |||
| fb5cabc747 | |||
| c5f6c4e0ae | |||
| 200c0f3f13 | |||
| fdf398b86e | |||
| 774e2b6cd5 | |||
| 837a4c92d1 | |||
| ddd999d47b | |||
| 9694a022c7 | |||
| 31968c7076 | |||
| abec141e4e | |||
| cdb6ae9d01 | |||
| dd8d86d3c4 | |||
| 99b42620d0 | |||
| 70b8335d49 | |||
| 8ca475beeb | |||
| 4f36c0dd2d | |||
| 00993bd763 | |||
| a0bca668cb | |||
| c6118c41b0 | |||
| 872d21170a | |||
| 44ab6cfac8 | |||
| ec30ac1922 | |||
| 74b6d0c653 | |||
| de462866b2 | |||
| 4ea8926363 | |||
| 04c98c7856 | |||
| 0757856187 | |||
| 19ec93f248 | |||
| 5877b9e80d | |||
| 5db0b5fad1 | |||
| 623a86daab | |||
| 64f24d3fc3 | |||
| 3097efe453 | |||
| b58eeffd2f | |||
| 62cec79005 | |||
| 03a5935107 | |||
| 0ecaec0545 | |||
| 74f2c6c950 | |||
| f35a467ebc | |||
| 64b907707a | |||
| a6e33edc7a | |||
| 94ef79c67d | |||
| a222015abb | |||
| 1c562f0e7b | |||
| 89535a6b1c | |||
| 6e90bc8d67 | |||
| 0e741802d1 | |||
| db3435fccf | |||
| e3294ec302 | |||
| bf99e01b51 | |||
| 1bd23b20c4 | |||
| 442dd55686 | |||
| abe5dd5251 | |||
| 1f737c0e29 | |||
| d41074c814 | |||
| 621441601a | |||
| e00d77f076 | |||
| d614ac0b15 | |||
| 592e2604d9 | |||
| dcbd2c6569 | |||
| 476acb0641 | |||
| 88a14f36b2 | |||
| 05d1fff125 | |||
| 49a4c9eb01 | |||
| 9e76c3e7ad | |||
| 9762739138 | |||
| 1f5509c17d | |||
| ed042cfffa | |||
| 128592e23c | |||
| 5ba36ed3e8 | |||
| 4dea948f82 | |||
| dc4074715e | |||
| 225802c1a8 | |||
| e851e33b2e | |||
| cb28a5b068 | |||
| ad58567ada | |||
| 0eee12d685 | |||
| 1e6ff1b30c | |||
| 0413fc281d | |||
| 8a7681ae31 | |||
| 1947f25ed6 | |||
| 488246525f | |||
| 534dcd5ade | |||
| ad58c0cc7c | |||
| d546148d69 | |||
| bf2d5ac707 | |||
| 628234f6e2 | |||
| 4b852e0049 | |||
| 6e3deced77 | |||
| 6a351413a1 | |||
| ad973d4230 | |||
| 1d315a9b62 | |||
| 4e32129b31 | |||
| 3f59719e16 | |||
| c548cceec6 | |||
| b3098310b4 | |||
| f48d559a7b | |||
| 14a57f0be6 | |||
| dff7b69b51 | |||
| d77ffd1db6 | |||
| 264cdb09e5 | |||
| fea7f9c81f | |||
| a1520f117b | |||
| ae5caf8475 | |||
| 980d9384d1 | |||
| 9ba0d1363a | |||
| c3ca4145b8 | |||
| 746fda1a5e | |||
| ec4fbb7f19 | |||
| f9c3cacfea | |||
| e35e18f3b7 | |||
| 83832d2060 | |||
| 4757425a15 | |||
| 21b3a0e846 |
135
.github/workflows/release-core.yml
vendored
Normal file
135
.github/workflows/release-core.yml
vendored
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
name: Release Partner Core
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
description: "Release tag, for example rel-v0.5.0"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "rel-v*"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-core:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Resolve release metadata
|
||||||
|
id: release
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||||
|
TAG="${{ inputs.tag }}"
|
||||||
|
else
|
||||||
|
TAG="${GITHUB_REF_NAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERSION="${TAG#rel-v}"
|
||||||
|
ASSET_NAME="partner-core-${VERSION}.jar"
|
||||||
|
ASSET_URL="https://github.com/slhaf/Partner/releases/download/${TAG}/${ASSET_NAME}"
|
||||||
|
|
||||||
|
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "asset_name=${ASSET_NAME}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "asset_url=${ASSET_URL}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
echo "Release tag: ${TAG}"
|
||||||
|
echo "Release version: ${VERSION}"
|
||||||
|
echo "Release asset: ${ASSET_NAME}"
|
||||||
|
|
||||||
|
- name: Checkout release source
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ steps.release.outputs.tag }}
|
||||||
|
|
||||||
|
- name: Set up Java 21
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: "21"
|
||||||
|
|
||||||
|
- name: Build Partner Core
|
||||||
|
run: |
|
||||||
|
mvn -B -DskipTests=true -pl Partner-Core -am package
|
||||||
|
|
||||||
|
- name: Prepare release artifact
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir -p dist
|
||||||
|
|
||||||
|
SOURCE_JAR="Partner-Core/target/partner-core-${{ steps.release.outputs.version }}.jar"
|
||||||
|
|
||||||
|
if [ ! -f "$SOURCE_JAR" ]; then
|
||||||
|
echo "Expected artifact not found: $SOURCE_JAR"
|
||||||
|
echo "Available Partner-Core target files:"
|
||||||
|
find Partner-Core/target -maxdepth 1 -type f -name "*.jar" -print
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp "$SOURCE_JAR" "dist/${{ steps.release.outputs.asset_name }}"
|
||||||
|
ls -lh dist
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
if gh release view "${{ steps.release.outputs.tag }}" >/dev/null 2>&1; then
|
||||||
|
echo "Release ${{ steps.release.outputs.tag }} already exists."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
gh release create "${{ steps.release.outputs.tag }}" \
|
||||||
|
"dist/${{ steps.release.outputs.asset_name }}" \
|
||||||
|
--title "${{ steps.release.outputs.tag }}" \
|
||||||
|
--notes "Partner Core ${{ steps.release.outputs.version }}"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
|
||||||
|
- name: Checkout master for registry update
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: master
|
||||||
|
path: registry-worktree
|
||||||
|
|
||||||
|
- name: Update latestRelease
|
||||||
|
working-directory: registry-worktree
|
||||||
|
run: |
|
||||||
|
python3 - <<'PY'
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
version = "${{ steps.release.outputs.version }}"
|
||||||
|
url = "${{ steps.release.outputs.asset_url }}"
|
||||||
|
|
||||||
|
index_path = Path("registry/index.json")
|
||||||
|
index = json.loads(index_path.read_text(encoding="utf-8"))
|
||||||
|
|
||||||
|
index["partner"]["latestRelease"] = {
|
||||||
|
"url": url,
|
||||||
|
"version": version
|
||||||
|
}
|
||||||
|
|
||||||
|
index_path.write_text(
|
||||||
|
json.dumps(index, ensure_ascii=False, indent=2) + "\n",
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
PY
|
||||||
|
|
||||||
|
- name: Commit registry update
|
||||||
|
working-directory: registry-worktree
|
||||||
|
run: |
|
||||||
|
if git diff --quiet registry/index.json; then
|
||||||
|
echo "No latestRelease changes."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
git add registry/index.json
|
||||||
|
git commit -m "chore(registry): update latest core release to ${{ steps.release.outputs.tag }}"
|
||||||
|
git push origin HEAD:master
|
||||||
36
.github/workflows/sync-from-gitea.yml
vendored
36
.github/workflows/sync-from-gitea.yml
vendored
@@ -1,36 +0,0 @@
|
|||||||
name: Sync from Gitea
|
|
||||||
|
|
||||||
# 1. 给 GITHUB_TOKEN 开写权限
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '*/30 * * * *'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
sync:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: 配置 Git 用户
|
|
||||||
run: |
|
|
||||||
git config --global user.name "Gitea Sync Bot"
|
|
||||||
git config --global user.email "slhafzjw@slhaf.work"
|
|
||||||
|
|
||||||
- name: 关闭全局 SSL 校验
|
|
||||||
run: git config --global http.sslVerify false
|
|
||||||
|
|
||||||
- name: Clone from Gitea (mirror)
|
|
||||||
run: |
|
|
||||||
git clone --mirror \
|
|
||||||
https://${{ secrets.GITEA_USER }}:${{ secrets.GITEA_TOKEN }}@${{ secrets.GITEA_URL }} \
|
|
||||||
gitea-mirror
|
|
||||||
|
|
||||||
- name: Push to GitHub
|
|
||||||
run: |
|
|
||||||
cd gitea-mirror
|
|
||||||
# 明确推到名为 "github" 的 remote
|
|
||||||
git remote add github \
|
|
||||||
https://${{ github.repository_owner }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
|
|
||||||
git push --mirror github
|
|
||||||
70
.github/workflows/update-latest-buildable.yml
vendored
Normal file
70
.github/workflows/update-latest-buildable.yml
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
name: Update Latest Buildable
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
ref:
|
||||||
|
description: "Buildable ref, for example buildable/v0.5.0"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "buildable/*"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-latest-buildable:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Resolve buildable ref
|
||||||
|
id: buildable
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||||
|
echo "ref=${{ inputs.ref }}" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "ref=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Checkout master
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: master
|
||||||
|
|
||||||
|
- name: Update latestBuildable
|
||||||
|
run: |
|
||||||
|
python3 - <<'PY'
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ref = "${{ steps.buildable.outputs.ref }}"
|
||||||
|
index_path = Path("registry/index.json")
|
||||||
|
|
||||||
|
index = json.loads(index_path.read_text(encoding="utf-8"))
|
||||||
|
|
||||||
|
index["partner"]["latestBuildable"] = {
|
||||||
|
"url": "https://github.com/slhaf/Partner.git",
|
||||||
|
"ref": ref
|
||||||
|
}
|
||||||
|
|
||||||
|
index_path.write_text(
|
||||||
|
json.dumps(index, ensure_ascii=False, indent=2) + "\n",
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
PY
|
||||||
|
|
||||||
|
- name: Commit registry update
|
||||||
|
run: |
|
||||||
|
if git diff --quiet registry/index.json; then
|
||||||
|
echo "No latestBuildable changes."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
git add registry/index.json
|
||||||
|
git commit -m "chore(registry): update latest buildable to ${{ steps.buildable.outputs.ref }}"
|
||||||
|
git push
|
||||||
38
.github/workflows/update-module-index.yml
vendored
Normal file
38
.github/workflows/update-module-index.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
name: Update Module Index
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- "registry/modules/*.json"
|
||||||
|
- "registry/index.json"
|
||||||
|
- "scripts/update_module_index.py"
|
||||||
|
- "update-module-index.yml"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-registry-index:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Update module index
|
||||||
|
run: python3 scripts/update_module_index.py
|
||||||
|
|
||||||
|
- name: Commit updated index
|
||||||
|
run: |
|
||||||
|
if git diff --quiet registry/index.json; then
|
||||||
|
echo "No module index changes"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
git add registry/index.json
|
||||||
|
git commit -m "chore: update registry index"
|
||||||
|
git push
|
||||||
14
.gitignore
vendored
14
.gitignore
vendored
@@ -4,10 +4,7 @@ target/
|
|||||||
!**/src/test/**/target/
|
!**/src/test/**/target/
|
||||||
|
|
||||||
### IntelliJ IDEA ###
|
### IntelliJ IDEA ###
|
||||||
.idea/modules.xml
|
.idea/
|
||||||
.idea/jarRepositories.xml
|
|
||||||
.idea/compiler.xml
|
|
||||||
.idea/libraries/
|
|
||||||
*.iws
|
*.iws
|
||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
@@ -55,3 +52,12 @@ build/
|
|||||||
/data/
|
/data/
|
||||||
/generated-classes/
|
/generated-classes/
|
||||||
/.idea/copilot.data.migration.ask2agent.xml
|
/.idea/copilot.data.migration.ask2agent.xml
|
||||||
|
/Partner-Main/data/
|
||||||
|
/AGENTS.md
|
||||||
|
/.serena/
|
||||||
|
/Partner-Core/data/
|
||||||
|
/.ai/mcp/mcp.json
|
||||||
|
/.codex
|
||||||
|
|
||||||
|
# Maven / build outputs
|
||||||
|
dependency-reduced-pom.xml
|
||||||
6
.idea/copilot.data.migration.agent.xml
generated
Normal file
6
.idea/copilot.data.migration.agent.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AgentMigrationStateService">
|
||||||
|
<option name="migrationStatus" value="COMPLETED" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/copilot.data.migration.ask.xml
generated
Normal file
6
.idea/copilot.data.migration.ask.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AskMigrationStateService">
|
||||||
|
<option name="migrationStatus" value="COMPLETED" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/copilot.data.migration.edit.xml
generated
Normal file
6
.idea/copilot.data.migration.edit.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="EditMigrationStateService">
|
||||||
|
<option name="migrationStatus" value="COMPLETED" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
4
.idea/dictionaries/project.xml
generated
4
.idea/dictionaries/project.xml
generated
@@ -1,6 +1,10 @@
|
|||||||
<component name="ProjectDictionaryState">
|
<component name="ProjectDictionaryState">
|
||||||
<dictionary name="project">
|
<dictionary name="project">
|
||||||
<words>
|
<words>
|
||||||
|
<w>Onebot</w>
|
||||||
|
<w>onebot</w>
|
||||||
|
<w>openai</w>
|
||||||
|
<w>partnerctl</w>
|
||||||
<w>zuper</w>
|
<w>zuper</w>
|
||||||
</words>
|
</words>
|
||||||
</dictionary>
|
</dictionary>
|
||||||
|
|||||||
22
.idea/encodings.xml
generated
22
.idea/encodings.xml
generated
@@ -1,14 +1,20 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="Encoding">
|
<component name="Encoding">
|
||||||
<file url="file://$PROJECT_DIR$/Partner-Api/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/Partner-Core/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/Partner-Api/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/Partner-Core/src/main/java/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/Partner-Main/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/Partner-Core/src/main/java/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/Partner-Main/src/main/java/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/Partner-Core/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/Partner-Main/src/main/java/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/Partner-External-Modules/Partner-Onebot-Adapter/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/Partner-Main/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/Partner-External-Modules/Partner-Onebot-Adapter/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/Partner-Test-Demo/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/Partner-External-Modules/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/Partner-Test-Demo/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/Partner-External-Modules/src/main/resources" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/Partner-Framework/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/Partner-Framework/src/main/resources" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/Partner-Interaction-Api/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/Partner-Interaction-Api/src/main/resources" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/PartnerCtl/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/PartnerCtl/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
44
.idea/misc.xml
generated
44
.idea/misc.xml
generated
@@ -1,25 +1,35 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="EntryPointsManager">
|
<component name="EntryPointsManager">
|
||||||
<list size="14">
|
<list size="21">
|
||||||
<item index="0" class="java.lang.String" itemvalue="lombok.Data" />
|
<item index="0" class="java.lang.String" itemvalue="lombok.Data" />
|
||||||
<item index="1" class="java.lang.String" itemvalue="net.bytebuddy.implementation.bind.annotation.RuntimeType" />
|
<item index="1" class="java.lang.String" itemvalue="net.bytebuddy.implementation.bind.annotation.RuntimeType" />
|
||||||
<item index="2" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.Capability" />
|
<item index="2" class="java.lang.String" itemvalue="picocli.CommandLine.Command" />
|
||||||
<item index="3" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityCore" />
|
<item index="3" class="java.lang.String" itemvalue="picocli.CommandLine.Mixin" />
|
||||||
<item index="4" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityMethod" />
|
<item index="4" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.Capability" />
|
||||||
<item index="5" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CoordinateManager" />
|
<item index="5" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityCore" />
|
||||||
<item index="6" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.Coordinated" />
|
<item index="6" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityMethod" />
|
||||||
<item index="7" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute" />
|
<item index="7" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CoordinateManager" />
|
||||||
<item index="8" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AgentModule" />
|
<item index="8" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.Coordinated" />
|
||||||
<item index="9" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute" />
|
<item index="9" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.component.annotation.Init" />
|
||||||
<item index="10" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.Init" />
|
<item index="10" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute" />
|
||||||
<item index="11" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CapabilityMethod" />
|
<item index="11" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AgentRunningModule" />
|
||||||
<item index="12" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CoordinateManager" />
|
<item index="12" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule" />
|
||||||
<item index="13" class="java.lang.String" itemvalue="work.slhaf.partner.api.register.capability.annotation.Capability" />
|
<item index="13" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute" />
|
||||||
|
<item index="14" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.Init" />
|
||||||
|
<item index="15" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CapabilityMethod" />
|
||||||
|
<item index="16" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CoordinateManager" />
|
||||||
|
<item index="17" class="java.lang.String" itemvalue="work.slhaf.partner.api.register.capability.annotation.Capability" />
|
||||||
|
<item index="18" class="java.lang.String" itemvalue="work.slhaf.partner.framework.agent.factory.capability.annotation.CapabilityCore" />
|
||||||
|
<item index="19" class="java.lang.String" itemvalue="work.slhaf.partner.framework.agent.factory.capability.annotation.CapabilityMethod" />
|
||||||
|
<item index="20" class="java.lang.String" itemvalue="work.slhaf.partner.framework.agent.factory.component.annotation.AgentComponent" />
|
||||||
</list>
|
</list>
|
||||||
<writeAnnotations>
|
<writeAnnotations>
|
||||||
<writeAnnotation name="work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability" />
|
<writeAnnotation name="work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability" />
|
||||||
|
<writeAnnotation name="work.slhaf.partner.api.agent.factory.component.annotation.InjectModule" />
|
||||||
<writeAnnotation name="work.slhaf.partner.api.agent.factory.module.annotation.InjectModule" />
|
<writeAnnotation name="work.slhaf.partner.api.agent.factory.module.annotation.InjectModule" />
|
||||||
|
<writeAnnotation name="work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability" />
|
||||||
|
<writeAnnotation name="work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule" />
|
||||||
</writeAnnotations>
|
</writeAnnotations>
|
||||||
</component>
|
</component>
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
@@ -27,8 +37,16 @@
|
|||||||
<option name="originalFiles">
|
<option name="originalFiles">
|
||||||
<list>
|
<list>
|
||||||
<option value="$PROJECT_DIR$/pom.xml" />
|
<option value="$PROJECT_DIR$/pom.xml" />
|
||||||
|
<option value="$PROJECT_DIR$/PartnerExecutor/pom.xml" />
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
|
<option name="ignoredFiles">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$/Partner-Common/pom.xml" />
|
||||||
|
<option value="$PROJECT_DIR$/Partner-SandboxRunner/pom.xml" />
|
||||||
|
<option value="$PROJECT_DIR$/Partner-Test-Demo/pom.xml" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="PWA">
|
<component name="PWA">
|
||||||
<option name="enabled" value="true" />
|
<option name="enabled" value="true" />
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
|
||||||
<parent>
|
|
||||||
<artifactId>Partner</artifactId>
|
|
||||||
<groupId>work.slhaf</groupId>
|
|
||||||
<version>0.5.0</version>
|
|
||||||
</parent>
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<artifactId>Partner-Api</artifactId>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<version>4.13.2</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<artifactId>hamcrest-core</artifactId>
|
|
||||||
<groupId>org.hamcrest</groupId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<properties>
|
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
</properties>
|
|
||||||
</project>
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>work.slhaf</groupId>
|
|
||||||
<artifactId>Partner</artifactId>
|
|
||||||
<version>0.5.0</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>Partner-Api</artifactId>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>net.bytebuddy</groupId>
|
|
||||||
<artifactId>byte-buddy</artifactId>
|
|
||||||
<version>1.17.6</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.reflections</groupId>
|
|
||||||
<artifactId>reflections</artifactId>
|
|
||||||
<version>0.10.2</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<version>1.18.36</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<version>4.13.2</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.junit.jupiter</groupId>
|
|
||||||
<artifactId>junit-jupiter</artifactId>
|
|
||||||
<version>5.13.2</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-api</artifactId>
|
|
||||||
<version>2.0.17</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>ch.qos.logback</groupId>
|
|
||||||
<artifactId>logback-classic</artifactId>
|
|
||||||
<version>1.5.17</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>commons-io</groupId>
|
|
||||||
<artifactId>commons-io</artifactId>
|
|
||||||
<version>2.18.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>cn.hutool</groupId>
|
|
||||||
<artifactId>hutool-all</artifactId>
|
|
||||||
<version>5.8.36</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.alibaba</groupId>
|
|
||||||
<artifactId>fastjson</artifactId>
|
|
||||||
<version>2.0.56</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
</project>
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import work.slhaf.partner.api.agent.factory.AgentRegisterFactory;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.exception.AgentExceptionCallback;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.exception.AgentLaunchFailedException;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.exception.GlobalExceptionHandler;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.AgentGateway;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>Agent 启动入口</h2>
|
|
||||||
* 详细启动流程请参阅{@link AgentRegisterFactory}
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public final class Agent {
|
|
||||||
|
|
||||||
public static AgentConfigManagerStep newAgent(Class<?> clazz) {
|
|
||||||
if (clazz == null) {
|
|
||||||
throw new AgentLaunchFailedException("Agent class 和 interaction flow context 不能为 null");
|
|
||||||
}
|
|
||||||
return new AgentApp(clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface AgentConfigManagerStep {
|
|
||||||
AgentGatewayStep setAgentConfigManager(Class<? extends AgentConfigManager> agentConfigManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface AgentGatewayStep {
|
|
||||||
AgentStep setGateway(Class<? extends AgentGateway> gateway);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface AgentStep {
|
|
||||||
AgentStep addBeforeLaunchRunners(Runnable... runners);
|
|
||||||
|
|
||||||
AgentStep addAfterLaunchRunners(Runnable... runners);
|
|
||||||
|
|
||||||
AgentStep setAgentExceptionCallback(Class<? extends AgentExceptionCallback> agentExceptionCallback);
|
|
||||||
|
|
||||||
AgentStep addScanPackage(String packageName);
|
|
||||||
|
|
||||||
AgentStep addScanDir(String externalPackagePath);
|
|
||||||
|
|
||||||
void launch();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static class AgentApp implements AgentStep, AgentGatewayStep, AgentConfigManagerStep {
|
|
||||||
|
|
||||||
private final ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();
|
|
||||||
private final List<Runnable> beforeLaunchRunners = new ArrayList<>();
|
|
||||||
private final List<Runnable> afterLaunchRunners = new ArrayList<>();
|
|
||||||
private AgentGateway gateway;
|
|
||||||
private final Class<?> applicationClass;
|
|
||||||
private Class<? extends AgentConfigManager> agentConfigManagerClass;
|
|
||||||
private Class<? extends AgentGateway> gatewayClass;
|
|
||||||
private Class<? extends AgentExceptionCallback> agentExceptionCallbackClass;
|
|
||||||
|
|
||||||
private final CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
|
|
||||||
private AgentApp(Class<?> clazz) {
|
|
||||||
this.applicationClass = clazz;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AgentStep setGateway(Class<? extends AgentGateway> gateway) {
|
|
||||||
this.gatewayClass = gateway;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AgentStep addBeforeLaunchRunners(Runnable... runners) {
|
|
||||||
this.beforeLaunchRunners.addAll(List.of(runners));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AgentStep addAfterLaunchRunners(Runnable... runners) {
|
|
||||||
this.afterLaunchRunners.addAll(List.of(runners));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AgentGatewayStep setAgentConfigManager(Class<? extends AgentConfigManager> agentConfigManager) {
|
|
||||||
this.agentConfigManagerClass = agentConfigManager;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AgentStep setAgentExceptionCallback(Class<? extends AgentExceptionCallback> agentExceptionCallback) {
|
|
||||||
agentExceptionCallbackClass = agentExceptionCallback;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AgentStep addScanPackage(String packageName) {
|
|
||||||
AgentRegisterFactory.addScanPackage(packageName);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AgentStep addScanDir(String externalPackagePath) {
|
|
||||||
AgentRegisterFactory.addScanDir(externalPackagePath);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void launch() {
|
|
||||||
beforeLaunch();
|
|
||||||
AgentRegisterFactory.launch(applicationClass.getPackageName());
|
|
||||||
afterLaunch();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void afterLaunch() {
|
|
||||||
try {
|
|
||||||
this.gateway = gatewayClass.getDeclaredConstructor().newInstance();
|
|
||||||
executorService.execute(() -> {
|
|
||||||
gateway.launch();
|
|
||||||
latch.countDown();
|
|
||||||
log.info("Gateway 启动完毕: {}", gatewayClass.getSimpleName());
|
|
||||||
});
|
|
||||||
latch.await();
|
|
||||||
launchRunners(afterLaunchRunners);
|
|
||||||
log.info("后置任务启动完毕");
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new AgentLaunchFailedException("Agent 后置任务启动失败", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void beforeLaunch() {
|
|
||||||
try {
|
|
||||||
AgentConfigManager.setINSTANCE(agentConfigManagerClass.getDeclaredConstructor().newInstance());
|
|
||||||
log.info("配置管理器设置完毕: {}",agentConfigManagerClass.getSimpleName());
|
|
||||||
GlobalExceptionHandler.setExceptionCallback(agentExceptionCallbackClass.getDeclaredConstructor().newInstance());
|
|
||||||
log.info("异常处理回调设置完毕: {}",agentExceptionCallbackClass.getSimpleName());
|
|
||||||
launchRunners(beforeLaunchRunners);
|
|
||||||
log.info("前置任务启动完毕");
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new AgentLaunchFailedException("Agent 前置任务启动失败", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void launchRunners(List<Runnable> runners) {
|
|
||||||
for (Runnable runner : runners) {
|
|
||||||
executorService.execute(runner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory;
|
|
||||||
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.exception.CapabilityFactoryExecuteFailedException;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
|
|
||||||
public abstract class AgentBaseFactory {
|
|
||||||
public void execute(AgentRegisterContext context) {
|
|
||||||
try {
|
|
||||||
setVariables(context);
|
|
||||||
run();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new CapabilityFactoryExecuteFailedException(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void setVariables(AgentRegisterContext context);
|
|
||||||
|
|
||||||
protected abstract void run() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException;
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory;
|
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
|
||||||
import org.reflections.util.ClasspathHelper;
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.CapabilityCheckFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.CapabilityInjectFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.CapabilityRegisterFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.config.ConfigLoaderFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.exception.ExternalModuleLoadFailedException;
|
|
||||||
import work.slhaf.partner.api.agent.factory.exception.ExternalModulePathNotExistException;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.ModuleCheckFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.ModuleInitHookExecuteFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.ModuleProxyFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.ModuleRegisterFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.data.AgentContext;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.AgentRunningFlow;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>Agent 注册工厂</h2>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* 具体流程依次按照 {@link AgentRegisterFactory#launch(String)} 方法顺序执行,最终将执行模块列表对应实例交给 {@link AgentConfigManager} ,传递给 {@link AgentRunningFlow} 针对交互做出调用
|
|
||||||
* <p/>
|
|
||||||
*/
|
|
||||||
public class AgentRegisterFactory {
|
|
||||||
|
|
||||||
private static final List<URL> urls = new ArrayList<>();
|
|
||||||
|
|
||||||
private AgentRegisterFactory() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void launch(String packageName) {
|
|
||||||
urls.addAll(packageNameToURL(packageName));
|
|
||||||
AgentRegisterContext registerContext = new AgentRegisterContext(urls);
|
|
||||||
//流程
|
|
||||||
//0. 加载配置
|
|
||||||
new ConfigLoaderFactory().execute(registerContext);
|
|
||||||
//1. 注册并检查Module
|
|
||||||
new ModuleCheckFactory().execute(registerContext);
|
|
||||||
new ModuleRegisterFactory().execute(registerContext);
|
|
||||||
//2. 为module通过动态代理添加PostHook逻辑并进行实例化
|
|
||||||
new ModuleProxyFactory().execute(registerContext);
|
|
||||||
//3. 加载检查Capability层内容后进行能力层的内容注册
|
|
||||||
new CapabilityCheckFactory().execute(registerContext);
|
|
||||||
new CapabilityRegisterFactory().execute(registerContext);
|
|
||||||
//. 先一步注入Capability,避免因前hook逻辑存在针对能力的引用而报错
|
|
||||||
new CapabilityInjectFactory().execute(registerContext);
|
|
||||||
//. 执行模块PreHook逻辑
|
|
||||||
new ModuleInitHookExecuteFactory().execute(registerContext);
|
|
||||||
|
|
||||||
List<MetaModule> moduleList = registerContext.getModuleFactoryContext().getAgentModuleList();
|
|
||||||
AgentConfigManager.INSTANCE.moduleEnabledStatusFilterAndRecord(moduleList);
|
|
||||||
|
|
||||||
BeanUtil.copyProperties(registerContext, AgentContext.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加可扫描包
|
|
||||||
*
|
|
||||||
* @param packageName 指定的包名
|
|
||||||
*/
|
|
||||||
public static void addScanPackage(String packageName) {
|
|
||||||
urls.addAll(packageNameToURL(packageName));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加外部模块目录
|
|
||||||
*
|
|
||||||
* @param externalPackagePath 指定的外部模块目录路径
|
|
||||||
*/
|
|
||||||
public static void addScanDir(String externalPackagePath) {
|
|
||||||
File file = new File(externalPackagePath);
|
|
||||||
if (!file.exists() || !file.isDirectory()) {
|
|
||||||
throw new ExternalModulePathNotExistException("不存在的外部模块目录: " + externalPackagePath);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
File[] files = file.listFiles();
|
|
||||||
if (files == null || files.length == 0) {
|
|
||||||
throw new ExternalModulePathNotExistException("外部模块目录为空: " + externalPackagePath);
|
|
||||||
}
|
|
||||||
for (File f : files) {
|
|
||||||
if (f.getName().endsWith(".jar")) {
|
|
||||||
urls.add(f.toURI().toURL());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ExternalModuleLoadFailedException("外部模块URL获取失败: " + externalPackagePath, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<URL> packageNameToURL(String packageName) {
|
|
||||||
return ClasspathHelper.forPackage(packageName).stream().toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,271 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability;
|
|
||||||
|
|
||||||
import cn.hutool.core.util.ClassUtil;
|
|
||||||
import org.reflections.Reflections;
|
|
||||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.annotation.*;
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.exception.*;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.CapabilityFactoryContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
|
|
||||||
import work.slhaf.partner.api.agent.util.AgentUtil;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static work.slhaf.partner.api.agent.util.AgentUtil.isAssignableFromAnnotation;
|
|
||||||
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>Agent启动流程 4</h2>
|
|
||||||
*
|
|
||||||
* <p>负责通过反射收集 {@link Capability} 和 {@link CapabilityCore} 注解所在类,并判断是否存在被错误忽略的方法</p>
|
|
||||||
*
|
|
||||||
* <ol>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link CapabilityCheckFactory#loadCoresAndCapabilities()}</p>
|
|
||||||
* 通过反射收集 {@link Capability} 和 {@link CapabilityCore} 注解所在类为对应集合
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link CapabilityCheckFactory#checkCountAndCapabilities()}</p>
|
|
||||||
* 检测 {@link Capability} 与 {@link CapabilityCore} 的数量、对应的能力是否相等。每一个core都将对应一个capability,并通过value属性进行匹配
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link CapabilityCheckFactory#checkCapabilityMethods()}</p>
|
|
||||||
* 检测在 {@link Capability} 与 {@link CapabilityCore} 中是否存在对方尚未实现/注册的方法
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link CapabilityCheckFactory#checkCoordinatedMethods()}</p>
|
|
||||||
* 检查是否包含协调方法({@link ToCoordinated}),如果存在,则进一步检查在 {@link CoordinateManager} 所注类中是否有提供对应的实现
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link CapabilityCheckFactory#checkInjectCapability()}</p>
|
|
||||||
* 检查 {@link InjectCapability} 注解是否只用在 {@link CapabilityHolder} 所标识类的字段上。{@link AgentModule} 与 {@link AgentSubModule} 已经被 {@link CapabilityHolder} 标注
|
|
||||||
* </li>
|
|
||||||
* </ol>
|
|
||||||
*
|
|
||||||
* <p>下一步流程请参阅{@link CapabilityRegisterFactory}</p>
|
|
||||||
*/
|
|
||||||
public class CapabilityCheckFactory extends AgentBaseFactory {
|
|
||||||
|
|
||||||
private Reflections reflections;
|
|
||||||
private Set<Class<?>> cores;
|
|
||||||
private Set<Class<?>> capabilities;
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setVariables(AgentRegisterContext context) {
|
|
||||||
CapabilityFactoryContext factoryContext = context.getCapabilityFactoryContext();
|
|
||||||
reflections = context.getReflections();
|
|
||||||
cores = factoryContext.getCores();
|
|
||||||
capabilities = factoryContext.getCapabilities();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void run() {
|
|
||||||
loadCoresAndCapabilities();
|
|
||||||
checkCountAndCapabilities();
|
|
||||||
checkCapabilityMethods();
|
|
||||||
checkCoordinatedMethods();
|
|
||||||
checkCoordinatedManager();
|
|
||||||
checkInjectCapability();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkCoordinatedManager() {
|
|
||||||
reflections.getTypesAnnotatedWith(CoordinateManager.class)
|
|
||||||
.stream()
|
|
||||||
.filter(ClassUtil::isNormalClass)
|
|
||||||
.forEach(managerClass -> {
|
|
||||||
try {
|
|
||||||
if (!managerClass.getDeclaredConstructor().canAccess(null)) {
|
|
||||||
throw new CapabilityCheckFailedException("CoordinateManager 所注类的无参构造方法未公开!");
|
|
||||||
}
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
throw new CapabilityCheckFailedException("CoordinateManager 所注类缺少无参构造方法!");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadCoresAndCapabilities() {
|
|
||||||
cores.addAll(reflections.getTypesAnnotatedWith(CapabilityCore.class));
|
|
||||||
capabilities.addAll(reflections.getTypesAnnotatedWith(Capability.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查<code>@InjectCapability</code>注解是否只用在<code>@CapabilityHolder</code>所标识类的字段上
|
|
||||||
*/
|
|
||||||
private void checkInjectCapability() {
|
|
||||||
reflections.getFieldsAnnotatedWith(InjectCapability.class).forEach(field -> {
|
|
||||||
Class<?> declaringClass = field.getDeclaringClass();
|
|
||||||
if (!isAssignableFromAnnotation(declaringClass, CapabilityHolder.class)) {
|
|
||||||
throw new UnMatchedCapabilityException("InjectCapability 注解只能用于 CapabilityHolder 注解所在类,检查该类是否使用了@CapabilityHolder注解或者受其标注的注解或父类: " + declaringClass);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查是否包含协调方法,如果存在,则进一步检查是否存在<code>@CoordinateManager</code>提供对应的实现
|
|
||||||
*/
|
|
||||||
private void checkCoordinatedMethods() {
|
|
||||||
//检查各个capability中是否含有ToCoordinated注解
|
|
||||||
//如果含有,则需要查找AbstractCognationManager的子类,看这里是否有对应的Coordinated注解所在方法
|
|
||||||
Set<String> methodsToCoordinated = capabilities.stream()
|
|
||||||
.flatMap(capability -> Arrays.stream(capability.getDeclaredMethods()))
|
|
||||||
.filter(method -> method.isAnnotationPresent(ToCoordinated.class))
|
|
||||||
.map(method -> {
|
|
||||||
String capabilityValue = method.getDeclaringClass().getAnnotation(Capability.class).value();
|
|
||||||
return capabilityValue + "." + methodSignature(method);
|
|
||||||
})
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
if (!methodsToCoordinated.isEmpty()) {
|
|
||||||
Set<Class<?>> subTypesOfAbsCM = reflections.getTypesAnnotatedWith(CoordinateManager.class);
|
|
||||||
Set<String> methodsCoordinated = getMethodsCoordinated(subTypesOfAbsCM);
|
|
||||||
if (!methodsCoordinated.equals(methodsToCoordinated)) {
|
|
||||||
// 找出缺少的协调方法
|
|
||||||
Set<String> missingMethods = new HashSet<>(methodsToCoordinated);
|
|
||||||
missingMethods.removeAll(methodsCoordinated);
|
|
||||||
|
|
||||||
// 找出多余的协调方法
|
|
||||||
Set<String> extraMethods = new HashSet<>(methodsCoordinated);
|
|
||||||
extraMethods.removeAll(methodsToCoordinated);
|
|
||||||
|
|
||||||
// 抛出异常或记录错误
|
|
||||||
if (!missingMethods.isEmpty()) {
|
|
||||||
throw new UnMatchedCoordinatedMethodException("缺少协调方法: " + String.join(", ", missingMethods));
|
|
||||||
}
|
|
||||||
if (!extraMethods.isEmpty()) {
|
|
||||||
throw new UnMatchedCoordinatedMethodException("发现多余的协调方法: " + String.join(", ", extraMethods));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<String> getMethodsCoordinated(Set<Class<?>> classes) {
|
|
||||||
Set<String> methodsCoordinated = new HashSet<>();
|
|
||||||
for (Class<?> cm : classes) {
|
|
||||||
Method[] methods = cm.getMethods();
|
|
||||||
for (Method method : methods) {
|
|
||||||
if (method.isAnnotationPresent(Coordinated.class)) {
|
|
||||||
methodsCoordinated.add(method.getAnnotation(Coordinated.class).capability() + "." + methodSignature(method));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return methodsCoordinated;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查看在<code>Capability</code>在对应的<core>CapabilityCore</core>中存在尚未实现的方法
|
|
||||||
*/
|
|
||||||
private void checkCapabilityMethods() {
|
|
||||||
HashMap<String, List<Method>> capabilitiesMethods = getCapabilityMethods(capabilities);
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (Class<?> core : cores) {
|
|
||||||
List<Method> methodsWithAnnotation = Arrays.stream(core.getMethods())
|
|
||||||
.filter(method -> method.isAnnotationPresent(CapabilityMethod.class))
|
|
||||||
.toList();
|
|
||||||
List<Method> capabilityMethods = capabilitiesMethods.get(core.getAnnotation(CapabilityCore.class).value());
|
|
||||||
LackRecord lackRecord = checkMethodsMatched(methodsWithAnnotation, capabilityMethods);
|
|
||||||
if (lackRecord.hasNotEmptyRecord()) {
|
|
||||||
sb.append(lackRecord.toLackErrorMsg(core.getAnnotation(CapabilityCore.class).value()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!sb.isEmpty()) {
|
|
||||||
throw new UnMatchedCapabilityMethodException(sb.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private LackRecord checkMethodsMatched(List<Method> methodsWithAnnotation, List<Method> capabilityMethods) {
|
|
||||||
Set<String> collectedMethodsWithAnnotation = methodsWithAnnotation.stream()
|
|
||||||
.filter(method -> !method.isAnnotationPresent(ToCoordinated.class))
|
|
||||||
.map(AgentUtil::methodSignature)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
Set<String> collectedCapabilityMethods = capabilityMethods.stream()
|
|
||||||
.filter(method -> !method.isAnnotationPresent(ToCoordinated.class))
|
|
||||||
.map(AgentUtil::methodSignature)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
return checkMethodsMatched(collectedMethodsWithAnnotation, collectedCapabilityMethods);
|
|
||||||
}
|
|
||||||
|
|
||||||
private LackRecord checkMethodsMatched(Set<String> collectedMethodsWithAnnotation, Set<String> collectedCapabilityMethods) {
|
|
||||||
List<String> coreLack = new ArrayList<>();
|
|
||||||
List<String> capLack = new ArrayList<>();
|
|
||||||
// 找出 core 中多余的方法
|
|
||||||
for (String coreSig : collectedMethodsWithAnnotation) {
|
|
||||||
if (!collectedCapabilityMethods.contains(coreSig)) {
|
|
||||||
capLack.add(coreSig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 找出 capability 中多余的方法
|
|
||||||
for (String capSig : collectedCapabilityMethods) {
|
|
||||||
if (!collectedMethodsWithAnnotation.contains(capSig)) {
|
|
||||||
coreLack.add(capSig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new LackRecord(coreLack, capLack);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private HashMap<String, List<Method>> getCapabilityMethods(Set<Class<?>> capabilities) {
|
|
||||||
HashMap<String, List<Method>> capabilityMethods = new HashMap<>();
|
|
||||||
capabilities.forEach(capability -> {
|
|
||||||
capabilityMethods.put(capability.getAnnotation(Capability.class).value(), Arrays.stream(capability.getMethods()).toList());
|
|
||||||
});
|
|
||||||
return capabilityMethods;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查<code>Capability</code>和<code>CapabilityCore</code>的数量和标识是否匹配
|
|
||||||
*/
|
|
||||||
private void checkCountAndCapabilities() {
|
|
||||||
if (cores.size() != capabilities.size()) {
|
|
||||||
throw new UnMatchedCapabilityException("Capability 注册异常: 已存在的CapabilityCore与Capability数量不匹配!");
|
|
||||||
}
|
|
||||||
if (!checkValuesMatched(cores, capabilities)) {
|
|
||||||
throw new UnMatchedCapabilityException("Capability 注册异常: 已存在的CapabilityCore与Capability不匹配!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkValuesMatched(Set<Class<?>> cores, Set<Class<?>> capabilities) {
|
|
||||||
Set<String> coresValues = new HashSet<>();
|
|
||||||
Set<String> capabilitiesValues = new HashSet<>();
|
|
||||||
for (Class<?> core : cores) {
|
|
||||||
CapabilityCore annotation = core.getAnnotation(CapabilityCore.class);
|
|
||||||
if (annotation != null) {
|
|
||||||
if (coresValues.contains(annotation.value())) {
|
|
||||||
throw new DuplicateCapabilityException(String.format("Capability 注册异常: 重复的Capability核心: %s", annotation.value()));
|
|
||||||
}
|
|
||||||
coresValues.add(annotation.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Class<?> capability : capabilities) {
|
|
||||||
Capability annotation = capability.getAnnotation(Capability.class);
|
|
||||||
if (annotation != null) {
|
|
||||||
if (capabilitiesValues.contains(annotation.value())) {
|
|
||||||
throw new DuplicateCapabilityException(String.format("Capability 注册异常: 重复的Capability接口: %s", annotation.value()));
|
|
||||||
}
|
|
||||||
capabilitiesValues.add(annotation.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return coresValues.equals(capabilitiesValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
record LackRecord(List<String> coreLack, List<String> capLack) {
|
|
||||||
public boolean hasNotEmptyRecord() {
|
|
||||||
return !coreLack.isEmpty() || !capLack.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toLackErrorMsg(String capabilityName) {
|
|
||||||
StringBuilder sb = new StringBuilder("\n").append(capabilityName).append("\n");
|
|
||||||
if (!coreLack.isEmpty()) {
|
|
||||||
sb.append("缺少Core方法:").append("\n").append(coreLack).append("\n");
|
|
||||||
}
|
|
||||||
if (!capLack.isEmpty()) {
|
|
||||||
sb.append("缺少Capability方法:").append("\n").append(capLack).append("\n");
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability;
|
|
||||||
|
|
||||||
import org.reflections.Reflections;
|
|
||||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.annotation.Capability;
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.annotation.ToCoordinated;
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.exception.ProxySetFailedExceptionCapability;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.CapabilityFactoryContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.ModuleInitHookExecuteFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>Agent启动流程 6</h2>
|
|
||||||
*
|
|
||||||
* <p>负责执行 {@link Capability} 的注入逻辑。</p>
|
|
||||||
*
|
|
||||||
* <p>实现方式:</p>
|
|
||||||
* <ol>
|
|
||||||
* <li>通过动态代理,为 {@link AgentModule} 与 {@link AgentSubModule} 中待注入的
|
|
||||||
* <b>能力接口</b> 类型(即 {@link Capability} 标注的接口类)生成代理对象。
|
|
||||||
* </li>
|
|
||||||
* <li>在代理对象内部,根据调用方法的签名确定路由,将调用转发至对应的具体函数。
|
|
||||||
* </li>
|
|
||||||
* <li>通过此机制,实现了 {@link Capability} 单一语义层面上普通方法与协调方法的统一入口。
|
|
||||||
* </li>
|
|
||||||
* </ol>
|
|
||||||
*
|
|
||||||
* <p>下一步流程请参阅 {@link ModuleInitHookExecuteFactory}</p>
|
|
||||||
*/public class CapabilityInjectFactory extends AgentBaseFactory {
|
|
||||||
|
|
||||||
private Reflections reflections;
|
|
||||||
private HashMap<String, Function<Object[], Object>> coordinatedMethodsRouterTable;
|
|
||||||
private HashMap<String, Function<Object[], Object>> methodsRouterTable;
|
|
||||||
private HashMap<Class<?>, Object> capabilityHolderInstances;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setVariables(AgentRegisterContext context) {
|
|
||||||
CapabilityFactoryContext factoryContext = context.getCapabilityFactoryContext();
|
|
||||||
reflections = context.getReflections();
|
|
||||||
coordinatedMethodsRouterTable = factoryContext.getCoordinatedMethodsRouterTable();
|
|
||||||
methodsRouterTable = factoryContext.getMethodsRouterTable();
|
|
||||||
capabilityHolderInstances = factoryContext.getCapabilityHolderInstances();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void run() {
|
|
||||||
//获取现有的`@InjectCapability`注解所在字段,并获取对应的类,通过动态代理注入对象
|
|
||||||
Set<Field> fields = reflections.getFieldsAnnotatedWith(InjectCapability.class);
|
|
||||||
//在动态代理内部,通过函数路由表调用对应的方法
|
|
||||||
createProxy(fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createProxy(Set<Field> fields) {
|
|
||||||
try {
|
|
||||||
for (Field field : fields) {
|
|
||||||
field.setAccessible(true);
|
|
||||||
Class<?> fieldType = field.getType();
|
|
||||||
Object instance = Proxy.newProxyInstance(
|
|
||||||
fieldType.getClassLoader(),
|
|
||||||
new Class[]{fieldType},
|
|
||||||
(proxy, method, objects) -> {
|
|
||||||
if (method.isAnnotationPresent(ToCoordinated.class)) {
|
|
||||||
String key = method.getDeclaringClass().getAnnotation(Capability.class).value() + "." + methodSignature(method);
|
|
||||||
return coordinatedMethodsRouterTable.get(key).apply(objects);
|
|
||||||
}
|
|
||||||
String key = fieldType.getAnnotation(Capability.class).value() + "." + methodSignature(method);
|
|
||||||
return methodsRouterTable.get(key).apply(objects);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
field.set(capabilityHolderInstances.get(field.getDeclaringClass()), instance);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ProxySetFailedExceptionCapability("代理设置失败", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,211 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability;
|
|
||||||
|
|
||||||
import cn.hutool.core.util.ClassUtil;
|
|
||||||
import org.reflections.Reflections;
|
|
||||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.annotation.*;
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.exception.CapabilityFactoryExecuteFailedException;
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.exception.CoreInstancesCreateFailedExceptionCapability;
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.exception.DuplicateMethodException;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.CapabilityFactoryContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
|
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>Agent启动流程 5</h2>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* 负责收集注解 {@link Capability} 和 {@link CapabilityCore} 标识的类,并生成函数路由表、创建core、capability实例,以及放入instanceMap供后续进行注入操作
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <ol>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link CapabilityRegisterFactory#setCoreInstances()}</p>
|
|
||||||
* 通过反射调用无参构造函数创建core实例,并将实例放入instanceMap供后续使用
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link CapabilityRegisterFactory#generateRouterTable()}</p>
|
|
||||||
* 生成函数路由表:
|
|
||||||
* <ul>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link CapabilityRegisterFactory#generateMethodsRouterTable()}</p>
|
|
||||||
* 生成普通方法对应的函数路由表
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link CapabilityRegisterFactory#generateCoordinatedMethodsRouterTable()}</p>
|
|
||||||
* 生成协调方法对应的函数路由表
|
|
||||||
* </li>
|
|
||||||
* </ul>
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* 函数路由表生成完毕、core实例创建完毕之后,将交由下一工厂完成能力(Capability)注入操作,注入到 {@link AgentModule} 与 {@link AgentSubModule} 对应的实例中
|
|
||||||
* </li>
|
|
||||||
* </ol>
|
|
||||||
*
|
|
||||||
* <p>下一步流程请参阅{@link CapabilityInjectFactory}</p>
|
|
||||||
*/
|
|
||||||
public class CapabilityRegisterFactory extends AgentBaseFactory {
|
|
||||||
|
|
||||||
private Reflections reflections;
|
|
||||||
private HashMap<String, Function<Object[], Object>> methodsRouterTable;
|
|
||||||
private HashMap<String, Function<Object[], Object>> coordinatedMethodsRouterTable;
|
|
||||||
private HashMap<Class<?>, Object> coreInstances;
|
|
||||||
private HashMap<Class<?>, Object> capabilityHolderInstances;
|
|
||||||
private Set<Class<?>> cores;
|
|
||||||
private Set<Class<?>> capabilities;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setVariables(AgentRegisterContext context) {
|
|
||||||
CapabilityFactoryContext factoryContext = context.getCapabilityFactoryContext();
|
|
||||||
reflections = context.getReflections();
|
|
||||||
methodsRouterTable = factoryContext.getMethodsRouterTable();
|
|
||||||
coordinatedMethodsRouterTable = factoryContext.getCoordinatedMethodsRouterTable();
|
|
||||||
coreInstances = factoryContext.getCapabilityCoreInstances();
|
|
||||||
cores = factoryContext.getCores();
|
|
||||||
capabilities = factoryContext.getCapabilities();
|
|
||||||
capabilityHolderInstances = factoryContext.getCapabilityHolderInstances();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void run() {
|
|
||||||
setCapabilityHolderInstances();
|
|
||||||
setCoreInstances();
|
|
||||||
generateRouterTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setCapabilityHolderInstances() {
|
|
||||||
Set<Class<?>> collect = reflections.getTypesAnnotatedWith(CapabilityHolder.class).stream()
|
|
||||||
.filter(ClassUtil::isNormalClass)
|
|
||||||
.filter(clazz -> !capabilityHolderInstances.containsKey(clazz))
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
for (Class<?> clazz : collect) {
|
|
||||||
try {
|
|
||||||
Constructor<?> constructor = clazz.getDeclaredConstructor();
|
|
||||||
if (constructor.canAccess(null)) {
|
|
||||||
throw new CapabilityFactoryExecuteFailedException("缺少无参构造方法的类: " + clazz);
|
|
||||||
}
|
|
||||||
Object o = constructor.newInstance();
|
|
||||||
capabilityHolderInstances.put(clazz, o);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new CapabilityFactoryExecuteFailedException("创建代理对象失败: " + clazz, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成函数路由表
|
|
||||||
*/
|
|
||||||
private void generateRouterTable() {
|
|
||||||
generateMethodsRouterTable();
|
|
||||||
generateCoordinatedMethodsRouterTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成协调函数对应的函数路由表
|
|
||||||
*/
|
|
||||||
private void generateCoordinatedMethodsRouterTable() {
|
|
||||||
Set<Method> methodsAnnotatedWith = reflections.getMethodsAnnotatedWith(Coordinated.class);
|
|
||||||
if (methodsAnnotatedWith.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
//获取所有CM实例
|
|
||||||
HashMap<String, Object> coordinateManagerInstances = getCoordinateManagerInstances();
|
|
||||||
methodsAnnotatedWith.forEach(method -> {
|
|
||||||
String key = method.getAnnotation(Coordinated.class).capability() + "." + methodSignature(method);
|
|
||||||
Function<Object[], Object> function = args -> {
|
|
||||||
try {
|
|
||||||
return method.invoke(coordinateManagerInstances.get(key), args);
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
coordinatedMethodsRouterTable.put(key, function);
|
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new CapabilityFactoryExecuteFailedException("创建协调方法路由表出错", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取<code>CoordinateManager</code>子类实例
|
|
||||||
*/
|
|
||||||
private HashMap<String, Object> getCoordinateManagerInstances() throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
|
|
||||||
HashMap<String, Object> map = new HashMap<>();
|
|
||||||
for (Class<?> c : reflections.getTypesAnnotatedWith(CoordinateManager.class)) {
|
|
||||||
Constructor<?> constructor = c.getDeclaredConstructor();
|
|
||||||
Object instance = constructor.newInstance();
|
|
||||||
setCores(instance, c);
|
|
||||||
Arrays.stream(c.getMethods())
|
|
||||||
.filter(method -> method.isAnnotationPresent(Coordinated.class))
|
|
||||||
.forEach(method -> {
|
|
||||||
String key = method.getAnnotation(Coordinated.class).capability() + "." + methodSignature(method);
|
|
||||||
map.put(key, instance);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setCores(Object cmInstance, Class<?> cmClazz) throws IllegalAccessException {
|
|
||||||
for (Field field : cmClazz.getFields()) {
|
|
||||||
if (field.getType().isAnnotationPresent(CapabilityCore.class)) {
|
|
||||||
field.setAccessible(true);
|
|
||||||
field.set(cmInstance, coreInstances.get(field.getType()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 扫描`@Capability`与`@CapabilityMethod`注解的类与方法
|
|
||||||
* 将`capabilityValue.methodSignature`作为key,函数对象为通过反射拿到的core实例对应的方法
|
|
||||||
*/
|
|
||||||
private void generateMethodsRouterTable() {
|
|
||||||
cores.forEach(core -> Arrays.stream(core.getMethods())
|
|
||||||
.filter(method -> method.isAnnotationPresent(CapabilityMethod.class))
|
|
||||||
.forEach(method -> {
|
|
||||||
Function<Object[], Object> function = args -> {
|
|
||||||
try {
|
|
||||||
return method.invoke(coreInstances.get(core), args);
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
String key = core.getAnnotation(CapabilityCore.class).value() + "." + methodSignature(method);
|
|
||||||
if (methodsRouterTable.containsKey(key)) {
|
|
||||||
throw new DuplicateMethodException("重复注册能力方法: " + core.getPackage().getName() + "." + core.getSimpleName() + "#" + method.getName());
|
|
||||||
}
|
|
||||||
methodsRouterTable.put(key, function);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 反射获取<code>CapabilityCore</code>实例
|
|
||||||
*/
|
|
||||||
private void setCoreInstances() {
|
|
||||||
try {
|
|
||||||
for (Class<?> core : cores) {
|
|
||||||
Constructor<?> constructor = core.getDeclaredConstructor();
|
|
||||||
constructor.setAccessible(true);
|
|
||||||
coreInstances.put(core, constructor.newInstance());
|
|
||||||
}
|
|
||||||
} catch (InvocationTargetException | NoSuchMethodException | InstantiationException |
|
|
||||||
IllegalAccessException e) {
|
|
||||||
throw new CoreInstancesCreateFailedExceptionCapability("core实例创建失败");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Core的协调类,该注解的实现类中如果存在任何{@link CapabilityCore}实例的引用,都将被自动注入
|
|
||||||
*/
|
|
||||||
@Target(ElementType.TYPE)
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface CoordinateManager {
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用于标注协调方法,`value`值需与对应的`@ToCoordinated`保持一致
|
|
||||||
*/
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
public @interface Coordinated {
|
|
||||||
String capability();
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 当`@Capability`所注接口中,如果存在方法需要协调多个Core服务的调用,可以通过该注解进行排除
|
|
||||||
* value值为方法对应标识,需与协调实现处的方法标识保持一致
|
|
||||||
*/
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface ToCoordinated {
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
|
||||||
|
|
||||||
import work.slhaf.partner.api.agent.runtime.exception.AgentLaunchFailedException;
|
|
||||||
|
|
||||||
public class CapabilityCheckFailedException extends AgentLaunchFailedException {
|
|
||||||
public CapabilityCheckFailedException(String message) {
|
|
||||||
super("Capability注册失败: " + message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CapabilityCheckFailedException(String message, Throwable cause) {
|
|
||||||
super("Capability注册失败: " + message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
|
||||||
|
|
||||||
import work.slhaf.partner.api.agent.runtime.exception.AgentLaunchFailedException;
|
|
||||||
|
|
||||||
public class CapabilityFactoryExecuteFailedException extends AgentLaunchFailedException {
|
|
||||||
public CapabilityFactoryExecuteFailedException(String message) {
|
|
||||||
super("CapabilityRegisterFactory 执行失败: " + message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CapabilityFactoryExecuteFailedException(String message, Throwable cause) {
|
|
||||||
super("CapabilityRegisterFactory 执行失败: " + message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
|
||||||
|
|
||||||
public class CoreInstancesCreateFailedExceptionCapability extends CapabilityFactoryExecuteFailedException {
|
|
||||||
public CoreInstancesCreateFailedExceptionCapability(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CoreInstancesCreateFailedExceptionCapability(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
|
||||||
|
|
||||||
public class DuplicateCapabilityException extends CapabilityCheckFailedException {
|
|
||||||
public DuplicateCapabilityException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DuplicateCapabilityException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
|
||||||
|
|
||||||
public class DuplicateMethodException extends CapabilityCheckFailedException{
|
|
||||||
public DuplicateMethodException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DuplicateMethodException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
|
||||||
|
|
||||||
public class EmptyCapabilityHolderException extends CapabilityCheckFailedException{
|
|
||||||
public EmptyCapabilityHolderException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EmptyCapabilityHolderException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
|
||||||
|
|
||||||
public class ProxySetFailedExceptionCapability extends CapabilityFactoryExecuteFailedException {
|
|
||||||
public ProxySetFailedExceptionCapability(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProxySetFailedExceptionCapability(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
|
||||||
|
|
||||||
public class UnMatchedCapabilityException extends CapabilityCheckFailedException{
|
|
||||||
public UnMatchedCapabilityException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnMatchedCapabilityException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
|
||||||
|
|
||||||
public class UnMatchedCapabilityMethodException extends CapabilityCheckFailedException {
|
|
||||||
public UnMatchedCapabilityMethodException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnMatchedCapabilityMethodException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
|
||||||
|
|
||||||
public class UnMatchedCoordinatedMethodException extends CapabilityCheckFailedException {
|
|
||||||
public UnMatchedCoordinatedMethodException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnMatchedCoordinatedMethodException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.config;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.config.exception.ConfigNotExistException;
|
|
||||||
import work.slhaf.partner.api.agent.factory.config.exception.PromptNotExistException;
|
|
||||||
import work.slhaf.partner.api.agent.factory.config.pojo.ModelConfig;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.ConfigFactoryContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.ModuleCheckFactory;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.config.FileAgentConfigManager;
|
|
||||||
import work.slhaf.partner.api.chat.pojo.Message;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>Agent启动流程 0</h2>
|
|
||||||
* <p>
|
|
||||||
* 通过指定的 {@link AgentConfigManager} 或者默认的 {@link FileAgentConfigManager} 加载配置文件
|
|
||||||
* <p/>
|
|
||||||
*
|
|
||||||
* <p>下一步流程请参阅{@link ModuleCheckFactory}</p>
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class ConfigLoaderFactory extends AgentBaseFactory {
|
|
||||||
|
|
||||||
private AgentConfigManager agentConfigManager;
|
|
||||||
private HashMap<String, ModelConfig> modelConfigMap;
|
|
||||||
private HashMap<String, List<Message>> modelPromptMap;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setVariables(AgentRegisterContext context) {
|
|
||||||
ConfigFactoryContext factoryContext = context.getConfigFactoryContext();
|
|
||||||
modelConfigMap = factoryContext.getModelConfigMap();
|
|
||||||
modelPromptMap = factoryContext.getModelPromptMap();
|
|
||||||
|
|
||||||
if (AgentConfigManager.INSTANCE == null) {
|
|
||||||
AgentConfigManager.setINSTANCE(new FileAgentConfigManager());
|
|
||||||
}
|
|
||||||
|
|
||||||
agentConfigManager = AgentConfigManager.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void run() {
|
|
||||||
agentConfigManager.load();
|
|
||||||
modelConfigMap.putAll(agentConfigManager.getModelConfigMap());
|
|
||||||
modelPromptMap.putAll(agentConfigManager.getModelPromptMap());
|
|
||||||
check();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对模型Config与Prompt分别进行检验,除了都必须包含default外,还需要确保数量、key一致,毕竟是模型配置与提示词
|
|
||||||
*/
|
|
||||||
private void check() {
|
|
||||||
log.info("执行config与prompt检测...");
|
|
||||||
if (!modelConfigMap.containsKey("default")) {
|
|
||||||
throw new ConfigNotExistException("缺少默认配置! 需确保存在一个模型配置的key为`default`");
|
|
||||||
}
|
|
||||||
if (!modelPromptMap.containsKey("basic")) {
|
|
||||||
throw new PromptNotExistException("缺少基础Prompt! 需要确保存在key为basic的Prompt文件,它将与其他Prompt共同作用于模块节点。");
|
|
||||||
}
|
|
||||||
Set<String> configKeySet = new HashSet<>(modelConfigMap.keySet());
|
|
||||||
configKeySet.remove("default");
|
|
||||||
Set<String> promptKeySet = new HashSet<>(modelPromptMap.keySet());
|
|
||||||
promptKeySet.remove("basic");
|
|
||||||
if (!promptKeySet.containsAll(configKeySet)) {
|
|
||||||
log.warn("存在未被提示词包含的模型配置,该配置将无法生效!");
|
|
||||||
}
|
|
||||||
//检查提示词数量与`ActivateModel`的实现数量是否一致
|
|
||||||
log.info("检测完毕.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
|
||||||
|
|
||||||
public class ConfigDirNotExistException extends ConfigFactoryInitFailedException {
|
|
||||||
public ConfigDirNotExistException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigDirNotExistException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
|
||||||
|
|
||||||
import work.slhaf.partner.api.agent.runtime.exception.AgentLaunchFailedException;
|
|
||||||
|
|
||||||
public class ConfigFactoryInitFailedException extends AgentLaunchFailedException {
|
|
||||||
public ConfigFactoryInitFailedException(String message, Throwable cause) {
|
|
||||||
super("AgentConfigManager 执行失败: " + message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigFactoryInitFailedException(String message) {
|
|
||||||
super("AgentConfigManager 执行失败: " + message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
|
||||||
|
|
||||||
import work.slhaf.partner.api.agent.runtime.exception.AgentRuntimeException;
|
|
||||||
|
|
||||||
public class ConfigFactoryRuntimeException extends AgentRuntimeException {
|
|
||||||
public ConfigFactoryRuntimeException(String message, Throwable cause) {
|
|
||||||
super("ConfigFactory 运行出错: " + message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigFactoryRuntimeException(String message) {
|
|
||||||
super("ConfigFactory 运行出错: " + message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
|
||||||
|
|
||||||
public class ConfigGenerateFailedException extends ConfigFactoryInitFailedException {
|
|
||||||
public ConfigGenerateFailedException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigGenerateFailedException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
|
||||||
|
|
||||||
public class ConfigNotExistException extends ConfigFactoryInitFailedException {
|
|
||||||
public ConfigNotExistException(String message, Throwable e) {
|
|
||||||
super(message, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigNotExistException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
|
||||||
|
|
||||||
public class ConfigUpdateFailedException extends ConfigFactoryRuntimeException{
|
|
||||||
public ConfigUpdateFailedException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigUpdateFailedException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
|
||||||
|
|
||||||
public class PromptDirNotExistException extends ConfigFactoryInitFailedException {
|
|
||||||
public PromptDirNotExistException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PromptDirNotExistException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
|
||||||
|
|
||||||
public class PromptNotExistException extends ConfigFactoryInitFailedException {
|
|
||||||
public PromptNotExistException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PromptNotExistException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.config.pojo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class ModelConfig {
|
|
||||||
private String baseUrl;
|
|
||||||
private String apikey;
|
|
||||||
private String model;
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.config.pojo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class PrimaryModelConfig {
|
|
||||||
private String key;
|
|
||||||
private ModelConfig modelConfig;
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.config.pojo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import work.slhaf.partner.api.chat.pojo.Message;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class PrimaryModelPrompt {
|
|
||||||
private String key;
|
|
||||||
private List<Message> messages;
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.context;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import org.reflections.Reflections;
|
|
||||||
import org.reflections.scanners.Scanners;
|
|
||||||
import org.reflections.util.ConfigurationBuilder;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class AgentRegisterContext {
|
|
||||||
private Reflections reflections;
|
|
||||||
private CapabilityFactoryContext capabilityFactoryContext = new CapabilityFactoryContext();
|
|
||||||
private ConfigFactoryContext configFactoryContext = new ConfigFactoryContext();
|
|
||||||
private ModuleFactoryContext moduleFactoryContext = new ModuleFactoryContext();
|
|
||||||
|
|
||||||
public AgentRegisterContext(List<URL> urls) {
|
|
||||||
reflections = new Reflections(new ConfigurationBuilder().setScanners(
|
|
||||||
Scanners.FieldsAnnotated,
|
|
||||||
Scanners.SubTypes,
|
|
||||||
Scanners.MethodsAnnotated,
|
|
||||||
Scanners.TypesAnnotated
|
|
||||||
)
|
|
||||||
.setUrls(urls)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.context;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class CapabilityFactoryContext {
|
|
||||||
private final HashMap<String, Function<Object[], Object>> methodsRouterTable = new HashMap<>();
|
|
||||||
private final HashMap<String, Function<Object[], Object>> coordinatedMethodsRouterTable = new HashMap<>();
|
|
||||||
private final HashMap<Class<?>, Object> capabilityCoreInstances = new HashMap<>();
|
|
||||||
private final HashMap<Class<?>, Object> capabilityHolderInstances = new HashMap<>();
|
|
||||||
private Set<Class<?>> cores = new HashSet<>();
|
|
||||||
private Set<Class<?>> capabilities = new HashSet<>();
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.context;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import work.slhaf.partner.api.agent.factory.config.pojo.ModelConfig;
|
|
||||||
import work.slhaf.partner.api.chat.pojo.Message;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class ConfigFactoryContext {
|
|
||||||
private HashMap<String, List<Message>> modelPromptMap = new HashMap<>();
|
|
||||||
private HashMap<String, ModelConfig> modelConfigMap = new HashMap<>();
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.context;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaSubModule;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class ModuleFactoryContext {
|
|
||||||
private List<MetaModule> agentModuleList = new ArrayList<>();
|
|
||||||
private List<MetaSubModule> agentSubModuleList = new ArrayList<>();
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.exception;
|
|
||||||
|
|
||||||
public class AgentRegisterFactoryFailedException extends RuntimeException {
|
|
||||||
public AgentRegisterFactoryFailedException(String message, Throwable cause) {
|
|
||||||
super("AgentRegisterFactory 执行失败: " + message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AgentRegisterFactoryFailedException(String message) {
|
|
||||||
super("AgentRegisterFactory 执行失败: " + message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.exception;
|
|
||||||
|
|
||||||
public class ExternalModuleLoadFailedException extends AgentRegisterFactoryFailedException{
|
|
||||||
public ExternalModuleLoadFailedException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExternalModuleLoadFailedException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.exception;
|
|
||||||
|
|
||||||
public class ExternalModulePathNotExistException extends AgentRegisterFactoryFailedException {
|
|
||||||
public ExternalModulePathNotExistException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExternalModulePathNotExistException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module;
|
|
||||||
|
|
||||||
import cn.hutool.core.util.ClassUtil;
|
|
||||||
import org.reflections.Reflections;
|
|
||||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.exception.ModuleCheckException;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static work.slhaf.partner.api.agent.util.AgentUtil.getMethodAnnotationTypeSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>Agent启动流程 1</h2>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* 检查模块部分抽象类与注解、接口的使用方式
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <ol>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link ModuleCheckFactory#annotationAbstractCheck(Set, Class)}</p>
|
|
||||||
* 所有添加了 {@link AgentModule} 注解的类都将作为Agent的执行模块,为规范模块入口,都必须实现抽象类: {@link AgentRunningModule}; {@link AgentSubModule} 注解所在类则必须实现 {@link AgentRunningSubModule}
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link ModuleCheckFactory#moduleConstructorsCheck(Set)}</p>
|
|
||||||
* 所有 {@link AgentModule} 与 {@link AgentSubModule} 注解所在类都必须具备空参构造方法,初始化逻辑可放在 @Init 注解所处方法中,将在 Capability 与 subModules 注入后才会执行
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link ModuleCheckFactory#activateModelImplCheck()}</p>
|
|
||||||
* 检查实现了 {@link ActivateModel} 的模块数量、名称与prompt是否一致
|
|
||||||
* </li>
|
|
||||||
* </ol>
|
|
||||||
*
|
|
||||||
* <p>下一步流程请参阅{@link ModuleRegisterFactory}</p>
|
|
||||||
*/
|
|
||||||
public class ModuleCheckFactory extends AgentBaseFactory {
|
|
||||||
|
|
||||||
private Reflections reflections;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setVariables(AgentRegisterContext context) {
|
|
||||||
reflections = context.getReflections();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void run() {
|
|
||||||
AnnotatedModules annotatedModules = getAnnotatedModules();
|
|
||||||
ExtendedModules extendedModules = getExtendedModules();
|
|
||||||
checkIfClassCorresponds(annotatedModules, extendedModules);
|
|
||||||
//检查注解AgentModule或AgentSubModule所在类是否继承了对应的抽象类
|
|
||||||
annotationAbstractCheck(annotatedModules.moduleTypes(), AgentRunningModule.class);
|
|
||||||
annotationAbstractCheck(annotatedModules.subModuleTypes(), AgentRunningSubModule.class);
|
|
||||||
//检查AgentModule是否具备无参构造方法
|
|
||||||
moduleConstructorsCheck(annotatedModules.moduleTypes());
|
|
||||||
moduleConstructorsCheck(annotatedModules.subModuleTypes());
|
|
||||||
//检查实现了ActivateModel的模块数量、名称与prompt是否一致
|
|
||||||
activateModelImplCheck();
|
|
||||||
//检查hook注解所在位置是否正确
|
|
||||||
hookLocationCheck();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExtendedModules getExtendedModules() {
|
|
||||||
Set<Class<?>> moduleTypes = reflections.getSubTypesOf(AgentRunningModule.class)
|
|
||||||
.stream()
|
|
||||||
.filter(ClassUtil::isNormalClass)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
Set<Class<?>> subModuleTypes = reflections.getSubTypesOf(AgentRunningSubModule.class)
|
|
||||||
.stream()
|
|
||||||
.filter(ClassUtil::isNormalClass)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
return new ExtendedModules(moduleTypes, subModuleTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AnnotatedModules getAnnotatedModules() {
|
|
||||||
Set<Class<?>> moduleTypes = reflections.getTypesAnnotatedWith(AgentModule.class)
|
|
||||||
.stream()
|
|
||||||
.filter(ClassUtil::isNormalClass)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
Set<Class<?>> subModuleTypes = reflections.getTypesAnnotatedWith(AgentSubModule.class)
|
|
||||||
.stream()
|
|
||||||
.filter(ClassUtil::isNormalClass)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
return new AnnotatedModules(moduleTypes, subModuleTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void moduleConstructorsCheck(Set<Class<?>> types) {
|
|
||||||
for (Class<?> type : types) {
|
|
||||||
try {
|
|
||||||
type.getConstructor();
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
throw new ModuleCheckException("缺少无参构造方法的模块: " + type.getSimpleName(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void activateModelImplCheck() {
|
|
||||||
try {
|
|
||||||
Set<Class<? extends ActivateModel>> types = reflections.getSubTypesOf(ActivateModel.class);
|
|
||||||
Set<String> modelKeySet = new HashSet<>();
|
|
||||||
for (Class<? extends ActivateModel> type : types) {
|
|
||||||
ActivateModel instance = type.getConstructor().newInstance();
|
|
||||||
modelKeySet.add(instance.modelKey());
|
|
||||||
}
|
|
||||||
Set<String> promptKeySet = AgentConfigManager.INSTANCE.getModelPromptMap().keySet();
|
|
||||||
if (!promptKeySet.containsAll(modelKeySet)) {
|
|
||||||
modelKeySet.removeAll(promptKeySet);
|
|
||||||
throw new ModuleCheckException("存在未配置Prompt的ActivateModel实现! 缺少Prompt的ModelKey列表: " + modelKeySet);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ModuleCheckException("ActivateModel 检测出错", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void hookLocationCheck() {
|
|
||||||
//检查@AfterExecute注解
|
|
||||||
postHookLocationCheck();
|
|
||||||
//检查@BeforeExecute注解
|
|
||||||
preHookLocationCheck();
|
|
||||||
//检查@Init注解
|
|
||||||
initHookLocationCheck();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initHookLocationCheck() {
|
|
||||||
Set<Class<?>> types = getMethodAnnotationTypeSet(AgentModule.class, reflections);
|
|
||||||
checkLocation(types);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void preHookLocationCheck() {
|
|
||||||
Set<Method> methods = reflections.getMethodsAnnotatedWith(BeforeExecute.class);
|
|
||||||
Set<Class<?>> types = methods.stream()
|
|
||||||
.map(Method::getDeclaringClass)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
checkLocation(types);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void postHookLocationCheck() {
|
|
||||||
Set<Method> methods = reflections.getMethodsAnnotatedWith(AfterExecute.class);
|
|
||||||
Set<Class<?>> types = methods.stream()
|
|
||||||
.map(Method::getDeclaringClass)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
checkLocation(types);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkLocation(Set<Class<?>> types) {
|
|
||||||
for (Class<?> type : types) {
|
|
||||||
if (AgentRunningModule.class.isAssignableFrom(type)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (AgentRunningSubModule.class.isAssignableFrom(type)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ActivateModel.class.isAssignableFrom(type)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
throw new ModuleCheckException("在不支持的类中使用了hook注解: " + type.getSimpleName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void annotationAbstractCheck(Set<Class<?>> types, Class<?> clazz) {
|
|
||||||
for (Class<?> type : types) {
|
|
||||||
if (type.isAnnotation()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (clazz.isAssignableFrom(type) && ClassUtil.isNormalClass(type)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
throw new ModuleCheckException("存在未继承AgentInteractionModule.class的AgentModule实现: " + type.getSimpleName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkIfClassCorresponds(AnnotatedModules annotatedModules, ExtendedModules extendedModules) {
|
|
||||||
// 检查是否有被@AgentModule注解但没有继承AgentRunningModule的类
|
|
||||||
checkSets(annotatedModules.moduleTypes(), extendedModules.moduleTypes(),
|
|
||||||
"存在被@AgentModule注解但未继承AgentRunningModule的类");
|
|
||||||
|
|
||||||
// 检查是否有继承AgentRunningModule但没有被@AgentModule注解的类
|
|
||||||
checkSets(extendedModules.moduleTypes(), annotatedModules.moduleTypes(),
|
|
||||||
"存在继承AgentRunningModule但未被@AgentModule注解的类");
|
|
||||||
|
|
||||||
// 检查是否有被@AgentSubModule注解但没有继承AgentRunningSubModule的类
|
|
||||||
checkSets(annotatedModules.subModuleTypes(), extendedModules.subModuleTypes(),
|
|
||||||
"存在被@AgentSubModule注解但未继承AgentRunningSubModule的类");
|
|
||||||
|
|
||||||
// 检查是否有继承AgentRunningSubModule但没有被@AgentSubModule注解的类
|
|
||||||
checkSets(extendedModules.subModuleTypes(), annotatedModules.subModuleTypes(),
|
|
||||||
"存在继承AgentRunningSubModule但未被@AgentSubModule注解的类");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查源集合中是否有不在目标集合中的元素
|
|
||||||
* @param source 源集合
|
|
||||||
* @param target 目标集合
|
|
||||||
* @param errorMessage 错误信息前缀
|
|
||||||
*/
|
|
||||||
private void checkSets(Set<Class<?>> source, Set<Class<?>> target, String errorMessage) {
|
|
||||||
// 只有在需要时才创建HashSet以节省内存
|
|
||||||
if (!target.containsAll(source)) {
|
|
||||||
// 使用流式处理找出差异部分,避免创建完整的中间集合
|
|
||||||
String classNames = source.stream()
|
|
||||||
.filter(clazz -> !target.contains(clazz))
|
|
||||||
.map(Class::getSimpleName)
|
|
||||||
.limit(10) // 限制显示数量,避免信息泄露
|
|
||||||
.collect(Collectors.joining(", ", "[", "]"));
|
|
||||||
|
|
||||||
throw new ModuleCheckException(errorMessage + ": " + classNames);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private record AnnotatedModules(Set<Class<?>> moduleTypes, Set<Class<?>> subModuleTypes) {
|
|
||||||
}
|
|
||||||
|
|
||||||
private record ExtendedModules(Set<Class<?>> moduleTypes, Set<Class<?>> subModuleTypes) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module;
|
|
||||||
|
|
||||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.AgentRegisterFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.ModuleFactoryContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.Init;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.exception.ModuleInitHookExecuteFailedException;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.BaseMetaModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaMethod;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaSubModule;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.Module;
|
|
||||||
import work.slhaf.partner.api.agent.util.AgentUtil;
|
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static work.slhaf.partner.api.agent.util.AgentUtil.collectExtendedClasses;
|
|
||||||
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>Agent启动流程 7</h2>
|
|
||||||
*
|
|
||||||
* <p>负责执行初始化hook逻辑,即 {@link Init} 注解所在方法</p>
|
|
||||||
*
|
|
||||||
* <ol>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link ModuleInitHookExecuteFactory#collectInitHookMethods(Class, Class)}</p>
|
|
||||||
* 分别遍历前置模块拿到的模块列表({@link ModuleInitHookExecuteFactory#moduleList}, {@link ModuleInitHookExecuteFactory#subModuleList}),通过 {@link AgentUtil#collectExtendedClasses(Class, Class)} 收集到当前模块类的继承链上的所有类后,收集其所有带有 {@link Init} 注解的方法
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link ModuleInitHookExecuteFactory#proceedInitMethods(BaseMetaModule, List)}</p>
|
|
||||||
* 收集好初始化方法后,将通过反射执行该方法,所用实例即为前置模块中收集到的执行模块与子模块的 {@link MetaModule} 与 {@link MetaSubModule} 内容
|
|
||||||
* </li>
|
|
||||||
* </ol>
|
|
||||||
*
|
|
||||||
* <p>Agent启动流程到此进行完毕。整个工厂执行链中均为针对 {@link AgentRegisterContext} 进行的操作,在 {@link AgentRegisterFactory} 中,将进行最终处理以及将必要内容进行传递。</p>
|
|
||||||
*/
|
|
||||||
public class ModuleInitHookExecuteFactory extends AgentBaseFactory {
|
|
||||||
|
|
||||||
private List<MetaModule> moduleList;
|
|
||||||
private List<MetaSubModule> subModuleList;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setVariables(AgentRegisterContext context) {
|
|
||||||
ModuleFactoryContext factoryContext = context.getModuleFactoryContext();
|
|
||||||
moduleList = factoryContext.getAgentModuleList();
|
|
||||||
subModuleList = factoryContext.getAgentSubModuleList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void run() {
|
|
||||||
//遍历模块列表,并向上查找@Init注解
|
|
||||||
for (MetaSubModule metaSubModule : subModuleList) {
|
|
||||||
List<MetaMethod> initHookMethods = collectInitHookMethods(metaSubModule.getClazz(),AgentRunningModule.class);
|
|
||||||
proceedInitMethods(metaSubModule, initHookMethods);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (MetaModule metaModule : moduleList) {
|
|
||||||
List<MetaMethod> initHookMethods = collectInitHookMethods(metaModule.getClazz(), AgentRunningSubModule.class);
|
|
||||||
proceedInitMethods(metaModule, initHookMethods);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void proceedInitMethods(BaseMetaModule metaModule, List<MetaMethod> initHookMethods) {
|
|
||||||
for (MetaMethod metaMethod : initHookMethods) {
|
|
||||||
try {
|
|
||||||
metaMethod.getMethod().invoke(metaModule.getInstance());
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
|
||||||
throw new ModuleInitHookExecuteFailedException("模块的init hook方法执行失败! 模块: " + metaModule.getClazz().getSimpleName() + " 方法签名: " + methodSignature(metaMethod.getMethod()), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<MetaMethod> collectInitHookMethods(Class<?> clazz, Class<? extends Module> target) {
|
|
||||||
Set<Class<?>> classes = collectExtendedClasses(clazz, target);
|
|
||||||
return classes.stream()
|
|
||||||
.map(Class::getDeclaredMethods)
|
|
||||||
.flatMap(Arrays::stream)
|
|
||||||
.filter(method -> method.isAnnotationPresent(Init.class))
|
|
||||||
.map(method -> {
|
|
||||||
MetaMethod metaMethod = new MetaMethod();
|
|
||||||
metaMethod.setMethod(method);
|
|
||||||
metaMethod.setOrder(method.getAnnotation(Init.class).order());
|
|
||||||
return metaMethod;
|
|
||||||
})
|
|
||||||
.sorted(Comparator.comparing(MetaMethod::getOrder))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,259 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import net.bytebuddy.ByteBuddy;
|
|
||||||
import net.bytebuddy.implementation.MethodDelegation;
|
|
||||||
import net.bytebuddy.implementation.bind.annotation.*;
|
|
||||||
import net.bytebuddy.matcher.ElementMatchers;
|
|
||||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.CapabilityCheckFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.CapabilityFactoryContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.ModuleFactoryContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.InjectModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.exception.ModuleInstanceGenerateFailedException;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.exception.ModuleProxyGenerateFailedException;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.exception.ProxiedModuleRunningException;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.BaseMetaModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaMethod;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaSubModule;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.Module;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static work.slhaf.partner.api.agent.util.AgentUtil.collectExtendedClasses;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>Agent启动流程 3</h2>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* 扫描前置模块各个hook注解生成代理对象,放入对应的list中并按照类型为键放入 {@link ModuleProxyFactory#capabilityHolderInstances} 中供后续完成能力(capability)注入
|
|
||||||
* <p/>
|
|
||||||
*
|
|
||||||
* <ol>
|
|
||||||
*
|
|
||||||
* <li>
|
|
||||||
* <p>{@link ModuleProxyFactory#createProxiedInstances()}</p>
|
|
||||||
* 根据moduleList中的类型信息,向上查找继承链获取所有hook方法收集为{@link MethodsListRecord},然后通过ByteBuddy根据收集到的preHook与postHook生成代理对象,放入对应的 {@link MetaModule} 对象以及 instanceMap 中
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link ModuleProxyFactory#injectSubModule()}</p>
|
|
||||||
* 通过反射将子模块实例注入到执行模块中带有注解 {@link InjectModule} 的字段
|
|
||||||
* </li>
|
|
||||||
* </ol>
|
|
||||||
*
|
|
||||||
* <p>下一步流程请参阅{@link CapabilityCheckFactory}</p>
|
|
||||||
*/
|
|
||||||
public class ModuleProxyFactory extends AgentBaseFactory {
|
|
||||||
|
|
||||||
private List<MetaModule> moduleList;
|
|
||||||
private List<MetaSubModule> subModuleList;
|
|
||||||
private HashMap<Class<?>, Object> capabilityHolderInstances;
|
|
||||||
private final HashMap<Class<?>, Object> subModuleInstances = new HashMap<>();
|
|
||||||
private final HashMap<Class<?>, Object> moduleInstances = new HashMap<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setVariables(AgentRegisterContext context) {
|
|
||||||
ModuleFactoryContext factoryContext = context.getModuleFactoryContext();
|
|
||||||
CapabilityFactoryContext capabilityFactoryContext = context.getCapabilityFactoryContext();
|
|
||||||
moduleList = factoryContext.getAgentModuleList();
|
|
||||||
subModuleList = factoryContext.getAgentSubModuleList();
|
|
||||||
capabilityHolderInstances = capabilityFactoryContext.getCapabilityHolderInstances();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void run() {
|
|
||||||
createProxiedInstances();
|
|
||||||
injectSubModule();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void injectSubModule() {
|
|
||||||
for (MetaModule module : moduleList) {
|
|
||||||
//因为实际上ByteBuddy生成的是module.getClazz()的子类,所以应当使用getDeclaredFields()获取字段
|
|
||||||
Arrays.stream(module.getClazz().getDeclaredFields())
|
|
||||||
.filter(field -> field.isAnnotationPresent(InjectModule.class))
|
|
||||||
.forEach(field -> {
|
|
||||||
try {
|
|
||||||
field.setAccessible(true);
|
|
||||||
field.set(
|
|
||||||
moduleInstances.get(module.getClazz()),
|
|
||||||
subModuleInstances.get(field.getType())
|
|
||||||
);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new ModuleInstanceGenerateFailedException("模块实例注入失败", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createProxiedInstances() {
|
|
||||||
generateModuleProxy(moduleList, AgentRunningModule.class);
|
|
||||||
generateModuleProxy(subModuleList, AgentRunningSubModule.class);
|
|
||||||
updateInstanceMap(moduleInstances, moduleList);
|
|
||||||
updateInstanceMap(subModuleInstances, subModuleList);
|
|
||||||
updateCapabilityHolderInstances();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateCapabilityHolderInstances() {
|
|
||||||
capabilityHolderInstances.putAll(moduleInstances);
|
|
||||||
capabilityHolderInstances.putAll(subModuleInstances);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateInstanceMap(HashMap<Class<?>, Object> instanceMap, List<? extends BaseMetaModule> list) {
|
|
||||||
for (BaseMetaModule baseMetaModule : list) {
|
|
||||||
instanceMap.put(baseMetaModule.getClazz(), baseMetaModule.getInstance());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void generateModuleProxy(List<? extends BaseMetaModule> list, Class<? extends Module> overrideSource) {
|
|
||||||
for (BaseMetaModule module : list) {
|
|
||||||
Class<?> clazz = module.getClazz();
|
|
||||||
try {
|
|
||||||
MethodsListRecord record = collectHookMethods(clazz);
|
|
||||||
//生成实例
|
|
||||||
generateProxiedInstances(record, module, overrideSource);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ModuleProxyGenerateFailedException("创建代理对象失败: " + clazz.getSimpleName(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateProxiedInstances(MethodsListRecord record, BaseMetaModule module, Class<? extends Module> overrideSource) {
|
|
||||||
try {
|
|
||||||
Class<? extends Module> clazz = module.getClazz();
|
|
||||||
Class<? extends Module> proxyClass = new ByteBuddy()
|
|
||||||
.subclass(clazz)
|
|
||||||
.method(ElementMatchers.isOverriddenFrom(overrideSource))
|
|
||||||
.intercept(MethodDelegation.to(new ModuleProxyInterceptor(record.post, record.pre)))
|
|
||||||
.make()
|
|
||||||
.load(ModuleProxyFactory.class.getClassLoader())
|
|
||||||
.getLoaded();
|
|
||||||
|
|
||||||
// new ByteBuddy()
|
|
||||||
// .subclass(clazz)
|
|
||||||
// .method(ElementMatchers.isOverriddenFrom(overrideSource))
|
|
||||||
// .intercept(MethodDelegation.to(new ModuleProxyInterceptor(record.post, record.pre)))
|
|
||||||
//
|
|
||||||
// .make()
|
|
||||||
// .saveIn(new File("./generated-classes"));
|
|
||||||
|
|
||||||
module.setInstance(proxyClass.getConstructor().newInstance());
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ModuleProxyGenerateFailedException("模块Hook代理生成失败! 代理失败的模块名: " + module.getClazz().getSimpleName(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private MethodsListRecord collectHookMethods(Class<?> clazz) {
|
|
||||||
List<MetaMethod> post = new ArrayList<>();
|
|
||||||
List<MetaMethod> pre = new ArrayList<>();
|
|
||||||
//获取该类本身的hook逻辑
|
|
||||||
collectHookMethods(post, pre, clazz);
|
|
||||||
//获取它所继承、实现的抽象类或接口, 以Module为终点,收集继承链上所有父类和接口
|
|
||||||
Set<Class<?>> classes = collectExtendedClasses(clazz, Module.class);
|
|
||||||
//获取这些类中的hook逻辑
|
|
||||||
collectHookMethods(post, pre, classes);
|
|
||||||
return new MethodsListRecord(post, pre);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void collectHookMethods(List<MetaMethod> post, List<MetaMethod> pre, Set<Class<?>> classes) {
|
|
||||||
for (Class<?> type : classes) {
|
|
||||||
collectPreHookMethods(pre, type);
|
|
||||||
collectPostHookMethods(post, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void collectPostHookMethods(List<MetaMethod> post, Class<?> type) {
|
|
||||||
Set<MetaMethod> collectedPostHookMethod = Arrays.stream(type.getDeclaredMethods())
|
|
||||||
.filter(method -> method.isAnnotationPresent(AfterExecute.class))
|
|
||||||
.map(method -> {
|
|
||||||
MetaMethod metaMethod = new MetaMethod();
|
|
||||||
metaMethod.setMethod(method);
|
|
||||||
metaMethod.setOrder(method.getAnnotation(AfterExecute.class).order());
|
|
||||||
return metaMethod;
|
|
||||||
})
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
post.addAll(collectedPostHookMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void collectPreHookMethods(List<MetaMethod> pre, Class<?> type) {
|
|
||||||
Set<MetaMethod> collectedPreHookMethods = Arrays.stream(type.getDeclaredMethods())
|
|
||||||
.filter(method -> method.isAnnotationPresent(BeforeExecute.class))
|
|
||||||
.map(method -> {
|
|
||||||
MetaMethod metaMethod = new MetaMethod();
|
|
||||||
metaMethod.setMethod(method);
|
|
||||||
metaMethod.setOrder(method.getAnnotation(BeforeExecute.class).order());
|
|
||||||
return metaMethod;
|
|
||||||
})
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
pre.addAll(collectedPreHookMethods);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void collectHookMethods(List<MetaMethod> post, List<MetaMethod> pre, Class<?> clazz) {
|
|
||||||
Method[] methods = clazz.getDeclaredMethods();
|
|
||||||
for (Method method : methods) {
|
|
||||||
if (method.isAnnotationPresent(BeforeExecute.class)) {
|
|
||||||
MetaMethod metaMethod = new MetaMethod();
|
|
||||||
metaMethod.setOrder(method.getAnnotation(BeforeExecute.class).order());
|
|
||||||
pre.add(metaMethod);
|
|
||||||
metaMethod.setMethod(method);
|
|
||||||
} else if (method.isAnnotationPresent(AfterExecute.class)) {
|
|
||||||
MetaMethod metaMethod = new MetaMethod();
|
|
||||||
metaMethod.setOrder(method.getAnnotation(AfterExecute.class).order());
|
|
||||||
post.add(metaMethod);
|
|
||||||
metaMethod.setMethod(method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@SuppressWarnings("ClassCanBeRecord")
|
|
||||||
public static class ModuleProxyInterceptor {
|
|
||||||
|
|
||||||
private final List<MetaMethod> postHookMethods;
|
|
||||||
private final List<MetaMethod> preHookMethods;
|
|
||||||
|
|
||||||
public ModuleProxyInterceptor(List<MetaMethod> postHookMethods, List<MetaMethod> preHookMethods) {
|
|
||||||
this.postHookMethods = postHookMethods;
|
|
||||||
this.preHookMethods = preHookMethods;
|
|
||||||
}
|
|
||||||
|
|
||||||
@RuntimeType
|
|
||||||
public Object intercept(@Origin Method method, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper, @This Object proxy) throws Exception {
|
|
||||||
executeHookMethods(preHookMethods, proxy);
|
|
||||||
Object res = zuper.call();
|
|
||||||
executeHookMethods(postHookMethods, proxy);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void executeHookMethods(List<MetaMethod> hookMethods, Object proxy) {
|
|
||||||
for (MetaMethod metaMethod : hookMethods) {
|
|
||||||
Method m = metaMethod.getMethod();
|
|
||||||
try {
|
|
||||||
m.setAccessible(true);
|
|
||||||
m.invoke(proxy);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ProxiedModuleRunningException("hook方法执行异常: " + m.getDeclaringClass() + "#" + m.getName(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
record MethodsListRecord(List<MetaMethod> post, List<MetaMethod> pre) {
|
|
||||||
public MethodsListRecord {
|
|
||||||
post.sort(Comparator.comparingInt(MetaMethod::getOrder));
|
|
||||||
pre.sort(Comparator.comparingInt(MetaMethod::getOrder));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module;
|
|
||||||
|
|
||||||
import cn.hutool.core.util.ClassUtil;
|
|
||||||
import org.reflections.Reflections;
|
|
||||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.context.ModuleFactoryContext;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.CoreModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaSubModule;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
|
|
||||||
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>Agent启动流程 2</h2>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* 负责收集 {@link AgentModule} 与 {@link AgentSubModule} 注解所在类的信息,供后续工厂完成动态代理、模块与能力注入
|
|
||||||
* <p/>
|
|
||||||
*
|
|
||||||
* <ol>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link ModuleRegisterFactory#setModuleList()}</p>
|
|
||||||
* 扫描 {@link AgentModule} 注解,获取执行模块信息: 类型、模块名称({@link AgentModule#name()}),执行顺序。并按照注解的 {@link AgentModule#order()} 字段进行排序
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* <p>{@link ModuleRegisterFactory#setSubModuleList()}</p>
|
|
||||||
* 扫描 {@link AgentSubModule} 注册,获取子模块类型信息
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* 两种模块都将存入各自的list中,供后续模块完成注册与注入
|
|
||||||
* </li>
|
|
||||||
* </ol>
|
|
||||||
*
|
|
||||||
* <p>下一步流程请参阅{@link ModuleProxyFactory}</p>
|
|
||||||
*/
|
|
||||||
public class ModuleRegisterFactory extends AgentBaseFactory {
|
|
||||||
|
|
||||||
private Reflections reflections;
|
|
||||||
private List<MetaModule> moduleList;
|
|
||||||
private List<MetaSubModule> subModuleList;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setVariables(AgentRegisterContext context) {
|
|
||||||
ModuleFactoryContext factoryContext = context.getModuleFactoryContext();
|
|
||||||
reflections = context.getReflections();
|
|
||||||
moduleList = factoryContext.getAgentModuleList();
|
|
||||||
subModuleList = factoryContext.getAgentSubModuleList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void run() {
|
|
||||||
setModuleList();
|
|
||||||
setSubModuleList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSubModuleList() {
|
|
||||||
Set<Class<?>> subModules = reflections.getTypesAnnotatedWith(AgentSubModule.class);
|
|
||||||
for (Class<?> subModule : subModules) {
|
|
||||||
if (!ClassUtil.isNormalClass(subModule)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Class<? extends AgentRunningSubModule> clazz = subModule.asSubclass(AgentRunningSubModule.class);
|
|
||||||
MetaSubModule metaSubModule = new MetaSubModule();
|
|
||||||
metaSubModule.setClazz(clazz);
|
|
||||||
subModuleList.add(metaSubModule);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setModuleList() {
|
|
||||||
//反射扫描获取@AgentModule所在类, 该部分为Agent流程执行模块
|
|
||||||
Set<Class<?>> modules = reflections.getTypesAnnotatedWith(AgentModule.class);
|
|
||||||
for (Class<?> module : modules) {
|
|
||||||
if (!ClassUtil.isNormalClass(module)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Class<? extends AgentRunningModule> clazz = module.asSubclass(AgentRunningModule.class);
|
|
||||||
MetaModule metaModule = getMetaModule(clazz);
|
|
||||||
moduleList.add(metaModule);
|
|
||||||
}
|
|
||||||
moduleList.sort(Comparator.comparing(MetaModule::getOrder));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MetaModule getMetaModule(Class<? extends AgentRunningModule> clazz) {
|
|
||||||
MetaModule metaModule = new MetaModule();
|
|
||||||
AgentModule agentModule;
|
|
||||||
if (clazz.isAnnotationPresent(CoreModule.class)){
|
|
||||||
agentModule = CoreModule.class.getAnnotation(AgentModule.class);
|
|
||||||
}else{
|
|
||||||
agentModule = clazz.getAnnotation(AgentModule.class);
|
|
||||||
}
|
|
||||||
metaModule.setName(agentModule.name());
|
|
||||||
metaModule.setOrder(agentModule.order());
|
|
||||||
metaModule.setClazz(clazz);
|
|
||||||
return metaModule;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module.annotation;
|
|
||||||
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 仅适用于以下类中的方法:
|
|
||||||
* 1. <code>@AgentModule</code>注解所在类
|
|
||||||
* 2. <code>ActivateModel</code>子类
|
|
||||||
* 3. <code>AgentRunningModule</code>或者<code>AgentRunningSubModule</code>子类
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface AfterExecute {
|
|
||||||
int order() default 0;
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module.annotation;
|
|
||||||
|
|
||||||
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityHolder;
|
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用于注解执行模块
|
|
||||||
*/
|
|
||||||
@Target(ElementType.TYPE)
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@CapabilityHolder
|
|
||||||
@Inherited
|
|
||||||
public @interface AgentModule {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模块名称
|
|
||||||
*/
|
|
||||||
String name();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模块执行顺序,数字越小执行越靠前
|
|
||||||
*/
|
|
||||||
int order();
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module.annotation;
|
|
||||||
|
|
||||||
import work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityHolder;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
@Target(ElementType.TYPE)
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@CapabilityHolder
|
|
||||||
public @interface AgentSubModule {
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 仅适用于以下类中的方法:
|
|
||||||
* 1. <code>@AgentModule</code>注解所在类
|
|
||||||
* 2. <code>ActivateModel</code>子类
|
|
||||||
* 3. <code>AgentRunningModule</code>或者<code>AgentRunningSubModule</code>子类
|
|
||||||
*/
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface BeforeExecute {
|
|
||||||
int order() default 0;
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@AgentModule(name = "core",order = 5)
|
|
||||||
public @interface CoreModule {
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module.exception;
|
|
||||||
|
|
||||||
public class ModuleCheckException extends ModuleFactoryInitFailedException {
|
|
||||||
public ModuleCheckException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ModuleCheckException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module.exception;
|
|
||||||
|
|
||||||
public class ModuleFactoryInitFailedException extends RuntimeException {
|
|
||||||
public ModuleFactoryInitFailedException(String message) {
|
|
||||||
super("ModuleFactory 执行失败: "+message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ModuleFactoryInitFailedException(String message, Throwable cause) {
|
|
||||||
super("ModuleFactory 执行失败: "+message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module.exception;
|
|
||||||
|
|
||||||
public class ModuleInitHookExecuteFailedException extends ModuleFactoryInitFailedException {
|
|
||||||
public ModuleInitHookExecuteFailedException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ModuleInitHookExecuteFailedException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module.exception;
|
|
||||||
|
|
||||||
public class ModuleInstanceGenerateFailedException extends ModuleFactoryInitFailedException {
|
|
||||||
public ModuleInstanceGenerateFailedException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ModuleInstanceGenerateFailedException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module.exception;
|
|
||||||
|
|
||||||
public class ModuleProxyGenerateFailedException extends ModuleFactoryInitFailedException {
|
|
||||||
public ModuleProxyGenerateFailedException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ModuleProxyGenerateFailedException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module.exception;
|
|
||||||
|
|
||||||
import work.slhaf.partner.api.agent.runtime.exception.AgentRuntimeException;
|
|
||||||
|
|
||||||
public class ProxiedModuleRunningException extends AgentRuntimeException {
|
|
||||||
public ProxiedModuleRunningException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProxiedModuleRunningException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module.pojo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.Module;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public abstract class BaseMetaModule <C extends Module> {
|
|
||||||
private Class<? extends C> clazz;
|
|
||||||
private C instance;
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module.pojo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class MetaMethod {
|
|
||||||
private int order;
|
|
||||||
private Method method;
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module.pojo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
|
||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Data
|
|
||||||
public class MetaModule extends BaseMetaModule<AgentRunningModule>{
|
|
||||||
private String name;
|
|
||||||
private int order;
|
|
||||||
private boolean enabled = true;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.factory.module.pojo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
|
|
||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Data
|
|
||||||
public class MetaSubModule extends BaseMetaModule<AgentRunningSubModule>{
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.config;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.Setter;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import work.slhaf.partner.api.agent.factory.config.exception.ConfigUpdateFailedException;
|
|
||||||
import work.slhaf.partner.api.agent.factory.config.exception.PromptNotExistException;
|
|
||||||
import work.slhaf.partner.api.agent.factory.config.pojo.ModelConfig;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
|
||||||
import work.slhaf.partner.api.chat.pojo.Message;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Data
|
|
||||||
public abstract class AgentConfigManager {
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
public static AgentConfigManager INSTANCE = new FileAgentConfigManager();
|
|
||||||
private static final String DEFAULT_KEY = "default";
|
|
||||||
|
|
||||||
protected HashMap<String, ModelConfig> modelConfigMap;
|
|
||||||
protected HashMap<String, List<Message>> modelPromptMap;
|
|
||||||
protected HashMap<String, Boolean> moduleEnabledStatus;
|
|
||||||
protected Map<Integer, List<MetaModule>> moduleOrderedMap = new LinkedHashMap<>();
|
|
||||||
protected Map<String, MetaModule> moduleMap = new HashMap<>();
|
|
||||||
|
|
||||||
public void load() {
|
|
||||||
modelConfigMap = loadModelConfig();
|
|
||||||
modelPromptMap = loadModelPrompt();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract HashMap<String, List<Message>> loadModelPrompt();
|
|
||||||
|
|
||||||
protected abstract HashMap<String, ModelConfig> loadModelConfig();
|
|
||||||
|
|
||||||
public abstract void dumpModelConfig(String key);
|
|
||||||
|
|
||||||
protected abstract void dumpModuleEnabledStatus();
|
|
||||||
|
|
||||||
protected abstract HashMap<String, Boolean> loadModuleEnabledStatusMap(List<MetaModule> moduleList);
|
|
||||||
|
|
||||||
public void moduleEnabledStatusFilterAndRecord(List<MetaModule> moduleList) {
|
|
||||||
updateModuleMap(moduleList);
|
|
||||||
updateModuleEnabledStatus(moduleList);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateModuleMap(List<MetaModule> moduleList) {
|
|
||||||
//在ModuleRegisterFactory已进行过排序操作
|
|
||||||
for (MetaModule module : moduleList) {
|
|
||||||
int k = module.getOrder();
|
|
||||||
moduleOrderedMap.computeIfAbsent(k, order -> new ArrayList<>()).add(module);
|
|
||||||
moduleMap.put(module.getName(), module);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateModuleEnabledStatus(List<MetaModule> moduleList) {
|
|
||||||
this.moduleEnabledStatus = loadModuleEnabledStatusMap(moduleList);
|
|
||||||
|
|
||||||
boolean unmatch = false;
|
|
||||||
for (MetaModule metaModule : moduleList) {
|
|
||||||
String moduleName = metaModule.getName();
|
|
||||||
if (moduleEnabledStatus.containsKey(moduleName)) {
|
|
||||||
metaModule.setEnabled(moduleEnabledStatus.get(moduleName));
|
|
||||||
} else {
|
|
||||||
log.warn("缺少Module {} 启用配置! 将触发更新操作!", moduleName);
|
|
||||||
unmatch = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (unmatch) {
|
|
||||||
dumpModuleEnabledStatus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Message> loadModelPrompt(String modelKey) {
|
|
||||||
if (!modelPromptMap.containsKey(modelKey)) {
|
|
||||||
throw new PromptNotExistException("不存在的modelPrompt: " + modelKey);
|
|
||||||
}
|
|
||||||
return modelPromptMap.get(modelKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ModelConfig loadModelConfig(String modelKey) {
|
|
||||||
if (!modelConfigMap.containsKey(modelKey)) {
|
|
||||||
return modelConfigMap.get(DEFAULT_KEY);
|
|
||||||
}
|
|
||||||
return modelConfigMap.get(modelKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateModelConfig(String modelKey, ModelConfig config) {
|
|
||||||
modelConfigMap.put(modelKey, config);
|
|
||||||
dumpModelConfig(modelKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateModuleEnabledStatus(String key, boolean status) {
|
|
||||||
if (!moduleEnabledStatus.containsKey(key)) {
|
|
||||||
throw new ConfigUpdateFailedException("模块状态更新失败! 不存在的ModuleKey: " + key);
|
|
||||||
}
|
|
||||||
moduleEnabledStatus.put(key, status);
|
|
||||||
dumpModuleEnabledStatus();
|
|
||||||
moduleMap.get(key).setEnabled(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.config;
|
|
||||||
|
|
||||||
import cn.hutool.json.JSONObject;
|
|
||||||
import cn.hutool.json.JSONUtil;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.commons.io.FileUtils;
|
|
||||||
import work.slhaf.partner.api.agent.factory.config.exception.*;
|
|
||||||
import work.slhaf.partner.api.agent.factory.config.pojo.ModelConfig;
|
|
||||||
import work.slhaf.partner.api.agent.factory.config.pojo.PrimaryModelConfig;
|
|
||||||
import work.slhaf.partner.api.agent.factory.config.pojo.PrimaryModelPrompt;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
|
||||||
import work.slhaf.partner.api.chat.pojo.Message;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 默认配置工厂
|
|
||||||
* 将从当前运行目录的config文件夹下创建并读取配置
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class FileAgentConfigManager extends AgentConfigManager {
|
|
||||||
|
|
||||||
protected static final String CONFIG_DIR = "./config/";
|
|
||||||
protected static final String MODEL_CONFIG_DIR = "./config/model/";
|
|
||||||
protected static final String PROMPT_CONFIG_DIR = "./config/prompt/";
|
|
||||||
protected static final String MODULE_ENABLED_STATUS_CONFIG_FILE = CONFIG_DIR + "module_enabled_status.json";
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected HashMap<String, List<Message>> loadModelPrompt() {
|
|
||||||
File file = new File(PROMPT_CONFIG_DIR);
|
|
||||||
if (!file.exists() && !file.isDirectory()) {
|
|
||||||
throw new PromptDirNotExistException("未找到提示词目录: " + PROMPT_CONFIG_DIR + " 请手动创建!");
|
|
||||||
}
|
|
||||||
File[] files = file.listFiles();
|
|
||||||
if (files == null || files.length == 0) {
|
|
||||||
throw new PromptNotExistException("在目录 " + PROMPT_CONFIG_DIR + " 中未找到提示词配置!");
|
|
||||||
}
|
|
||||||
HashMap<String, List<Message>> promptMap = new HashMap<>();
|
|
||||||
for (File f : files) {
|
|
||||||
if (f.isDirectory()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
PrimaryModelPrompt primaryModelPrompt = JSONUtil.readJSONObject(f, StandardCharsets.UTF_8).toBean(PrimaryModelPrompt.class);
|
|
||||||
promptMap.put(primaryModelPrompt.getKey(), primaryModelPrompt.getMessages());
|
|
||||||
}
|
|
||||||
return promptMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected HashMap<String, ModelConfig> loadModelConfig() {
|
|
||||||
File file = new File(MODEL_CONFIG_DIR);
|
|
||||||
if (!file.exists() || !file.isDirectory()) {
|
|
||||||
throw new ConfigDirNotExistException("未找到配置目录: " + MODEL_CONFIG_DIR + " 请手动创建!");
|
|
||||||
}
|
|
||||||
File[] files = file.listFiles();
|
|
||||||
if (files == null || files.length == 0) {
|
|
||||||
throw new ConfigNotExistException("在目录" + MODEL_CONFIG_DIR + "中未找到配置文件!");
|
|
||||||
}
|
|
||||||
//遍历文件获取所有配置文件并返回
|
|
||||||
HashMap<String, ModelConfig> configMap = new HashMap<>();
|
|
||||||
for (File f : files) {
|
|
||||||
if (f.isDirectory()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
PrimaryModelConfig primaryModelConfig = JSONUtil.readJSONObject(f, StandardCharsets.UTF_8).toBean(PrimaryModelConfig.class);
|
|
||||||
configMap.put(primaryModelConfig.getKey(), primaryModelConfig.getModelConfig());
|
|
||||||
}
|
|
||||||
return configMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected HashMap<String, Boolean> loadModuleEnabledStatusMap(List<MetaModule> moduleList) {
|
|
||||||
File file = new File(MODULE_ENABLED_STATUS_CONFIG_FILE);
|
|
||||||
try {
|
|
||||||
moduleEnabledStatus = new HashMap<>();
|
|
||||||
if (!file.exists()) {
|
|
||||||
file.createNewFile();
|
|
||||||
for (MetaModule module : moduleList) {
|
|
||||||
moduleEnabledStatus.put(module.getName(), module.isEnabled());
|
|
||||||
}
|
|
||||||
dumpModuleEnabledStatus();
|
|
||||||
} else {
|
|
||||||
JSONObject obj = JSONUtil.readJSONObject(file, StandardCharsets.UTF_8);
|
|
||||||
for (String s : obj.keySet()) {
|
|
||||||
moduleEnabledStatus.put(s, obj.getBool(s));
|
|
||||||
}
|
|
||||||
log.info("ModuleEnabledStatusConfig 配置文件已成功读取!");
|
|
||||||
}
|
|
||||||
return moduleEnabledStatus;
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ConfigGenerateFailedException("ModuleEnabledStatusConfig 配置文件创建失败!", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dumpModelConfig(String key) {
|
|
||||||
try {
|
|
||||||
File file = new File(MODEL_CONFIG_DIR + key + ".json");
|
|
||||||
if (!file.exists()) {
|
|
||||||
file.createNewFile();
|
|
||||||
}
|
|
||||||
FileUtils.writeStringToFile(file, JSONUtil.toJsonPrettyStr(modelConfigMap.get(key)), StandardCharsets.UTF_8, false);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ConfigUpdateFailedException("ModelConfig 配置文件更新失败!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void dumpModuleEnabledStatus() {
|
|
||||||
try {
|
|
||||||
File file = new File(MODULE_ENABLED_STATUS_CONFIG_FILE);
|
|
||||||
FileUtils.writeStringToFile(file, JSONUtil.toJsonPrettyStr(moduleEnabledStatus), StandardCharsets.UTF_8, false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new ConfigGenerateFailedException("ModuleEnabledStatus 配置文件更新失败!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.data;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class AgentContext {
|
|
||||||
|
|
||||||
public static AgentContext INSTANCE = new AgentContext();
|
|
||||||
|
|
||||||
private HashMap<String, Function<Object[], Object>> methodsRouterTable;
|
|
||||||
private HashMap<String, Function<Object[], Object>> coordinatedMethodsRouterTable;
|
|
||||||
private HashMap<Class<?>, Object> capabilityCoreInstances;
|
|
||||||
private HashMap<Class<?>, Object> capabilityHolderInstances;
|
|
||||||
private Set<Class<?>> cores;
|
|
||||||
private Set<Class<?>> capabilities;
|
|
||||||
private List<MetaModule> moduleList;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.exception;
|
|
||||||
|
|
||||||
public interface AgentExceptionCallback {
|
|
||||||
void onRuntimeException(AgentRuntimeException e);
|
|
||||||
void onFailedException(AgentLaunchFailedException e);
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.exception;
|
|
||||||
|
|
||||||
public class AgentLaunchFailedException extends RuntimeException {
|
|
||||||
public AgentLaunchFailedException(String message, Throwable cause) {
|
|
||||||
super("Agent 启动失败 " + message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AgentLaunchFailedException(String message) {
|
|
||||||
super("Agent 启动失败 " + message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.exception;
|
|
||||||
|
|
||||||
public class AgentRunningFailedException extends AgentRuntimeException{
|
|
||||||
public AgentRunningFailedException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AgentRunningFailedException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.exception;
|
|
||||||
|
|
||||||
public class AgentRuntimeException extends RuntimeException {
|
|
||||||
public AgentRuntimeException(String message) {
|
|
||||||
super("Agent 执行出错 " + message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AgentRuntimeException(String message, Throwable cause) {
|
|
||||||
super("Agent 执行出错 " + message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.exception;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class GlobalExceptionHandler {
|
|
||||||
|
|
||||||
public static GlobalExceptionHandler INSTANCE = new GlobalExceptionHandler();
|
|
||||||
|
|
||||||
private AgentExceptionCallback exceptionCallback = new LogAgentExceptionCallback();
|
|
||||||
|
|
||||||
public boolean handle(Throwable e) {
|
|
||||||
boolean exit;
|
|
||||||
Throwable cause = e.getCause();
|
|
||||||
switch (cause) {
|
|
||||||
case AgentRunningFailedException arfe -> {
|
|
||||||
exit = true;
|
|
||||||
exceptionCallback.onRuntimeException((AgentRuntimeException) cause);
|
|
||||||
}
|
|
||||||
case AgentRuntimeException are -> {
|
|
||||||
exit = false;
|
|
||||||
exceptionCallback.onRuntimeException((AgentRuntimeException) cause);
|
|
||||||
}
|
|
||||||
case AgentLaunchFailedException alfe -> {
|
|
||||||
exit = true;
|
|
||||||
exceptionCallback.onFailedException((AgentLaunchFailedException) cause);
|
|
||||||
}
|
|
||||||
default -> {
|
|
||||||
exit = true;
|
|
||||||
log.error("意外异常: ", cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setExceptionCallback(AgentExceptionCallback callback) {
|
|
||||||
INSTANCE.exceptionCallback = callback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.exception;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class LogAgentExceptionCallback implements AgentExceptionCallback {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRuntimeException(AgentRuntimeException e) {
|
|
||||||
log.error("Agent 运行异常: ", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailedException(AgentLaunchFailedException e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.interaction;
|
|
||||||
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.data.AgentInputData;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.data.AgentOutputData;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.entity.RunningFlowContext;
|
|
||||||
|
|
||||||
public interface AgentGateway <I extends AgentInputData, O extends AgentOutputData, C extends RunningFlowContext>{
|
|
||||||
|
|
||||||
void launch();
|
|
||||||
|
|
||||||
default void receive(I inputData){
|
|
||||||
C finalInputData = adapter().parseInputData(inputData);
|
|
||||||
C outputContext = adapter().call(finalInputData);
|
|
||||||
O outputData = adapter().parseOutputData(outputContext);
|
|
||||||
send(outputData);
|
|
||||||
}
|
|
||||||
|
|
||||||
void send(O outputData);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通过adapter提供的receive、send方法进行与客户端的交互行为
|
|
||||||
*
|
|
||||||
* @return adapter实例
|
|
||||||
*/
|
|
||||||
AgentInteractionAdapter<I, O, C> adapter();
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.interaction;
|
|
||||||
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.data.AgentInputData;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.data.AgentOutputData;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.AgentRunningFlow;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.entity.RunningFlowContext;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public abstract class AgentInteractionAdapter<I extends AgentInputData, O extends AgentOutputData, C extends RunningFlowContext> {
|
|
||||||
|
|
||||||
protected AgentRunningFlow<C> agentRunningFlow = new AgentRunningFlow<>();
|
|
||||||
protected Map<Integer, List<MetaModule>> moduleOrderedMap = AgentConfigManager.INSTANCE.getModuleOrderedMap();
|
|
||||||
|
|
||||||
public C call(C finalInputData){
|
|
||||||
return agentRunningFlow.launch(moduleOrderedMap, finalInputData);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract O parseOutputData(C outputContext);
|
|
||||||
|
|
||||||
protected abstract C parseInputData(I inputData);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.interaction.data;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Data
|
|
||||||
public abstract class AgentInputData extends InteractionData{
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.interaction.data;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Data
|
|
||||||
public abstract class AgentOutputData extends InteractionData{
|
|
||||||
|
|
||||||
protected int code;
|
|
||||||
|
|
||||||
public static class StatusCode {
|
|
||||||
public static final int SUCCESS = 1;
|
|
||||||
public static final int FAILED = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.interaction.data;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public abstract class InteractionData {
|
|
||||||
protected String userInfo;
|
|
||||||
protected String content;
|
|
||||||
protected LocalDateTime dateTime;
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.interaction.flow;
|
|
||||||
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.exception.AgentRuntimeException;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.exception.GlobalExceptionHandler;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.entity.RunningFlowContext;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Agent执行流程
|
|
||||||
*/
|
|
||||||
public class AgentRunningFlow<C extends RunningFlowContext> {
|
|
||||||
|
|
||||||
public C launch(Map<Integer, List<MetaModule>> modules, C interactionContext) {
|
|
||||||
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
|
|
||||||
//流程执行启动
|
|
||||||
for (Map.Entry<Integer, List<MetaModule>> entry : modules.entrySet()) {
|
|
||||||
List<Future<?>> futures = new ArrayList<>();
|
|
||||||
List<MetaModule> moduleList = entry.getValue();
|
|
||||||
for (MetaModule module : moduleList) {
|
|
||||||
Future<?> future = executor.submit(() -> {
|
|
||||||
module.getInstance().execute(interactionContext);
|
|
||||||
});
|
|
||||||
futures.add(future);
|
|
||||||
}
|
|
||||||
for (Future<?> future : futures) {
|
|
||||||
try {
|
|
||||||
future.get();
|
|
||||||
} catch (Exception e) {
|
|
||||||
boolean exit = GlobalExceptionHandler.INSTANCE.handle(e);
|
|
||||||
if (exit) throw new AgentRuntimeException("Agent执行出错!", e);
|
|
||||||
interactionContext.getErrMsg().add(e.getLocalizedMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
interactionContext.setOk(1);
|
|
||||||
} catch (Exception e) {
|
|
||||||
interactionContext.setOk(0);
|
|
||||||
interactionContext.getErrMsg().add(e.getLocalizedMessage());
|
|
||||||
}
|
|
||||||
return interactionContext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts;
|
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
|
||||||
import work.slhaf.partner.api.agent.factory.config.pojo.ModelConfig;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.Init;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.entity.Model;
|
|
||||||
import work.slhaf.partner.api.chat.ChatClient;
|
|
||||||
import work.slhaf.partner.api.chat.constant.ChatConstant;
|
|
||||||
import work.slhaf.partner.api.chat.pojo.ChatResponse;
|
|
||||||
import work.slhaf.partner.api.chat.pojo.Message;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface ActivateModel {
|
|
||||||
|
|
||||||
AgentConfigManager AGENT_CONFIG_MANAGER = AgentConfigManager.INSTANCE;
|
|
||||||
|
|
||||||
@Init(order = -1)
|
|
||||||
default void modelSettings() {
|
|
||||||
Model model = new Model();
|
|
||||||
ModelConfig modelConfig = AgentConfigManager.INSTANCE.loadModelConfig(modelKey());
|
|
||||||
model.setBaseMessages(withBasicPrompt() ? loadSpecificPromptAndBasicPrompt(modelKey()) : loadSpecificPrompt(modelKey()));
|
|
||||||
model.setChatClient(new ChatClient(modelConfig.getBaseUrl(), modelConfig.getApikey(), modelConfig.getModel()));
|
|
||||||
((Module) this).setModel(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
default void updateModelSettings(ChatClient newChatClient) {
|
|
||||||
BeanUtil.copyProperties(newChatClient, chatClient());
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Message> loadSpecificPrompt(String modelKey) {
|
|
||||||
return AGENT_CONFIG_MANAGER.loadModelPrompt(modelKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Message> loadSpecificPromptAndBasicPrompt(String modelKey) {
|
|
||||||
List<Message> messages = new ArrayList<>();
|
|
||||||
messages.addAll(AGENT_CONFIG_MANAGER.loadModelPrompt("basic"));
|
|
||||||
messages.addAll(AGENT_CONFIG_MANAGER.loadModelPrompt(modelKey));
|
|
||||||
return messages;
|
|
||||||
}
|
|
||||||
|
|
||||||
default ChatResponse chat() {
|
|
||||||
Model model = getModel();
|
|
||||||
List<Message> temp = new ArrayList<>();
|
|
||||||
temp.addAll(model.getBaseMessages());
|
|
||||||
temp.addAll(model.getChatMessages());
|
|
||||||
return model.getChatClient().runChat(temp);
|
|
||||||
}
|
|
||||||
|
|
||||||
default ChatResponse singleChat(String input) {
|
|
||||||
Model model = getModel();
|
|
||||||
List<Message> temp = new ArrayList<>(model.getBaseMessages());
|
|
||||||
temp.add(new Message(ChatConstant.Character.USER, input));
|
|
||||||
return model.getChatClient().runChat(temp);
|
|
||||||
}
|
|
||||||
|
|
||||||
default void updateChatClientSettings() {
|
|
||||||
Model model = getModel();
|
|
||||||
model.getChatClient().setTemperature(0.4);
|
|
||||||
model.getChatClient().setTop_p(0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
default List<Message> chatMessages() {
|
|
||||||
return getModel().getChatMessages();
|
|
||||||
}
|
|
||||||
|
|
||||||
default List<Message> baseMessages() {
|
|
||||||
return getModel().getBaseMessages();
|
|
||||||
}
|
|
||||||
|
|
||||||
default ChatClient chatClient() {
|
|
||||||
return getModel().getChatClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 仅适用Module子类,否则需要重写
|
|
||||||
*
|
|
||||||
* @return 持有的model实例
|
|
||||||
*/
|
|
||||||
default Model getModel() {
|
|
||||||
return ((Module) this).getModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
default void setModel(Model model) {
|
|
||||||
((Module) this).setModel(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对应调用的模型配置名称
|
|
||||||
*/
|
|
||||||
String modelKey();
|
|
||||||
|
|
||||||
boolean withBasicPrompt();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.CoreModule;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.entity.RunningFlowContext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 流程执行模块基类
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public abstract class AgentRunningModule<C extends RunningFlowContext> extends Module {
|
|
||||||
public abstract void execute(C context);
|
|
||||||
|
|
||||||
@BeforeExecute
|
|
||||||
private void beforeLog() {
|
|
||||||
log.debug("[{}] 模块执行开始...", getModuleName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterExecute
|
|
||||||
private void afterLog() {
|
|
||||||
log.debug("[{}] 模块执行结束...", getModuleName());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getModuleName(){
|
|
||||||
if (this.getClass().isAnnotationPresent(AgentModule.class)) {
|
|
||||||
return this.getClass().getAnnotation(AgentModule.class).name();
|
|
||||||
} else if (this.getClass().isAnnotationPresent(CoreModule.class)) {
|
|
||||||
return CoreModule.class.getAnnotation(AgentModule.class).name();
|
|
||||||
}else {
|
|
||||||
return "Unknown Module";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts;
|
|
||||||
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute;
|
|
||||||
import work.slhaf.partner.api.agent.factory.module.annotation.CoreModule;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public abstract class AgentRunningSubModule<I, O> extends Module {
|
|
||||||
|
|
||||||
public abstract O execute(I data);
|
|
||||||
|
|
||||||
|
|
||||||
@BeforeExecute
|
|
||||||
private void beforeLog() {
|
|
||||||
log.debug("[{}] 模块执行开始...", getModuleName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterExecute
|
|
||||||
private void afterLog() {
|
|
||||||
log.debug("[{}] 模块执行结束...", getModuleName());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getModuleName(){
|
|
||||||
if (this.getClass().isAnnotationPresent(AgentModule.class)) {
|
|
||||||
return this.getClass().getAnnotation(AgentModule.class).name();
|
|
||||||
} else if (this.getClass().isAnnotationPresent(CoreModule.class)) {
|
|
||||||
return CoreModule.class.getAnnotation(AgentModule.class).name();
|
|
||||||
}else {
|
|
||||||
return "Unknown Module";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.entity.Model;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模块基类
|
|
||||||
*/
|
|
||||||
public abstract class Module {
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
protected Model model = new Model();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.interaction.flow.entity;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import work.slhaf.partner.api.chat.ChatClient;
|
|
||||||
import work.slhaf.partner.api.chat.pojo.Message;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class Model {
|
|
||||||
|
|
||||||
protected ChatClient chatClient;
|
|
||||||
protected List<Message> chatMessages;
|
|
||||||
protected List<Message> baseMessages;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package work.slhaf.partner.api.agent.runtime.interaction.flow.entity;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import work.slhaf.partner.api.common.entity.PersistableObject;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 流程上下文
|
|
||||||
*/
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Data
|
|
||||||
public abstract class RunningFlowContext extends PersistableObject {
|
|
||||||
protected int ok;
|
|
||||||
protected List<String> errMsg = new ArrayList<>();
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package work.slhaf.partner.api.chat;
|
|
||||||
|
|
||||||
import cn.hutool.http.HttpRequest;
|
|
||||||
import cn.hutool.http.HttpResponse;
|
|
||||||
import cn.hutool.json.JSONUtil;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import work.slhaf.partner.api.chat.constant.ChatConstant;
|
|
||||||
import work.slhaf.partner.api.chat.pojo.ChatBody;
|
|
||||||
import work.slhaf.partner.api.chat.pojo.ChatResponse;
|
|
||||||
import work.slhaf.partner.api.chat.pojo.Message;
|
|
||||||
import work.slhaf.partner.api.chat.pojo.PrimaryChatResponse;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
public class ChatClient {
|
|
||||||
private String clientId;
|
|
||||||
|
|
||||||
private String url;
|
|
||||||
private String apikey;
|
|
||||||
private String model;
|
|
||||||
|
|
||||||
private double top_p;
|
|
||||||
private double temperature;
|
|
||||||
private int max_tokens;
|
|
||||||
|
|
||||||
public ChatClient(String url, String apikey, String model) {
|
|
||||||
this.url = url;
|
|
||||||
this.apikey = apikey;
|
|
||||||
this.model = model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChatResponse runChat(List<Message> messages) {
|
|
||||||
HttpRequest request = HttpRequest.post(url);
|
|
||||||
request.header("Content-Type", "application/json");
|
|
||||||
request.header("Authorization", "Bearer " + apikey);
|
|
||||||
|
|
||||||
ChatBody body;
|
|
||||||
if (top_p > 0) {
|
|
||||||
body = ChatBody.builder()
|
|
||||||
.model(model)
|
|
||||||
.messages(messages)
|
|
||||||
.top_p(top_p)
|
|
||||||
.temperature(temperature)
|
|
||||||
.max_tokens(max_tokens)
|
|
||||||
.build();
|
|
||||||
} else {
|
|
||||||
body = ChatBody.builder()
|
|
||||||
.model(model)
|
|
||||||
.messages(messages)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpResponse response = request.body(JSONUtil.toJsonStr(body)).execute();
|
|
||||||
ChatResponse finalResponse;
|
|
||||||
|
|
||||||
PrimaryChatResponse primaryChatResponse = JSONUtil.toBean(response.body(), PrimaryChatResponse.class);
|
|
||||||
finalResponse = ChatResponse.builder()
|
|
||||||
.type(ChatConstant.Response.SUCCESS)
|
|
||||||
.message(primaryChatResponse.getChoices().get(0).getMessage().getContent())
|
|
||||||
.usageBean(primaryChatResponse.getUsage())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
response.close();
|
|
||||||
return finalResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package work.slhaf.partner.api.chat.constant;
|
|
||||||
|
|
||||||
public class ChatConstant {
|
|
||||||
|
|
||||||
public static class Character {
|
|
||||||
public static final String USER = "user";
|
|
||||||
public static final String SYSTEM = "system";
|
|
||||||
public static final String ASSISTANT = "assistant";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Response {
|
|
||||||
public static final String SUCCESS = "success";
|
|
||||||
public static final String ERROR = "error";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package work.slhaf.partner.api.chat.pojo;
|
|
||||||
|
|
||||||
import lombok.*;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Builder
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
@NoArgsConstructor
|
|
||||||
public class ChatBody {
|
|
||||||
@NonNull
|
|
||||||
private String model;
|
|
||||||
@NonNull
|
|
||||||
private List<Message> messages;
|
|
||||||
@Builder.Default
|
|
||||||
private double temperature = 1;
|
|
||||||
@Builder.Default
|
|
||||||
private double top_p = 1;
|
|
||||||
private boolean stream;
|
|
||||||
@Builder.Default
|
|
||||||
private int max_tokens = 1024;
|
|
||||||
private int presence_penalty;
|
|
||||||
private int frequency_penalty;
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package work.slhaf.partner.api.chat.pojo;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@Builder
|
|
||||||
@AllArgsConstructor
|
|
||||||
@NoArgsConstructor
|
|
||||||
public class ChatResponse {
|
|
||||||
private String type;
|
|
||||||
private String message;
|
|
||||||
private PrimaryChatResponse.UsageBean usageBean;
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package work.slhaf.partner.api.chat.pojo;
|
|
||||||
|
|
||||||
import lombok.*;
|
|
||||||
import work.slhaf.partner.api.common.entity.PersistableObject;
|
|
||||||
|
|
||||||
import java.io.Serial;
|
|
||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Builder
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
@NoArgsConstructor
|
|
||||||
public class Message extends PersistableObject {
|
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private String role;
|
|
||||||
@NonNull
|
|
||||||
private String content;
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package work.slhaf.partner.api.chat.pojo;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import work.slhaf.partner.api.common.entity.PersistableObject;
|
|
||||||
|
|
||||||
import java.io.Serial;
|
|
||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class MetaMessage extends PersistableObject {
|
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private Message userMessage;
|
|
||||||
private Message assistantMessage;
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
package work.slhaf.partner.api.chat.pojo;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
public class PrimaryChatResponse {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* id
|
|
||||||
*/
|
|
||||||
private String id;
|
|
||||||
/**
|
|
||||||
* object
|
|
||||||
*/
|
|
||||||
private String object;
|
|
||||||
/**
|
|
||||||
* created
|
|
||||||
*/
|
|
||||||
private int created;
|
|
||||||
/**
|
|
||||||
* model
|
|
||||||
*/
|
|
||||||
private String model;
|
|
||||||
/**
|
|
||||||
* choices
|
|
||||||
*/
|
|
||||||
private List<ChoicesBean> choices;
|
|
||||||
/**
|
|
||||||
* usage
|
|
||||||
*/
|
|
||||||
private UsageBean usage;
|
|
||||||
/**
|
|
||||||
* system_fingerprint
|
|
||||||
*/
|
|
||||||
private String system_fingerprint;
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
@Getter
|
|
||||||
public static class UsageBean {
|
|
||||||
/**
|
|
||||||
* prompt_tokens
|
|
||||||
*/
|
|
||||||
private int prompt_tokens;
|
|
||||||
/**
|
|
||||||
* completion_tokens
|
|
||||||
*/
|
|
||||||
private int completion_tokens;
|
|
||||||
/**
|
|
||||||
* total_tokens
|
|
||||||
*/
|
|
||||||
private int total_tokens;
|
|
||||||
/**
|
|
||||||
* prompt_cache_hit_tokens
|
|
||||||
*/
|
|
||||||
private int prompt_cache_hit_tokens;
|
|
||||||
/**
|
|
||||||
* prompt_cache_miss_tokens
|
|
||||||
*/
|
|
||||||
private int prompt_cache_miss_tokens;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "UsageBean{" +
|
|
||||||
"prompt_tokens=" + prompt_tokens +
|
|
||||||
", completion_tokens=" + completion_tokens +
|
|
||||||
", total_tokens=" + total_tokens +
|
|
||||||
", prompt_cache_hit_tokens=" + prompt_cache_hit_tokens +
|
|
||||||
", prompt_cache_miss_tokens=" + prompt_cache_miss_tokens +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
@Getter
|
|
||||||
public static class ChoicesBean {
|
|
||||||
/**
|
|
||||||
* index
|
|
||||||
*/
|
|
||||||
private int index;
|
|
||||||
/**
|
|
||||||
* message
|
|
||||||
*/
|
|
||||||
private MessageBean message;
|
|
||||||
/**
|
|
||||||
* logprobs
|
|
||||||
*/
|
|
||||||
private Object logprobs;
|
|
||||||
/**
|
|
||||||
* finish_reason
|
|
||||||
*/
|
|
||||||
private String finish_reason;
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
@Getter
|
|
||||||
public static class MessageBean {
|
|
||||||
/**
|
|
||||||
* role
|
|
||||||
*/
|
|
||||||
private String role;
|
|
||||||
/**
|
|
||||||
* content
|
|
||||||
*/
|
|
||||||
private String content;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package work.slhaf.partner.api.common.entity;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
public abstract class PersistableObject implements Serializable {
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package factory;
|
|
||||||
|
|
||||||
public class AgentRegisterTest {
|
|
||||||
public static void main(String[] args) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user