diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/SmartShell \351\241\271\347\233\256\350\207\252\351\252\214\346\212\245\345\221\212.pdf" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/SmartShell \351\241\271\347\233\256\350\207\252\351\252\214\346\212\245\345\221\212.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..4fc7615fec922bdef0de98bcf3c2fcb0f54d29fe Binary files /dev/null and "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/SmartShell \351\241\271\347\233\256\350\207\252\351\252\214\346\212\245\345\221\212.pdf" differ diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/LICENSE" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/LICENSE" new file mode 100644 index 0000000000000000000000000000000000000000..f63f5a9cf3498818a73068495709cceed67efd6a --- /dev/null +++ "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/LICENSE" @@ -0,0 +1,194 @@ +木兰宽松许可证,第2版 + +木兰宽松许可证,第2版 + +2020年1月 http://license.coscl.org.cn/MulanPSL2 + +您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: + +0. 定义 + +“软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 + +“贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 + +“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 + +“法人实体” 是指提交贡献的机构及其“关联实体”。 + +“关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是 +指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 + +1. 授予版权许可 + +每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可 +以复制、使用、修改、分发其“贡献”,不论修改与否。 + +2. 授予专利许可 + +每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定 +撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡 +献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软 +件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“ +关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或 +其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权 +行动之日终止。 + +3. 无商标许可 + +“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定 +的声明义务而必须使用除外。 + +4. 分发限制 + +您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“ +本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 + +5. 免责声明与责任限制 + +“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对 +任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于 +何种法律理论,即使其曾被建议有此种损失的可能性。 + +6. 语言 + +“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文 +版为准。 + +条款结束 + +如何将木兰宽松许可证,第2版,应用到您的软件 + +如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: + +1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; + +2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; + +3, 请将如下声明文本放入每个源文件的头部注释中。 + +Copyright (c) [Year] [name of copyright holder] +[Software Name] is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan +PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. + +Mulan Permissive Software License,Version 2 + +Mulan Permissive Software License,Version 2 (Mulan PSL v2) + +January 2020 http://license.coscl.org.cn/MulanPSL2 + +Your reproduction, use, modification and distribution of the Software shall +be subject to Mulan PSL v2 (this License) with the following terms and +conditions: + +0. Definition + +Software means the program and related documents which are licensed under +this License and comprise all Contribution(s). + +Contribution means the copyrightable work licensed by a particular +Contributor under this License. + +Contributor means the Individual or Legal Entity who licenses its +copyrightable work under this License. + +Legal Entity means the entity making a Contribution and all its +Affiliates. + +Affiliates means entities that control, are controlled by, or are under +common control with the acting entity under this License, ‘control’ means +direct or indirect ownership of at least fifty percent (50%) of the voting +power, capital or other securities of controlled or commonly controlled +entity. + +1. Grant of Copyright License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to you a perpetual, worldwide, royalty-free, non-exclusive, +irrevocable copyright license to reproduce, use, modify, or distribute its +Contribution, with modification or not. + +2. Grant of Patent License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to you a perpetual, worldwide, royalty-free, non-exclusive, +irrevocable (except for revocation under this Section) patent license to +make, have made, use, offer for sale, sell, import or otherwise transfer its +Contribution, where such patent license is only limited to the patent claims +owned or controlled by such Contributor now or in future which will be +necessarily infringed by its Contribution alone, or by combination of the +Contribution with the Software to which the Contribution was contributed. +The patent license shall not apply to any modification of the Contribution, +and any other combination which includes the Contribution. If you or your +Affiliates directly or indirectly institute patent litigation (including a +cross claim or counterclaim in a litigation) or other patent enforcement +activities against any individual or entity by alleging that the Software or +any Contribution in it infringes patents, then any patent license granted to +you under this License for the Software shall terminate as of the date such +litigation or activity is filed or taken. + +3. No Trademark License + +No trademark license is granted to use the trade names, trademarks, service +marks, or product names of Contributor, except as required to fulfill notice +requirements in section 4. + +4. Distribution Restriction + +You may distribute the Software in any medium with or without modification, +whether in source or executable forms, provided that you provide recipients +with a copy of this License and retain copyright, patent, trademark and +disclaimer statements in the Software. + +5. Disclaimer of Warranty and Limitation of Liability + +THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY +KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR +COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT +LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING +FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO +MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGES. + +6. Language + +THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION +AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF +DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION +SHALL PREVAIL. + +END OF THE TERMS AND CONDITIONS + +How to Apply the Mulan Permissive Software License,Version 2 +(Mulan PSL v2) to Your Software + +To apply the Mulan PSL v2 to your work, for easy identification by +recipients, you are suggested to complete following three steps: + +i. Fill in the blanks in following statement, including insert your software +name, the year of the first publication of your software, and your name +identified as the copyright owner; + +ii. Create a file named "LICENSE" which contains the whole context of this +License in the first directory of your software package; + +iii. Attach the statement to the appropriate annotated syntax at the +beginning of each source file. + +Copyright (c) [Year] [name of copyright holder] +[Software Name] is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan +PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/README.md" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/README.md" new file mode 100644 index 0000000000000000000000000000000000000000..ffabe311cb015ae53804bcb9ef6d2e6d02e2ddd4 --- /dev/null +++ "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/README.md" @@ -0,0 +1,136 @@ +# SmartShell + +## 介绍 + +传统的命令行界面虽然功能强大,但对于不熟悉计算机操作的用户来说,使用起来较为复杂。本项目旨在开发一个基于 LangChain 和 ReAct 的智能 Shell 命令执行工具。该系统结合了 LangChain 的自然语言处理能力和 ReAct 的推理与行动能力,为用户提供更加智能、便捷的 Shell 命令执行体验。 + +## 软件环境要求 + +- **操作系统**:Linux, macOS, Windows, EulerOS +- **Python 版本**:Python 3.9 及以上 +- 依赖库: + - `typer` + - `click` + - `openai` + - `litellm` (可选,用于使用 LiteLLM) + - `rich` + - `distro` + +## 安装教程 + +1. **[openEuler]通过 dnf 包管理器安装**: + + ```bash + sudo dnf install python-smart_shell + pip install click==8.1.7 distro==1.9.0 litellm==1.42.5 openai==1.36.1 rich==13.7.1 typer==0.12.3 + smsh + ``` + +2. **下载源码并运行**: + + ```bash + git clone https://gitee.com/Delthin/smart_shell.git + pip install -r requirements.txt + cd smart_shell/smsh + python app.py + ``` + +3. **设置 OpenAI API Key及其他配置项**: + 在第一次运行时,系统会提示输入 OpenAI API Key,你也可以在 `.config/smart_shell/.smsh` 文件中设置 `OPENAI_API_KEY`和其他配置项。 + +## 功能展示 + +1. **智能命令生成**:根据用户的自然语言输入,智能生成相应的Shell命令。 +2. **角色定制**:允许用户定义特定角色,以适应不同的命令生成需求。 +3. **配置模式**:用户可以进入配置模式,实时修改和测试配置项。 +4. **交互模式**:支持交互式命令生成,用户可以持续与系统交互,生成所需的Shell命令。 +5. **本地大模型接入**:支持用户使用本地大模型接口使用此工具,比如Ollama等。 + +## 使用说明 + +以下是一些示例命令: + +```bash +smsh 列出当前目录下的所有文件 +# ls -a +# [E]xecute, [D]escribe, [C]ancel, [Q]uit: +``` +```bash +smsh 创建一个名为new_folder的新目录 --describe-shell +#命令:`mkdir new_folder` +# +#描述:创建一个名为`new_folder`的新目录。 +# +#参数和选项: +#- `mkdir`:命令,用于创建新目录。 +#- `new_folder`:要创建的新目录的名称。 +# +#简短响应:使用`mkdir new_folder`命令可以在当前路径下创建一个名为`new_folder`的新目录。 +``` +```bash +smsh --config +#Entering configuration mode. +#Current configuration: +#CHAT_CACHE_LENGTH = 100 +#REQUEST_TIMEOUT = 60 +#DEFAULT_MODEL = gpt-4o +#DEFAULT_COLOR = magenta +#ROLE_STORAGE_PATH = /home/xxx/.config/smart_shell/roles +#DEFAULT_EXECUTE_SHELL_CMD = false +#DISABLE_STREAMING = false +#CODE_THEME = dracula +#API_BASE_URL = https://xxx +#PRETTIFY_MARKDOWN = true +#USE_LITELLM = false +#OPENAI_API_KEY = xxx +#Enter the config key you want to edit, or press 'q' to exit.: +REQUEST_TIMEOUT +#Enter the new value for REQUEST_TIMEOUT: +61 +#Configuration for REQUEST_TIMEOUT has been updated to 61 +``` +本地大模型的接入需要修改配置文件,以ollama/qwen举例: +``` +DEFAULT_MODEL = ollama/qwen, +API_BASE_URL = http://localhost:11434, +USE_LITELLM = true +``` + +你可以使用以下选项来控制命令的生成和执行: + + ``````bash + ╭─ Arguments ──────────────────────────────────────────────────────────────────────────────────────────────╮ + │ prompt [PROMPT] The prompt to generate completions for. │ + ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + ╭─ Options ────────────────────────────────────────────────────────────────────────────────────────────────╮ + │ --model TEXT Large language model to use. [default: gpt-4o] │ + │ --temperature FLOAT RANGE [0.0<=x<=2.0] Randomness of generated output. [default: 0.0] │ + │ --top-p FLOAT RANGE [0.0<=x<=1.0] Limits highest probable tokens (words). [default: 1.0] │ + │ --md --no-md Prettify markdown output. [default: md] │ + │ --version Show version. │ + │ --help Show this message and exit. │ + ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + ╭─ Assistance Options ─────────────────────────────────────────────────────────────────────────────────────╮ + │ --shell -s Generate and execute shell commands. │ + │ --interaction -i Interactive mode for --shell option. | + │ --describe-shell -d Describe a shell command. │ + │ --config -c Enter configuration edit mode. │ + ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + ╭─ Role Options ───────────────────────────────────────────────────────────────────────────────────────────╮ + │ --role TEXT System role for GPT model. [default: None] │ + │ --create-role TEXT Create role. [default: None] │ + │ --show-role TEXT Show role. [default: None] │ + │ --list-roles -lr List roles. │ + ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + `````` + +## 参与贡献 + +1. Fork 本仓库 +2. 新建 feature_xxx 分支 +3. 提交代码 +4. 新建 Pull Request + +## 许可证 + +本项目采用Mulan2.0开源。 diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/requirements.txt" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/requirements.txt" new file mode 100644 index 0000000000000000000000000000000000000000..20c2818df6266b667a5ba640a99b7bd2c7374c23 --- /dev/null +++ "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/requirements.txt" @@ -0,0 +1,6 @@ +click==8.1.7 +distro==1.9.0 +litellm==1.42.5 +openai==1.36.1 +rich==13.7.1 +typer==0.12.3 diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/rpm/python3-smsh-1.0.0-1.noarch.rpm" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/rpm/python3-smsh-1.0.0-1.noarch.rpm" new file mode 100644 index 0000000000000000000000000000000000000000..8b1e602854dc51b42492c26d3923fd70cdfd1b03 Binary files /dev/null and "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/rpm/python3-smsh-1.0.0-1.noarch.rpm" differ diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/setup.py" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/setup.py" new file mode 100644 index 0000000000000000000000000000000000000000..6048e89dcc567c8ea4cfb75ebef6312e2eb3e9a3 --- /dev/null +++ "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/setup.py" @@ -0,0 +1,52 @@ +from setuptools import setup, find_packages + +# 定义项目所需的依赖 +# install_requires = [ +# 'click==8.1.7', +# 'distro==1.9.0', +# 'litellm==1.42.5', +# 'openai==1.36.1', +# 'rich==13.7.1', +# 'typer==0.12.3', +# ] + +setup( + name='smart_shell', + + version='1.0.0', + + author='Delthin', + + author_email='1059661071@qq.com', + + description='A smart command line tool for executing shell commands', + + long_description=open('README.md', encoding='utf-8').read(), + + url='https://gitee.com/Delthin/smart_shell', + + packages=find_packages(), + + # install_requires=install_requires, + + entry_points={ + 'console_scripts': [ + 'smsh=smsh.app:entry', # 'smsh' 是命令 'app:main' 是你的应用模块和主函数 + ], + }, + + license='MulanPSL2.0', + + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Mulan PSL v2', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + ], + + # 项目所需的Python版本 + python_requires='>=3.9', +) \ No newline at end of file diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smart_shell.spec" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smart_shell.spec" new file mode 100644 index 0000000000000000000000000000000000000000..0354c95c62bb2f0d95856e6ca22da5c0335279e7 --- /dev/null +++ "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smart_shell.spec" @@ -0,0 +1,46 @@ +%global python3_pkgversion 3 + +Name: python-smart_shell +Version: 1.0.0 +Release: 1%{?dist} +Summary: SmartShell command execution tool + +License: MulanPSL2.0 +URL: https://gitee.com/Delthin/smart_shell +Source: smart_shell-%{version}.tar.gz + +BuildArch: noarch +BuildRequires: python3-devel +BuildRequires: python3-setuptools +Requires: python3 +Requires: python3-pip + +%description +SmartShell is a command execution tool that integrates langchain and ReAct to provide an intelligent and convenient shell command execution experience. + +%package -n python%{python3_pkgversion}-smsh +Summary: %{summary} + +%description -n python%{python3_pkgversion}-smsh +SmartShell是一个智能命令执行工具,集成了LangChain和ReAct,提供智能且便捷的shell命令执行体验。 + +%prep +%autosetup -p1 -n smart_shell-%{version} + +%build +%py3_build + +%install +%py3_install + +%files -n python%{python3_pkgversion}-smsh +%doc README.md +%license LICENSE +%{_bindir}/* +%{python3_sitelib}/smsh/ +%{python3_sitelib}/smart_shell-*.egg-info/ +%exclude %{python3_sitelib}/tests/* + +%changelog +* Thu Aug 29 2024 Delthin <1059661071@qq.com> - 1.0.0-1 +- First release \ No newline at end of file diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/__init__.py" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/__init__.py" new file mode 100644 index 0000000000000000000000000000000000000000..37b05295f6057595e13178592ba5e3eee603632f --- /dev/null +++ "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/__init__.py" @@ -0,0 +1,2 @@ +from .app import main as main +from .app import entry as cli diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/__main__.py" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/__main__.py" new file mode 100644 index 0000000000000000000000000000000000000000..96f61ae2bcab4ca3b58d6e0c358f138d05f8c8bc --- /dev/null +++ "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/__main__.py" @@ -0,0 +1,3 @@ +from .app import entry + +entry() \ No newline at end of file diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/__version__.py" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/__version__.py" new file mode 100644 index 0000000000000000000000000000000000000000..5becc17c04a9e3ad1c2a15f53252b7bb5a7517e7 --- /dev/null +++ "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/__version__.py" @@ -0,0 +1 @@ +__version__ = "1.0.0" diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/app.py" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/app.py" new file mode 100644 index 0000000000000000000000000000000000000000..2ef882ae68551dc3676c22a8cea33613573e3f56 --- /dev/null +++ "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/app.py" @@ -0,0 +1,177 @@ +import typer +from click import Choice + +from smsh.config import cfg +from smsh.handler import Handler +from smsh.role import SystemRole, DefaultRoles +from smsh.utils import run_command, get_smsh_version + + +def main( + prompt: str = typer.Argument( + "", + show_default=False, + help="The prompt to generate completions for.", + ), + model: str = typer.Option( + cfg.get("DEFAULT_MODEL"), + help="Large language model to use.", + ), + temperature: float = typer.Option( + 0.0, + min=0.0, + max=2.0, + help="Randomness of generated output.", + ), + top_p: float = typer.Option( + 1.0, + min=0.0, + max=1.0, + help="Limits highest probable tokens (words).", + ), + md: bool = typer.Option( + cfg.get("PRETTIFY_MARKDOWN") == "true", + help="Prettify markdown output.", + ), + shell: bool = typer.Option( + True, + "--shell", + "-s", + help="Generate and execute shell commands.", + rich_help_panel="Assistance Options", + ), + interaction: bool = typer.Option( + False, + "--interaction", + "-i", + help="Interactive mode for --shell option.", + rich_help_panel="Assistance Options", + ), + describe_shell: bool = typer.Option( + False, + "--describe-shell", + "-d", + help="Describe a shell command.", + rich_help_panel="Assistance Options", + ), + role: str = typer.Option( + None, + help="System role for GPT model.", + rich_help_panel="Role Options", + ), + create_role: str = typer.Option( + None, + help="Create role.", + callback=SystemRole.create, + rich_help_panel="Role Options", + ), + show_role: str = typer.Option( + None, + help="Show role.", + callback=SystemRole.show, + rich_help_panel="Role Options", + ), + list_roles: bool = typer.Option( + False, + "--list-roles", + "-lr", + help="List roles.", + callback=SystemRole.list, + rich_help_panel="Role Options", + ), + version: bool = typer.Option( + False, + "--version", + help="Show version.", + callback=get_smsh_version, + ), + config_mode: bool = typer.Option( + False, + "-c", + "--config", + help="Enter configuration edit mode.", + rich_help_panel="Assistance Options", + ), +) -> None: + + if prompt == "" or config_mode: + typer.echo("Entering configuration mode.") + typer.echo("Current configuration:") + for key, value in cfg.items(): + typer.echo(f"{key} = {value}") + key = typer.prompt("Enter the config key you want to edit, or press 'q' to exit.") + if key == "q": + raise typer.Exit() + value = typer.prompt(f"Enter the new value for {key}") + cfg.edit(key, value) + typer.echo(f"Configuration for {key} has been updated to {value}") + raise typer.Exit() + + role_class = ( + DefaultRoles.check_get(shell, describe_shell) + if not role + else SystemRole.get(role) + ) + + if describe_shell: + Handler(DefaultRoles.DESCRIBE_SHELL.get_role(), md).handle( + prompt=prompt, + model=model, + temperature=temperature, + top_p=top_p + ) + raise typer.Exit() + + full_completion = Handler(role_class, md).handle( + prompt=prompt, + model=model, + temperature=temperature, + top_p=top_p + ) + + while shell: + option = typer.prompt( + text="[E]xecute, [D]escribe, [C]ancel, [Q]uit", + type=Choice(("e", "d", "c", "q"), case_sensitive=False), + default="e" if cfg.get("DEFAULT_EXECUTE_SHELL_CMD") == "true" else "c", + show_choices=False, + show_default=False, + ) + if option == "e": + run_command(full_completion) + elif option == "d": + Handler(DefaultRoles.DESCRIBE_SHELL.get_role(), md).handle( + full_completion, + model=model, + temperature=temperature, + top_p=top_p + ) + continue + elif option == "q": + break + if interaction: + option = typer.prompt( + text="Enter a new prompt or [Q]uit", + show_choices=False, + default="q", + show_default=False, + ) + if option == "q": + break + full_completion = Handler(role_class, md).handle( + option, + model=model, + temperature=temperature, + top_p=top_p + ) + else: + break + + +def entry() -> None: + typer.run(main) + + +if __name__ == '__main__': + entry() + diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/config.py" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/config.py" new file mode 100644 index 0000000000000000000000000000000000000000..5a09131baca408e34e607076172da9d36f8b6128 --- /dev/null +++ "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/config.py" @@ -0,0 +1,89 @@ +import os +from getpass import getpass +from pathlib import Path +from typing import Any + +import typer +from click import UsageError + +CONFIG_FOLDER = os.path.expanduser("~/.config") +SMART_SHELL_CONFIG_FOLDER = Path(CONFIG_FOLDER) / "smart_shell" +SMART_SHELL_CONFIG_PATH = SMART_SHELL_CONFIG_FOLDER / ".smshrc" +ROLE_STORAGE_PATH = SMART_SHELL_CONFIG_FOLDER / "roles" + +DEFAULT_CONFIG = { + "CHAT_CACHE_LENGTH": int(os.getenv("CHAT_CACHE_LENGTH", "100")), + "REQUEST_TIMEOUT": int(os.getenv("REQUEST_TIMEOUT", "60")), + "DEFAULT_MODEL": os.getenv("DEFAULT_MODEL", "gpt-4o"), + "DEFAULT_COLOR": os.getenv("DEFAULT_COLOR", "magenta"), + "ROLE_STORAGE_PATH": os.getenv("ROLE_STORAGE_PATH", str(ROLE_STORAGE_PATH)), + "DEFAULT_EXECUTE_SHELL_CMD": os.getenv("DEFAULT_EXECUTE_SHELL_CMD", "false"), + "DISABLE_STREAMING": os.getenv("DISABLE_STREAMING", "false"), + "CODE_THEME": os.getenv("CODE_THEME", "dracula"), + "API_BASE_URL": os.getenv("API_BASE_URL", "default"), + "PRETTIFY_MARKDOWN": os.getenv("PRETTIFY_MARKDOWN", "true"), + "USE_LITELLM": os.getenv("USE_LITELLM", "false"), + + # Set the following to use ollama + # "DEFAULT_MODEL": os.getenv("DEFAULT_MODEL", "ollama/qwen"), + # "API_BASE_URL": os.getenv("API_BASE_URL", "http://localhost:11434"), + # "USE_LITELLM": os.getenv("USE_LITELLM", "true") +} + + +class Config(dict): # type: ignore + def __init__(self, config_path: Path, **defaults: Any): + self.config_path = config_path + + if self._exists: + self._read() + has_new_config = False + for key, value in defaults.items(): + if key not in self: + has_new_config = True + self[key] = value + if has_new_config: + self._write() + else: + config_path.parent.mkdir(parents=True, exist_ok=True) + if not defaults.get("OPENAI_API_KEY") and not os.getenv("OPENAI_API_KEY"): + __api_key = getpass(prompt="Please enter your OpenAI API key: ") + defaults["OPENAI_API_KEY"] = __api_key + super().__init__(**defaults) + self._write() + + @property + def _exists(self) -> bool: + return self.config_path.exists() + + def _write(self) -> None: + with open(self.config_path, "w", encoding="utf-8") as file: + string_config = "" + for key, value in self.items(): + string_config += f"{key}={value}\n" + file.write(string_config) + + def _read(self) -> None: + with open(self.config_path, "r", encoding="utf-8") as file: + for line in file: + if line.strip() and not line.startswith("#"): + key, value = line.strip().split("=", 1) + self[key] = value + + def edit(self, key: str, value: str) -> None: + """Edit a specific configuration key.""" + self[key] = value + self._write() + + def get(self, key: str) -> str: # type: ignore + # Prioritize environment variables over config file. + value = os.getenv(key) or super().get(key) + if not value: + raise UsageError(f"Missing config key: {key}") + return value + + +cfg = Config(SMART_SHELL_CONFIG_PATH, **DEFAULT_CONFIG) + + + diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/handler.py" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/handler.py" new file mode 100644 index 0000000000000000000000000000000000000000..273999c286e07f423d21c2aa585af97a19f0fd5f --- /dev/null +++ "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/handler.py" @@ -0,0 +1,96 @@ +from typing import Any, Callable, Dict, Generator, List + +from smsh.config import cfg +from smsh.printer import MarkdownPrinter, Printer, TextPrinter +from smsh.role import SystemRole + +completion: Callable[..., Any] = lambda *args, **kwargs: Generator[Any, None, None] +base_url = cfg.get("API_BASE_URL") +use_litellm = cfg.get("USE_LITELLM") == "true" +additional_kwargs = { + "timeout": int(cfg.get("REQUEST_TIMEOUT")), + "api_key": cfg.get("OPENAI_API_KEY"), + "base_url": None if base_url == "default" else base_url, +} + +if use_litellm: + import litellm + + completion = litellm.completion + litellm.suppress_debug_info = False +else: + from openai import OpenAI + + client = OpenAI(**additional_kwargs) # type: ignore + completion = client.chat.completions.create + additional_kwargs = {} + + +class Handler: + + def __init__(self, role: SystemRole, markdown: bool) -> None: + self.role = role + + api_base_url = cfg.get("API_BASE_URL") + self.base_url = None if api_base_url == "default" else api_base_url + self.timeout = int(cfg.get("REQUEST_TIMEOUT")) + + self.markdown = "APPLY MARKDOWN" in self.role.role and markdown + self.color = cfg.get("DEFAULT_COLOR") + self.code_theme = cfg.get("CODE_THEME") + + @property + def printer(self) -> Printer: + return ( + MarkdownPrinter(self.code_theme) + if self.markdown + else TextPrinter(self.color) + ) + + def make_messages(self, prompt: str) -> List[Dict[str, str]]: + messages = [ + {"role": "system", "content": self.role.role}, + {"role": "user", "content": prompt}, + ] + return messages + + def get_completion( + self, + model: str, + temperature: float, + top_p: float, + messages: List[Dict[str, Any]], + ) -> Generator[str, None, None]: + + response = completion( + model=model, + temperature=temperature, + top_p=top_p, + messages=messages, + stream=True, + **additional_kwargs, + ) + + try: + for chunk in response: + delta = chunk.choices[0].delta + yield delta.content or "" + except KeyboardInterrupt: + response.close() + + def handle( + self, + prompt: str, + model: str, + temperature: float, + top_p: float, + ) -> str: + disable_stream = cfg.get("DISABLE_STREAMING") == "true" + messages = self.make_messages(prompt.strip()) + generator = self.get_completion( + model=model, + temperature=temperature, + top_p=top_p, + messages=messages, + ) + return self.printer(generator, not disable_stream) diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/printer.py" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/printer.py" new file mode 100644 index 0000000000000000000000000000000000000000..565b8376f2cec62de29f73fc8158384bdfbfebc0 --- /dev/null +++ "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/printer.py" @@ -0,0 +1,65 @@ +from abc import ABC, abstractmethod +from typing import Generator + +from rich.console import Console +from rich.live import Live +from rich.markdown import Markdown +from typer import secho + + +class Printer(ABC): + console = Console() + + @abstractmethod + def live_print(self, chunks: Generator[str, None, None]) -> str: + pass + + @abstractmethod + def static_print(self, text: str) -> str: + pass + + def __call__(self, chunks: Generator[str, None, None], live: bool = True) -> str: + if live: + return self.live_print(chunks) + with self.console.status("[bold green]Loading..."): + full_completion = "".join(chunks) + self.static_print(full_completion) + return full_completion + + +class MarkdownPrinter(Printer): + def __init__(self, theme: str) -> None: + self.console = Console() + self.theme = theme + + def live_print(self, chunks: Generator[str, None, None]) -> str: + full_completion = "" + with Live(console=self.console) as live: + for chunk in chunks: + full_completion += chunk + markdown = Markdown(markup=full_completion, code_theme=self.theme) + live.update(markdown, refresh=True) + return full_completion + + def static_print(self, text: str) -> str: + markdown = Markdown(markup=text, code_theme=self.theme) + self.console.print(markdown) + return text + + +class TextPrinter(Printer): + def __init__(self, color: str) -> None: + self.color = color + + def live_print(self, chunks: Generator[str, None, None]) -> str: + full_text = "" + for chunk in chunks: + full_text += chunk + secho(chunk, fg=self.color, nl=False) + else: + print() # Add new line after last chunk. + return full_text + + def static_print(self, text: str) -> str: + secho(text, fg=self.color) + return text diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/role.py" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/role.py" new file mode 100644 index 0000000000000000000000000000000000000000..7fc5f363c0d6f30be5b41f126d3d521600b9cbce --- /dev/null +++ "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/role.py" @@ -0,0 +1,163 @@ +import json +import platform +from enum import Enum +from os import getenv, pathsep +from os.path import basename +from pathlib import Path +from typing import Optional, Dict + +import typer +from click import BadArgumentUsage +from distro import name as distro_name +from smsh.config import cfg +from smsh.utils import option_callback + +SHELL_ROLE = """Provide only {shell} commands for {os} without any description. +If there is a lack of details, provide most logical solution. +Ensure the output is a valid shell command. +If multiple steps required try to combine them together using &&. +Provide only plain text without Markdown formatting. +Do not provide markdown formatting such as ```. +""" + +DESCRIBE_SHELL_ROLE = """请你用中文描述以下需求。 +为给定的shell命令提供一个简洁的单句描述。 +描述命令的每个参数和选项。 +在大约80个词内提供简短的响应。 +尽可能应用Markdown格式。 +""" +# Provide a terse, single sentence description of the given shell command. +# Describe each argument and option of the command. +# Provide short responses in about 80 words. +# APPLY MARKDOWN formatting when possible. + + +ROLE_TEMPLATE = "You are {name}\n{role}" + + +class SystemRole: + storage: Path = Path(cfg.get("ROLE_STORAGE_PATH")) + + def __init__( + self, + name: str, + role: str, + variables: Optional[Dict[str, str]] = None, + ) -> None: + self.storage.mkdir(parents=True, exist_ok=True) + self.name = name + if variables: + role = role.format(**variables) + self.role = role + + @classmethod + def create_defaults(cls) -> None: + cls.storage.parent.mkdir(parents=True, exist_ok=True) + variables = {"shell": cls._shell_name(), "os": cls._os_name()} + for default_role in ( + SystemRole("Shell Command Generator", SHELL_ROLE, variables), + SystemRole("Shell Command Descriptor", DESCRIBE_SHELL_ROLE, variables), + ): + if not default_role._exists: + default_role._save() + + @classmethod + def get(cls, name: str) -> "SystemRole": + file_path = cls.storage / f"{name}.json" + if not file_path.exists(): + raise BadArgumentUsage(f'Role "{name}" not found.') + return cls(**json.loads(file_path.read_text())) + + @classmethod + @option_callback + def create(cls, name: str) -> None: + role = typer.prompt("Enter role description") + role = cls(name, role) + role._save() + + @classmethod + @option_callback + def list(cls, _value: str) -> None: + if not cls.storage.exists(): + return + # Get all files in the folder. + files = cls.storage.glob("*") + # Sort files by last modification time in ascending order. + for path in sorted(files, key=lambda f: f.stat().st_mtime): + typer.echo(path) + + @classmethod + @option_callback + def show(cls, name: str) -> None: + typer.echo(cls.get(name).role) + + @classmethod + def get_role_name(cls, initial_message: str) -> Optional[str]: + if not initial_message: + return None + message_lines = initial_message.splitlines() + if "You are" in message_lines[0]: + return message_lines[0].split("You are ")[1].strip() + return None + + @classmethod + def _os_name(cls) -> str: + current_platform = platform.system() + if current_platform == "Linux": + return "Linux/" + distro_name(pretty=True) + if current_platform == "Windows": + return "Windows " + platform.release() + if current_platform == "Darwin": + return "Darwin/MacOS " + platform.mac_ver()[0] + return current_platform + + @classmethod + def _shell_name(cls) -> str: + current_platform = platform.system() + if current_platform in ("Windows", "nt"): + is_powershell = len(getenv("PSModulePath", "").split(pathsep)) >= 3 + return "powershell.exe" if is_powershell else "cmd.exe" + return basename(getenv("SHELL", "/bin/sh")) + + @property + def _exists(self) -> bool: + return self._file_path.exists() + + @property + def _file_path(self) -> Path: + return self.storage / f"{self.name}.json" + + def _save(self) -> None: + if self._exists: + typer.confirm( + f'Role "{self.name}" already exists, overwrite it?', + abort=True, + ) + + self.role = ROLE_TEMPLATE.format(name=self.name, role=self.role) + self._file_path.write_text(json.dumps(self.__dict__), encoding="utf-8") + + def same_role(self, initial_message: str) -> bool: + if not initial_message: + return False + return True if f"You are {self.name}" in initial_message else False + + +class DefaultRoles(Enum): + SHELL = "Shell Command Generator" + DESCRIBE_SHELL = "Shell Command Descriptor" + + @classmethod + def check_get(cls, shell: bool, describe_shell: bool) -> SystemRole: + if shell: + return SystemRole.get(DefaultRoles.SHELL.value) + if describe_shell: + return SystemRole.get(DefaultRoles.DESCRIBE_SHELL.value) + else: + raise BadArgumentUsage("No role selected.") + + def get_role(self) -> SystemRole: + return SystemRole.get(self.value) + + +SystemRole.create_defaults() diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/utils.py" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/utils.py" new file mode 100644 index 0000000000000000000000000000000000000000..4ac0e6c0dd50add1dfe99a8f2b50012c8f18bbb5 --- /dev/null +++ "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/smsh/utils.py" @@ -0,0 +1,45 @@ +import os +import platform +import shlex +from typing import Any, Callable + +import typer +from smsh.__version__ import __version__ + + +def run_command(command: str) -> None: + """ + Runs a command in the user's shell. + It is aware of the current user's $SHELL. + :param command: A shell command to run. + """ + if platform.system() == "Windows": + is_powershell = len(os.getenv("PSModulePath", "").split(os.pathsep)) >= 3 + full_command = ( + f'powershell.exe -Command "{command}"' + if is_powershell + else f'cmd.exe /c "{command}"' + ) + else: + shell = os.environ.get("SHELL", "/bin/sh") + full_command = f"{shell} -c {shlex.quote(command)}" + + os.system(full_command) + + +def option_callback(func: Callable) -> Callable: # type: ignore + def wrapper(cls: Any, value: str) -> None: + if not value: + return + func(cls, value) + raise typer.Exit() + + return wrapper + + +@option_callback +def get_smsh_version(*_args: Any) -> None: + """ + Displays the current installed version of ShellGPT + """ + typer.echo(f"SmartShell {__version__}") diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/tests/__init__.py" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/tests/__init__.py" new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/tests/simpleTest.py" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/tests/simpleTest.py" new file mode 100644 index 0000000000000000000000000000000000000000..ca9aea322ae8e5894c9a3d81c527bfb9a721650e --- /dev/null +++ "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/smart_shell/tests/simpleTest.py" @@ -0,0 +1,86 @@ +from unittest import TestCase +from unittest.mock import ANY, patch + +import typer +from typer.testing import CliRunner + +from smsh.__version__ import __version__ +from smsh.app import main +from smsh.config import cfg + +runner = CliRunner() +app = typer.Typer() +app.command()(main) + + +class SimpleTest(TestCase): + @classmethod + def setUpClass(cls): + assert cfg.get("DISABLE_STREAMING") == "false" + assert cfg.get("DEFAULT_MODEL") == "gpt-4o" + + @staticmethod + def get_arguments(prompt, **kwargs): + arguments = [prompt] + for key, value in kwargs.items(): + arguments.append(key) + if isinstance(value, bool): + continue + arguments.append(value) + return arguments + + def test_shell(self): + dict_arguments = { + "prompt": "make a commit using git", + } + result = runner.invoke(app, self.get_arguments(**dict_arguments)) + assert "git commit" in result.stdout + + def test_describe_shell(self): + dict_arguments = { + "prompt": "ls", + "--describe-shell": True, + } + result = runner.invoke(app, self.get_arguments(**dict_arguments)) + assert result.exit_code == 0 + assert "ls" in result.stdout.lower() + + @patch("smsh.handler.Handler.get_completion") + def test_model_option(self, mocked_get_completion): + dict_arguments = { + "prompt": "make a commit using git", + "--model": "gpt-4", + } + result = runner.invoke(app, self.get_arguments(**dict_arguments)) + mocked_get_completion.assert_called_once_with( + messages=ANY, + model="gpt-4", + temperature=0.0, + top_p=1.0, + ) + assert result.exit_code == 0 + + def test_version(self): + dict_arguments = { + "prompt": "", + "--version": True, + } + result = runner.invoke(app, self.get_arguments(**dict_arguments), input="d\n") + assert __version__ in result.stdout + + def test_help(self): + dict_arguments = { + "prompt": "", + "--help": True, + } + result = runner.invoke(app, self.get_arguments(**dict_arguments)) + assert result.exit_code == 0 + + def test_config(self): + dict_arguments = { + "prompt": "", + "--config": True, + } + + result = runner.invoke(app, self.get_arguments(**dict_arguments)) + assert "DEFAULT_MODEL" in result.stdout diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/\346\235\216\346\200\235\346\207\277-24b970406-\347\273\223\351\241\271\346\212\245\345\221\212.pdf" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/\346\235\216\346\200\235\346\207\277-24b970406-\347\273\223\351\241\271\346\212\245\345\221\212.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..62133799066782663d0b1c405d7160aa2c58eff7 Binary files /dev/null and "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/\346\235\216\346\200\235\346\207\277-24b970406-\347\273\223\351\241\271\346\212\245\345\221\212.pdf" differ diff --git "a/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/\346\235\216\346\200\235\346\207\277-24b970406-\351\241\271\347\233\256\347\224\263\350\257\267\344\271\246.pdf" "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/\346\235\216\346\200\235\346\207\277-24b970406-\351\241\271\347\233\256\347\224\263\350\257\267\344\271\246.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..f382546be30612d48c9c74865babf46e6b04cd03 Binary files /dev/null and "b/summer2024\345\237\272\344\272\216langchain\345\222\214ReAct\345\256\236\347\216\260\346\231\272\350\203\275shell\345\221\275\344\273\244\346\211\247\350\241\214/\346\235\216\346\200\235\346\207\277-24b970406-\351\241\271\347\233\256\347\224\263\350\257\267\344\271\246.pdf" differ