ayechat-dev 0.36.9.20260204003405__tar.gz → 0.36.9.20260204171331__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ayechat_dev-0.36.9.20260204003405/src/ayechat_dev.egg-info → ayechat_dev-0.36.9.20260204171331}/PKG-INFO +1 -1
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/pyproject.toml +1 -1
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/controller/command_handlers.py +21 -2
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/controller/llm_handler.py +55 -3
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/controller/repl.py +5 -1
- ayechat_dev-0.36.9.20260204171331/src/aye/model/autodiff_config.py +32 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/snapshot/__init__.py +98 -0
- ayechat_dev-0.36.9.20260204171331/src/aye/plugins/databricks_model.py +311 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/presenter/repl_ui.py +5 -4
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331/src/ayechat_dev.egg-info}/PKG-INFO +1 -1
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/ayechat_dev.egg-info/SOURCES.txt +2 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.github/dependabot.yml +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.github/workflows/build-windows-installer.yml +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.github/workflows/message-releases-to-discord.yml +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.github/workflows/pylint.yml +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.github/workflows/python-publish-dev.yml +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.github/workflows/python-publish.yml +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.github/workflows/python-testing.yml +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.github/workflows/test-homebrew.yml +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.github/workflows/test-nix-github.yml +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.github/workflows/test-nix.yml +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.github/workflows/test-windows-installer.yml +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.github/workflows/update-homebrew.yml +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.gitignore +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/.pylintrc +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/BUILD.md +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/DISCLAIMER +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/Formula/aye-chat.rb +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/LICENSE +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/README.md +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/assets/aye-chat.ico +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/aye-chat.spec +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/ayechat.nix +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/flake.lock +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/flake.nix +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/installer.iss +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/publish_pypi.sh +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/requirements.txt +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/run_tests.cmd +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/run_tests.sh +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/setup.cfg +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/.gitignore +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/__init__.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/__main__.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/__main_chat__.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/controller/__init__.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/controller/commands.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/controller/llm_invoker.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/controller/plugin_manager.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/controller/tutorial.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/controller/util.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/__init__.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/api.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/ast_chunker.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/auth.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/config.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/download_plugins.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/file_processor.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/ignore_patterns.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/index_manager/__init__.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/index_manager/index_manager.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/index_manager/index_manager_executor.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/index_manager/index_manager_file_ops.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/index_manager/index_manager_state.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/index_manager/index_manager_utils.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/json_extractor.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/models.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/offline_llm_manager.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/onnx_manager.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/snapshot/base.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/snapshot/file_backend.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/snapshot/git_ref_backend.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/source_collector.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/telemetry.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/vector_db.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/version_checker.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/model/write_validator.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/plugins/__init__.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/plugins/at_file_completer.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/plugins/auto_detect_mask.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/plugins/completer.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/plugins/local_model.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/plugins/offline_llm.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/plugins/plugin_base.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/plugins/shell_executor.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/plugins/slash_completer.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/presenter/__init__.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/presenter/cli_ui.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/presenter/diff_presenter.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/presenter/streaming_ui.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/presenter/ui_utils.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/ayechat_dev.egg-info/dependency_links.txt +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/ayechat_dev.egg-info/entry_points.txt +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/ayechat_dev.egg-info/requires.txt +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/ayechat_dev.egg-info/top_level.txt +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/.gitignore +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/config/unittest-env.sh +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/e2e/test_chat_workflow.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_api.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_ast_chunker.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_at_file_completer.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_auth.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_auth_uat_1.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_auto_detect_mask.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_chromadb_corruption_recovery.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_cli.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_command_handlers.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_commands.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_completer_plugin.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_config.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_diff_presenter.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_download_plugins.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_file_processor.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_git_ref_backend.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_index_manager.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_index_manager_executor.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_index_manager_more.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_index_manager_state.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_index_manager_utils.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_llm_handler.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_llm_invoker.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_local_model_plugin.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_offline_llm.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_offline_llm_manager.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_onnx_manager.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_plugin_base.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_plugin_manager.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_presenter.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_rag_context_retrieval.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_repl.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_service.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_shell_executor_plugin.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_slash_completer.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_snapshot.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_source_collector.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_streaming_ui.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_telemetry.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_tutorial.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_ui_utils.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_util.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_vector_db.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_version_checker.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/test_write_validator.py +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/ua/api_tests.md +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/ua/auth_tests.md +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/ua/demo_tests.md +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/ua/download_plugins_tests.md +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/ua/plugin_tests.md +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/ua/service_tests.md +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/ua/snapshot_tests.md +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/ua/source_collector_tests.md +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/tests/ua/ui_tests.md +0 -0
- {ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/version_info.txt +0 -0
|
@@ -8,7 +8,7 @@ description = "Aye Chat: Terminal-first AI Code Generator"
|
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
requires-python = ">=3.10, <3.14"
|
|
10
10
|
dependencies = [ "typer>=0.20.0", "httpx>=0.28.1", "keyring>=25.7.0", "prompt-toolkit>=3.0.52", "pathspec>=0.12.1", "chromadb>=1.3.5", "rapidfuzz",]
|
|
11
|
-
version = "0.36.9.
|
|
11
|
+
version = "0.36.9.20260204171331"
|
|
12
12
|
[[project.authors]]
|
|
13
13
|
name = "Acrotron, Inc."
|
|
14
14
|
email = "info@acrotron.com"
|
|
@@ -145,6 +145,25 @@ def handle_debug_command(tokens: list):
|
|
|
145
145
|
rprint(f"[yellow]Debug mode is {current.title()}[/]")
|
|
146
146
|
|
|
147
147
|
|
|
148
|
+
def handle_autodiff_command(tokens: list):
|
|
149
|
+
"""Handle the 'autodiff' command for toggling automatic diff display.
|
|
150
|
+
|
|
151
|
+
When autodiff is enabled, diffs are automatically displayed for every
|
|
152
|
+
file modified by an LLM response.
|
|
153
|
+
"""
|
|
154
|
+
if len(tokens) > 1:
|
|
155
|
+
val = tokens[1].lower()
|
|
156
|
+
if val in ("on", "off"):
|
|
157
|
+
set_user_config("autodiff", val)
|
|
158
|
+
rprint(f"[green]Autodiff set to {val.title()}[/]")
|
|
159
|
+
else:
|
|
160
|
+
rprint("[red]Usage: autodiff on|off[/]")
|
|
161
|
+
else:
|
|
162
|
+
current = get_user_config("autodiff", "off")
|
|
163
|
+
rprint(f"[yellow]Autodiff is {current.title()}[/]")
|
|
164
|
+
rprint("[dim]When on, diffs are shown automatically after each LLM file update.[/]")
|
|
165
|
+
|
|
166
|
+
|
|
148
167
|
def handle_completion_command(tokens: list) -> Optional[str]:
|
|
149
168
|
"""Handle the 'completion' command for switching completion styles.
|
|
150
169
|
|
|
@@ -264,9 +283,9 @@ def handle_llm_command(session: Optional[PromptSession], tokens: list[str]) -> N
|
|
|
264
283
|
|
|
265
284
|
# Show status message
|
|
266
285
|
if final_url and final_key:
|
|
267
|
-
rprint("\n[green]
|
|
286
|
+
rprint("\n[green] OpenAI-compatible endpoint is configured and active.[/]")
|
|
268
287
|
else:
|
|
269
|
-
rprint("\n[yellow]
|
|
288
|
+
rprint("\n[yellow] Both URL and KEY are required for the local LLM endpoint to be active.[/]")
|
|
270
289
|
|
|
271
290
|
|
|
272
291
|
def _expand_file_patterns(patterns: list[str], conf: Any) -> list[str]:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from typing import Any, Optional
|
|
2
|
+
from typing import Any, Optional, List
|
|
3
3
|
|
|
4
4
|
from rich import print as rprint
|
|
5
5
|
from rich.console import Console
|
|
@@ -11,10 +11,12 @@ from aye.presenter.repl_ui import (
|
|
|
11
11
|
print_files_updated,
|
|
12
12
|
print_error
|
|
13
13
|
)
|
|
14
|
-
from aye.
|
|
14
|
+
from aye.presenter import diff_presenter
|
|
15
|
+
from aye.model.snapshot import apply_updates, get_diff_base_for_file
|
|
15
16
|
from aye.model.file_processor import filter_unchanged_files, make_paths_relative
|
|
16
17
|
from aye.model.models import LLMResponse
|
|
17
18
|
from aye.model.auth import get_user_config
|
|
19
|
+
from aye.model.autodiff_config import is_autodiff_enabled
|
|
18
20
|
from aye.model.write_validator import (
|
|
19
21
|
check_files_against_ignore_patterns,
|
|
20
22
|
is_strict_mode_enabled,
|
|
@@ -52,6 +54,51 @@ def _maybe_print_restore_tip(conf: Any, console: Console) -> None:
|
|
|
52
54
|
console.print(Padding(msg, (0, 4, 0, 4)))
|
|
53
55
|
|
|
54
56
|
|
|
57
|
+
def _run_autodiff(updated_files: List[dict], batch_id: str, conf: Any, console: Console) -> None:
|
|
58
|
+
"""Display diffs for all updated files against their snapshot versions.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
updated_files: List of file dicts with 'file_name' keys
|
|
62
|
+
batch_id: The batch identifier from apply_updates()
|
|
63
|
+
conf: Configuration object with root path
|
|
64
|
+
console: Rich console for output
|
|
65
|
+
"""
|
|
66
|
+
verbose = getattr(conf, 'verbose', False)
|
|
67
|
+
debug = get_user_config("debug", "off").lower() == "on"
|
|
68
|
+
|
|
69
|
+
console.print(Padding("[dim]───── Auto-diff (autodiff=on) ─────[/]", (1, 0, 0, 0)))
|
|
70
|
+
|
|
71
|
+
for item in updated_files:
|
|
72
|
+
file_name = item.get("file_name")
|
|
73
|
+
if not file_name:
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
file_path = Path(file_name)
|
|
77
|
+
|
|
78
|
+
# Get the snapshot reference for this file
|
|
79
|
+
diff_base = get_diff_base_for_file(batch_id, file_path)
|
|
80
|
+
|
|
81
|
+
if diff_base is None:
|
|
82
|
+
if verbose or debug:
|
|
83
|
+
rprint(f"[yellow]Warning: Could not find snapshot for {file_name}, skipping autodiff[/]")
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
snapshot_ref, is_git_ref = diff_base
|
|
87
|
+
|
|
88
|
+
# Print file header
|
|
89
|
+
console.print(f"\n[bold cyan]{file_name}[/]")
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
# show_diff expects: (current_file, snapshot_ref, is_stash_ref)
|
|
93
|
+
# For autodiff, we diff the current (new) file against the snapshot (old)
|
|
94
|
+
diff_presenter.show_diff(file_path, snapshot_ref, is_stash_ref=is_git_ref)
|
|
95
|
+
except Exception as e:
|
|
96
|
+
if verbose or debug:
|
|
97
|
+
rprint(f"[yellow]Warning: Could not show diff for {file_name}: {e}[/]")
|
|
98
|
+
|
|
99
|
+
console.print(Padding("[dim]───── End auto-diff ─────[/]", (1, 0, 0, 0)))
|
|
100
|
+
|
|
101
|
+
|
|
55
102
|
def process_llm_response(
|
|
56
103
|
response: LLMResponse,
|
|
57
104
|
conf: Any,
|
|
@@ -118,12 +165,17 @@ def process_llm_response(
|
|
|
118
165
|
else:
|
|
119
166
|
# Apply updates to the model (Model update)
|
|
120
167
|
try:
|
|
121
|
-
apply_updates(updated_files, prompt)
|
|
168
|
+
batch_id = apply_updates(updated_files, prompt)
|
|
122
169
|
file_names = [item.get("file_name") for item in updated_files if "file_name" in item]
|
|
123
170
|
if file_names:
|
|
124
171
|
# Update the view
|
|
125
172
|
print_files_updated(console, file_names)
|
|
126
173
|
_maybe_print_restore_tip(conf, console)
|
|
174
|
+
|
|
175
|
+
# Run autodiff if enabled
|
|
176
|
+
if is_autodiff_enabled():
|
|
177
|
+
_run_autodiff(updated_files, batch_id, conf, console)
|
|
178
|
+
|
|
127
179
|
except Exception as e:
|
|
128
180
|
rprint(f"[red]Error applying updates:[/] {e}")
|
|
129
181
|
|
{ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/controller/repl.py
RENAMED
|
@@ -42,6 +42,7 @@ from aye.controller.command_handlers import (
|
|
|
42
42
|
handle_with_command,
|
|
43
43
|
handle_blog_command,
|
|
44
44
|
handle_llm_command,
|
|
45
|
+
handle_autodiff_command,
|
|
45
46
|
)
|
|
46
47
|
|
|
47
48
|
DEBUG = False
|
|
@@ -285,7 +286,7 @@ def _execute_forced_shell_command(command: str, args: List[str], conf: Any) -> N
|
|
|
285
286
|
def chat_repl(conf: Any) -> None:
|
|
286
287
|
is_first_run = run_first_time_tutorial_if_needed()
|
|
287
288
|
|
|
288
|
-
BUILTIN_COMMANDS = ["with", "blog", "new", "history", "diff", "restore", "undo", "keep", "model", "verbose", "debug", "completion", "exit", "quit", ":q", "help", "cd", "db", "llm"]
|
|
289
|
+
BUILTIN_COMMANDS = ["with", "blog", "new", "history", "diff", "restore", "undo", "keep", "model", "verbose", "debug", "autodiff", "completion", "exit", "quit", ":q", "help", "cd", "db", "llm"]
|
|
289
290
|
|
|
290
291
|
# Get the completion style setting
|
|
291
292
|
completion_style = get_user_config("completion_style", "readline").lower()
|
|
@@ -416,6 +417,9 @@ def chat_repl(conf: Any) -> None:
|
|
|
416
417
|
elif lowered_first == "debug":
|
|
417
418
|
telemetry.record_command("debug", has_args=len(tokens) > 1, prefix=_AYE_PREFIX)
|
|
418
419
|
handle_debug_command(tokens)
|
|
420
|
+
elif lowered_first == "autodiff":
|
|
421
|
+
telemetry.record_command("autodiff", has_args=len(tokens) > 1, prefix=_AYE_PREFIX)
|
|
422
|
+
handle_autodiff_command(tokens)
|
|
419
423
|
elif lowered_first == "completion":
|
|
420
424
|
telemetry.record_command("completion", has_args=len(tokens) > 1, prefix=_AYE_PREFIX)
|
|
421
425
|
new_style = handle_completion_command(tokens)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Autodiff configuration for automatic diff display after LLM changes.
|
|
2
|
+
|
|
3
|
+
This module provides functionality to check if autodiff mode is enabled.
|
|
4
|
+
When enabled, diffs are automatically displayed for every file modified
|
|
5
|
+
by an LLM response.
|
|
6
|
+
|
|
7
|
+
See: autodiff.md for the full design plan.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from aye.model.auth import get_user_config
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Config key for autodiff mode
|
|
14
|
+
AUTODIFF_KEY = "autodiff"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def is_autodiff_enabled() -> bool:
|
|
18
|
+
"""Check if autodiff mode is enabled.
|
|
19
|
+
|
|
20
|
+
When enabled, diffs are automatically displayed for every file
|
|
21
|
+
modified by an LLM response, immediately after the optimistic
|
|
22
|
+
write is applied.
|
|
23
|
+
|
|
24
|
+
Can be set via:
|
|
25
|
+
- Environment variable: AYE_AUTODIFF=on
|
|
26
|
+
- Config file (~/.ayecfg): autodiff=on
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
True if autodiff mode is enabled, False otherwise (default)
|
|
30
|
+
"""
|
|
31
|
+
value = get_user_config(AUTODIFF_KEY, "off")
|
|
32
|
+
return str(value).lower() in ("on", "true", "1", "yes")
|
|
@@ -7,6 +7,7 @@ Note:
|
|
|
7
7
|
- Git stash snapshots (GitStashBackend) are intentionally NOT used.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
import json
|
|
10
11
|
import subprocess
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
from typing import Dict, List, Optional, Tuple, Union
|
|
@@ -32,6 +33,7 @@ __all__ = [
|
|
|
32
33
|
"delete_snapshot",
|
|
33
34
|
"prune_snapshots",
|
|
34
35
|
"cleanup_snapshots",
|
|
36
|
+
"get_diff_base_for_file",
|
|
35
37
|
# Utilities
|
|
36
38
|
"get_backend",
|
|
37
39
|
"reset_backend",
|
|
@@ -145,6 +147,102 @@ def cleanup_snapshots(older_than_days: int = 30) -> int:
|
|
|
145
147
|
return get_backend().cleanup_snapshots(older_than_days)
|
|
146
148
|
|
|
147
149
|
|
|
150
|
+
def get_diff_base_for_file(batch_id: str, file_path: Path) -> Optional[Tuple[str, bool]]:
|
|
151
|
+
"""Return the snapshot reference for a file in a given batch.
|
|
152
|
+
|
|
153
|
+
This provides a backend-agnostic way to get a reference suitable for
|
|
154
|
+
diff_presenter.show_diff().
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
batch_id: The batch identifier returned by apply_updates()
|
|
158
|
+
file_path: The file path to get the snapshot reference for
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Tuple of (snapshot_ref, is_git_ref) where:
|
|
162
|
+
- snapshot_ref: For FileBasedBackend, a filesystem path to the snapshot file.
|
|
163
|
+
For GitRefBackend, a 'refname:repo_rel_path' string.
|
|
164
|
+
- is_git_ref: True if snapshot_ref is a git ref format, False for filesystem path.
|
|
165
|
+
Returns None if the file is not found in the snapshot.
|
|
166
|
+
"""
|
|
167
|
+
backend = get_backend()
|
|
168
|
+
|
|
169
|
+
if isinstance(backend, FileBasedBackend):
|
|
170
|
+
return _get_diff_base_file_backend(backend, batch_id, file_path)
|
|
171
|
+
elif isinstance(backend, GitRefBackend):
|
|
172
|
+
return _get_diff_base_git_backend(backend, batch_id, file_path)
|
|
173
|
+
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _get_diff_base_file_backend(
|
|
178
|
+
backend: FileBasedBackend, batch_id: str, file_path: Path
|
|
179
|
+
) -> Optional[Tuple[str, bool]]:
|
|
180
|
+
"""Get diff base for FileBasedBackend.
|
|
181
|
+
|
|
182
|
+
Reads the metadata.json from the snapshot batch directory to find
|
|
183
|
+
the snapshot path for the given file.
|
|
184
|
+
"""
|
|
185
|
+
# Find the batch directory matching the batch_id
|
|
186
|
+
# batch_id format is like "001_20231201T120000"
|
|
187
|
+
batch_dir = None
|
|
188
|
+
if backend.snap_root.is_dir():
|
|
189
|
+
for dir_path in backend.snap_root.iterdir():
|
|
190
|
+
if dir_path.is_dir() and dir_path.name == batch_id:
|
|
191
|
+
batch_dir = dir_path
|
|
192
|
+
break
|
|
193
|
+
|
|
194
|
+
if batch_dir is None:
|
|
195
|
+
return None
|
|
196
|
+
|
|
197
|
+
meta_file = batch_dir / "metadata.json"
|
|
198
|
+
if not meta_file.is_file():
|
|
199
|
+
return None
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
meta = json.loads(meta_file.read_text(encoding="utf-8"))
|
|
203
|
+
except (json.JSONDecodeError, OSError):
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
# Resolve the target file path for comparison
|
|
207
|
+
file_resolved = file_path.resolve()
|
|
208
|
+
|
|
209
|
+
# Find the matching entry in metadata
|
|
210
|
+
for entry in meta.get("files", []):
|
|
211
|
+
original_path = entry.get("original")
|
|
212
|
+
snapshot_path = entry.get("snapshot")
|
|
213
|
+
|
|
214
|
+
if not original_path or not snapshot_path:
|
|
215
|
+
continue
|
|
216
|
+
|
|
217
|
+
if Path(original_path).resolve() == file_resolved:
|
|
218
|
+
# Return the snapshot path as a string, not a git ref
|
|
219
|
+
return (snapshot_path, False)
|
|
220
|
+
|
|
221
|
+
return None
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _get_diff_base_git_backend(
|
|
225
|
+
backend: GitRefBackend, batch_id: str, file_path: Path
|
|
226
|
+
) -> Optional[Tuple[str, bool]]:
|
|
227
|
+
"""Get diff base for GitRefBackend.
|
|
228
|
+
|
|
229
|
+
Returns a ref:path format suitable for git show.
|
|
230
|
+
"""
|
|
231
|
+
# Construct the refname from batch_id
|
|
232
|
+
refname = f"{backend.REF_NAMESPACE}/{batch_id}"
|
|
233
|
+
|
|
234
|
+
# Get repo-relative path
|
|
235
|
+
try:
|
|
236
|
+
file_resolved = file_path.resolve()
|
|
237
|
+
rel_path = file_resolved.relative_to(backend.git_root).as_posix()
|
|
238
|
+
except ValueError:
|
|
239
|
+
# File is outside git root
|
|
240
|
+
return None
|
|
241
|
+
|
|
242
|
+
# Return the git ref format
|
|
243
|
+
return (f"{refname}:{rel_path}", True)
|
|
244
|
+
|
|
245
|
+
|
|
148
246
|
# ------------------------------------------------------------------
|
|
149
247
|
# Legacy helper functions (for backward compatibility with tests)
|
|
150
248
|
# ------------------------------------------------------------------
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
from typing import Dict, Any, Optional
|
|
4
|
+
import httpx
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import traceback
|
|
7
|
+
|
|
8
|
+
from rich import print as rprint
|
|
9
|
+
|
|
10
|
+
from .plugin_base import Plugin
|
|
11
|
+
from aye.model.config import SYSTEM_PROMPT, MODELS, DEFAULT_MAX_OUTPUT_TOKENS
|
|
12
|
+
from aye.model.auth import get_user_config
|
|
13
|
+
from aye.controller.util import is_truncated_json
|
|
14
|
+
|
|
15
|
+
LLM_TIMEOUT = 600.0
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Message shown when LLM response is truncated due to output token limits
|
|
19
|
+
TRUNCATED_RESPONSE_MESSAGE = (
|
|
20
|
+
"It looks like my response was cut off because it exceeded the output limit. "
|
|
21
|
+
"This usually happens when you ask me to generate or modify many files at once.\n\n"
|
|
22
|
+
"**To fix this, please try:**\n"
|
|
23
|
+
"1. Break your request into smaller parts (e.g., one file at a time)\n"
|
|
24
|
+
"2. Use the `with` command to focus on specific files: `with file1.py, file2.py: your request`\n"
|
|
25
|
+
"3. Ask me to work on fewer files or smaller changes in each request\n\n"
|
|
26
|
+
"For example, instead of 'update all files to add logging', try:\n"
|
|
27
|
+
" `with src/main.py: add logging to this file`"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _get_model_config(model_id: str) -> Optional[Dict[str, Any]]:
|
|
32
|
+
"""Get configuration for a specific model."""
|
|
33
|
+
for model in MODELS:
|
|
34
|
+
if model["id"] == model_id:
|
|
35
|
+
return model
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _extract_json_object(raw_response: str, prefer_last: bool = True, require_keys=None):
|
|
40
|
+
"""
|
|
41
|
+
Best-effort extraction of a JSON object (dict) from a raw LLM response.
|
|
42
|
+
|
|
43
|
+
Handles common failure modes where the model returns:
|
|
44
|
+
- extra commentary before/after JSON
|
|
45
|
+
- multiple JSON objects (e.g., an invalid attempt + a corrected attempt)
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
raw_response: raw LLM response string
|
|
49
|
+
prefer_last: when multiple JSON objects exist, return the last parsed object
|
|
50
|
+
require_keys: optional iterable of keys; if provided, only consider objects
|
|
51
|
+
that contain all of these keys
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
dict or None
|
|
55
|
+
"""
|
|
56
|
+
# 1) Direct JSON parse
|
|
57
|
+
try:
|
|
58
|
+
obj = json.loads(raw_response)
|
|
59
|
+
if isinstance(obj, dict):
|
|
60
|
+
if require_keys and not all(k in obj for k in require_keys):
|
|
61
|
+
return None
|
|
62
|
+
return obj
|
|
63
|
+
except Exception:
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
text = str(raw_response)
|
|
67
|
+
|
|
68
|
+
# 2) Scan for balanced JSON object candidates (string/escape-aware)
|
|
69
|
+
candidates = []
|
|
70
|
+
depth = 0
|
|
71
|
+
start = None
|
|
72
|
+
in_str = False
|
|
73
|
+
escape = False
|
|
74
|
+
|
|
75
|
+
for i, ch in enumerate(text):
|
|
76
|
+
if in_str:
|
|
77
|
+
if escape:
|
|
78
|
+
escape = False
|
|
79
|
+
elif ch == '\\':
|
|
80
|
+
escape = True
|
|
81
|
+
elif ch == '"':
|
|
82
|
+
in_str = False
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
# not in string
|
|
86
|
+
if ch == '"':
|
|
87
|
+
in_str = True
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
if ch == '{':
|
|
91
|
+
if depth == 0:
|
|
92
|
+
start = i
|
|
93
|
+
depth += 1
|
|
94
|
+
elif ch == '}' and depth > 0:
|
|
95
|
+
depth -= 1
|
|
96
|
+
if depth == 0 and start is not None:
|
|
97
|
+
candidates.append(text[start : i + 1])
|
|
98
|
+
start = None
|
|
99
|
+
|
|
100
|
+
parsed = []
|
|
101
|
+
for cand in candidates:
|
|
102
|
+
try:
|
|
103
|
+
obj = json.loads(cand)
|
|
104
|
+
if not isinstance(obj, dict):
|
|
105
|
+
continue
|
|
106
|
+
if require_keys and not all(k in obj for k in require_keys):
|
|
107
|
+
continue
|
|
108
|
+
parsed.append(obj)
|
|
109
|
+
except Exception:
|
|
110
|
+
continue
|
|
111
|
+
|
|
112
|
+
if not parsed:
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
return parsed[-1] if prefer_last else parsed[0]
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class DatabricksModelPlugin(Plugin):
|
|
119
|
+
name = "databricks_model"
|
|
120
|
+
version = "1.0.0"
|
|
121
|
+
premium = "free"
|
|
122
|
+
|
|
123
|
+
def __init__(self):
|
|
124
|
+
super().__init__()
|
|
125
|
+
self.chat_history: Dict[str, list] = {}
|
|
126
|
+
self.history_file: Optional[Path] = None
|
|
127
|
+
|
|
128
|
+
def init(self, cfg: Dict[str, Any]) -> None:
|
|
129
|
+
"""Initialize the local model plugin."""
|
|
130
|
+
super().init(cfg)
|
|
131
|
+
if self.debug:
|
|
132
|
+
rprint(f"[bold yellow]Initializing {self.name} v{self.version}[/]")
|
|
133
|
+
|
|
134
|
+
def _load_history(self) -> None:
|
|
135
|
+
"""Load chat history from disk."""
|
|
136
|
+
if not self.history_file:
|
|
137
|
+
if self.verbose:
|
|
138
|
+
rprint("[yellow]History file path not set for local model. Skipping load.[/]")
|
|
139
|
+
self.chat_history = {}
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
if self.history_file.exists():
|
|
143
|
+
try:
|
|
144
|
+
data = json.loads(self.history_file.read_text(encoding="utf-8"))
|
|
145
|
+
self.chat_history = data.get("conversations", {})
|
|
146
|
+
except Exception as e:
|
|
147
|
+
if self.verbose:
|
|
148
|
+
rprint(f"[yellow]Could not load chat history: {e}[/]")
|
|
149
|
+
self.chat_history = {}
|
|
150
|
+
else:
|
|
151
|
+
self.chat_history = {}
|
|
152
|
+
|
|
153
|
+
def _save_history(self) -> None:
|
|
154
|
+
"""Save chat history to disk."""
|
|
155
|
+
if not self.history_file:
|
|
156
|
+
if self.verbose:
|
|
157
|
+
rprint("[yellow]History file path not set for local model. Skipping save.[/]")
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
self.history_file.parent.mkdir(parents=True, exist_ok=True)
|
|
162
|
+
data = {"conversations": self.chat_history}
|
|
163
|
+
self.history_file.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
|
164
|
+
except Exception as e:
|
|
165
|
+
if self.verbose:
|
|
166
|
+
rprint(f"[yellow]Could not save chat history: {e}[/]")
|
|
167
|
+
|
|
168
|
+
def _get_conversation_id(self, chat_id: Optional[int] = None) -> str:
|
|
169
|
+
"""Get conversation ID for history tracking."""
|
|
170
|
+
return str(chat_id) if chat_id and chat_id > 0 else "default"
|
|
171
|
+
|
|
172
|
+
def _build_user_message(self, prompt: str, source_files: Dict[str, str]) -> str:
|
|
173
|
+
"""Build the user message with optional source files appended."""
|
|
174
|
+
user_message = prompt
|
|
175
|
+
if source_files:
|
|
176
|
+
user_message += "\n\n--- Source files are below. ---\n"
|
|
177
|
+
for file_name, content in source_files.items():
|
|
178
|
+
user_message += f"\n** {file_name} **\n```\n{content}\n```\n"
|
|
179
|
+
return user_message
|
|
180
|
+
|
|
181
|
+
def _parse_llm_response(self, generated_text: str) -> Dict[str, Any]:
|
|
182
|
+
"""Parse LLM response text and convert to expected format."""
|
|
183
|
+
try:
|
|
184
|
+
llm_response = json.loads(generated_text)
|
|
185
|
+
except json.JSONDecodeError:
|
|
186
|
+
# Check if this looks like a truncated response
|
|
187
|
+
#if is_truncated_json(generated_text):
|
|
188
|
+
# return {
|
|
189
|
+
# "summary": TRUNCATED_RESPONSE_MESSAGE,
|
|
190
|
+
# "updated_files": []
|
|
191
|
+
# }
|
|
192
|
+
|
|
193
|
+
# Not truncated, just malformed - return as plain text
|
|
194
|
+
llm_response = {
|
|
195
|
+
"answer_summary": generated_text,
|
|
196
|
+
"source_files": []
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
"summary": llm_response.get("answer_summary", ""),
|
|
201
|
+
"updated_files": [
|
|
202
|
+
{
|
|
203
|
+
"file_name": f.get("file_name"),
|
|
204
|
+
"file_content": f.get("file_content")
|
|
205
|
+
}
|
|
206
|
+
for f in llm_response.get("source_files", [])
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
def _create_error_response(self, error_msg: str) -> Dict[str, Any]:
|
|
211
|
+
"""Create a standardized error response."""
|
|
212
|
+
if self.verbose:
|
|
213
|
+
rprint(f"[red]{error_msg}[/]")
|
|
214
|
+
return {
|
|
215
|
+
"summary": error_msg,
|
|
216
|
+
"updated_files": []
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
def _handle_databricks(self, prompt: str, source_files: Dict[str, str], chat_id: Optional[int] = None, system_prompt: Optional[str] = None, max_output_tokens: int = DEFAULT_MAX_OUTPUT_TOKENS) -> Optional[Dict[str, Any]]:
|
|
220
|
+
api_url = os.environ.get("AYE_DBX_API_URL")
|
|
221
|
+
api_key = os.environ.get("AYE_DBX_API_KEY")
|
|
222
|
+
model_name = os.environ.get("AYE_DBX_MODEL", "gpt-3.5-turbo")
|
|
223
|
+
|
|
224
|
+
if not api_url or not api_key:
|
|
225
|
+
return None
|
|
226
|
+
|
|
227
|
+
conv_id = self._get_conversation_id(chat_id)
|
|
228
|
+
if conv_id not in self.chat_history:
|
|
229
|
+
self.chat_history[conv_id] = []
|
|
230
|
+
|
|
231
|
+
user_message = self._build_user_message(prompt, source_files)
|
|
232
|
+
effective_system_prompt = system_prompt if system_prompt else SYSTEM_PROMPT
|
|
233
|
+
|
|
234
|
+
messages_json = [{"role": "system", "content": effective_system_prompt}] + self.chat_history[conv_id] + [{"role": "user", "content": user_message}]
|
|
235
|
+
messages = messages_json # json.dumps(messages_json)
|
|
236
|
+
if self.debug:
|
|
237
|
+
print(">>>>>>>>>>>>>>>>")
|
|
238
|
+
print(self.chat_history[conv_id])
|
|
239
|
+
print(">>>>>>>>>>>>>>>>")
|
|
240
|
+
|
|
241
|
+
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}
|
|
242
|
+
#payload = {"model": model_name, "messages": messages, "temperature": 0.7, "max_tokens": max_output_tokens, "response_format": {"type": "json_object"}}
|
|
243
|
+
payload = {"model": model_name, "messages": messages, "temperature": 0.7, "max_tokens": max_output_tokens}
|
|
244
|
+
|
|
245
|
+
try:
|
|
246
|
+
with httpx.Client(timeout=LLM_TIMEOUT) as client:
|
|
247
|
+
response = client.post(api_url, json=payload, headers=headers)
|
|
248
|
+
if self.debug:
|
|
249
|
+
print("-----------------")
|
|
250
|
+
print(response.text)
|
|
251
|
+
print("-----------------")
|
|
252
|
+
response.raise_for_status()
|
|
253
|
+
result = response.json()
|
|
254
|
+
if result.get("choices") and result["choices"][0].get("message"):
|
|
255
|
+
raw_response = result["choices"][0]["message"]["content"]
|
|
256
|
+
generated_json = _extract_json_object(raw_response)
|
|
257
|
+
generated_text = json.dumps(generated_json)
|
|
258
|
+
if self.debug:
|
|
259
|
+
print("-----------------")
|
|
260
|
+
print(response.text)
|
|
261
|
+
print("-----------------")
|
|
262
|
+
print(generated_text)
|
|
263
|
+
print("-----------------")
|
|
264
|
+
#generated_text = result["choices"][0]["message"]["content"][1]["text"]
|
|
265
|
+
self.chat_history[conv_id].append({"role": "user", "content": user_message})
|
|
266
|
+
self.chat_history[conv_id].append({"role": "assistant", "content": generated_text})
|
|
267
|
+
self._save_history()
|
|
268
|
+
#return json.dumps(generated_text)
|
|
269
|
+
return self._parse_llm_response(generated_text)
|
|
270
|
+
return self._create_error_response("Failed to get a valid response from the Databricks API")
|
|
271
|
+
except httpx.HTTPStatusError as e:
|
|
272
|
+
traceback.print_exc()
|
|
273
|
+
error_msg = f"DBX API error: {e.response.status_code}"
|
|
274
|
+
try:
|
|
275
|
+
error_detail = e.response.json()
|
|
276
|
+
if "error" in error_detail:
|
|
277
|
+
error_msg += f" - {error_detail['error'].get('message', str(error_detail['error']))}"
|
|
278
|
+
except: error_msg += f" - {e.response.text[:200]}"
|
|
279
|
+
return self._create_error_response(error_msg)
|
|
280
|
+
except Exception as e:
|
|
281
|
+
traceback.print_exc()
|
|
282
|
+
return self._create_error_response(f"Error calling Databricks API: {str(e)}")
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def on_command(self, command_name: str, params: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
286
|
+
if command_name == "new_chat":
|
|
287
|
+
root = params.get("root")
|
|
288
|
+
history_file = Path(root) / ".aye" / "chat_history.json" if root else Path.cwd() / ".aye" / "chat_history.json"
|
|
289
|
+
history_file.unlink(missing_ok=True)
|
|
290
|
+
self.chat_history = {}
|
|
291
|
+
if self.verbose: rprint("[yellow]Local model chat history cleared.[/]")
|
|
292
|
+
return {"status": "local_history_cleared"}
|
|
293
|
+
|
|
294
|
+
if command_name == "local_model_invoke":
|
|
295
|
+
prompt = params.get("prompt", "").strip()
|
|
296
|
+
model_id = params.get("model_id", "")
|
|
297
|
+
source_files = params.get("source_files", {})
|
|
298
|
+
chat_id = params.get("chat_id")
|
|
299
|
+
root = params.get("root")
|
|
300
|
+
system_prompt = params.get("system_prompt")
|
|
301
|
+
max_output_tokens = params.get("max_output_tokens", DEFAULT_MAX_OUTPUT_TOKENS)
|
|
302
|
+
|
|
303
|
+
self.history_file = Path(root) / ".aye" / "chat_history.json" if root else Path.cwd() / ".aye" / "chat_history.json"
|
|
304
|
+
self._load_history()
|
|
305
|
+
|
|
306
|
+
result = self._handle_databricks(prompt, source_files, chat_id, system_prompt, max_output_tokens)
|
|
307
|
+
if result is not None: return result
|
|
308
|
+
|
|
309
|
+
return None
|
|
310
|
+
|
|
311
|
+
return None
|
{ayechat_dev-0.36.9.20260204003405 → ayechat_dev-0.36.9.20260204171331}/src/aye/presenter/repl_ui.py
RENAMED
|
@@ -88,14 +88,15 @@ def print_help_message():
|
|
|
88
88
|
# Some commands are intentionally undocumented: keep them as such.
|
|
89
89
|
("@filename", "Include a file in your prompt inline (e.g., \"explain @main.py\"). Supports wildcards (e.g., @*.py, @src/*.js)."),
|
|
90
90
|
("!command", "Force shell execution (e.g., \"!echo hello\")."),
|
|
91
|
+
("new", "Start a new chat session (if you want to change the subject)"),
|
|
92
|
+
(r"restore, undo \[id] \[file]", "Revert changes to the last state, a specific snapshot `id`, or for a single `file`."),
|
|
93
|
+
("history", "Show snapshot history"),
|
|
94
|
+
(r"diff <file> \[snapshot_id]", "Show diff of file with the latest snapshot, or a specified snapshot"),
|
|
91
95
|
("model", "Select a different model. Selection will persist between sessions."),
|
|
96
|
+
(r"autodiff \[on|off]", "Toggle automatic diff display after LLM file updates (off by default, persists between sessions)"),
|
|
92
97
|
("llm", "Configure OpenAI-compatible LLM endpoint (URL, key, model). Use 'llm clear' to reset."),
|
|
93
98
|
(r"verbose \[on|off]", "Toggle verbose mode to increase or decrease chattiness (on/off, persists between sessions)"),
|
|
94
99
|
(r"completion \[readline|multi]", "Switch auto-completion style (readline or multi, persists between sessions)"),
|
|
95
|
-
("new", "Start a new chat session (if you want to change the subject)"),
|
|
96
|
-
("history", "Show snapshot history"),
|
|
97
|
-
(r"diff <file> \[snapshot_id]", "Show diff of file with the latest snapshot, or a specified snapshot"),
|
|
98
|
-
(r"restore, undo \[id] \[file]", "Revert changes to the last state, a specific snapshot `id`, or for a single `file`."),
|
|
99
100
|
("keep [N]", "Keep only N most recent snapshots (10 by default)"),
|
|
100
101
|
("exit, quit, Ctrl+D", "Exit the chat session"),
|
|
101
102
|
("help", "Show this help message"),
|