├── wut ├── __init__.py ├── __main__.py ├── prompts.py ├── wut.py └── utils.py ├── demo.gif ├── Pipfile ├── LICENSE ├── setup.py ├── README.md ├── .gitignore └── Pipfile.lock /wut/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shobrook/wut/HEAD/demo.gif -------------------------------------------------------------------------------- /wut/__main__.py: -------------------------------------------------------------------------------- 1 | # Local 2 | from wut import main 3 | 4 | if __name__ == "__main__": 5 | main() 6 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | psutil = "*" 8 | tiktoken = "*" 9 | openai = "*" 10 | rich = "*" 11 | anthropic = "*" 12 | ollama = "*" 13 | 14 | [dev-packages] 15 | 16 | [requires] 17 | python_version = "3.13" 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Jonathan Shobrook 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | try: 4 | from setuptools import setup, find_packages 5 | except ImportError: 6 | from distutils.core import setup 7 | 8 | if sys.version_info[:3] < (3, 0, 0): 9 | print("Requires Python 3 to run.") 10 | sys.exit(1) 11 | 12 | setup( 13 | name="wut-cli", 14 | version="1.0.8", 15 | description="CLI that explains the output of your last command", 16 | url="https://github.com/shobrook/wut", 17 | author="shobrook", 18 | author_email="shobrookj@gmail.com", 19 | classifiers=[ 20 | "Environment :: Console", 21 | "Intended Audience :: Developers", 22 | "Topic :: Software Development", 23 | "Topic :: Software Development :: Debuggers", 24 | "Natural Language :: English", 25 | "License :: OSI Approved :: MIT License", 26 | "Programming Language :: Python", 27 | ], 28 | keywords="openai claude cli commandline error stack-trace explain assistant terminal", 29 | include_package_data=True, 30 | packages=find_packages(), 31 | entry_points={"console_scripts": ["wut = wut.wut:main"]}, 32 | install_requires=["openai", "anthropic", "ollama", "rich", "psutil"], 33 | requires=["openai", "anthropic", "ollama", "rich", "psutil"], 34 | python_requires=">=3", 35 | license="MIT", 36 | ) 37 | -------------------------------------------------------------------------------- /wut/prompts.py: -------------------------------------------------------------------------------- 1 | EXPLAIN_PROMPT = """ 2 | You are a command-line assistant whose job is to explain the output of the most recently executed command in the terminal. 3 | Your goal is to help users understand (and potentially fix) things like stack traces, error messages, logs, or any other confusing output from the terminal. 4 | 5 | 6 | 7 | - Receive the last command in the terminal history and the previous commands before it as context. 8 | - Explain the output of the last command. 9 | - Use a clear, concise, and informative tone. 10 | - If the output is an error or warning, e.g. a stack trace or incorrect command, identify the root cause and suggest a fix. 11 | - Otherwise, if the output is something else, e.g. logs or a web response, summarize the key points. 12 | 13 | 14 | 15 | - Use Markdown to format your response. 16 | - Commands (both single and multi-line) should be placed in fenced markdown blocks. 17 | - Code snippets should be placed in fenced markdown blocks. 18 | - Only use bold for warnings or key takeaways. 19 | - Break down your response into digestible parts. 20 | - Keep your response as short as possible. No more than 5 sentences, unless the issue is complex. 21 | """ 22 | 23 | ANSWER_PROMPT = """ 24 | You are a command-line assistant whose job is to answer the user's question about the most recently executed command in the terminal. 25 | 26 | 27 | 28 | - Receive the last command in the terminal history and the previous commands before it as context. 29 | - Use a clear, concise, and informative tone. 30 | 31 | 32 | 33 | - Use Markdown to format your response. 34 | - Commands (both single and multi-line) should be placed in fenced markdown blocks. 35 | - Code snippets should be placed in fenced markdown blocks. 36 | - Only use bold for warnings or key takeaways. 37 | - Break down your response into digestible parts. 38 | - Keep your response as short as possible. No more than 5 sentences, unless the issue is complex. 39 | """ 40 | -------------------------------------------------------------------------------- /wut/wut.py: -------------------------------------------------------------------------------- 1 | # Standard library 2 | import os 3 | import argparse 4 | 5 | # Third party 6 | from rich.console import Console 7 | 8 | # Local 9 | from wut.utils import ( 10 | get_shell, 11 | get_terminal_context, 12 | explain, 13 | ) 14 | 15 | # from utils import ( 16 | # get_shell, 17 | # get_terminal_context, 18 | # explain, 19 | # ) 20 | 21 | 22 | def main(): 23 | parser = argparse.ArgumentParser( 24 | description="Understand the output of your latest terminal command." 25 | ) 26 | parser.add_argument( 27 | "--query", 28 | type=str, 29 | required=False, 30 | default="", 31 | help="A specific question about what's on your terminal.", 32 | ) 33 | parser.add_argument( 34 | "--debug", 35 | action="store_true", 36 | help="Print debug information.", 37 | ) 38 | args = parser.parse_args() 39 | console = Console() 40 | debug = lambda text: console.print(f"wut | {text}") if args.debug else None 41 | 42 | with console.status("[bold green]Trying my best..."): 43 | # Ensure environment is set up correctly 44 | if not os.environ.get("TMUX") and not os.environ.get("STY"): 45 | console.print( 46 | "[bold red]wut must be run inside a tmux or screen session.[/bold red]" 47 | ) 48 | return 49 | if ( 50 | not os.environ.get("OPENAI_API_KEY", None) 51 | and not os.environ.get("ANTHROPIC_API_KEY", None) 52 | and not os.environ.get("OLLAMA_MODEL", None) 53 | ): 54 | console.print( 55 | "[bold red]Please set your OpenAI or Anthropic API key in your environment variables. Or, alternatively, specify an Ollama model name.[/bold red]" 56 | ) 57 | return 58 | 59 | # Gather context 60 | shell = get_shell() 61 | terminal_context = get_terminal_context(shell) 62 | 63 | debug(f"Retrieved shell information:\n{shell}") 64 | debug(f"Retrieved terminal context:\n{terminal_context}") 65 | debug("Sending request to LLM...") 66 | 67 | # Get response 68 | response = explain(terminal_context, args.query) 69 | 70 | console.print(response) 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wut 2 | 3 | **CLI that explains the output of your last command.** 4 | 5 | Just type `wut` and an LLM will help you understand whatever's in your terminal. You'll be surprised how useful this can be. It can help you: 6 | 7 | - Understand stack traces 8 | - Decipher error codes 9 | - Fix incorrect commands 10 | - Summarize logs 11 | 12 | ![Demo](./demo.gif) 13 | 14 | ## Installation 15 | 16 | ```bash 17 | > pipx install wut-cli 18 | ``` 19 | 20 | 31 | 32 | Once installed, you can use OpenAI or Claude as your LLM provider. Just add the appropriate API key to your environment: 33 | 34 | ```bash 35 | > export OPENAI_API_KEY="..." 36 | > export ANTHROPIC_API_KEY="..." 37 | ``` 38 | 39 | You can also use a local model with Ollama. Just add the model name that's being served to your environment: 40 | 41 | ```bash 42 | > export OLLAMA_MODEL="..." 43 | ``` 44 | 45 | If you're using OpenAI, you can customize your model and API URL by adding the following to your environment: 46 | 47 | ```bash 48 | > export OPENAI_MODEL="..." # Default to "gpt-4o" 49 | > export OPENAI_BASE_URL="..." # Default to None 50 | ``` 51 | 52 | ## Usage 53 | 54 | `wut` must be used inside a `tmux` or `screen` session to capture the last command's output. To use it, just type `wut` after running a command: 55 | 56 | ```bash 57 | > git create-pr 58 | git: 'create-pr' is not a git command. 59 | > wut 60 | ``` 61 | 62 | You'll quickly get a brief explanation of the issue: 63 | 64 | ``` 65 | This error occurs because Git doesn't have a built-in `create-pr` command. 66 | To create a pull request, you typically need to: 67 | 68 | 1. Push your branch to the remote repository 69 | 2. Use the GitHub web interface 70 | ``` 71 | 72 | If you have a _specific question_ about your last command, you can include a query: 73 | 74 | ```bash 75 | > brew install pip 76 | ... 77 | > wut "how do i add this to my PATH variable?" 78 | ``` 79 | 80 | ## Roadmap 81 | 82 | 1. [If possible,](https://stackoverflow.com/questions/24283097/reusing-output-from-last-command-in-bash/75629157#75629157) drop the requirement of being inside a tmux or screen session. 83 | 2. Add a `--fix` option to automatically execute a command suggested by `wut`. 84 | 3. Add `wut` to Homebrew. 85 | 4. Make some unit tests. 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # System files 2 | .DS_Store 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | cover/ 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | db.sqlite3 65 | db.sqlite3-journal 66 | 67 | # Flask stuff: 68 | instance/ 69 | .webassets-cache 70 | 71 | # Scrapy stuff: 72 | .scrapy 73 | 74 | # Sphinx documentation 75 | docs/_build/ 76 | 77 | # PyBuilder 78 | .pybuilder/ 79 | target/ 80 | 81 | # Jupyter Notebook 82 | .ipynb_checkpoints 83 | 84 | # IPython 85 | profile_default/ 86 | ipython_config.py 87 | 88 | # pyenv 89 | # For a library or package, you might want to ignore these files since the code is 90 | # intended to run in multiple environments; otherwise, check them in: 91 | # .python-version 92 | 93 | # pipenv 94 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 95 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 96 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 97 | # install all needed dependencies. 98 | #Pipfile.lock 99 | 100 | # poetry 101 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 102 | # This is especially recommended for binary packages to ensure reproducibility, and is more 103 | # commonly ignored for libraries. 104 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 105 | #poetry.lock 106 | 107 | # pdm 108 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 109 | #pdm.lock 110 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 111 | # in version control. 112 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 113 | .pdm.toml 114 | .pdm-python 115 | .pdm-build/ 116 | 117 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 118 | __pypackages__/ 119 | 120 | # Celery stuff 121 | celerybeat-schedule 122 | celerybeat.pid 123 | 124 | # SageMath parsed files 125 | *.sage.py 126 | 127 | # Environments 128 | .env 129 | .venv 130 | env/ 131 | venv/ 132 | ENV/ 133 | env.bak/ 134 | venv.bak/ 135 | 136 | # Spyder project settings 137 | .spyderproject 138 | .spyproject 139 | 140 | # Rope project settings 141 | .ropeproject 142 | 143 | # mkdocs documentation 144 | /site 145 | 146 | # mypy 147 | .mypy_cache/ 148 | .dmypy.json 149 | dmypy.json 150 | 151 | # Pyre type checker 152 | .pyre/ 153 | 154 | # pytype static type analyzer 155 | .pytype/ 156 | 157 | # Cython debug symbols 158 | cython_debug/ 159 | 160 | # PyCharm 161 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 162 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 163 | # and can be added to the global gitignore or merged into this file. For a more nuclear 164 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 165 | #.idea/ 166 | -------------------------------------------------------------------------------- /wut/utils.py: -------------------------------------------------------------------------------- 1 | # Standard library 2 | import os 3 | import tempfile 4 | from collections import namedtuple 5 | from subprocess import check_output, run, CalledProcessError, DEVNULL 6 | from typing import List, Optional, Tuple 7 | 8 | # Third party 9 | from ollama import chat 10 | from psutil import Process 11 | from openai import OpenAI 12 | from anthropic import Anthropic 13 | from rich.markdown import Markdown 14 | 15 | # Local 16 | from wut.prompts import EXPLAIN_PROMPT, ANSWER_PROMPT 17 | 18 | # from prompts import EXPLAIN_PROMPT, ANSWER_PROMPT 19 | 20 | MAX_CHARS = 10000 21 | MAX_COMMANDS = 3 22 | SHELLS = ["bash", "fish", "zsh", "csh", "tcsh", "powershell", "pwsh"] 23 | 24 | Shell = namedtuple("Shell", ["path", "name", "prompt"]) 25 | Command = namedtuple("Command", ["text", "output"]) 26 | 27 | 28 | ######### 29 | # HELPERS 30 | ######### 31 | 32 | 33 | def count_chars(text: str) -> int: 34 | return len(text) 35 | 36 | 37 | def truncate_chars(text: str, reverse: bool = False) -> str: 38 | return text[-MAX_CHARS:] if reverse else text[:MAX_CHARS] 39 | 40 | 41 | def get_shell_name(shell_path: Optional[str] = None) -> Optional[str]: 42 | if not shell_path: 43 | return None 44 | 45 | if os.path.splitext(shell_path)[-1].lower() in SHELLS: 46 | return os.path.splitext(shell_path)[-1].lower() 47 | 48 | if os.path.splitext(shell_path)[0].lower() in SHELLS: 49 | return os.path.splitext(shell_path)[0].lower() 50 | 51 | if shell_path.lower() in SHELLS: 52 | return shell_path.lower() 53 | 54 | return None 55 | 56 | 57 | def get_shell_name_and_path() -> Tuple[Optional[str], Optional[str]]: 58 | path = os.environ.get("SHELL", None) or os.environ.get("TF_SHELL", None) 59 | if shell_name := get_shell_name(path): 60 | return shell_name, path 61 | 62 | proc = Process(os.getpid()) 63 | while proc is not None and proc.pid > 0: 64 | try: 65 | _path = proc.name() 66 | except TypeError: 67 | _path = proc.name 68 | 69 | if shell_name := get_shell_name(_path): 70 | return shell_name, _path 71 | 72 | try: 73 | proc = proc.parent() 74 | except TypeError: 75 | proc = proc.parent 76 | 77 | return None, path 78 | 79 | 80 | def get_shell_prompt(shell_name: str, shell_path: str) -> Optional[str]: 81 | shell_prompt = None 82 | try: 83 | if shell_name == "zsh": 84 | cmd = [ 85 | shell_path, 86 | # "-i", 87 | "-c", 88 | "print -P $PS1", 89 | ] 90 | shell_prompt = check_output(cmd, text=True, stderr=DEVNULL) 91 | elif shell_name == "bash": 92 | # Uses parameter transformation; only supported in Bash 4.4+ 93 | cmd = [ 94 | # shell_path, 95 | "echo", 96 | '"${PS1@P}"', 97 | ] 98 | shell_prompt = check_output(cmd, text=True, stderr=DEVNULL) 99 | if shell_prompt.strip() == '"${PS1@P}"': 100 | return None 101 | elif shell_name == "fish": 102 | cmd = [shell_path, "fish_prompt"] 103 | shell_prompt = check_output(cmd, text=True, stderr=DEVNULL) 104 | elif shell_name in ["csh", "tcsh"]: 105 | cmd = [shell_path, "-c", "echo $prompt"] 106 | shell_prompt = check_output(cmd, text=True, stderr=DEVNULL) 107 | elif shell_name in ["pwsh", "powershell"]: 108 | cmd = [shell_path, "-c", "Write-Host $prompt"] 109 | shell_prompt = check_output(cmd, text=True, stderr=DEVNULL) 110 | except: 111 | shell_prompt = None 112 | 113 | return shell_prompt.strip() if shell_prompt else None 114 | 115 | 116 | def get_pane_output() -> str: 117 | output_file = None 118 | output = "" 119 | try: 120 | with tempfile.NamedTemporaryFile(delete=False) as temp_file: 121 | output_file = temp_file.name 122 | 123 | if os.getenv("TMUX"): # tmux session 124 | cmd = [ 125 | "tmux", 126 | "capture-pane", 127 | "-p", 128 | "-S", 129 | # f"-{MAX_HISTORY_LINES}", 130 | "-", 131 | ] 132 | with open(output_file, "w") as f: 133 | run(cmd, stdout=f, text=True) 134 | elif os.getenv("STY"): # screen session 135 | cmd = ["screen", "-X", "hardcopy", "-h", output_file] 136 | check_output(cmd, text=True) 137 | else: 138 | return "" 139 | 140 | with open(output_file, "r", encoding="utf-8", errors="replace") as f: 141 | output = f.read() 142 | except CalledProcessError as e: 143 | pass 144 | 145 | if output_file: 146 | os.remove(output_file) 147 | 148 | return output 149 | 150 | 151 | def get_commands(pane_output: str, shell: Shell) -> List[Command]: 152 | # TODO: Handle edge cases. E.g. if you change the shell prompt in the middle of a session, 153 | # only the latest prompt will be used to split the pane output into `Command` objects. 154 | 155 | commands = [] # Order: newest to oldest 156 | buffer = [] 157 | for line in reversed(pane_output.splitlines()): 158 | if not line.strip(): 159 | continue 160 | 161 | if shell.prompt.lower() in line.lower(): 162 | command_text = line.split(shell.prompt, 1)[1].strip() 163 | command = Command(command_text, "\n".join(reversed(buffer)).strip()) 164 | commands.append(command) 165 | buffer = [] 166 | continue 167 | 168 | buffer.append(line) 169 | 170 | return commands[1:] # Exclude the wut command itself 171 | 172 | 173 | def truncate_commands(commands: List[Command]) -> List[Command]: 174 | num_chars = 0 175 | truncated_commands = [] 176 | for command in commands: 177 | command_chars = count_chars(command.text) 178 | if command_chars + num_chars > MAX_CHARS: 179 | break 180 | num_chars += command_chars 181 | 182 | output = [] 183 | for line in reversed(command.output.splitlines()): 184 | line_chars = count_chars(line) 185 | if line_chars + num_chars > MAX_CHARS: 186 | break 187 | 188 | output.append(line) 189 | num_chars += line_chars 190 | 191 | output = "\n".join(reversed(output)) 192 | command = Command(command.text, output) 193 | truncated_commands.append(command) 194 | 195 | return truncated_commands 196 | 197 | 198 | def truncate_pane_output(output: str) -> str: 199 | hit_non_empty_line = False 200 | lines = [] # Order: newest to oldest 201 | for line in reversed(output.splitlines()): 202 | if line and line.strip(): 203 | hit_non_empty_line = True 204 | 205 | if hit_non_empty_line: 206 | lines.append(line) 207 | 208 | lines = lines[1:] # Remove wut command 209 | output = "\n".join(reversed(lines)) 210 | output = truncate_chars(output, reverse=True) 211 | output = output.strip() 212 | 213 | return output 214 | 215 | 216 | def command_to_string(command: Command, shell_prompt: Optional[str] = None) -> str: 217 | shell_prompt = shell_prompt if shell_prompt else "$" 218 | command_str = f"{shell_prompt} {command.text}" 219 | command_str += f"\n{command.output}" if command.output.strip() else "" 220 | return command_str 221 | 222 | 223 | def format_output(output: str) -> str: 224 | return Markdown( 225 | output, 226 | code_theme="monokai", 227 | inline_code_lexer="python", 228 | inline_code_theme="monokai", 229 | ) 230 | 231 | 232 | def run_anthropic(system_message: str, user_message: str) -> str: 233 | anthropic = Anthropic() 234 | response = anthropic.messages.create( 235 | model="claude-3-5-sonnet-20241022", 236 | max_tokens=1024, 237 | system=system_message, 238 | messages=[{"role": "user", "content": user_message}], 239 | ) 240 | return response.content[0].text 241 | 242 | 243 | def run_openai(system_message: str, user_message: str) -> str: 244 | openai = OpenAI(base_url=os.getenv("OPENAI_BASE_URL", None)) 245 | response = openai.chat.completions.create( 246 | messages=[ 247 | {"role": "system", "content": system_message}, 248 | {"role": "user", "content": user_message}, 249 | ], 250 | model=os.getenv("OPENAI_MODEL", None) or "gpt-4o", 251 | temperature=0.7, 252 | ) 253 | return response.choices[0].message.content 254 | 255 | 256 | def run_ollama(system_message: str, user_message: str) -> str: 257 | response = chat( 258 | model=os.getenv("OLLAMA_MODEL", None), 259 | messages=[ 260 | {"role": "system", "content": system_message}, 261 | {"role": "user", "content": user_message}, 262 | ], 263 | ) 264 | return response.message.content 265 | 266 | 267 | def get_llm_provider() -> str: 268 | if os.getenv("OPENAI_API_KEY", None): # Default 269 | return "openai" 270 | 271 | if os.getenv("ANTHROPIC_API_KEY", None): 272 | return "anthropic" 273 | 274 | if os.getenv("OLLAMA_MODEL", None): 275 | return "ollama" 276 | 277 | raise ValueError("No API key found for OpenAI or Anthropic.") 278 | 279 | 280 | ###### 281 | # MAIN 282 | ###### 283 | 284 | 285 | def get_shell() -> Shell: 286 | name, path = get_shell_name_and_path() 287 | prompt = get_shell_prompt(name, path) 288 | return Shell(path, name, prompt) # NOTE: Could all be null values 289 | 290 | 291 | def get_terminal_context(shell: Shell) -> str: 292 | pane_output = get_pane_output() 293 | if not pane_output: 294 | return "No terminal output found." 295 | 296 | if not shell.prompt: 297 | # W/o the prompt, we can't reliably separate commands in terminal output 298 | pane_output = truncate_pane_output(pane_output) 299 | context = f"\n{pane_output}\n" 300 | else: 301 | commands = get_commands(pane_output, shell) 302 | commands = truncate_commands(commands[:MAX_COMMANDS]) 303 | commands = list(reversed(commands)) # Order: Oldest to newest 304 | 305 | previous_commands = commands[:-1] 306 | last_command = commands[-1] 307 | 308 | context = "\n" 309 | context += "\n" 310 | context += "\n".join( 311 | command_to_string(c, shell.prompt) for c in previous_commands 312 | ) 313 | context += "\n\n" 314 | context += "\n\n" 315 | context += command_to_string(last_command, shell.prompt) 316 | context += "\n" 317 | context += "\n" 318 | 319 | return context 320 | 321 | 322 | def build_query(context: str, query: Optional[str] = None) -> str: 323 | if not (query and query.strip()): 324 | query = "Explain the last command's output. Use the previous commands as context, if relevant, but focus on the last command." 325 | 326 | return f"{context}\n\n{query}" 327 | 328 | 329 | def explain(context: str, query: Optional[str] = None) -> str: 330 | system_message = EXPLAIN_PROMPT if not query else ANSWER_PROMPT 331 | user_message = build_query(context, query) 332 | provider = get_llm_provider() 333 | 334 | call_llm = run_openai 335 | if provider == "anthropic": 336 | call_llm = run_anthropic 337 | elif provider == "ollama": 338 | call_llm = run_ollama 339 | 340 | output = call_llm(system_message, user_message) 341 | return format_output(output) 342 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "afdb5b9a26c34169f3ed914da5b01f9c9c81dc43307675f6e351acf741b9ff1e" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.13" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "annotated-types": { 20 | "hashes": [ 21 | "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", 22 | "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" 23 | ], 24 | "markers": "python_version >= '3.8'", 25 | "version": "==0.7.0" 26 | }, 27 | "anthropic": { 28 | "hashes": [ 29 | "sha256:46775f65b723c078a2ac9e9de44a46db5c6a4fabeacfd165e5ea78e6817f4eff", 30 | "sha256:bf8b0ed8c8cb2c2118038f29c58099d2f99f7847296cafdaa853910bfff4edf4" 31 | ], 32 | "index": "pypi", 33 | "markers": "python_version >= '3.8'", 34 | "version": "==0.42.0" 35 | }, 36 | "anyio": { 37 | "hashes": [ 38 | "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48", 39 | "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352" 40 | ], 41 | "markers": "python_version >= '3.9'", 42 | "version": "==4.7.0" 43 | }, 44 | "certifi": { 45 | "hashes": [ 46 | "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", 47 | "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db" 48 | ], 49 | "markers": "python_version >= '3.6'", 50 | "version": "==2024.12.14" 51 | }, 52 | "charset-normalizer": { 53 | "hashes": [ 54 | "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621", 55 | "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", 56 | "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", 57 | "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", 58 | "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", 59 | "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", 60 | "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", 61 | "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", 62 | "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", 63 | "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", 64 | "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", 65 | "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", 66 | "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab", 67 | "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", 68 | "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", 69 | "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", 70 | "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", 71 | "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", 72 | "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62", 73 | "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", 74 | "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", 75 | "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", 76 | "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", 77 | "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", 78 | "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455", 79 | "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858", 80 | "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", 81 | "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", 82 | "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", 83 | "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", 84 | "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", 85 | "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea", 86 | "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", 87 | "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", 88 | "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", 89 | "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", 90 | "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", 91 | "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", 92 | "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", 93 | "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee", 94 | "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", 95 | "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", 96 | "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51", 97 | "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", 98 | "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8", 99 | "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", 100 | "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613", 101 | "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", 102 | "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", 103 | "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", 104 | "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", 105 | "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", 106 | "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", 107 | "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", 108 | "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", 109 | "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", 110 | "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417", 111 | "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", 112 | "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", 113 | "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", 114 | "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", 115 | "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", 116 | "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149", 117 | "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41", 118 | "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574", 119 | "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", 120 | "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f", 121 | "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", 122 | "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654", 123 | "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", 124 | "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19", 125 | "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", 126 | "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578", 127 | "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", 128 | "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", 129 | "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51", 130 | "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", 131 | "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", 132 | "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", 133 | "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", 134 | "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade", 135 | "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", 136 | "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", 137 | "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6", 138 | "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", 139 | "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", 140 | "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6", 141 | "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2", 142 | "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12", 143 | "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf", 144 | "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", 145 | "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7", 146 | "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", 147 | "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", 148 | "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", 149 | "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", 150 | "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", 151 | "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4", 152 | "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", 153 | "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", 154 | "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", 155 | "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748", 156 | "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", 157 | "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", 158 | "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482" 159 | ], 160 | "markers": "python_full_version >= '3.7.0'", 161 | "version": "==3.4.0" 162 | }, 163 | "distro": { 164 | "hashes": [ 165 | "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", 166 | "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2" 167 | ], 168 | "markers": "python_version >= '3.6'", 169 | "version": "==1.9.0" 170 | }, 171 | "h11": { 172 | "hashes": [ 173 | "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", 174 | "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761" 175 | ], 176 | "markers": "python_version >= '3.7'", 177 | "version": "==0.14.0" 178 | }, 179 | "httpcore": { 180 | "hashes": [ 181 | "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", 182 | "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd" 183 | ], 184 | "markers": "python_version >= '3.8'", 185 | "version": "==1.0.7" 186 | }, 187 | "httpx": { 188 | "hashes": [ 189 | "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0", 190 | "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2" 191 | ], 192 | "markers": "python_version >= '3.8'", 193 | "version": "==0.27.2" 194 | }, 195 | "idna": { 196 | "hashes": [ 197 | "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", 198 | "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" 199 | ], 200 | "markers": "python_version >= '3.6'", 201 | "version": "==3.10" 202 | }, 203 | "jiter": { 204 | "hashes": [ 205 | "sha256:025337859077b41548bdcbabe38698bcd93cfe10b06ff66617a48ff92c9aec60", 206 | "sha256:03c9df035d4f8d647f8c210ddc2ae0728387275340668fb30d2421e17d9a0841", 207 | "sha256:08d4c92bf480e19fc3f2717c9ce2aa31dceaa9163839a311424b6862252c943e", 208 | "sha256:0cf5dfa9956d96ff2efb0f8e9c7d055904012c952539a774305aaaf3abdf3d6c", 209 | "sha256:14601dcac4889e0a1c75ccf6a0e4baf70dbc75041e51bcf8d0e9274519df6887", 210 | "sha256:180a8aea058f7535d1c84183c0362c710f4750bef66630c05f40c93c2b152a0f", 211 | "sha256:1c0dfbd1be3cbefc7510102370d86e35d1d53e5a93d48519688b1bf0f761160a", 212 | "sha256:2dd61c5afc88a4fda7d8b2cf03ae5947c6ac7516d32b7a15bf4b49569a5c076b", 213 | "sha256:317b25e98a35ffec5c67efe56a4e9970852632c810d35b34ecdd70cc0e47b3b6", 214 | "sha256:32475a42b2ea7b344069dc1e81445cfc00b9d0e3ca837f0523072432332e9f74", 215 | "sha256:37b2998606d6dadbb5ccda959a33d6a5e853252d921fec1792fc902351bb4e2c", 216 | "sha256:3ac9f578c46f22405ff7f8b1f5848fb753cc4b8377fbec8470a7dc3997ca7566", 217 | "sha256:3b94a33a241bee9e34b8481cdcaa3d5c2116f575e0226e421bed3f7a6ea71cff", 218 | "sha256:4a9220497ca0cb1fe94e3f334f65b9b5102a0b8147646118f020d8ce1de70105", 219 | "sha256:4ab9a87f3784eb0e098f84a32670cfe4a79cb6512fd8f42ae3d0709f06405d18", 220 | "sha256:5127dc1abd809431172bc3fbe8168d6b90556a30bb10acd5ded41c3cfd6f43b6", 221 | "sha256:5672a86d55416ccd214c778efccf3266b84f87b89063b582167d803246354be4", 222 | "sha256:580ccf358539153db147e40751a0b41688a5ceb275e6f3e93d91c9467f42b2e3", 223 | "sha256:58dc9bc9767a1101f4e5e22db1b652161a225874d66f0e5cb8e2c7d1c438b587", 224 | "sha256:5a90a923338531b7970abb063cfc087eebae6ef8ec8139762007188f6bc69a9f", 225 | "sha256:653cf462db4e8c41995e33d865965e79641ef45369d8a11f54cd30888b7e6ff1", 226 | "sha256:66227a2c7b575720c1871c8800d3a0122bb8ee94edb43a5685aa9aceb2782d44", 227 | "sha256:6e5337bf454abddd91bd048ce0dca5134056fc99ca0205258766db35d0a2ea43", 228 | "sha256:70bf4c43652cc294040dbb62256c83c8718370c8b93dd93d934b9a7bf6c4f53c", 229 | "sha256:711e408732d4e9a0208008e5892c2966b485c783cd2d9a681f3eb147cf36c7ef", 230 | "sha256:76e324da7b5da060287c54f2fabd3db5f76468006c811831f051942bf68c9d44", 231 | "sha256:789361ed945d8d42850f919342a8665d2dc79e7e44ca1c97cc786966a21f627a", 232 | "sha256:79aec8172b9e3c6d05fd4b219d5de1ac616bd8da934107325a6c0d0e866a21b6", 233 | "sha256:7efe4853ecd3d6110301665a5178b9856be7e2a9485f49d91aa4d737ad2ae49e", 234 | "sha256:7f22b16b35d5c1df9dfd58843ab2cd25e6bf15191f5a236bed177afade507bfc", 235 | "sha256:83c0efd80b29695058d0fd2fa8a556490dbce9804eac3e281f373bbc99045f6c", 236 | "sha256:859e8eb3507894093d01929e12e267f83b1d5f6221099d3ec976f0c995cb6bd9", 237 | "sha256:8b9931fd36ee513c26b5bf08c940b0ac875de175341cbdd4fa3be109f0492586", 238 | "sha256:8bd2a824d08d8977bb2794ea2682f898ad3d8837932e3a74937e93d62ecbb637", 239 | "sha256:8f2d5ed877f089862f4c7aacf3a542627c1496f972a34d0474ce85ee7d939c27", 240 | "sha256:8ffc86ae5e3e6a93765d49d1ab47b6075a9c978a2b3b80f0f32628f39caa0c88", 241 | "sha256:92249669925bc1c54fcd2ec73f70f2c1d6a817928480ee1c65af5f6b81cdf12d", 242 | "sha256:99d9a1eded738299ba8e106c6779ce5c3893cffa0e32e4485d680588adae6db8", 243 | "sha256:9c63eaef32b7bebac8ebebf4dabebdbc6769a09c127294db6babee38e9f405b9", 244 | "sha256:9e1fa156ee9454642adb7e7234a383884452532bc9d53d5af2d18d98ada1d79c", 245 | "sha256:a2ecaa3c23e7a7cf86d00eda3390c232f4d533cd9ddea4b04f5d0644faf642c5", 246 | "sha256:a6c710d657c8d1d2adbbb5c0b0c6bfcec28fd35bd6b5f016395f9ac43e878a15", 247 | "sha256:a9584de0cd306072635fe4b89742bf26feae858a0683b399ad0c2509011b9dc0", 248 | "sha256:ab7f43235d71e03b941c1630f4b6e3055d46b6cb8728a17663eaac9d8e83a865", 249 | "sha256:af102d3372e917cffce49b521e4c32c497515119dc7bd8a75665e90a718bbf08", 250 | "sha256:b25bd626bde7fb51534190c7e3cb97cee89ee76b76d7585580e22f34f5e3f393", 251 | "sha256:b2dd880785088ff2ad21ffee205e58a8c1ddabc63612444ae41e5e4b321b39c0", 252 | "sha256:b426f72cd77da3fec300ed3bc990895e2dd6b49e3bfe6c438592a3ba660e41ca", 253 | "sha256:ba5bdf56969cad2019d4e8ffd3f879b5fdc792624129741d3d83fc832fef8c7d", 254 | "sha256:bf55846c7b7a680eebaf9c3c48d630e1bf51bdf76c68a5f654b8524335b0ad29", 255 | "sha256:ca1f08b8e43dc3bd0594c992fb1fd2f7ce87f7bf0d44358198d6da8034afdf84", 256 | "sha256:ca29b6371ebc40e496995c94b988a101b9fbbed48a51190a4461fcb0a68b4a36", 257 | "sha256:ca8577f6a413abe29b079bc30f907894d7eb07a865c4df69475e868d73e71c7b", 258 | "sha256:cadcc978f82397d515bb2683fc0d50103acff2a180552654bb92d6045dec2c49", 259 | "sha256:cd646c827b4f85ef4a78e4e58f4f5854fae0caf3db91b59f0d73731448a970c6", 260 | "sha256:cd73d3e740666d0e639f678adb176fad25c1bcbdae88d8d7b857e1783bb4212d", 261 | "sha256:cde031d8413842a1e7501e9129b8e676e62a657f8ec8166e18a70d94d4682855", 262 | "sha256:ce0820f4a3a59ddced7fce696d86a096d5cc48d32a4183483a17671a61edfddc", 263 | "sha256:d20be8b7f606df096e08b0b1b4a3c6f0515e8dac296881fe7461dfa0fb5ec817", 264 | "sha256:d21974d246ed0181558087cd9f76e84e8321091ebfb3a93d4c341479a736f099", 265 | "sha256:d33f94615fcaf872f7fd8cd98ac3b429e435c77619777e8a449d9d27e01134d1", 266 | "sha256:d35c864c2dff13dfd79fb070fc4fc6235d7b9b359efe340e1261deb21b9fcb66", 267 | "sha256:d5c826a221851a8dc028eb6d7d6429ba03184fa3c7e83ae01cd6d3bd1d4bd17d", 268 | "sha256:e41e75344acef3fc59ba4765df29f107f309ca9e8eace5baacabd9217e52a5ee", 269 | "sha256:e52bf98c7e727dd44f7c4acb980cb988448faeafed8433c867888268899b298b", 270 | "sha256:e6ec2be506e7d6f9527dae9ff4b7f54e68ea44a0ef6b098256ddf895218a2f8f", 271 | "sha256:e725edd0929fa79f8349ab4ec7f81c714df51dc4e991539a578e5018fa4a7152", 272 | "sha256:eaa58399c01db555346647a907b4ef6d4f584b123943be6ed5588c3f2359c9f4", 273 | "sha256:eb21aaa9a200d0a80dacc7a81038d2e476ffe473ffdd9c91eb745d623561de05", 274 | "sha256:ecff0dc14f409599bbcafa7e470c00b80f17abc14d1405d38ab02e4b42e55b57", 275 | "sha256:f557c55bc2b7676e74d39d19bcb8775ca295c7a028246175d6a8b431e70835e5", 276 | "sha256:f7200b8f7619d36aa51c803fd52020a2dfbea36ffec1b5e22cab11fd34d95a6d", 277 | "sha256:f9d471356dc16f84ed48768b8ee79f29514295c7295cb41e1133ec0b2b8d637d", 278 | "sha256:fc5adda618205bd4678b146612ce44c3cbfdee9697951f2c0ffdef1f26d72b63", 279 | "sha256:fc9043259ee430ecd71d178fccabd8c332a3bf1e81e50cae43cc2b28d19e4cb7", 280 | "sha256:ffd9fee7d0775ebaba131f7ca2e2d83839a62ad65e8e02fe2bd8fc975cedeb9e" 281 | ], 282 | "markers": "python_version >= '3.8'", 283 | "version": "==0.8.2" 284 | }, 285 | "markdown-it-py": { 286 | "hashes": [ 287 | "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", 288 | "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb" 289 | ], 290 | "markers": "python_version >= '3.8'", 291 | "version": "==3.0.0" 292 | }, 293 | "mdurl": { 294 | "hashes": [ 295 | "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", 296 | "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" 297 | ], 298 | "markers": "python_version >= '3.7'", 299 | "version": "==0.1.2" 300 | }, 301 | "ollama": { 302 | "hashes": [ 303 | "sha256:0f466e845e2205a1cbf5a2fef4640027b90beaa3b06c574426d8b6b17fd6e139", 304 | "sha256:e1db064273c739babc2dde9ea84029c4a43415354741b6c50939ddd3dd0f7ffb" 305 | ], 306 | "index": "pypi", 307 | "markers": "python_version >= '3.8' and python_version < '4.0'", 308 | "version": "==0.4.4" 309 | }, 310 | "openai": { 311 | "hashes": [ 312 | "sha256:e2910b1170a6b7f88ef491ac3a42c387f08bd3db533411f7ee391d166571d63c", 313 | "sha256:f5a035fd01e141fc743f4b0e02c41ca49be8fab0866d3b67f5f29b4f4d3c0973" 314 | ], 315 | "index": "pypi", 316 | "markers": "python_version >= '3.8'", 317 | "version": "==1.58.1" 318 | }, 319 | "psutil": { 320 | "hashes": [ 321 | "sha256:018aeae2af92d943fdf1da6b58665124897cfc94faa2ca92098838f83e1b1bca", 322 | "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377", 323 | "sha256:1924e659d6c19c647e763e78670a05dbb7feaf44a0e9c94bf9e14dfc6ba50468", 324 | "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3", 325 | "sha256:384636b1a64b47814437d1173be1427a7c83681b17a450bfc309a1953e329603", 326 | "sha256:6d4281f5bbca041e2292be3380ec56a9413b790579b8e593b1784499d0005dac", 327 | "sha256:8be07491f6ebe1a693f17d4f11e69d0dc1811fa082736500f649f79df7735303", 328 | "sha256:8df0178ba8a9e5bc84fed9cfa61d54601b371fbec5c8eebad27575f1e105c0d4", 329 | "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160", 330 | "sha256:9ccc4316f24409159897799b83004cb1e24f9819b0dcf9c0b68bdcb6cefee6a8", 331 | "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003", 332 | "sha256:c777eb75bb33c47377c9af68f30e9f11bc78e0f07fbf907be4a5d70b2fe5f030", 333 | "sha256:ca9609c77ea3b8481ab005da74ed894035936223422dc591d6772b147421f777", 334 | "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5", 335 | "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53", 336 | "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649", 337 | "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8" 338 | ], 339 | "index": "pypi", 340 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", 341 | "version": "==6.1.1" 342 | }, 343 | "pydantic": { 344 | "hashes": [ 345 | "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d", 346 | "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06" 347 | ], 348 | "markers": "python_version >= '3.8'", 349 | "version": "==2.10.4" 350 | }, 351 | "pydantic-core": { 352 | "hashes": [ 353 | "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278", 354 | "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50", 355 | "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9", 356 | "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f", 357 | "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", 358 | "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc", 359 | "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54", 360 | "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630", 361 | "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9", 362 | "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236", 363 | "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", 364 | "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", 365 | "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b", 366 | "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048", 367 | "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", 368 | "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", 369 | "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", 370 | "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd", 371 | "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4", 372 | "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7", 373 | "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7", 374 | "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", 375 | "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e", 376 | "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa", 377 | "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6", 378 | "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962", 379 | "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", 380 | "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f", 381 | "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474", 382 | "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5", 383 | "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459", 384 | "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf", 385 | "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a", 386 | "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c", 387 | "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76", 388 | "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362", 389 | "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4", 390 | "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", 391 | "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320", 392 | "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118", 393 | "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96", 394 | "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306", 395 | "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046", 396 | "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3", 397 | "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", 398 | "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af", 399 | "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", 400 | "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67", 401 | "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a", 402 | "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", 403 | "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35", 404 | "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", 405 | "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151", 406 | "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b", 407 | "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", 408 | "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133", 409 | "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", 410 | "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145", 411 | "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15", 412 | "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", 413 | "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc", 414 | "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", 415 | "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", 416 | "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", 417 | "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5", 418 | "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", 419 | "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", 420 | "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8", 421 | "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", 422 | "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da", 423 | "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", 424 | "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc", 425 | "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993", 426 | "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656", 427 | "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4", 428 | "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c", 429 | "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb", 430 | "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d", 431 | "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", 432 | "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e", 433 | "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", 434 | "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc", 435 | "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a", 436 | "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9", 437 | "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506", 438 | "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b", 439 | "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1", 440 | "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d", 441 | "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99", 442 | "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", 443 | "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31", 444 | "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c", 445 | "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", 446 | "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", 447 | "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308", 448 | "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2", 449 | "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228", 450 | "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b", 451 | "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", 452 | "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad" 453 | ], 454 | "markers": "python_version >= '3.8'", 455 | "version": "==2.27.2" 456 | }, 457 | "pygments": { 458 | "hashes": [ 459 | "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", 460 | "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a" 461 | ], 462 | "markers": "python_version >= '3.8'", 463 | "version": "==2.18.0" 464 | }, 465 | "regex": { 466 | "hashes": [ 467 | "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", 468 | "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", 469 | "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84" 470 | ], 471 | "markers": "python_version >= '3.8'", 472 | "version": "==2024.11.6" 473 | }, 474 | "requests": { 475 | "hashes": [ 476 | "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", 477 | "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" 478 | ], 479 | "markers": "python_version >= '3.8'", 480 | "version": "==2.32.3" 481 | }, 482 | "rich": { 483 | "hashes": [ 484 | "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", 485 | "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90" 486 | ], 487 | "index": "pypi", 488 | "markers": "python_full_version >= '3.8.0'", 489 | "version": "==13.9.4" 490 | }, 491 | "sniffio": { 492 | "hashes": [ 493 | "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", 494 | "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" 495 | ], 496 | "markers": "python_version >= '3.7'", 497 | "version": "==1.3.1" 498 | }, 499 | "tiktoken": { 500 | "hashes": [ 501 | "sha256:02be1666096aff7da6cbd7cdaa8e7917bfed3467cd64b38b1f112e96d3b06a24", 502 | "sha256:1473cfe584252dc3fa62adceb5b1c763c1874e04511b197da4e6de51d6ce5a02", 503 | "sha256:18228d624807d66c87acd8f25fc135665617cab220671eb65b50f5d70fa51f69", 504 | "sha256:25e13f37bc4ef2d012731e93e0fef21dc3b7aea5bb9009618de9a4026844e560", 505 | "sha256:294440d21a2a51e12d4238e68a5972095534fe9878be57d905c476017bff99fc", 506 | "sha256:2efaf6199717b4485031b4d6edb94075e4d79177a172f38dd934d911b588d54a", 507 | "sha256:326624128590def898775b722ccc327e90b073714227175ea8febbc920ac0a99", 508 | "sha256:4177faa809bd55f699e88c96d9bb4635d22e3f59d635ba6fd9ffedf7150b9953", 509 | "sha256:5376b6f8dc4753cd81ead935c5f518fa0fbe7e133d9e25f648d8c4dabdd4bad7", 510 | "sha256:5637e425ce1fc49cf716d88df3092048359a4b3bbb7da762840426e937ada06d", 511 | "sha256:56edfefe896c8f10aba372ab5706b9e3558e78db39dd497c940b47bf228bc419", 512 | "sha256:6adc8323016d7758d6de7313527f755b0fc6c72985b7d9291be5d96d73ecd1e1", 513 | "sha256:6b231f5e8982c245ee3065cd84a4712d64692348bc609d84467c57b4b72dcbc5", 514 | "sha256:6b2ddbc79a22621ce8b1166afa9f9a888a664a579350dc7c09346a3b5de837d9", 515 | "sha256:7e17807445f0cf1f25771c9d86496bd8b5c376f7419912519699f3cc4dc5c12e", 516 | "sha256:845287b9798e476b4d762c3ebda5102be87ca26e5d2c9854002825d60cdb815d", 517 | "sha256:881839cfeae051b3628d9823b2e56b5cc93a9e2efb435f4cf15f17dc45f21586", 518 | "sha256:886f80bd339578bbdba6ed6d0567a0d5c6cfe198d9e587ba6c447654c65b8edc", 519 | "sha256:9269348cb650726f44dd3bbb3f9110ac19a8dcc8f54949ad3ef652ca22a38e21", 520 | "sha256:9a58deb7075d5b69237a3ff4bb51a726670419db6ea62bdcd8bd80c78497d7ab", 521 | "sha256:9ccbb2740f24542534369c5635cfd9b2b3c2490754a78ac8831d99f89f94eeb2", 522 | "sha256:9fb0e352d1dbe15aba082883058b3cce9e48d33101bdaac1eccf66424feb5b47", 523 | "sha256:b07e33283463089c81ef1467180e3e00ab00d46c2c4bbcef0acab5f771d6695e", 524 | "sha256:b591fb2b30d6a72121a80be24ec7a0e9eb51c5500ddc7e4c2496516dd5e3816b", 525 | "sha256:c94ff53c5c74b535b2cbf431d907fc13c678bbd009ee633a2aca269a04389f9a", 526 | "sha256:d2908c0d043a7d03ebd80347266b0e58440bdef5564f84f4d29fb235b5df3b04", 527 | "sha256:d622d8011e6d6f239297efa42a2657043aaed06c4f68833550cac9e9bc723ef1", 528 | "sha256:d8c2d0e5ba6453a290b86cd65fc51fedf247e1ba170191715b049dac1f628005", 529 | "sha256:d8f3192733ac4d77977432947d563d7e1b310b96497acd3c196c9bddb36ed9db", 530 | "sha256:f13d13c981511331eac0d01a59b5df7c0d4060a8be1e378672822213da51e0a2", 531 | "sha256:fe9399bdc3f29d428f16a2f86c3c8ec20be3eac5f53693ce4980371c3245729b" 532 | ], 533 | "index": "pypi", 534 | "markers": "python_version >= '3.9'", 535 | "version": "==0.8.0" 536 | }, 537 | "tqdm": { 538 | "hashes": [ 539 | "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", 540 | "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2" 541 | ], 542 | "markers": "python_version >= '3.7'", 543 | "version": "==4.67.1" 544 | }, 545 | "typing-extensions": { 546 | "hashes": [ 547 | "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", 548 | "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" 549 | ], 550 | "markers": "python_version >= '3.8'", 551 | "version": "==4.12.2" 552 | }, 553 | "urllib3": { 554 | "hashes": [ 555 | "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", 556 | "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9" 557 | ], 558 | "markers": "python_version >= '3.8'", 559 | "version": "==2.2.3" 560 | } 561 | }, 562 | "develop": {} 563 | } 564 | --------------------------------------------------------------------------------