summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNate Sesti <sestinj@gmail.com>2023-07-22 22:37:13 -0700
committerNate Sesti <sestinj@gmail.com>2023-07-22 22:37:13 -0700
commit4d7e72970f770eb49627589fb142c93dfb6fd73b (patch)
tree7c85fb17a9e10ac8e387a001f021aa45c8c46582
parent007780d6d60095d4e0b238358ec26b2ec776b73e (diff)
downloadsncontinue-4d7e72970f770eb49627589fb142c93dfb6fd73b.tar.gz
sncontinue-4d7e72970f770eb49627589fb142c93dfb6fd73b.tar.bz2
sncontinue-4d7e72970f770eb49627589fb142c93dfb6fd73b.zip
@ feature (very large commit)
-rw-r--r--continuedev/poetry.lock340
-rw-r--r--continuedev/pyproject.toml2
-rw-r--r--continuedev/src/continuedev/core/autopilot.py138
-rw-r--r--continuedev/src/continuedev/core/config.py3
-rw-r--r--continuedev/src/continuedev/core/context.py205
-rw-r--r--continuedev/src/continuedev/core/context_manager.py119
-rw-r--r--continuedev/src/continuedev/core/main.py59
-rw-r--r--continuedev/src/continuedev/core/sdk.py31
-rw-r--r--continuedev/src/continuedev/libs/context_providers/highlighted_code_context_provider.py191
-rw-r--r--continuedev/src/continuedev/libs/util/paths.py20
-rw-r--r--continuedev/src/continuedev/models/generate_json_schema.py3
-rw-r--r--continuedev/src/continuedev/server/gui.py37
-rw-r--r--continuedev/src/continuedev/server/gui_protocol.py10
-rw-r--r--continuedev/src/continuedev/server/main.py28
-rw-r--r--continuedev/src/continuedev/server/meilisearch_server.py56
-rw-r--r--continuedev/src/continuedev/server/session_manager.py6
-rw-r--r--extension/react-app/package-lock.json98
-rw-r--r--extension/react-app/package.json1
-rw-r--r--extension/react-app/src/components/ComboBox.tsx174
-rw-r--r--extension/react-app/src/components/PillButton.tsx87
-rw-r--r--extension/react-app/src/components/StepContainer.tsx29
-rw-r--r--extension/react-app/src/components/StyledMarkdownPreview.tsx37
-rw-r--r--extension/react-app/src/components/TextDialog.tsx2
-rw-r--r--extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts8
-rw-r--r--extension/react-app/src/hooks/ContinueGUIClientProtocol.ts15
-rw-r--r--extension/react-app/src/pages/gui.tsx81
-rw-r--r--extension/react-app/src/redux/selectors/uiStateSelectors.ts5
-rw-r--r--extension/react-app/src/redux/slices/uiStateSlice.ts24
-rw-r--r--extension/react-app/src/redux/store.ts6
-rw-r--r--extension/schema/ContextItem.d.ts45
-rw-r--r--extension/schema/FullState.d.ts65
-rw-r--r--schema/json/ContextItem.json76
-rw-r--r--schema/json/FullState.json132
33 files changed, 1554 insertions, 579 deletions
diff --git a/continuedev/poetry.lock b/continuedev/poetry.lock
index 1cd4a591..b67128fd 100644
--- a/continuedev/poetry.lock
+++ b/continuedev/poetry.lock
@@ -221,6 +221,24 @@ files = [
]
[[package]]
+name = "camel-converter"
+version = "3.0.2"
+description = "Converts a string from snake case to camel case or camel case to snake case"
+category = "main"
+optional = false
+python-versions = ">=3.8,<4.0"
+files = [
+ {file = "camel_converter-3.0.2-py3-none-any.whl", hash = "sha256:88e5d91be5b2dff9c0748ba515774c3421088922d9e77c39f8742eb41cb7db88"},
+ {file = "camel_converter-3.0.2.tar.gz", hash = "sha256:3b3d076e824ae979b271b4d497c90514c2b218811f76b0c368fb69da2556fe07"},
+]
+
+[package.dependencies]
+pydantic = {version = ">=1.8.2", optional = true, markers = "extra == \"pydantic\""}
+
+[package.extras]
+pydantic = ["pydantic (>=1.8.2)"]
+
+[[package]]
name = "certifi"
version = "2022.12.7"
description = "Python package for providing Mozilla's CA Bundle."
@@ -233,6 +251,83 @@ files = [
]
[[package]]
+name = "cffi"
+version = "1.15.1"
+description = "Foreign Function Interface for Python calling C code."
+category = "main"
+optional = false
+python-versions = "*"
+files = [
+ {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
+ {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
+ {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"},
+ {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"},
+ {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"},
+ {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"},
+ {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"},
+ {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"},
+ {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"},
+ {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"},
+ {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"},
+ {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"},
+ {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"},
+ {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"},
+ {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"},
+ {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"},
+ {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"},
+ {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"},
+ {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"},
+ {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"},
+ {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"},
+ {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"},
+ {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"},
+ {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"},
+ {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"},
+ {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"},
+ {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"},
+ {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"},
+ {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"},
+ {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"},
+ {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"},
+ {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"},
+ {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"},
+ {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
+ {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
+]
+
+[package.dependencies]
+pycparser = "*"
+
+[[package]]
name = "charset-normalizer"
version = "3.1.0"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
@@ -357,6 +452,52 @@ files = [
]
[[package]]
+name = "cryptography"
+version = "41.0.2"
+description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
+category = "main"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "cryptography-41.0.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:01f1d9e537f9a15b037d5d9ee442b8c22e3ae11ce65ea1f3316a41c78756b711"},
+ {file = "cryptography-41.0.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:079347de771f9282fbfe0e0236c716686950c19dee1b76240ab09ce1624d76d7"},
+ {file = "cryptography-41.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:439c3cc4c0d42fa999b83ded80a9a1fb54d53c58d6e59234cfe97f241e6c781d"},
+ {file = "cryptography-41.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f14ad275364c8b4e525d018f6716537ae7b6d369c094805cae45300847e0894f"},
+ {file = "cryptography-41.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:84609ade00a6ec59a89729e87a503c6e36af98ddcd566d5f3be52e29ba993182"},
+ {file = "cryptography-41.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:49c3222bb8f8e800aead2e376cbef687bc9e3cb9b58b29a261210456a7783d83"},
+ {file = "cryptography-41.0.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:d73f419a56d74fef257955f51b18d046f3506270a5fd2ac5febbfa259d6c0fa5"},
+ {file = "cryptography-41.0.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:2a034bf7d9ca894720f2ec1d8b7b5832d7e363571828037f9e0c4f18c1b58a58"},
+ {file = "cryptography-41.0.2-cp37-abi3-win32.whl", hash = "sha256:d124682c7a23c9764e54ca9ab5b308b14b18eba02722b8659fb238546de83a76"},
+ {file = "cryptography-41.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:9c3fe6534d59d071ee82081ca3d71eed3210f76ebd0361798c74abc2bcf347d4"},
+ {file = "cryptography-41.0.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a719399b99377b218dac6cf547b6ec54e6ef20207b6165126a280b0ce97e0d2a"},
+ {file = "cryptography-41.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:182be4171f9332b6741ee818ec27daff9fb00349f706629f5cbf417bd50e66fd"},
+ {file = "cryptography-41.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:7a9a3bced53b7f09da251685224d6a260c3cb291768f54954e28f03ef14e3766"},
+ {file = "cryptography-41.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f0dc40e6f7aa37af01aba07277d3d64d5a03dc66d682097541ec4da03cc140ee"},
+ {file = "cryptography-41.0.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:674b669d5daa64206c38e507808aae49904c988fa0a71c935e7006a3e1e83831"},
+ {file = "cryptography-41.0.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7af244b012711a26196450d34f483357e42aeddb04128885d95a69bd8b14b69b"},
+ {file = "cryptography-41.0.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9b6d717393dbae53d4e52684ef4f022444fc1cce3c48c38cb74fca29e1f08eaa"},
+ {file = "cryptography-41.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:192255f539d7a89f2102d07d7375b1e0a81f7478925b3bc2e0549ebf739dae0e"},
+ {file = "cryptography-41.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f772610fe364372de33d76edcd313636a25684edb94cee53fd790195f5989d14"},
+ {file = "cryptography-41.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:b332cba64d99a70c1e0836902720887fb4529ea49ea7f5462cf6640e095e11d2"},
+ {file = "cryptography-41.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9a6673c1828db6270b76b22cc696f40cde9043eb90373da5c2f8f2158957f42f"},
+ {file = "cryptography-41.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:342f3767e25876751e14f8459ad85e77e660537ca0a066e10e75df9c9e9099f0"},
+ {file = "cryptography-41.0.2.tar.gz", hash = "sha256:7d230bf856164de164ecb615ccc14c7fc6de6906ddd5b491f3af90d3514c925c"},
+]
+
+[package.dependencies]
+cffi = ">=1.12"
+
+[package.extras]
+docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
+docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
+nox = ["nox"]
+pep8test = ["black", "check-sdist", "mypy", "ruff"]
+sdist = ["build"]
+ssh = ["bcrypt (>=3.1.5)"]
+test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
+test-randomorder = ["pytest-randomly"]
+
+[[package]]
name = "dataclasses-json"
version = "0.5.7"
description = "Easily serialize dataclasses to and from JSON"
@@ -377,6 +518,24 @@ typing-inspect = ">=0.4.0"
dev = ["flake8", "hypothesis", "ipython", "mypy (>=0.710)", "portray", "pytest (>=6.2.3)", "simplejson", "types-dataclasses"]
[[package]]
+name = "deprecated"
+version = "1.2.14"
+description = "Python @deprecated decorator to deprecate old python classes, functions or methods."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+ {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"},
+ {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"},
+]
+
+[package.dependencies]
+wrapt = ">=1.10,<2"
+
+[package.extras]
+dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"]
+
+[[package]]
name = "diff-match-patch"
version = "20230430"
description = "Diff Match and Patch"
@@ -817,6 +976,22 @@ files = [
marshmallow = ">=2.0.0"
[[package]]
+name = "meilisearch"
+version = "0.28.1"
+description = "The python client for Meilisearch API."
+category = "main"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "meilisearch-0.28.1-py3-none-any.whl", hash = "sha256:b4cfaf140fc323c429a4e174b801bcc6dddef88bf901560ebf2fc5c0014099b5"},
+ {file = "meilisearch-0.28.1.tar.gz", hash = "sha256:4e8627abe1394f04125c99e60f9f4356ba200effe5735ce9b8a44b09e3c6403c"},
+]
+
+[package.dependencies]
+camel-converter = {version = "*", extras = ["pydantic"]}
+requests = "*"
+
+[[package]]
name = "monotonic"
version = "1.6"
description = "An implementation of time.monotonic() for Python 2 & < 3.3"
@@ -1199,6 +1374,18 @@ files = [
test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
[[package]]
+name = "pycparser"
+version = "2.21"
+description = "C parser in Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
+ {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
+]
+
+[[package]]
name = "pydantic"
version = "1.10.7"
description = "Data validation and settings management using python type hints"
@@ -1252,6 +1439,72 @@ dotenv = ["python-dotenv (>=0.10.4)"]
email = ["email-validator (>=1.0.3)"]
[[package]]
+name = "pygithub"
+version = "1.59.0"
+description = "Use the full Github API v3"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "PyGithub-1.59.0-py3-none-any.whl", hash = "sha256:126bdbae72087d8d038b113aab6b059b4553cb59348e3024bb1a1cae406ace9e"},
+ {file = "PyGithub-1.59.0.tar.gz", hash = "sha256:6e05ff49bac3caa7d1d6177a10c6e55a3e20c85b92424cc198571fd0cf786690"},
+]
+
+[package.dependencies]
+deprecated = "*"
+pyjwt = {version = ">=2.4.0", extras = ["crypto"]}
+pynacl = ">=1.4.0"
+requests = ">=2.14.0"
+
+[[package]]
+name = "pyjwt"
+version = "2.8.0"
+description = "JSON Web Token implementation in Python"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"},
+ {file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"},
+]
+
+[package.dependencies]
+cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"crypto\""}
+
+[package.extras]
+crypto = ["cryptography (>=3.4.0)"]
+dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
+docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
+tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
+
+[[package]]
+name = "pynacl"
+version = "1.5.0"
+description = "Python binding to the Networking and Cryptography (NaCl) library"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"},
+ {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"},
+ {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"},
+ {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"},
+ {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"},
+ {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"},
+ {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"},
+ {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"},
+ {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"},
+ {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"},
+]
+
+[package.dependencies]
+cffi = ">=1.4.1"
+
+[package.extras]
+docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"]
+tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"]
+
+[[package]]
name = "pyrsistent"
version = "0.19.3"
description = "Persistent/Functional/Immutable data structures"
@@ -1936,6 +2189,91 @@ files = [
]
[[package]]
+name = "wrapt"
+version = "1.15.0"
+description = "Module for decorators, wrappers and monkey patching."
+category = "main"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+files = [
+ {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"},
+ {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"},
+ {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"},
+ {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"},
+ {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"},
+ {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"},
+ {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"},
+ {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"},
+ {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"},
+ {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"},
+ {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"},
+ {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"},
+ {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"},
+ {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"},
+ {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"},
+ {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"},
+ {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"},
+ {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"},
+ {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"},
+ {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"},
+ {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"},
+ {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"},
+ {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"},
+ {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"},
+ {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"},
+ {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"},
+ {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"},
+ {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"},
+ {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"},
+ {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"},
+ {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"},
+ {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"},
+ {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"},
+ {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"},
+ {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"},
+ {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"},
+ {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"},
+ {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"},
+ {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"},
+ {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"},
+ {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"},
+ {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"},
+ {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"},
+ {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"},
+ {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"},
+ {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"},
+ {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"},
+ {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"},
+ {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"},
+ {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"},
+ {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"},
+ {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"},
+ {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"},
+ {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"},
+ {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"},
+ {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"},
+ {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"},
+ {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"},
+ {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"},
+ {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"},
+ {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"},
+ {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"},
+ {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"},
+ {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"},
+ {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"},
+ {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"},
+ {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"},
+ {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"},
+ {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"},
+ {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"},
+ {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"},
+ {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"},
+ {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"},
+ {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"},
+ {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"},
+]
+
+[[package]]
name = "yarl"
version = "1.9.2"
description = "Yet another URL library"
@@ -2042,4 +2380,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
[metadata]
lock-version = "2.0"
python-versions = "^3.8.1"
-content-hash = "3fcd19c11b9c338a181e591b56e21d59c7834abff431fb9f40cc1ea874b64557"
+content-hash = "83880c8de27a4094fa8e695bd8db5d4053fc2d61ffc028f09cfa097f315c35f2"
diff --git a/continuedev/pyproject.toml b/continuedev/pyproject.toml
index 0abc9504..64d5e90c 100644
--- a/continuedev/pyproject.toml
+++ b/continuedev/pyproject.toml
@@ -27,6 +27,8 @@ directory-tree = "^0.0.3.1"
anthropic = "^0.3.4"
chevron = "^0.14.0"
psutil = "^5.9.5"
+meilisearch = "^0.28.1"
+pygithub = "^1.59.0"
[tool.poetry.scripts]
typegen = "src.continuedev.models.generate_json_schema:main"
diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py
index abda50b0..c0f95414 100644
--- a/continuedev/src/continuedev/core/autopilot.py
+++ b/continuedev/src/continuedev/core/autopilot.py
@@ -9,10 +9,12 @@ from pydantic import root_validator
from ..models.filesystem import RangeInFileWithContents
from ..models.filesystem_edit import FileEditWithFullContents
from .observation import Observation, InternalErrorObservation
+from .context import ContextItem, ContextItemDescription, ContextItemId, ContextManager
+from ..libs.context_providers.highlighted_code_context_provider import HighlightedCodeContextProvider
from ..server.ide_protocol import AbstractIdeProtocolServer
from ..libs.util.queue import AsyncSubscriptionQueue
from ..models.main import ContinueBaseModel
-from .main import Context, ContinueCustomException, HighlightedRangeContext, Policy, History, FullState, Step, HistoryNode
+from .main import Context, ContinueCustomException, Policy, History, FullState, Step, HistoryNode
from ..steps.core.core import ReversibleStep, ManualEditStep, UserInputStep
from ..libs.util.telemetry import capture_event
from .sdk import ContinueSDK
@@ -47,10 +49,11 @@ class Autopilot(ContinueBaseModel):
history: History = History.from_empty()
context: Context = Context()
full_state: Union[FullState, None] = None
- _on_update_callbacks: List[Callable[[FullState], None]] = []
-
+ context_manager: Union[ContextManager, None] = None
continue_sdk: ContinueSDK = None
+ _on_update_callbacks: List[Callable[[FullState], None]] = []
+
_active: bool = False
_should_halt: bool = False
_main_user_input_queue: List[str] = []
@@ -62,6 +65,14 @@ class Autopilot(ContinueBaseModel):
async def create(cls, policy: Policy, ide: AbstractIdeProtocolServer, full_state: FullState) -> "Autopilot":
autopilot = cls(ide=ide, policy=policy)
autopilot.continue_sdk = await ContinueSDK.create(autopilot)
+
+ # Load documents into the search index
+ autopilot.context_manager = ContextManager(
+ autopilot.continue_sdk.config.context_providers + [
+ HighlightedCodeContextProvider(ide=ide)
+ ])
+ await autopilot.context_manager.load_index()
+
return autopilot
class Config:
@@ -75,15 +86,16 @@ class Autopilot(ContinueBaseModel):
values['history'] = full_state.history
return values
- def get_full_state(self) -> FullState:
+ async def get_full_state(self) -> FullState:
full_state = FullState(
history=self.history,
active=self._active,
user_input_queue=self._main_user_input_queue,
default_model=self.continue_sdk.config.default_model,
- highlighted_ranges=self._highlighted_ranges,
slash_commands=self.get_available_slash_commands(),
- adding_highlighted_code=self._adding_highlighted_code,
+ adding_highlighted_code=self.context_manager.context_providers[
+ "code"].adding_highlighted_code,
+ selected_context_items=await self.context_manager.get_selected_items()
)
self.full_state = full_state
return full_state
@@ -104,8 +116,8 @@ class Autopilot(ContinueBaseModel):
self._main_user_input_queue = []
self._active = False
- # Also remove all context
- self._highlighted_ranges = []
+ # Clear context
+ await self.context_manager.clear_context()
await self.update_subscribers()
@@ -114,7 +126,7 @@ class Autopilot(ContinueBaseModel):
self._on_update_callbacks.append(callback)
async def update_subscribers(self):
- full_state = self.get_full_state()
+ full_state = await self.get_full_state()
for callback in self._on_update_callbacks:
await callback(full_state)
@@ -159,81 +171,10 @@ class Autopilot(ContinueBaseModel):
step = tb_step.step({"output": output, **tb_step.params})
await self._run_singular_step(step)
- _highlighted_ranges: List[HighlightedRangeContext] = []
- _adding_highlighted_code: bool = False
-
- def _make_sure_is_editing_range(self):
- """If none of the highlighted ranges are currently being edited, the first should be selected"""
- if len(self._highlighted_ranges) == 0:
- return
- if not any(map(lambda x: x.editing, self._highlighted_ranges)):
- self._highlighted_ranges[0].editing = True
-
- def _disambiguate_highlighted_ranges(self):
- """If any files have the same name, also display their folder name"""
- name_status: Dict[str, set] = {
- } # basename -> set of full paths with that basename
- for rif in self._highlighted_ranges:
- basename = os.path.basename(rif.range.filepath)
- if basename in name_status:
- name_status[basename].add(rif.range.filepath)
- else:
- name_status[basename] = {rif.range.filepath}
-
- for rif in self._highlighted_ranges:
- basename = os.path.basename(rif.range.filepath)
- if len(name_status[basename]) > 1:
- rif.display_name = os.path.join(
- os.path.basename(os.path.dirname(rif.range.filepath)), basename)
- else:
- rif.display_name = basename
-
async def handle_highlighted_code(self, range_in_files: List[RangeInFileWithContents]):
- # Filter out rifs from ~/.continue/diffs folder
- range_in_files = [
- rif for rif in range_in_files if not os.path.dirname(rif.filepath) == os.path.expanduser("~/.continue/diffs")]
-
- # Make sure all filepaths are relative to workspace
- workspace_path = self.continue_sdk.ide.workspace_directory
-
- # If not adding highlighted code
- if not self._adding_highlighted_code:
- if len(self._highlighted_ranges) == 1 and len(range_in_files) <= 1 and (len(range_in_files) == 0 or range_in_files[0].range.start == range_in_files[0].range.end):
- # If un-highlighting the range to edit, then remove the range
- self._highlighted_ranges = []
- await self.update_subscribers()
- elif len(range_in_files) > 0:
- # Otherwise, replace the current range with the new one
- # This is the first range to be highlighted
- self._highlighted_ranges = [HighlightedRangeContext(
- range=range_in_files[0], editing=True, pinned=False, display_name=os.path.basename(range_in_files[0].filepath))]
- await self.update_subscribers()
- return
-
- # If current range overlaps with any others, delete them and only keep the new range
- new_ranges = []
- for i, rif in enumerate(self._highlighted_ranges):
- found_overlap = False
- for new_rif in range_in_files:
- if rif.range.filepath == new_rif.filepath and rif.range.range.overlaps_with(new_rif.range):
- found_overlap = True
- break
-
- # Also don't allow multiple ranges in same file with same content. This is useless to the model, and avoids
- # the bug where cmd+f causes repeated highlights
- if rif.range.filepath == new_rif.filepath and rif.range.contents == new_rif.contents:
- found_overlap = True
- break
-
- if not found_overlap:
- new_ranges.append(rif)
-
- self._highlighted_ranges = new_ranges + [HighlightedRangeContext(
- range=rif, editing=False, pinned=False, display_name=os.path.basename(rif.filepath)
- ) for rif in range_in_files]
-
- self._make_sure_is_editing_range()
- self._disambiguate_highlighted_ranges()
+ # Add to context manager
+ await self.context_manager.context_providers["code"].handle_highlighted_code(
+ range_in_files)
await self.update_subscribers()
@@ -250,29 +191,16 @@ class Autopilot(ContinueBaseModel):
await self.update_subscribers()
- async def delete_context_at_indices(self, indices: List[int]):
- kept_ranges = []
- for i, rif in enumerate(self._highlighted_ranges):
- if i not in indices:
- kept_ranges.append(rif)
- self._highlighted_ranges = kept_ranges
-
- self._make_sure_is_editing_range()
-
+ async def delete_context_with_ids(self, ids: List[str]):
+ await self.context_manager.delete_context_with_ids(ids)
await self.update_subscribers()
async def toggle_adding_highlighted_code(self):
- self._adding_highlighted_code = not self._adding_highlighted_code
- await self.update_subscribers()
-
- async def set_editing_at_indices(self, indices: List[int]):
- for i in range(len(self._highlighted_ranges)):
- self._highlighted_ranges[i].editing = i in indices
+ self.context_manager.context_providers["code"].adding_highlighted_code = not self.context_manager.context_providers["code"].adding_highlighted_code
await self.update_subscribers()
- async def set_pinned_at_indices(self, indices: List[int]):
- for i in range(len(self._highlighted_ranges)):
- self._highlighted_ranges[i].pinned = i in indices
+ async def set_editing_at_ids(self, ids: List[str]):
+ self.context_manager.context_providers["code"].set_editing_at_ids(ids)
await self.update_subscribers()
async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]:
@@ -437,10 +365,6 @@ class Autopilot(ContinueBaseModel):
if len(self._main_user_input_queue) > 1:
return
- # Remove context unless pinned
- # self._highlighted_ranges = [
- # hr for hr in self._highlighted_ranges if hr.pinned]
-
# await self._request_halt()
# Just run the step that takes user input, and
# then up to the policy to decide how to deal with it.
@@ -456,3 +380,7 @@ class Autopilot(ContinueBaseModel):
await self._request_halt()
await self.reverse_to_index(index)
await self.run_from_step(UserInputStep(user_input=user_input))
+
+ async def select_context_item(self, id: str, query: str):
+ await self.context_manager.select_context_item(id, query)
+ await self.update_subscribers()
diff --git a/continuedev/src/continuedev/core/config.py b/continuedev/src/continuedev/core/config.py
index 54f15143..bb9ca323 100644
--- a/continuedev/src/continuedev/core/config.py
+++ b/continuedev/src/continuedev/core/config.py
@@ -1,6 +1,7 @@
import json
import os
from .main import Step
+from .context import ContextProvider
from pydantic import BaseModel, validator
from typing import List, Literal, Optional, Dict, Type, Union
import yaml
@@ -50,6 +51,8 @@ class ContinueConfig(BaseModel):
system_message: Optional[str] = None
azure_openai_info: Optional[AzureInfo] = None
+ context_providers: List[ContextProvider] = []
+
# Want to force these to be the slash commands for now
@validator('slash_commands', pre=True)
def default_slash_commands_validator(cls, v):
diff --git a/continuedev/src/continuedev/core/context.py b/continuedev/src/continuedev/core/context.py
new file mode 100644
index 00000000..67bba651
--- /dev/null
+++ b/continuedev/src/continuedev/core/context.py
@@ -0,0 +1,205 @@
+
+from abc import abstractmethod
+from typing import Dict, List
+import meilisearch
+from pydantic import BaseModel
+
+
+from .main import ChatMessage, ContextItem, ContextItemDescription, ContextItemId
+from ..server.meilisearch_server import check_meilisearch_running
+
+
+SEARCH_INDEX_NAME = "continue_context_items"
+
+
+class ContextProvider(BaseModel):
+ """
+ The ContextProvider class is a plugin that lets you provide new information to the LLM by typing '@'.
+ When you type '@', the context provider will be asked to populate a list of options.
+ These options will be updated on each keystroke.
+ When you hit enter on an option, the context provider will add that item to the autopilot's list of context (which is all stored in the ContextManager object).
+ """
+
+ title: str
+
+ selected_items: List[ContextItem] = []
+
+ async def get_selected_items(self) -> List[ContextItem]:
+ """
+ Returns all of the selected ContextItems.
+
+ Default implementation simply returns self.selected_items.
+
+ Other implementations may add an async processing step.
+ """
+ return self.selected_items
+
+ @abstractmethod
+ async def provide_context_items(self) -> List[ContextItem]:
+ """
+ Provide documents for search index. This is run on startup.
+
+ This is the only method that must be implemented.
+ """
+
+ async def get_chat_messages(self) -> List[ChatMessage]:
+ """
+ Returns all of the chat messages for the context provider.
+
+ Default implementation has a string template.
+ """
+ return [ChatMessage(role="user", content=f"{item.description.name}: {item.description.description}\n\n{item.content}", summary=item.description.description) for item in await self.get_selected_items()]
+
+ async def get_item(self, id: ContextItemId, query: str, search_client: meilisearch.Client) -> ContextItem:
+ """
+ Returns the ContextItem with the given id.
+
+ Default implementation uses the search index to get the item.
+ """
+ result = search_client.index(
+ SEARCH_INDEX_NAME).get_document(id.to_string())
+ return ContextItem(
+ description=ContextItemDescription(
+ name=result.name,
+ description=result.description,
+ id=id
+ ),
+ content=result.content
+ )
+
+ async def delete_context_with_ids(self, ids: List[ContextItemId]):
+ """
+ Deletes the ContextItems with the given IDs, lets ContextProviders recalculate.
+
+ Default implementation simply deletes those with the given ids.
+ """
+ id_strings = {id.to_string() for id in ids}
+ self.selected_items = list(
+ filter(lambda item: item.description.id.to_string() not in id_strings, self.selected_items))
+
+ async def clear_context(self):
+ """
+ Clears all context.
+
+ Default implementation simply clears the selected items.
+ """
+ self.selected_items = []
+
+ async def add_context_item(self, id: ContextItemId, query: str, search_client: meilisearch.Client):
+ """
+ Adds the given ContextItem to the list of ContextItems.
+
+ Default implementation simply appends the item, not allowing duplicates.
+
+ This method also allows you not to have to load all of the information until an item is selected.
+ """
+
+ # Don't add duplicate context
+ for item in self.selected_items:
+ if item.description.id.item_id == id.item_id:
+ return
+
+ new_item = await self.get_item(id, query, search_client)
+ self.selected_items.append(new_item)
+
+
+class ContextManager:
+ """
+ The context manager is responsible for storing the context to be passed to the LLM, including
+ - ContextItems (highlighted code, GitHub Issues, etc.)
+ - ChatMessages in the history
+ - System Message
+ - Functions
+
+ It is responsible for compiling all of this information into a single prompt without exceeding the token limit.
+ """
+
+ async def get_selected_items(self) -> List[ContextItem]:
+ """
+ Returns all of the selected ContextItems.
+ """
+ return sum([await provider.get_selected_items() for provider in self.context_providers.values()], [])
+
+ async def get_chat_messages(self) -> List[ChatMessage]:
+ """
+ Returns chat messages from each provider.
+ """
+ return sum([await provider.get_chat_messages() for provider in self.context_providers.values()], [])
+
+ search_client: meilisearch.Client
+
+ def __init__(self, context_providers: List[ContextProvider]):
+ self.search_client = meilisearch.Client('http://localhost:7700')
+
+ # If meilisearch isn't running, don't use any ContextProviders that might depend on it
+ if not check_meilisearch_running():
+ context_providers = list(
+ filter(lambda cp: cp.title == "code", context_providers))
+
+ self.context_providers = {
+ prov.title: prov for prov in context_providers}
+ self.provider_titles = {
+ provider.title for provider in context_providers}
+
+ async def load_index(self):
+ for _, provider in self.context_providers.items():
+ context_items = await provider.provide_context_items()
+ documents = [
+ {
+ "id": item.description.id.to_string(),
+ "name": item.description.name,
+ "description": item.description.description,
+ "content": item.content
+ }
+ for item in context_items
+ ]
+ if len(documents) > 0:
+ self.search_client.index(
+ SEARCH_INDEX_NAME).add_documents(documents)
+
+ # def compile_chat_messages(self, max_tokens: int) -> List[Dict]:
+ # """
+ # Compiles the chat prompt into a single string.
+ # """
+ # return compile_chat_messages(self.model, self.chat_history, max_tokens, self.prompt, self.functions, self.system_message)
+
+ async def select_context_item(self, id: str, query: str):
+ """
+ Selects the ContextItem with the given id.
+ """
+ id: ContextItemId = ContextItemId.from_string(id)
+ if id.provider_title not in self.provider_titles:
+ raise ValueError(
+ f"Context provider with title {id.provider_title} not found")
+
+ await self.context_providers[id.provider_title].add_context_item(id, query, self.search_client)
+
+ async def delete_context_with_ids(self, ids: List[str]):
+ """
+ Deletes the ContextItems with the given IDs, lets ContextProviders recalculate.
+ """
+
+ # Group by provider title
+ provider_title_to_ids: Dict[str, List[ContextItemId]] = {}
+ for id in ids:
+ id: ContextItemId = ContextItemId.from_string(id)
+ if id.provider_title not in provider_title_to_ids:
+ provider_title_to_ids[id.provider_title] = []
+ provider_title_to_ids[id.provider_title].append(id)
+
+ # Recalculate context for each updated provider
+ for provider_title, ids in provider_title_to_ids.items():
+ await self.context_providers[provider_title].delete_context_with_ids(ids)
+
+ async def clear_context(self):
+ """
+ Clears all context.
+ """
+ for provider in self.context_providers.values():
+ await self.context_providers[provider.title].clear_context()
+
+
+"""
+Should define "ArgsTransformer" and "PromptTransformer" classes for the different LLMs. A standard way for them to ingest the
+same format of prompts so you don't have to redo all of this logic.
+"""
diff --git a/continuedev/src/continuedev/core/context_manager.py b/continuedev/src/continuedev/core/context_manager.py
deleted file mode 100644
index 37905535..00000000
--- a/continuedev/src/continuedev/core/context_manager.py
+++ /dev/null
@@ -1,119 +0,0 @@
-
-from abc import ABC, abstractmethod, abstractproperty
-from ast import List
-from pydantic import BaseModel
-
-from ..libs.util.count_tokens import compile_chat_messages
-
-
-class ContextItemDescription(BaseModel):
- """
- A ContextItemDescription is a description of a ContextItem that is displayed to the user when they type '@'.
-
- The id can be used to retrieve the ContextItem from the ContextManager.
- """
- name: str
- description: str
- id: str
-
-
-class ContextItem(BaseModel):
- """
- A ContextItem is a single item that is stored in the ContextManager.
- """
- description: ContextItemDescription
- content: str
-
-
-class ContextManager(ABC):
- """
- The context manager is responsible for storing the context to be passed to the LLM, including
- - ContextItems (highlighted code, GitHub Issues, etc.)
- - ChatMessages in the history
- - System Message
- - Functions
-
- It is responsible for compiling all of this information into a single prompt without exceeding the token limit.
- """
-
- def compile_chat_messages(self, max_tokens: int) -> List[Dict]:
- """
- Compiles the chat prompt into a single string.
- """
- return compile_chat_messages(self.model, self.chat_history, max_tokens, self.prompt, self.functions, self.system_message)
-
-
-"""
-Should define "ArgsTransformer" and "PromptTransformer" classes for the different LLMs. A standard way for them to ingest the
-same format of prompts so you don't have to redo all of this logic.
-"""
-
-
-class ContextProvider(ABC):
- """
- The ContextProvider class is a plugin that lets you provide new information to the LLM by typing '@'.
- When you type '@', the context provider will be asked to populate a list of options.
- These options will be updated on each keystroke.
- When you hit enter on an option, the context provider will add that item to the autopilot's list of context (which is all stored in the ContextManager object).
- """
-
- title: str
-
- @abstractmethod
- async def load(self):
- """
- Loads the ContextProvider, possibly reading persisted data from disk. This will be called on startup.
- """
-
- @abstractmethod
- async def save(self):
- """
- Saves the ContextProvider, possibly writing persisted data to disk. This will be called upon cache refresh.
- """
-
- @abstractmethod
- async def refresh_cache(self):
- """
- Refreshes the cache of items. This will be called on startup and periodically.
- """
-
- @abstractmethod
- async def get_item_descriptions(self, query: str) -> List[ContextItemDescription]:
- """
- Returns a list of options that should be displayed to the user.
- """
-
- @abstractmethod
- async def get_item(self, id: str) -> ContextItem:
- """
- Returns the ContextItem with the given id. This allows you not to have to load all of the information until an item is selected.
- """
-
- @abstractmethod
- async def should_refresh(self) -> bool:
- """
- Returns whether the ContextProvider should be refreshed.
-
- For example, embeddings might need to be recalculated after commits,
- or GitHub issues might need to be refreshed after a new issue is created.
-
- This method will be called every startup? Every once in a while? Every hour?
- User defined? Maybe just have a schedule instead of this method.
- """
-
-
-class GitHubIssuesContextProvider(ContextProvider):
- """
- The GitHubIssuesContextProvider is a ContextProvider that allows you to search GitHub issues in a repo.
- """
-
- title = "issues"
-
- def __init__(self, repo: str):
- self.repo = repo
-
- async def get_item_descriptions(self, query: str) -> List[ContextItemDescription]:
- pass
-
- async def get_item(self, id: str) -> ContextItem:
- pass
diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py
index 50d01f8d..6c6adccc 100644
--- a/continuedev/src/continuedev/core/main.py
+++ b/continuedev/src/continuedev/core/main.py
@@ -1,12 +1,11 @@
import json
-from textwrap import dedent
-from typing import Callable, Coroutine, Dict, Generator, List, Literal, Tuple, Union
+from typing import Coroutine, Dict, List, Literal, Union
+from pydantic.schema import schema
+
-from ..models.filesystem import RangeInFileWithContents
from ..models.main import ContinueBaseModel
-from pydantic import validator
+from pydantic import BaseModel, validator
from .observation import Observation
-from pydantic.schema import schema
ChatMessageRole = Literal["assistant", "user", "system", "function"]
@@ -201,12 +200,48 @@ class SlashCommandDescription(ContinueBaseModel):
description: str
-class HighlightedRangeContext(ContinueBaseModel):
- """Context for a highlighted range"""
- range: RangeInFileWithContents
- editing: bool
- pinned: bool
- display_name: str
+class ContextItemId(BaseModel):
+ """
+ A ContextItemId is a unique identifier for a ContextItem.
+ """
+ provider_title: str
+ item_id: str
+
+ def to_string(self) -> str:
+ return f"{self.provider_title}-{self.item_id}"
+
+ @staticmethod
+ def from_string(string: str) -> 'ContextItemId':
+ provider_title, item_id = string.split('-')
+ return ContextItemId(provider_title=provider_title, item_id=item_id)
+
+
+class ContextItemDescription(BaseModel):
+ """
+ A ContextItemDescription is a description of a ContextItem that is displayed to the user when they type '@'.
+
+ The id can be used to retrieve the ContextItem from the ContextManager.
+ """
+ name: str
+ description: str
+ id: ContextItemId
+
+
+class ContextItem(BaseModel):
+ """
+ A ContextItem is a single item that is stored in the ContextManager.
+ """
+ description: ContextItemDescription
+ content: str
+
+ @validator('content', pre=True)
+ def content_must_be_string(cls, v):
+ if v is None:
+ return ''
+ return v
+
+ editing: bool = False
+ editable: bool = False
class FullState(ContinueBaseModel):
@@ -215,9 +250,9 @@ class FullState(ContinueBaseModel):
active: bool
user_input_queue: List[str]
default_model: str
- highlighted_ranges: List[HighlightedRangeContext]
slash_commands: List[SlashCommandDescription]
adding_highlighted_code: bool
+ selected_context_items: List[ContextItem]
class ContinueSDK:
diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py
index 4100efa6..59f33707 100644
--- a/continuedev/src/continuedev/core/sdk.py
+++ b/continuedev/src/continuedev/core/sdk.py
@@ -3,8 +3,10 @@ from functools import cached_property
from typing import Coroutine, Dict, Union
import os
+
from ..steps.core.core import DefaultModelEditCodeStep
from ..models.main import Range
+from .context import ContextItem
from .abstract_sdk import AbstractContinueSDK
from .config import ContinueConfig, load_config, load_global_config, update_global_config
from ..models.filesystem_edit import FileEdit, FileSystemEdit, AddFile, DeleteFile, AddDirectory, DeleteDirectory
@@ -289,28 +291,13 @@ class ContinueSDK(AbstractContinueSDK):
async def get_chat_context(self) -> List[ChatMessage]:
history_context = self.history.to_chat_history()
- highlighted_code = [
- hr.range for hr in self.__autopilot._highlighted_ranges]
-
- preface = "The following code is highlighted"
-
- # If no higlighted ranges, use first file as context
- if len(highlighted_code) == 0:
- preface = "The following file is open"
- visible_files = await self.ide.getVisibleFiles()
- if len(visible_files) > 0:
- content = await self.ide.readFile(visible_files[0])
- highlighted_code = [
- RangeInFileWithContents.from_entire_file(visible_files[0], content)]
-
- for rif in highlighted_code:
- msg = ChatMessage(content=f"{preface} ({rif.filepath}):\n```\n{rif.contents}\n```",
- role="user", summary=f"{preface}: {rif.filepath}")
-
- # Don't insert after latest user message or function call
- i = -1
- if len(history_context) > 0 and (history_context[i].role == "user" or history_context[i].role == "function"):
- i -= 1
+
+ context_messages: List[ChatMessage] = await self.__autopilot.context_manager.get_chat_messages()
+
+ # Insert at the end, but don't insert after latest user message or function call
+ i = -2 if (len(history_context) > 0 and (
+ history_context[-1].role == "user" or history_context[-1].role == "function")) else -1
+ for msg in context_messages:
history_context.insert(i, msg)
return history_context
diff --git a/continuedev/src/continuedev/libs/context_providers/highlighted_code_context_provider.py b/continuedev/src/continuedev/libs/context_providers/highlighted_code_context_provider.py
new file mode 100644
index 00000000..23d4fc86
--- /dev/null
+++ b/continuedev/src/continuedev/libs/context_providers/highlighted_code_context_provider.py
@@ -0,0 +1,191 @@
+import os
+from typing import Any, Dict, List
+
+import meilisearch
+from ...core.main import ChatMessage
+from ...models.filesystem import RangeInFile, RangeInFileWithContents
+from ...core.context import ContextItem, ContextItemDescription, ContextItemId
+from pydantic import BaseModel
+
+
+class HighlightedRangeContextItem(BaseModel):
+ rif: RangeInFileWithContents
+ item: ContextItem
+
+
+class HighlightedCodeContextProvider(BaseModel):
+ """
+ The ContextProvider class is a plugin that lets you provide new information to the LLM by typing '@'.
+ When you type '@', the context provider will be asked to populate a list of options.
+ These options will be updated on each keystroke.
+ When you hit enter on an option, the context provider will add that item to the autopilot's list of context (which is all stored in the ContextManager object).
+ """
+
+ title = "code"
+
+ ide: Any # IdeProtocolServer
+
+ highlighted_ranges: List[HighlightedRangeContextItem] = []
+ adding_highlighted_code: bool = False
+
+ should_get_fallback_context_item: bool = True
+ last_added_fallback: bool = False
+
+ async def _get_fallback_context_item(self) -> HighlightedRangeContextItem:
+ if not self.should_get_fallback_context_item:
+ return None
+
+ visible_files = await self.ide.getVisibleFiles()
+ if len(visible_files) > 0:
+ content = await self.ide.readFile(visible_files[0])
+ rif = RangeInFileWithContents.from_entire_file(
+ visible_files[0], content)
+
+ item = self._rif_to_context_item(rif, 0, True)
+ item.description.name = self._rif_to_name(
+ rif, show_line_nums=False)
+
+ self.last_added_fallback = True
+ return HighlightedRangeContextItem(rif=rif, item=item)
+
+ return None
+
+ async def get_selected_items(self) -> List[ContextItem]:
+ items = [hr.item for hr in self.highlighted_ranges]
+
+ if len(items) == 0 and (fallback_item := await self._get_fallback_context_item()):
+ items = [fallback_item.item]
+
+ return items
+
+ async def get_chat_messages(self) -> List[ContextItem]:
+ ranges = self.highlighted_ranges
+ if len(ranges) == 0 and (fallback_item := await self._get_fallback_context_item()):
+ ranges = [fallback_item]
+
+ return [ChatMessage(
+ role="user",
+ content=f"Code in this file is highlighted ({r.rif.filepath}):\n```\n{r.rif.contents}\n```",
+ summary=f"Code in this file is highlighted: {r.rif.filepath}"
+ ) for r in ranges]
+
+ def _make_sure_is_editing_range(self):
+ """If none of the highlighted ranges are currently being edited, the first should be selected"""
+ if len(self.highlighted_ranges) == 0:
+ return
+ if not any(map(lambda x: x.item.editing, self.highlighted_ranges)):
+ self.highlighted_ranges[0].item.editing = True
+
+ def _disambiguate_highlighted_ranges(self):
+ """If any files have the same name, also display their folder name"""
+ name_status: Dict[str, set] = {
+ } # basename -> set of full paths with that basename
+ for hr in self.highlighted_ranges:
+ basename = os.path.basename(hr.rif.filepath)
+ if basename in name_status:
+ name_status[basename].add(hr.rif.filepath)
+ else:
+ name_status[basename] = {hr.rif.filepath}
+
+ for hr in self.highlighted_ranges:
+ if len(name_status[basename]) > 1:
+ hr.item.description.name = self._rif_to_name(hr.rif, display_filename=os.path.join(
+ os.path.basename(os.path.dirname(hr.rif.filepath)), basename))
+ else:
+ hr.item.description.name = self._rif_to_name(
+ hr.rif, display_filename=basename)
+
+ async def provide_context_items(self) -> List[ContextItem]:
+ return []
+
+ async def delete_context_with_ids(self, ids: List[ContextItemId]) -> List[ContextItem]:
+ indices_to_delete = [
+ int(id.item_id) for id in ids
+ ]
+
+ kept_ranges = []
+ for i, hr in enumerate(self.highlighted_ranges):
+ if i not in indices_to_delete:
+ kept_ranges.append(hr)
+ self.highlighted_ranges = kept_ranges
+
+ self._make_sure_is_editing_range()
+
+ if len(self.highlighted_ranges) == 0 and self.last_added_fallback:
+ self.should_get_fallback_context_item = False
+
+ return [hr.item for hr in self.highlighted_ranges]
+
+ def _rif_to_name(self, rif: RangeInFileWithContents, display_filename: str = None, show_line_nums: bool = True) -> str:
+ line_nums = f" ({rif.range.start.line + 1}-{rif.range.end.line + 1})" if show_line_nums else ""
+ return f"{display_filename or os.path.basename(rif.filepath)}{line_nums}"
+
+ def _rif_to_context_item(self, rif: RangeInFileWithContents, idx: int, editing: bool) -> ContextItem:
+ return ContextItem(
+ description=ContextItemDescription(
+ name=self._rif_to_name(rif),
+ description=rif.filepath,
+ id=ContextItemId(
+ provider_title=self.title,
+ item_id=str(idx)
+ )
+ ),
+ content=rif.contents,
+ editing=editing,
+ editable=True
+ )
+
+ async def handle_highlighted_code(self, range_in_files: List[RangeInFileWithContents]):
+ self.should_get_fallback_context_item = True
+ self.last_added_fallback = False
+
+ # Filter out rifs from ~/.continue/diffs folder
+ range_in_files = [
+ rif for rif in range_in_files if not os.path.dirname(rif.filepath) == os.path.expanduser("~/.continue/diffs")]
+
+ # If not adding highlighted code
+ if not self.adding_highlighted_code:
+ if len(self.highlighted_ranges) == 1 and len(range_in_files) <= 1 and (len(range_in_files) == 0 or range_in_files[0].range.start == range_in_files[0].range.end):
+ # If un-highlighting the range to edit, then remove the range
+ self.highlighted_ranges = []
+ elif len(range_in_files) > 0:
+ # Otherwise, replace the current range with the new one
+ # This is the first range to be highlighted
+ self.highlighted_ranges = [
+ HighlightedRangeContextItem(
+ rif=range_in_files[0],
+ item=self._rif_to_context_item(range_in_files[0], 0, True))]
+
+ return
+
+ # If current range overlaps with any others, delete them and only keep the new range
+ new_ranges = []
+ for i, hr in enumerate(self.highlighted_ranges):
+ found_overlap = False
+ for new_rif in range_in_files:
+ if hr.rif.filepath == new_rif.filepath and hr.rif.range.overlaps_with(new_rif.range):
+ found_overlap = True
+ break
+
+ # Also don't allow multiple ranges in same file with same content. This is useless to the model, and avoids
+ # the bug where cmd+f causes repeated highlights
+ if hr.rif.filepath == new_rif.filepath and hr.rif.contents == new_rif.contents:
+ found_overlap = True
+ break
+
+ if not found_overlap:
+ new_ranges.append(HighlightedRangeContextItem(rif=hr.rif, item=self._rif_to_context_item(
+ hr.rif, len(new_ranges), False)))
+
+ self.highlighted_ranges = new_ranges + [HighlightedRangeContextItem(rif=rif, item=self._rif_to_context_item(
+ rif, len(new_ranges) + idx, False)) for idx, rif in enumerate(range_in_files)]
+
+ self._make_sure_is_editing_range()
+ self._disambiguate_highlighted_ranges()
+
+ async def set_editing_at_ids(self, ids: List[str]):
+ for hr in self.highlighted_ranges:
+ hr.item.editing = hr.item.description.id.to_string() in ids
+
+ async def add_context_item(self, id: ContextItemId, query: str, search_client: meilisearch.Client, prev: List[ContextItem] = None) -> List[ContextItem]:
+ raise NotImplementedError()
diff --git a/continuedev/src/continuedev/libs/util/paths.py b/continuedev/src/continuedev/libs/util/paths.py
index fddef887..d6ce13b3 100644
--- a/continuedev/src/continuedev/libs/util/paths.py
+++ b/continuedev/src/continuedev/libs/util/paths.py
@@ -2,16 +2,26 @@ import os
from ..constants.main import CONTINUE_SESSIONS_FOLDER, CONTINUE_GLOBAL_FOLDER, CONTINUE_SERVER_FOLDER
-def getGlobalFolderPath():
- return os.path.join(os.path.expanduser("~"), CONTINUE_GLOBAL_FOLDER)
+def getGlobalFolderPath():
+ path = os.path.join(os.path.expanduser("~"), CONTINUE_GLOBAL_FOLDER)
+ os.makedirs(path, exist_ok=True)
+ return path
def getSessionsFolderPath():
- return os.path.join(getGlobalFolderPath(), CONTINUE_SESSIONS_FOLDER)
+ path = os.path.join(getGlobalFolderPath(), CONTINUE_SESSIONS_FOLDER)
+ os.makedirs(path, exist_ok=True)
+ return path
+
def getServerFolderPath():
- return os.path.join(getGlobalFolderPath(), CONTINUE_SERVER_FOLDER)
+ path = os.path.join(getGlobalFolderPath(), CONTINUE_SERVER_FOLDER)
+ os.makedirs(path, exist_ok=True)
+ return path
+
def getSessionFilePath(session_id: str):
- return os.path.join(getSessionsFolderPath(), f"{session_id}.json") \ No newline at end of file
+ path = os.path.join(getSessionsFolderPath(), f"{session_id}.json")
+ os.makedirs(os.path.dirname(path), exist_ok=True)
+ return path
diff --git a/continuedev/src/continuedev/models/generate_json_schema.py b/continuedev/src/continuedev/models/generate_json_schema.py
index 6cebf429..06614984 100644
--- a/continuedev/src/continuedev/models/generate_json_schema.py
+++ b/continuedev/src/continuedev/models/generate_json_schema.py
@@ -2,6 +2,7 @@ from .main import *
from .filesystem import RangeInFile, FileEdit
from .filesystem_edit import FileEditWithFullContents
from ..core.main import History, HistoryNode, FullState
+from ..core.context import ContextItem
from pydantic import schema_json_of
import os
@@ -13,6 +14,8 @@ MODELS_TO_GENERATE = [
FileEditWithFullContents
] + [
History, HistoryNode, FullState
+] + [
+ ContextItem
]
RENAMES = {
diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py
index ae57c0b6..36b2f3fa 100644
--- a/continuedev/src/continuedev/server/gui.py
+++ b/continuedev/src/continuedev/server/gui.py
@@ -91,25 +91,19 @@ class GUIProtocolServer(AbstractGUIProtocolServer):
self.on_clear_history()
elif message_type == "delete_at_index":
self.on_delete_at_index(data["index"])
- elif message_type == "delete_context_at_indices":
- self.on_delete_context_at_indices(data["indices"])
+ elif message_type == "delete_context_with_ids":
+ self.on_delete_context_with_ids(data["ids"])
elif message_type == "toggle_adding_highlighted_code":
self.on_toggle_adding_highlighted_code()
elif message_type == "set_editing_at_indices":
self.on_set_editing_at_indices(data["indices"])
- elif message_type == "set_pinned_at_indices":
- self.on_set_pinned_at_indices(data["indices"])
elif message_type == "show_logs_at_index":
self.on_show_logs_at_index(data["index"])
+ elif message_type == "select_context_item":
+ self.select_context_item(data["id"], data["query"])
except Exception as e:
print(e)
- async def send_state_update(self):
- state = self.session.autopilot.get_full_state().dict()
- await self._send_json("state_update", {
- "state": state
- })
-
def on_main_input(self, input: str):
# Do something with user input
create_async_task(self.session.autopilot.accept_user_input(
@@ -144,10 +138,10 @@ class GUIProtocolServer(AbstractGUIProtocolServer):
create_async_task(self.session.autopilot.delete_at_index(
index), self.session.autopilot.continue_sdk.ide.unique_id)
- def on_delete_context_at_indices(self, indices: List[int]):
+ def on_delete_context_with_ids(self, ids: List[str]):
create_async_task(
- self.session.autopilot.delete_context_at_indices(
- indices), self.session.autopilot.continue_sdk.ide.unique_id
+ self.session.autopilot.delete_context_with_ids(
+ ids), self.session.autopilot.continue_sdk.ide.unique_id
)
def on_toggle_adding_highlighted_code(self):
@@ -162,18 +156,17 @@ class GUIProtocolServer(AbstractGUIProtocolServer):
indices), self.session.autopilot.continue_sdk.ide.unique_id
)
- def on_set_pinned_at_indices(self, indices: List[int]):
- create_async_task(
- self.session.autopilot.set_pinned_at_indices(
- indices), self.session.autopilot.continue_sdk.ide.unique_id
- )
-
def on_show_logs_at_index(self, index: int):
name = f"continue_logs.txt"
logs = "\n\n############################################\n\n".join(
["This is a log of the exact prompt/completion pairs sent/received from the LLM during this step"] + self.session.autopilot.continue_sdk.history.timeline[index].logs)
create_async_task(
- self.session.autopilot.ide.showVirtualFile(name, logs))
+ self.session.autopilot.ide.showVirtualFile(name, logs), self.session.autopilot.continue_sdk.ide.unique_id)
+
+ def select_context_item(self, id: str, query: str):
+ """Called when user selects an item from the dropdown"""
+ create_async_task(
+ self.session.autopilot.select_context_item(id, query), self.session.autopilot.continue_sdk.ide.unique_id)
@router.websocket("/ws")
@@ -188,7 +181,7 @@ async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(we
protocol.websocket = websocket
# Update any history that may have happened before connection
- await protocol.send_state_update()
+ await protocol.session.autopilot.update_subscribers()
while AppStatus.should_exit is False:
message = await websocket.receive_text()
@@ -214,5 +207,5 @@ async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(we
if websocket.client_state != WebSocketState.DISCONNECTED:
await websocket.close()
- session_manager.persist_session(session.session_id)
+ await session_manager.persist_session(session.session_id)
session_manager.remove_session(session.session_id)
diff --git a/continuedev/src/continuedev/server/gui_protocol.py b/continuedev/src/continuedev/server/gui_protocol.py
index 9766fcd0..fb230216 100644
--- a/continuedev/src/continuedev/server/gui_protocol.py
+++ b/continuedev/src/continuedev/server/gui_protocol.py
@@ -1,6 +1,8 @@
from typing import Any, Dict, List
from abc import ABC, abstractmethod
+from ..core.context import ContextItem
+
class AbstractGUIProtocolServer(ABC):
@abstractmethod
@@ -24,10 +26,6 @@ class AbstractGUIProtocolServer(ABC):
"""Called when the user inputs a step"""
@abstractmethod
- async def send_state_update(self, state: dict):
- """Send a state update to the client"""
-
- @abstractmethod
def on_retry_at_index(self, index: int):
"""Called when the user requests a retry at a previous index"""
@@ -42,3 +40,7 @@ class AbstractGUIProtocolServer(ABC):
@abstractmethod
def on_delete_at_index(self, index: int):
"""Called when the user requests to delete a step at a given index"""
+
+ @abstractmethod
+ def select_context_item(self, id: str, query: str):
+ """Called when user selects an item from the dropdown"""
diff --git a/continuedev/src/continuedev/server/main.py b/continuedev/src/continuedev/server/main.py
index 42dc0cc1..7ee64041 100644
--- a/continuedev/src/continuedev/server/main.py
+++ b/continuedev/src/continuedev/server/main.py
@@ -1,15 +1,20 @@
+import asyncio
+import subprocess
import time
+import meilisearch
import psutil
import os
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
-from .ide import router as ide_router
-from .gui import router as gui_router
-from .session_manager import session_manager
import atexit
import uvicorn
import argparse
+from .ide import router as ide_router
+from .gui import router as gui_router
+from .session_manager import session_manager
+from .meilisearch_server import start_meilisearch
+
app = FastAPI()
app.include_router(ide_router)
@@ -41,15 +46,20 @@ args = parser.parse_args()
# log_file = open('output.log', 'a')
# sys.stdout = log_file
-
def run_server():
uvicorn.run(app, host="0.0.0.0", port=args.port)
-def cleanup():
+async def cleanup_coroutine():
print("Cleaning up sessions")
for session_id in session_manager.sessions:
- session_manager.persist_session(session_id)
+ await session_manager.persist_session(session_id)
+
+
+def cleanup():
+ loop = asyncio.new_event_loop()
+ loop.run_until_complete(cleanup_coroutine())
+ loop.close()
def cpu_usage_report():
@@ -77,6 +87,12 @@ if __name__ == "__main__":
# cpu_thread = threading.Thread(target=cpu_usage_loop)
# cpu_thread.start()
+ try:
+ start_meilisearch()
+ except Exception as e:
+ print("Failed to start MeiliSearch")
+ print(e)
+
run_server()
except Exception as e:
cleanup()
diff --git a/continuedev/src/continuedev/server/meilisearch_server.py b/continuedev/src/continuedev/server/meilisearch_server.py
new file mode 100644
index 00000000..419f081f
--- /dev/null
+++ b/continuedev/src/continuedev/server/meilisearch_server.py
@@ -0,0 +1,56 @@
+import os
+import subprocess
+
+import meilisearch
+from ..libs.util.paths import getServerFolderPath
+
+
+def check_meilisearch_installed() -> bool:
+ """
+ Checks if MeiliSearch is installed.
+ """
+
+ serverPath = getServerFolderPath()
+ meilisearchPath = os.path.join(serverPath, "meilisearch")
+
+ return os.path.exists(meilisearchPath)
+
+
+def check_meilisearch_running() -> bool:
+ """
+ Checks if MeiliSearch is running.
+ """
+
+ try:
+ client = meilisearch.Client('http://localhost:7700')
+ resp = client.health()
+ if resp["status"] != "available":
+ return False
+ return True
+ except Exception:
+ return False
+
+
+def start_meilisearch():
+ """
+ Starts the MeiliSearch server, wait for it.
+ """
+
+ # Doesn't work on windows for now
+ if not os.name == "posix":
+ return
+
+ serverPath = getServerFolderPath()
+
+ # Check if MeiliSearch is installed
+ if not check_meilisearch_installed():
+ # Download MeiliSearch
+ print("Downloading MeiliSearch...")
+ subprocess.run(
+ f"curl -L https://install.meilisearch.com | sh", shell=True, check=True, cwd=serverPath)
+
+ # Check if MeiliSearch is running
+ if not check_meilisearch_running():
+ print("Starting MeiliSearch...")
+ subprocess.Popen(["./meilisearch"], cwd=serverPath, stdout=subprocess.DEVNULL,
+ stderr=subprocess.STDOUT, close_fds=True, start_new_session=True)
diff --git a/continuedev/src/continuedev/server/session_manager.py b/continuedev/src/continuedev/server/session_manager.py
index 90172a4e..96daf92c 100644
--- a/continuedev/src/continuedev/server/session_manager.py
+++ b/continuedev/src/continuedev/server/session_manager.py
@@ -74,7 +74,7 @@ class SessionManager:
async def on_update(state: FullState):
await session_manager.send_ws_data(session_id, "state_update", {
- "state": autopilot.get_full_state().dict()
+ "state": state.dict()
})
autopilot.on_update(on_update)
@@ -84,9 +84,9 @@ class SessionManager:
def remove_session(self, session_id: str):
del self.sessions[session_id]
- def persist_session(self, session_id: str):
+ async def persist_session(self, session_id: str):
"""Save the session's FullState as a json file"""
- full_state = self.sessions[session_id].autopilot.get_full_state()
+ full_state = await self.sessions[session_id].autopilot.get_full_state()
if not os.path.exists(getSessionsFolderPath()):
os.mkdir(getSessionsFolderPath())
with open(getSessionFilePath(session_id), "w") as f:
diff --git a/extension/react-app/package-lock.json b/extension/react-app/package-lock.json
index 13e02e86..fa7834f1 100644
--- a/extension/react-app/package-lock.json
+++ b/extension/react-app/package-lock.json
@@ -13,6 +13,7 @@
"@types/vscode-webview": "^1.57.1",
"@uiw/react-markdown-preview": "^4.1.13",
"downshift": "^7.6.0",
+ "meilisearch": "^0.33.0",
"posthog-js": "^1.58.0",
"prismjs": "^1.29.0",
"react": "^18.2.0",
@@ -1423,6 +1424,14 @@
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-2.0.4.tgz",
"integrity": "sha512-y/ZA3BGnxoM/QHHQ2Uy49CLtnWPbt4tTPpEEZiEmmiWBFKjej7nEyH8Ryz54jH0MLXflUYA3Er2zUxPSJu5R+g=="
},
+ "node_modules/cross-fetch": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz",
+ "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==",
+ "dependencies": {
+ "node-fetch": "^2.6.12"
+ }
+ },
"node_modules/css-color-keywords": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
@@ -2500,6 +2509,14 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/meilisearch": {
+ "version": "0.33.0",
+ "resolved": "https://registry.npmjs.org/meilisearch/-/meilisearch-0.33.0.tgz",
+ "integrity": "sha512-bYPb9WyITnJfzf92e7QFK8Rc50DmshFWxypXCs3ILlpNh8pT15A7KSu9Xgnnk/K3G/4vb3wkxxtFS4sxNkWB8w==",
+ "dependencies": {
+ "cross-fetch": "^3.1.6"
+ }
+ },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -3092,6 +3109,25 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
+ "node_modules/node-fetch": {
+ "version": "2.6.12",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
+ "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
"node_modules/node-releases": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
@@ -4115,6 +4151,11 @@
"node": ">=8.0"
}
},
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
"node_modules/trim-lines": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
@@ -4423,6 +4464,20 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@@ -5325,6 +5380,14 @@
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-2.0.4.tgz",
"integrity": "sha512-y/ZA3BGnxoM/QHHQ2Uy49CLtnWPbt4tTPpEEZiEmmiWBFKjej7nEyH8Ryz54jH0MLXflUYA3Er2zUxPSJu5R+g=="
},
+ "cross-fetch": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz",
+ "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==",
+ "requires": {
+ "node-fetch": "^2.6.12"
+ }
+ },
"css-color-keywords": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
@@ -6098,6 +6161,14 @@
"@types/mdast": "^3.0.0"
}
},
+ "meilisearch": {
+ "version": "0.33.0",
+ "resolved": "https://registry.npmjs.org/meilisearch/-/meilisearch-0.33.0.tgz",
+ "integrity": "sha512-bYPb9WyITnJfzf92e7QFK8Rc50DmshFWxypXCs3ILlpNh8pT15A7KSu9Xgnnk/K3G/4vb3wkxxtFS4sxNkWB8w==",
+ "requires": {
+ "cross-fetch": "^3.1.6"
+ }
+ },
"merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -6434,6 +6505,14 @@
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"dev": true
},
+ "node-fetch": {
+ "version": "2.6.12",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
+ "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
+ "requires": {
+ "whatwg-url": "^5.0.0"
+ }
+ },
"node-releases": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
@@ -7129,6 +7208,11 @@
"is-number": "^7.0.0"
}
},
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
"trim-lines": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
@@ -7315,6 +7399,20 @@
"resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
"integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="
},
+ "webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
diff --git a/extension/react-app/package.json b/extension/react-app/package.json
index 704f520a..e5f5a329 100644
--- a/extension/react-app/package.json
+++ b/extension/react-app/package.json
@@ -14,6 +14,7 @@
"@types/vscode-webview": "^1.57.1",
"@uiw/react-markdown-preview": "^4.1.13",
"downshift": "^7.6.0",
+ "meilisearch": "^0.33.0",
"posthog-js": "^1.58.0",
"prismjs": "^1.29.0",
"react": "^18.2.0",
diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx
index f327e3a3..22a6d000 100644
--- a/extension/react-app/src/components/ComboBox.tsx
+++ b/extension/react-app/src/components/ComboBox.tsx
@@ -1,4 +1,9 @@
-import React, { useEffect, useImperativeHandle, useState } from "react";
+import React, {
+ useContext,
+ useEffect,
+ useImperativeHandle,
+ useState,
+} from "react";
import { useCombobox } from "downshift";
import styled from "styled-components";
import {
@@ -8,13 +13,15 @@ import {
vscBackground,
vscForeground,
} from ".";
-import CodeBlock from "./CodeBlock";
import PillButton from "./PillButton";
import HeaderButtonWithText from "./HeaderButtonWithText";
import { DocumentPlus } from "@styled-icons/heroicons-outline";
-import { HighlightedRangeContext } from "../../../schema/FullState";
+import { ContextItem } from "../../../schema/FullState";
import { postVscMessage } from "../vscode";
-import { getMetaKeyLabel } from "../util";
+import { GUIClientContext } from "../App";
+import { MeiliSearch } from "meilisearch";
+
+const SEARCH_INDEX_NAME = "continue_context_items";
// #region styled components
const mainInputFontSize = 13;
@@ -64,14 +71,14 @@ const Ul = styled.ul<{
hidden: boolean;
showAbove: boolean;
ulHeightPixels: number;
+ inputBoxHeight?: string;
}>`
${(props) =>
props.showAbove
? `transform: translateY(-${props.ulHeightPixels + 8}px);`
- : `transform: translateY(${2 * mainInputFontSize}px);`}
+ : `transform: translateY(${5 * mainInputFontSize}px);`}
position: absolute;
background: ${vscBackground};
- background-color: ${secondaryDark};
color: ${vscForeground};
max-height: ${UlMaxHeight}px;
width: calc(100% - 16px);
@@ -80,15 +87,9 @@ const Ul = styled.ul<{
padding: 0;
${({ hidden }) => hidden && "display: none;"}
border-radius: ${defaultBorderRadius};
- outline: 0.5px solid gray;
+ outline: 1px solid ${lightGray};
z-index: 2;
- // Get rid of scrollbar and its padding
- scrollbar-width: none;
-ms-overflow-style: none;
- &::-webkit-scrollbar {
- width: 0px;
- background: transparent; /* make scrollbar transparent */
- }
`;
const Li = styled.li<{
@@ -96,49 +97,90 @@ const Li = styled.li<{
selected: boolean;
isLastItem: boolean;
}>`
- background-color: ${secondaryDark};
- ${({ highlighted }) => highlighted && "background: #ff000066;"}
+ background-color: ${({ highlighted }) =>
+ highlighted ? lightGray : secondaryDark};
+ ${({ highlighted }) => highlighted && `background: ${vscBackground};`}
${({ selected }) => selected && "font-weight: bold;"}
padding: 0.5rem 0.75rem;
display: flex;
flex-direction: column;
${({ isLastItem }) => isLastItem && "border-bottom: 1px solid gray;"}
- border-top: 1px solid gray;
+ /* border-top: 1px solid gray; */
cursor: pointer;
`;
// #endregion
interface ComboBoxProps {
- items: { name: string; description: string }[];
+ items: { name: string; description: string; id?: string }[];
onInputValueChange: (inputValue: string) => void;
disabled?: boolean;
onEnter: (e: React.KeyboardEvent<HTMLInputElement>) => void;
- highlightedCodeSections: HighlightedRangeContext[];
- deleteContextItems: (indices: number[]) => void;
- onTogglePin: () => void;
+ selectedContextItems: ContextItem[];
onToggleAddContext: () => void;
addingHighlightedCode: boolean;
}
const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
+ const searchClient = new MeiliSearch({ host: "http://127.0.0.1:7700" });
+ const client = useContext(GUIClientContext);
+
const [history, setHistory] = React.useState<string[]>([]);
// The position of the current command you are typing now, so the one that will be appended to history once you press enter
const [positionInHistory, setPositionInHistory] = React.useState<number>(0);
const [items, setItems] = React.useState(props.items);
- const [highlightedCodeSections, setHighlightedCodeSections] = React.useState(
- props.highlightedCodeSections || []
- );
+
const inputRef = React.useRef<HTMLInputElement>(null);
+ const [inputBoxHeight, setInputBoxHeight] = useState<string | undefined>(
+ undefined
+ );
- useEffect(() => {
- setHighlightedCodeSections(props.highlightedCodeSections || []);
- }, [props.highlightedCodeSections]);
+ // Whether the current input follows an '@' and should be treated as context query
+ const [currentlyInContextQuery, setCurrentlyInContextQuery] = useState(false);
const { getInputProps, ...downshiftProps } = useCombobox({
- onInputValueChange({ inputValue }) {
+ onSelectedItemChange: ({ selectedItem }) => {
+ if (selectedItem?.id) {
+ // Get the query from the input value
+ const segs = downshiftProps.inputValue.split("@");
+ const query = segs[segs.length - 1];
+ const restOfInput = segs.splice(0, segs.length - 1).join("@");
+
+ // Tell server the context item was selected
+ client?.selectContextItem(selectedItem.id, query);
+
+ // Remove the '@' and the context query from the input
+ if (downshiftProps.inputValue.includes("@")) {
+ downshiftProps.setInputValue(restOfInput);
+ }
+ }
+ },
+ onInputValueChange({ inputValue, highlightedIndex }) {
if (!inputValue) return;
props.onInputValueChange(inputValue);
+
+ if (inputValue.endsWith("@") || currentlyInContextQuery) {
+ setCurrentlyInContextQuery(true);
+
+ const segs = inputValue.split("@");
+ const providerAndQuery = segs[segs.length - 1];
+ const [provider, query] = providerAndQuery.split(" ");
+ searchClient
+ .index(SEARCH_INDEX_NAME)
+ .search(providerAndQuery)
+ .then((res) => {
+ setItems(
+ res.hits.map((hit) => {
+ return {
+ name: hit.name,
+ description: hit.description,
+ id: hit.id,
+ };
+ })
+ );
+ });
+ return;
+ }
setItems(
props.items.filter((item) =>
item.name.toLowerCase().startsWith(inputValue.toLowerCase())
@@ -151,6 +193,16 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
},
});
+ useEffect(() => {
+ console.log(
+ "downshiftProps.highlightedIndex",
+ downshiftProps.highlightedIndex
+ );
+ if (downshiftProps.highlightedIndex < 0) {
+ downshiftProps.setHighlightedIndex(0);
+ }
+ }, [downshiftProps.inputValue]);
+
useImperativeHandle(ref, () => downshiftProps, [downshiftProps]);
const [metaKeyPressed, setMetaKeyPressed] = useState(false);
@@ -199,50 +251,22 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
return (
<>
<div className="px-2 flex gap-2 items-center flex-wrap mt-2">
- {/* {highlightedCodeSections.length > 1 && (
- <>
- <HeaderButtonWithText
- text="Clear Context"
- onClick={() => {
- props.deleteContextItems(
- highlightedCodeSections.map((_, idx) => idx)
- );
- }}
- >
- <Trash size="1.6em" />
- </HeaderButtonWithText>
- </>
- )} */}
- {highlightedCodeSections.map((section, idx) => (
- <PillButton
- warning={
- section.range.contents.length > 4000 && section.editing
- ? "Editing such a large range may be slow"
- : undefined
- }
- onlyShowDelete={
- highlightedCodeSections.length <= 1 || section.editing
- }
- editing={section.editing}
- pinned={section.pinned}
- index={idx}
- key={`${section.display_name}${idx}`}
- title={`${section.display_name} (${
- section.range.range.start.line + 1
- }-${section.range.range.end.line + 1})`}
- onDelete={() => {
- if (props.deleteContextItems) {
- props.deleteContextItems([idx]);
+ {props.selectedContextItems.map((item, idx) => {
+ return (
+ <PillButton
+ key={`${item.description.id.item_id}${idx}`}
+ item={item}
+ warning={
+ item.content.length > 4000 && item.editing
+ ? "Editing such a large range may be slow"
+ : undefined
}
- setHighlightedCodeSections((prev) => {
- const newSections = [...prev];
- newSections.splice(idx, 1);
- return newSections;
- });
- }}
- />
- ))}
- {props.highlightedCodeSections.length > 0 &&
+ addingHighlightedCode={props.addingHighlightedCode}
+ index={idx}
+ />
+ );
+ })}
+ {props.selectedContextItems.length > 0 &&
(props.addingHighlightedCode ? (
<EmptyPillDiv
onClick={() => {
@@ -275,6 +299,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
target.scrollHeight,
300
).toString()}px`;
+ setInputBoxHeight(target.style.height);
// setShowContextDropdown(target.value.endsWith("@"));
},
@@ -289,6 +314,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
if (event.key === "Enter" && event.shiftKey) {
// Prevent Downshift's default 'Enter' behavior.
(event.nativeEvent as any).preventDownshiftDefault = true;
+ setCurrentlyInContextQuery(false);
} else if (
event.key === "Enter" &&
(!downshiftProps.isOpen || items.length === 0)
@@ -302,6 +328,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
(event.nativeEvent as any).preventDownshiftDefault = true;
if (props.onEnter) props.onEnter(event);
+ setCurrentlyInContextQuery(false);
} else if (event.key === "Tab" && items.length > 0) {
downshiftProps.setInputValue(items[0].name);
event.preventDefault();
@@ -321,6 +348,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
}
downshiftProps.setInputValue(history[positionInHistory - 1]);
setPositionInHistory((prev) => prev - 1);
+ setCurrentlyInContextQuery(false);
} else if (event.key === "ArrowDown") {
if (positionInHistory < history.length) {
downshiftProps.setInputValue(history[positionInHistory + 1]);
@@ -328,6 +356,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
setPositionInHistory((prev) =>
Math.min(prev + 1, history.length)
);
+ setCurrentlyInContextQuery(false);
}
},
ref: inputRef,
@@ -351,13 +380,14 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
selected={downshiftProps.selectedItem === item}
>
<span>
- {item.name}: {item.description}
+ {item.name}:{" "}
+ <span style={{ color: lightGray }}>{item.description}</span>
</span>
</Li>
))}
</Ul>
</div>
- {highlightedCodeSections.length === 0 &&
+ {props.selectedContextItems.length === 0 &&
(downshiftProps.inputValue?.startsWith("/edit") ||
(focused &&
metaKeyPressed &&
diff --git a/extension/react-app/src/components/PillButton.tsx b/extension/react-app/src/components/PillButton.tsx
index c24dba83..af4263af 100644
--- a/extension/react-app/src/components/PillButton.tsx
+++ b/extension/react-app/src/components/PillButton.tsx
@@ -1,9 +1,10 @@
-import { useContext, useState } from "react";
+import { useContext, useEffect, useState } from "react";
import styled from "styled-components";
import {
StyledTooltip,
defaultBorderRadius,
secondaryDark,
+ vscBackground,
vscForeground,
} from ".";
import {
@@ -12,6 +13,14 @@ import {
ExclamationTriangle,
} from "@styled-icons/heroicons-outline";
import { GUIClientContext } from "../App";
+import { useDispatch } from "react-redux";
+import {
+ setBottomMessage,
+ setBottomMessageCloseTimeout,
+} from "../redux/slices/uiStateSlice";
+import { ContextItem } from "../../../schema/FullState";
+import { ReactMarkdown } from "react-markdown/lib/react-markdown";
+import StyledMarkdownPreview from "./StyledMarkdownPreview";
const Button = styled.button`
border: none;
@@ -67,19 +76,48 @@ const CircleDiv = styled.div`
interface PillButtonProps {
onHover?: (arg0: boolean) => void;
- onDelete?: () => void;
- title: string;
- index: number;
- editing: boolean;
- pinned: boolean;
+ item: ContextItem;
warning?: string;
- onlyShowDelete?: boolean;
+ index: number;
+ addingHighlightedCode?: boolean;
}
const PillButton = (props: PillButtonProps) => {
const [isHovered, setIsHovered] = useState(false);
const client = useContext(GUIClientContext);
+ const dispatch = useDispatch();
+
+ useEffect(() => {
+ if (isHovered) {
+ dispatch(setBottomMessageCloseTimeout(undefined));
+ dispatch(
+ setBottomMessage(
+ <>
+ <b>{props.item.description.name}</b>:{" "}
+ {props.item.description.description}
+ <StyledMarkdownPreview
+ source={`\`\`\`\n${props.item.content}\n\`\`\``}
+ wrapperElement={{
+ "data-color-mode": "dark",
+ }}
+ />
+ </>
+ )
+ );
+ } else {
+ dispatch(
+ setBottomMessageCloseTimeout(
+ setTimeout(() => {
+ if (!isHovered) {
+ dispatch(setBottomMessage(undefined));
+ }
+ }, 2000)
+ )
+ );
+ }
+ }, [isHovered]);
+
return (
<>
<div style={{ position: "relative" }}>
@@ -88,10 +126,8 @@ const PillButton = (props: PillButtonProps) => {
position: "relative",
borderColor: props.warning
? "red"
- : props.editing
+ : props.item.editing
? "#8800aa"
- : props.pinned
- ? "#ffff0099"
: "transparent",
borderWidth: "1px",
borderStyle: "solid",
@@ -112,10 +148,14 @@ const PillButton = (props: PillButtonProps) => {
{isHovered && (
<GridDiv
style={{
- gridTemplateColumns: props.onlyShowDelete ? "1fr" : "1fr 1fr",
+ gridTemplateColumns:
+ props.item.editable && props.addingHighlightedCode
+ ? "1fr 1fr"
+ : "1fr",
+ backgroundColor: vscBackground,
}}
>
- {props.onlyShowDelete || (
+ {props.item.editable && props.addingHighlightedCode && (
<ButtonDiv
data-tooltip-id={`edit-${props.index}`}
backgroundColor={"#8800aa55"}
@@ -130,15 +170,6 @@ const PillButton = (props: PillButtonProps) => {
</ButtonDiv>
)}
- {/* <ButtonDiv
- data-tooltip-id={`pin-${props.index}`}
- backgroundColor={"#ffff0055"}
- onClick={() => {
- client?.setPinnedAtIndices([props.index]);
- }}
- >
- <MapPin style={{ margin: "auto" }} width="1.6em"></MapPin>
- </ButtonDiv> */}
<StyledTooltip id={`pin-${props.index}`}>
Edit this range
</StyledTooltip>
@@ -146,33 +177,33 @@ const PillButton = (props: PillButtonProps) => {
data-tooltip-id={`delete-${props.index}`}
backgroundColor={"#cc000055"}
onClick={() => {
- if (props.onDelete) {
- props.onDelete();
- }
+ client?.deleteContextWithIds([props.item.description.id]);
}}
>
<Trash style={{ margin: "auto" }} width="1.6em"></Trash>
</ButtonDiv>
</GridDiv>
)}
- {props.title}
+ {props.item.description.name}
</Button>
<StyledTooltip id={`edit-${props.index}`}>
- {props.editing
+ {props.item.editing
? "Editing this section (with entire file as context)"
: "Edit this section"}
</StyledTooltip>
<StyledTooltip id={`delete-${props.index}`}>Delete</StyledTooltip>
{props.warning && (
<>
- <CircleDiv data-tooltip-id={`circle-div-${props.title}`}>
+ <CircleDiv
+ data-tooltip-id={`circle-div-${props.item.description.name}`}
+ >
<ExclamationTriangle
style={{ margin: "auto" }}
width="1.0em"
strokeWidth={2}
/>
</CircleDiv>
- <StyledTooltip id={`circle-div-${props.title}`}>
+ <StyledTooltip id={`circle-div-${props.item.description.name}`}>
{props.warning}
</StyledTooltip>
</>
diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx
index bc8665fd..2cfe7ecd 100644
--- a/extension/react-app/src/components/StepContainer.tsx
+++ b/extension/react-app/src/components/StepContainer.tsx
@@ -18,9 +18,9 @@ import {
import { StopCircle } from "@styled-icons/heroicons-solid";
import { HistoryNode } from "../../../schema/HistoryNode";
import HeaderButtonWithText from "./HeaderButtonWithText";
-import MarkdownPreview from "@uiw/react-markdown-preview";
import { getMetaKeyLabel, isMetaEquivalentKeyPressed } from "../util";
import { GUIClientContext } from "../App";
+import StyledMarkdownPreview from "./StyledMarkdownPreview";
interface StepContainerProps {
historyNode: HistoryNode;
@@ -109,33 +109,6 @@ const GradientBorder = styled.div<{
background-size: 200% 200%;
`;
-const StyledMarkdownPreview = styled(MarkdownPreview)`
- pre {
- background-color: ${secondaryDark};
- padding: 1px;
- border-radius: ${defaultBorderRadius};
- border: 0.5px solid white;
- }
-
- code {
- color: #f78383;
- word-wrap: break-word;
- border-radius: ${defaultBorderRadius};
- background-color: ${secondaryDark};
- }
-
- pre > code {
- background-color: ${secondaryDark};
- color: ${vscForeground};
- }
-
- background-color: ${vscBackground};
- font-family: "Lexend", sans-serif;
- font-size: 13px;
- padding: 8px;
- color: ${vscForeground};
-`;
-
// #endregion
function StepContainer(props: StepContainerProps) {
diff --git a/extension/react-app/src/components/StyledMarkdownPreview.tsx b/extension/react-app/src/components/StyledMarkdownPreview.tsx
new file mode 100644
index 00000000..9c2ecb62
--- /dev/null
+++ b/extension/react-app/src/components/StyledMarkdownPreview.tsx
@@ -0,0 +1,37 @@
+import styled from "styled-components";
+import {
+ defaultBorderRadius,
+ secondaryDark,
+ vscBackground,
+ vscForeground,
+} from ".";
+import MarkdownPreview from "@uiw/react-markdown-preview";
+
+const StyledMarkdownPreview = styled(MarkdownPreview)`
+ pre {
+ background-color: ${secondaryDark};
+ padding: 1px;
+ border-radius: ${defaultBorderRadius};
+ border: 0.5px solid white;
+ }
+
+ code {
+ color: #f78383;
+ word-wrap: break-word;
+ border-radius: ${defaultBorderRadius};
+ background-color: ${secondaryDark};
+ }
+
+ pre > code {
+ background-color: ${secondaryDark};
+ color: ${vscForeground};
+ }
+
+ background-color: ${vscBackground};
+ font-family: "Lexend", sans-serif;
+ font-size: 13px;
+ padding: 8px;
+ color: ${vscForeground};
+`;
+
+export default StyledMarkdownPreview;
diff --git a/extension/react-app/src/components/TextDialog.tsx b/extension/react-app/src/components/TextDialog.tsx
index cba3852d..7c6ba052 100644
--- a/extension/react-app/src/components/TextDialog.tsx
+++ b/extension/react-app/src/components/TextDialog.tsx
@@ -5,7 +5,7 @@ import { Button, secondaryDark, vscBackground, vscForeground } from ".";
import { isMetaEquivalentKeyPressed } from "../util";
const ScreenCover = styled.div`
- position: absolute;
+ position: fixed;
width: 100%;
height: 100%;
background-color: rgba(168, 168, 168, 0.5);
diff --git a/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts
index 6c0df8fc..ddf65272 100644
--- a/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts
+++ b/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts
@@ -1,3 +1,5 @@
+import { ContextItemId } from "../../../schema/FullState";
+
abstract class AbstractContinueGUIClientProtocol {
abstract sendMainInput(input: string): void;
@@ -21,15 +23,15 @@ abstract class AbstractContinueGUIClientProtocol {
abstract deleteAtIndex(index: number): void;
- abstract deleteContextAtIndices(indices: number[]): void;
+ abstract deleteContextWithIds(ids: ContextItemId[]): void;
abstract setEditingAtIndices(indices: number[]): void;
- abstract setPinnedAtIndices(indices: number[]): void;
-
abstract toggleAddingHighlightedCode(): void;
abstract showLogsAtIndex(index: number): void;
+
+ abstract selectContextItem(id: string, query: string): void;
}
export default AbstractContinueGUIClientProtocol;
diff --git a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts
index 7d6c2a71..1048e956 100644
--- a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts
+++ b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts
@@ -1,3 +1,4 @@
+import { ContextItemId } from "../../../schema/FullState";
import AbstractContinueGUIClientProtocol from "./AbstractContinueGUIClientProtocol";
import { Messenger, WebsocketMessenger } from "./messenger";
import { VscodeMessenger } from "./vscodeMessenger";
@@ -68,18 +69,16 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol {
this.messenger.send("delete_at_index", { index });
}
- deleteContextAtIndices(indices: number[]) {
- this.messenger.send("delete_context_at_indices", { indices });
+ deleteContextWithIds(ids: ContextItemId[]) {
+ this.messenger.send("delete_context_with_ids", {
+ ids: ids.map((id) => `${id.provider_title}-${id.item_id}`),
+ });
}
setEditingAtIndices(indices: number[]) {
this.messenger.send("set_editing_at_indices", { indices });
}
- setPinnedAtIndices(indices: number[]) {
- this.messenger.send("set_pinned_at_indices", { indices });
- }
-
toggleAddingHighlightedCode(): void {
this.messenger.send("toggle_adding_highlighted_code", {});
}
@@ -87,6 +86,10 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol {
showLogsAtIndex(index: number): void {
this.messenger.send("show_logs_at_index", { index });
}
+
+ selectContextItem(id: string, query: string): void {
+ this.messenger.send("select_context_item", { id, query });
+ }
}
export default ContinueGUIClientProtocol;
diff --git a/extension/react-app/src/pages/gui.tsx b/extension/react-app/src/pages/gui.tsx
index fccc9b4b..a1ba1c33 100644
--- a/extension/react-app/src/pages/gui.tsx
+++ b/extension/react-app/src/pages/gui.tsx
@@ -6,7 +6,7 @@ import {
} from "../components";
import Loader from "../components/Loader";
import ContinueButton from "../components/ContinueButton";
-import { FullState, HighlightedRangeContext } from "../../../schema/FullState";
+import { ContextItem, FullState } from "../../../schema/FullState";
import { useCallback, useEffect, useRef, useState, useContext } from "react";
import { History } from "../../../schema/History";
import { HistoryNode } from "../../../schema/HistoryNode";
@@ -22,12 +22,16 @@ import TextDialog from "../components/TextDialog";
import HeaderButtonWithText from "../components/HeaderButtonWithText";
import ReactSwitch from "react-switch";
import { usePostHog } from "posthog-js/react";
-import { useSelector } from "react-redux";
+import { useDispatch, useSelector } from "react-redux";
import { RootStore } from "../redux/store";
import { postVscMessage } from "../vscode";
import UserInputContainer from "../components/UserInputContainer";
import Onboarding from "../components/Onboarding";
import { isMetaEquivalentKeyPressed } from "../util";
+import {
+ setBottomMessage,
+ setBottomMessageCloseTimeout,
+} from "../redux/slices/uiStateSlice";
const TopGUIDiv = styled.div`
overflow: hidden;
@@ -78,15 +82,13 @@ function GUI(props: GUIProps) {
const [usingFastModel, setUsingFastModel] = useState(false);
const [waitingForSteps, setWaitingForSteps] = useState(false);
const [userInputQueue, setUserInputQueue] = useState<string[]>([]);
- const [highlightedRanges, setHighlightedRanges] = useState<
- HighlightedRangeContext[]
- >([]);
const [addingHighlightedCode, setAddingHighlightedCode] = useState(false);
+ const [selectedContextItems, setSelectedContextItems] = useState<
+ ContextItem[]
+ >([]);
const [availableSlashCommands, setAvailableSlashCommands] = useState<
{ name: string; description: string }[]
>([]);
- const [pinned, setPinned] = useState(false);
- const [showDataSharingInfo, setShowDataSharingInfo] = useState(false);
const [stepsOpen, setStepsOpen] = useState<boolean[]>([
true,
true,
@@ -118,6 +120,11 @@ function GUI(props: GUIProps) {
const [showFeedbackDialog, setShowFeedbackDialog] = useState(false);
const [feedbackDialogMessage, setFeedbackDialogMessage] = useState("");
+ const dispatch = useDispatch();
+ const bottomMessage = useSelector(
+ (state: RootStore) => state.uiState.bottomMessage
+ );
+
const topGuiDivRef = useRef<HTMLDivElement>(null);
const [scrollTimeout, setScrollTimeout] = useState<NodeJS.Timeout | null>(
@@ -179,7 +186,8 @@ function GUI(props: GUIProps) {
setWaitingForSteps(waitingForSteps);
setHistory(state.history);
- setHighlightedRanges(state.highlighted_ranges);
+ console.log((state as any).selected_context_items);
+ setSelectedContextItems(state.selected_context_items);
setUserInputQueue(state.user_input_queue);
setAddingHighlightedCode(state.adding_highlighted_code);
setAvailableSlashCommands(
@@ -214,13 +222,6 @@ function GUI(props: GUIProps) {
const mainTextInputRef = useRef<HTMLInputElement>(null);
- const deleteContextItems = useCallback(
- (indices: number[]) => {
- client?.deleteContextAtIndices(indices);
- },
- [client]
- );
-
const onMainTextInput = (event?: any) => {
if (mainTextInputRef.current) {
let input = (mainTextInputRef.current as any).inputValue;
@@ -360,11 +361,7 @@ function GUI(props: GUIProps) {
}}
onInputValueChange={() => {}}
items={availableSlashCommands}
- highlightedCodeSections={highlightedRanges}
- deleteContextItems={deleteContextItems}
- onTogglePin={() => {
- setPinned((prev: boolean) => !prev);
- }}
+ selectedContextItems={selectedContextItems}
onToggleAddContext={() => {
client?.toggleAddingHighlightedCode();
}}
@@ -373,29 +370,30 @@ function GUI(props: GUIProps) {
<ContinueButton onClick={onMainTextInput} />
</TopGUIDiv>
<div
+ onMouseEnter={() => {
+ dispatch(setBottomMessageCloseTimeout(undefined));
+ }}
+ onMouseLeave={() => {
+ dispatch(setBottomMessage(undefined));
+ }}
style={{
position: "fixed",
bottom: "50px",
+ left: "0",
+ right: "0",
+ margin: "16px",
backgroundColor: vscBackground,
color: vscForeground,
borderRadius: defaultBorderRadius,
padding: "16px",
- margin: "16px",
zIndex: 100,
boxShadow: `0px 0px 10px 0px ${vscForeground}`,
+ maxHeight: "50vh",
+ overflow: "scroll",
}}
- hidden={!showDataSharingInfo}
+ hidden={!bottomMessage}
>
- By turning on this switch, you will begin collecting accepted and
- rejected suggestions in .continue/suggestions.json. This data is stored
- locally on your machine and not sent anywhere.
- <br />
- <br />
- <b>
- {dataSwitchChecked
- ? "👍 Data is being collected"
- : "👎 No data is being collected"}
- </b>
+ {bottomMessage}
</div>
<Footer dataSwitchChecked={dataSwitchChecked}>
<div
@@ -406,10 +404,25 @@ function GUI(props: GUIProps) {
alignItems: "center",
}}
onMouseEnter={() => {
- setShowDataSharingInfo(true);
+ dispatch(
+ setBottomMessage(
+ <>
+ By turning on this switch, you will begin collecting accepted
+ and rejected suggestions in .continue/suggestions.json. This
+ data is stored locally on your machine and not sent anywhere.
+ <br />
+ <br />
+ <b>
+ {dataSwitchChecked
+ ? "👍 Data is being collected"
+ : "👎 No data is being collected"}
+ </b>
+ </>
+ )
+ );
}}
onMouseLeave={() => {
- setShowDataSharingInfo(false);
+ dispatch(setBottomMessage(undefined));
}}
>
<ReactSwitch
diff --git a/extension/react-app/src/redux/selectors/uiStateSelectors.ts b/extension/react-app/src/redux/selectors/uiStateSelectors.ts
new file mode 100644
index 00000000..7ebc9338
--- /dev/null
+++ b/extension/react-app/src/redux/selectors/uiStateSelectors.ts
@@ -0,0 +1,5 @@
+import { RootStore } from "../store";
+
+const selectBottomMessage = (state: RootStore) => state.uiState.bottomMessage;
+
+export { selectBottomMessage };
diff --git a/extension/react-app/src/redux/slices/uiStateSlice.ts b/extension/react-app/src/redux/slices/uiStateSlice.ts
new file mode 100644
index 00000000..837d19e9
--- /dev/null
+++ b/extension/react-app/src/redux/slices/uiStateSlice.ts
@@ -0,0 +1,24 @@
+import { createSlice } from "@reduxjs/toolkit";
+
+export const uiStateSlice = createSlice({
+ name: "uiState",
+ initialState: {
+ bottomMessage: undefined,
+ bottomMessageCloseTimeout: undefined,
+ },
+ reducers: {
+ setBottomMessage: (state, action) => {
+ state.bottomMessage = action.payload;
+ },
+ setBottomMessageCloseTimeout: (state, action) => {
+ if (state.bottomMessageCloseTimeout) {
+ clearTimeout(state.bottomMessageCloseTimeout);
+ }
+ state.bottomMessageCloseTimeout = action.payload;
+ },
+ },
+});
+
+export const { setBottomMessage, setBottomMessageCloseTimeout } =
+ uiStateSlice.actions;
+export default uiStateSlice.reducer;
diff --git a/extension/react-app/src/redux/store.ts b/extension/react-app/src/redux/store.ts
index b6eb55b3..d49513e5 100644
--- a/extension/react-app/src/redux/store.ts
+++ b/extension/react-app/src/redux/store.ts
@@ -3,6 +3,7 @@ import debugStateReducer from "./slices/debugContexSlice";
import chatReducer from "./slices/chatSlice";
import configReducer from "./slices/configSlice";
import miscReducer from "./slices/miscSlice";
+import uiStateReducer from "./slices/uiStateSlice";
import { RangeInFile, SerializedDebugContext } from "../../../src/client";
export interface ChatMessage {
@@ -31,6 +32,10 @@ export interface RootStore {
misc: {
highlightedCode: RangeInFile | undefined;
};
+ uiState: {
+ bottomMessage: JSX.Element | undefined;
+ bottomMessageCloseTimeout: NodeJS.Timeout | undefined;
+ };
}
const store = configureStore({
@@ -39,6 +44,7 @@ const store = configureStore({
chat: chatReducer,
config: configReducer,
misc: miscReducer,
+ uiState: uiStateReducer,
},
});
diff --git a/extension/schema/ContextItem.d.ts b/extension/schema/ContextItem.d.ts
new file mode 100644
index 00000000..62d2c32f
--- /dev/null
+++ b/extension/schema/ContextItem.d.ts
@@ -0,0 +1,45 @@
+/* eslint-disable */
+/**
+ * This file was automatically generated by json-schema-to-typescript.
+ * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
+ * and run json-schema-to-typescript to regenerate this file.
+ */
+
+export type ContextItem = ContextItem1;
+export type Name = string;
+export type Description = string;
+export type ProviderTitle = string;
+export type ItemId = string;
+export type Content = string;
+export type Editing = boolean;
+export type Editable = boolean;
+
+/**
+ * A ContextItem is a single item that is stored in the ContextManager.
+ */
+export interface ContextItem1 {
+ description: ContextItemDescription;
+ content: Content;
+ editing?: Editing;
+ editable?: Editable;
+ [k: string]: unknown;
+}
+/**
+ * A ContextItemDescription is a description of a ContextItem that is displayed to the user when they type '@'.
+ *
+ * The id can be used to retrieve the ContextItem from the ContextManager.
+ */
+export interface ContextItemDescription {
+ name: Name;
+ description: Description;
+ id: ContextItemId;
+ [k: string]: unknown;
+}
+/**
+ * A ContextItemId is a unique identifier for a ContextItem.
+ */
+export interface ContextItemId {
+ provider_title: ProviderTitle;
+ item_id: ItemId;
+ [k: string]: unknown;
+}
diff --git a/extension/schema/FullState.d.ts b/extension/schema/FullState.d.ts
index 1b7b1f3b..0095f41b 100644
--- a/extension/schema/FullState.d.ts
+++ b/extension/schema/FullState.d.ts
@@ -27,18 +27,18 @@ export type CurrentIndex = number;
export type Active1 = boolean;
export type UserInputQueue = string[];
export type DefaultModel = string;
-export type Filepath = string;
-export type Line = number;
-export type Character = number;
-export type Contents = string;
-export type Editing = boolean;
-export type Pinned = boolean;
-export type DisplayName = string;
-export type HighlightedRanges = HighlightedRangeContext[];
export type Name3 = string;
export type Description1 = string;
export type SlashCommands = SlashCommandDescription[];
export type AddingHighlightedCode = boolean;
+export type Name4 = string;
+export type Description2 = string;
+export type ProviderTitle = string;
+export type ItemId = string;
+export type Content1 = string;
+export type Editing = boolean;
+export type Editable = boolean;
+export type SelectedContextItems = ContextItem[];
/**
* A full state of the program, including the history
@@ -48,9 +48,9 @@ export interface FullState1 {
active: Active1;
user_input_queue: UserInputQueue;
default_model: DefaultModel;
- highlighted_ranges: HighlightedRanges;
slash_commands: SlashCommands;
adding_highlighted_code: AddingHighlightedCode;
+ selected_context_items: SelectedContextItems;
[k: string]: unknown;
}
/**
@@ -98,40 +98,37 @@ export interface FunctionCall {
export interface Observation {
[k: string]: unknown;
}
-/**
- * Context for a highlighted range
- */
-export interface HighlightedRangeContext {
- range: RangeInFileWithContents;
- editing: Editing;
- pinned: Pinned;
- display_name: DisplayName;
+export interface SlashCommandDescription {
+ name: Name3;
+ description: Description1;
[k: string]: unknown;
}
/**
- * A range in a file with the contents of the range.
+ * A ContextItem is a single item that is stored in the ContextManager.
*/
-export interface RangeInFileWithContents {
- filepath: Filepath;
- range: Range;
- contents: Contents;
+export interface ContextItem {
+ description: ContextItemDescription;
+ content: Content1;
+ editing?: Editing;
+ editable?: Editable;
[k: string]: unknown;
}
/**
- * A range in a file. 0-indexed.
+ * A ContextItemDescription is a description of a ContextItem that is displayed to the user when they type '@'.
+ *
+ * The id can be used to retrieve the ContextItem from the ContextManager.
*/
-export interface Range {
- start: Position;
- end: Position;
+export interface ContextItemDescription {
+ name: Name4;
+ description: Description2;
+ id: ContextItemId;
[k: string]: unknown;
}
-export interface Position {
- line: Line;
- character: Character;
- [k: string]: unknown;
-}
-export interface SlashCommandDescription {
- name: Name3;
- description: Description1;
+/**
+ * A ContextItemId is a unique identifier for a ContextItem.
+ */
+export interface ContextItemId {
+ provider_title: ProviderTitle;
+ item_id: ItemId;
[k: string]: unknown;
}
diff --git a/schema/json/ContextItem.json b/schema/json/ContextItem.json
new file mode 100644
index 00000000..35766f11
--- /dev/null
+++ b/schema/json/ContextItem.json
@@ -0,0 +1,76 @@
+{
+ "title": "ContextItem",
+ "$ref": "#/definitions/src__continuedev__core__context__ContextItem",
+ "definitions": {
+ "ContextItemId": {
+ "title": "ContextItemId",
+ "description": "A ContextItemId is a unique identifier for a ContextItem.",
+ "type": "object",
+ "properties": {
+ "provider_title": {
+ "title": "Provider Title",
+ "type": "string"
+ },
+ "item_id": {
+ "title": "Item Id",
+ "type": "string"
+ }
+ },
+ "required": [
+ "provider_title",
+ "item_id"
+ ]
+ },
+ "ContextItemDescription": {
+ "title": "ContextItemDescription",
+ "description": "A ContextItemDescription is a description of a ContextItem that is displayed to the user when they type '@'.\n\nThe id can be used to retrieve the ContextItem from the ContextManager.",
+ "type": "object",
+ "properties": {
+ "name": {
+ "title": "Name",
+ "type": "string"
+ },
+ "description": {
+ "title": "Description",
+ "type": "string"
+ },
+ "id": {
+ "$ref": "#/definitions/ContextItemId"
+ }
+ },
+ "required": [
+ "name",
+ "description",
+ "id"
+ ]
+ },
+ "src__continuedev__core__context__ContextItem": {
+ "title": "ContextItem",
+ "description": "A ContextItem is a single item that is stored in the ContextManager.",
+ "type": "object",
+ "properties": {
+ "description": {
+ "$ref": "#/definitions/ContextItemDescription"
+ },
+ "content": {
+ "title": "Content",
+ "type": "string"
+ },
+ "editing": {
+ "title": "Editing",
+ "default": false,
+ "type": "boolean"
+ },
+ "editable": {
+ "title": "Editable",
+ "default": false,
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "description",
+ "content"
+ ]
+ }
+ }
+} \ No newline at end of file
diff --git a/schema/json/FullState.json b/schema/json/FullState.json
index 62ed337b..7b6dfd6b 100644
--- a/schema/json/FullState.json
+++ b/schema/json/FullState.json
@@ -157,108 +157,92 @@
"current_index"
]
},
- "Position": {
- "title": "Position",
+ "SlashCommandDescription": {
+ "title": "SlashCommandDescription",
"type": "object",
"properties": {
- "line": {
- "title": "Line",
- "type": "integer"
+ "name": {
+ "title": "Name",
+ "type": "string"
},
- "character": {
- "title": "Character",
- "type": "integer"
+ "description": {
+ "title": "Description",
+ "type": "string"
}
},
"required": [
- "line",
- "character"
+ "name",
+ "description"
]
},
- "Range": {
- "title": "Range",
- "description": "A range in a file. 0-indexed.",
+ "ContextItemId": {
+ "title": "ContextItemId",
+ "description": "A ContextItemId is a unique identifier for a ContextItem.",
"type": "object",
"properties": {
- "start": {
- "$ref": "#/definitions/Position"
+ "provider_title": {
+ "title": "Provider Title",
+ "type": "string"
},
- "end": {
- "$ref": "#/definitions/Position"
+ "item_id": {
+ "title": "Item Id",
+ "type": "string"
}
},
"required": [
- "start",
- "end"
+ "provider_title",
+ "item_id"
]
},
- "RangeInFileWithContents": {
- "title": "RangeInFileWithContents",
- "description": "A range in a file with the contents of the range.",
+ "ContextItemDescription": {
+ "title": "ContextItemDescription",
+ "description": "A ContextItemDescription is a description of a ContextItem that is displayed to the user when they type '@'.\n\nThe id can be used to retrieve the ContextItem from the ContextManager.",
"type": "object",
"properties": {
- "filepath": {
- "title": "Filepath",
+ "name": {
+ "title": "Name",
"type": "string"
},
- "range": {
- "$ref": "#/definitions/Range"
- },
- "contents": {
- "title": "Contents",
+ "description": {
+ "title": "Description",
"type": "string"
+ },
+ "id": {
+ "$ref": "#/definitions/ContextItemId"
}
},
"required": [
- "filepath",
- "range",
- "contents"
+ "name",
+ "description",
+ "id"
]
},
- "HighlightedRangeContext": {
- "title": "HighlightedRangeContext",
- "description": "Context for a highlighted range",
+ "ContextItem": {
+ "title": "ContextItem",
+ "description": "A ContextItem is a single item that is stored in the ContextManager.",
"type": "object",
"properties": {
- "range": {
- "$ref": "#/definitions/RangeInFileWithContents"
+ "description": {
+ "$ref": "#/definitions/ContextItemDescription"
+ },
+ "content": {
+ "title": "Content",
+ "type": "string"
},
"editing": {
"title": "Editing",
+ "default": false,
"type": "boolean"
},
- "pinned": {
- "title": "Pinned",
+ "editable": {
+ "title": "Editable",
+ "default": false,
"type": "boolean"
- },
- "display_name": {
- "title": "Display Name",
- "type": "string"
- }
- },
- "required": [
- "range",
- "editing",
- "pinned",
- "display_name"
- ]
- },
- "SlashCommandDescription": {
- "title": "SlashCommandDescription",
- "type": "object",
- "properties": {
- "name": {
- "title": "Name",
- "type": "string"
- },
- "description": {
- "title": "Description",
- "type": "string"
}
},
"required": [
- "name",
- "description"
+ "description",
+ "content"
]
},
"src__continuedev__core__main__FullState": {
@@ -284,13 +268,6 @@
"title": "Default Model",
"type": "string"
},
- "highlighted_ranges": {
- "title": "Highlighted Ranges",
- "type": "array",
- "items": {
- "$ref": "#/definitions/HighlightedRangeContext"
- }
- },
"slash_commands": {
"title": "Slash Commands",
"type": "array",
@@ -301,6 +278,13 @@
"adding_highlighted_code": {
"title": "Adding Highlighted Code",
"type": "boolean"
+ },
+ "selected_context_items": {
+ "title": "Selected Context Items",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ContextItem"
+ }
}
},
"required": [
@@ -308,9 +292,9 @@
"active",
"user_input_queue",
"default_model",
- "highlighted_ranges",
"slash_commands",
- "adding_highlighted_code"
+ "adding_highlighted_code",
+ "selected_context_items"
]
}
}