From 9e04a97e14bacda010ea0cda02d0684938637902 Mon Sep 17 00:00:00 2001 From: liblaf <30631553+liblaf@users.noreply.github.com> Date: Fri, 29 Nov 2024 23:01:20 +0800 Subject: [PATCH] refactor: streamline CLI and logging initialization - Simplify CLI command initialization by integrating dependency injection - Optimize logging setup with cached initialization functions - Remove redundant model list and fallback configurations - Update API keys for model configurations --- docs/help.md | 4 +- docs/schema/config.json | 185 +---------------------- pyproject.toml | 2 + src/llm_cli/cmd/_app.py | 16 +- src/llm_cli/cmd/commit/_app.py | 6 +- src/llm_cli/cmd/repo/_app.py | 4 +- src/llm_cli/cmd/repo/description/_app.py | 8 +- src/llm_cli/cmd/repo/topics/_app.py | 8 +- src/llm_cli/config/_router_config.py | 11 +- src/llm_cli/logging/_fix_litellm.py | 4 +- src/llm_cli/logging/_init.py | 3 + src/llm_cli/logging/_loguru.py | 15 +- src/llm_cli/utils/__init__.pyi | 2 + src/llm_cli/utils/_get_config.py | 13 ++ uv.lock | 14 ++ 15 files changed, 83 insertions(+), 212 deletions(-) create mode 100644 src/llm_cli/utils/_get_config.py diff --git a/docs/help.md b/docs/help.md index 4bd582e..23403ae 100644 --- a/docs/help.md +++ b/docs/help.md @@ -8,7 +8,6 @@ llm-cli [OPTIONS] COMMAND [ARGS]... **Options**: -- `--model TEXT` - `--help`: Show this message and exit. **Commands**: @@ -32,6 +31,7 @@ llm-cli commit [OPTIONS] [PATH]... - `--default-exclude / --no-default-exclude`: [default: default-exclude] - `--verify / --no-verify`: [default: verify] +- `--model TEXT` - `--help`: Show this message and exit. ## `llm-cli repo` @@ -61,6 +61,7 @@ llm-cli repo description [OPTIONS] **Options**: +- `--model TEXT` - `--help`: Show this message and exit. ### `llm-cli repo topics` @@ -73,4 +74,5 @@ llm-cli repo topics [OPTIONS] **Options**: +- `--model TEXT` - `--help`: Show this message and exit. diff --git a/docs/schema/config.json b/docs/schema/config.json index 971fe03..07991e6 100644 --- a/docs/schema/config.json +++ b/docs/schema/config.json @@ -242,173 +242,6 @@ "RouterConfig": { "properties": { "model_list": { - "default": [ - { - "model_name": "deepseek-chat", - "litellm_params": { - "api_key": null, - "api_version": null, - "base_url": null, - "deployment_id": null, - "frequency_penalty": null, - "function_call": null, - "functions": null, - "logit_bias": null, - "logprobs": null, - "max_tokens": null, - "messages": [], - "model": "deepseek/deepseek-chat", - "model_list": null, - "n": null, - "presence_penalty": null, - "response_format": null, - "seed": null, - "stop": null, - "stream": null, - "temperature": null, - "timeout": null, - "tool_choice": null, - "tools": null, - "top_logprobs": null, - "top_p": null, - "user": null - }, - "tpm": null, - "rpm": null - }, - { - "model_name": "qwen-max", - "litellm_params": { - "api_key": null, - "api_version": null, - "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", - "deployment_id": null, - "frequency_penalty": null, - "function_call": null, - "functions": null, - "logit_bias": null, - "logprobs": null, - "max_tokens": null, - "messages": [], - "model": "openai/qwen-max", - "model_list": null, - "n": null, - "presence_penalty": null, - "response_format": null, - "seed": null, - "stop": null, - "stream": null, - "temperature": null, - "timeout": null, - "tool_choice": null, - "tools": null, - "top_logprobs": null, - "top_p": null, - "user": null - }, - "tpm": null, - "rpm": null - }, - { - "model_name": "qwen-plus", - "litellm_params": { - "api_key": null, - "api_version": null, - "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", - "deployment_id": null, - "frequency_penalty": null, - "function_call": null, - "functions": null, - "logit_bias": null, - "logprobs": null, - "max_tokens": null, - "messages": [], - "model": "openai/qwen-plus", - "model_list": null, - "n": null, - "presence_penalty": null, - "response_format": null, - "seed": null, - "stop": null, - "stream": null, - "temperature": null, - "timeout": null, - "tool_choice": null, - "tools": null, - "top_logprobs": null, - "top_p": null, - "user": null - }, - "tpm": null, - "rpm": null - }, - { - "model_name": "qwen-turbo", - "litellm_params": { - "api_key": null, - "api_version": null, - "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", - "deployment_id": null, - "frequency_penalty": null, - "function_call": null, - "functions": null, - "logit_bias": null, - "logprobs": null, - "max_tokens": null, - "messages": [], - "model": "openai/qwen-turbo", - "model_list": null, - "n": null, - "presence_penalty": null, - "response_format": null, - "seed": null, - "stop": null, - "stream": null, - "temperature": null, - "timeout": null, - "tool_choice": null, - "tools": null, - "top_logprobs": null, - "top_p": null, - "user": null - }, - "tpm": null, - "rpm": null - }, - { - "model_name": "qwen-long", - "litellm_params": { - "api_key": null, - "api_version": null, - "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", - "deployment_id": null, - "frequency_penalty": null, - "function_call": null, - "functions": null, - "logit_bias": null, - "logprobs": null, - "max_tokens": null, - "messages": [], - "model": "openai/qwen-long", - "model_list": null, - "n": null, - "presence_penalty": null, - "response_format": null, - "seed": null, - "stop": null, - "stream": null, - "temperature": null, - "timeout": null, - "tool_choice": null, - "tools": null, - "top_logprobs": null, - "top_p": null, - "user": null - }, - "tpm": null, - "rpm": null - } - ], "items": { "$ref": "#/$defs/ModelConfig" }, "title": "Model List", "type": "array" @@ -491,16 +324,6 @@ "title": "Set Verbose" }, "fallbacks": { - "default": [ - { - "deepseek-chat": [ - "qwen-max", - "qwen-plus", - "qwen-turbo", - "qwen-long" - ] - } - ], "items": { "additionalProperties": { "items": { "type": "string" }, @@ -628,7 +451,7 @@ }, { "litellm_params": { - "api_key": null, + "api_key": "sk-45fb748c5bd14b19b4af8f47db71be59", "api_version": null, "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", "deployment_id": null, @@ -661,7 +484,7 @@ }, { "litellm_params": { - "api_key": null, + "api_key": "sk-45fb748c5bd14b19b4af8f47db71be59", "api_version": null, "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", "deployment_id": null, @@ -694,7 +517,7 @@ }, { "litellm_params": { - "api_key": null, + "api_key": "sk-45fb748c5bd14b19b4af8f47db71be59", "api_version": null, "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", "deployment_id": null, @@ -727,7 +550,7 @@ }, { "litellm_params": { - "api_key": null, + "api_key": "sk-45fb748c5bd14b19b4af8f47db71be59", "api_version": null, "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", "deployment_id": null, diff --git a/pyproject.toml b/pyproject.toml index 00bf613..109d3cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ dependencies = [ "python-dotenv>=1.0.1", "rich>=13.9.4", "transformers>=4.46.3", + "typer-di>=0.1.2", "typer>=0.13.1", ] description = "🚀 LLM CLI - a powerful, open-source command-line interface for AI-driven repository management. Simplifies commit message generation, repository description, and topic suggestion, enhancing productivity and collaboration for developers." @@ -30,6 +31,7 @@ requires-python = ">=3.12" version = "0.0.0" [project.scripts] +aic = "llm_cli.cmd.commit:app" llm-cli = "llm_cli:app" [project.urls] diff --git a/src/llm_cli/cmd/_app.py b/src/llm_cli/cmd/_app.py index 35dc94a..eaef873 100644 --- a/src/llm_cli/cmd/_app.py +++ b/src/llm_cli/cmd/_app.py @@ -1,20 +1,8 @@ -from typing import Annotated +import typer_di -import typer - -import llm_cli as lc import llm_cli.cmd as lcm -import llm_cli.config as lcc import llm_cli.utils as lcu -app: typer.Typer = typer.Typer(name="llm-cli", no_args_is_help=True) +app = typer_di.TyperDI(name="llm-cli", no_args_is_help=True) lcu.add_command(app, lcm.repo.app) lcu.add_command(app, lcm.commit.app) - - -@app.callback() -def init(model: Annotated[str | None, typer.Option()] = None) -> None: - lc.logging.init() - cfg: lcc.Config = lcc.get_config() - if model: - cfg.completion.model = model diff --git a/src/llm_cli/cmd/commit/_app.py b/src/llm_cli/cmd/commit/_app.py index 95c9c6e..add9da7 100644 --- a/src/llm_cli/cmd/commit/_app.py +++ b/src/llm_cli/cmd/commit/_app.py @@ -2,8 +2,11 @@ from typing import Annotated import typer +import typer_di -app = typer.Typer(name="commit") +import llm_cli.utils as lcu + +app = typer_di.TyperDI(name="commit") @app.command() @@ -12,6 +15,7 @@ def main( *, default_exclude: Annotated[bool, typer.Option()] = True, verify: Annotated[bool, typer.Option()] = True, + _: None = typer_di.Depends(lcu.get_config), ) -> None: from ._main import main diff --git a/src/llm_cli/cmd/repo/_app.py b/src/llm_cli/cmd/repo/_app.py index be19684..bfd54e9 100644 --- a/src/llm_cli/cmd/repo/_app.py +++ b/src/llm_cli/cmd/repo/_app.py @@ -1,8 +1,8 @@ -import typer +import typer_di import llm_cli.utils as lcu from llm_cli import cmd as lcm -app: typer.Typer = typer.Typer(name="repo", no_args_is_help=True) +app = typer_di.TyperDI(name="repo", no_args_is_help=True) lcu.add_command(app, lcm.repo.description.app) lcu.add_command(app, lcm.repo.topics.app) diff --git a/src/llm_cli/cmd/repo/description/_app.py b/src/llm_cli/cmd/repo/description/_app.py index ec7cf79..044714f 100644 --- a/src/llm_cli/cmd/repo/description/_app.py +++ b/src/llm_cli/cmd/repo/description/_app.py @@ -1,12 +1,14 @@ import asyncio -import typer +import typer_di -app: typer.Typer = typer.Typer(name="description", no_args_is_help=True) +import llm_cli.utils as lcu + +app = typer_di.TyperDI(name="description") @app.command() -def main() -> None: +def main(_: None = typer_di.Depends(lcu.get_config)) -> None: from ._main import main asyncio.run(main()) diff --git a/src/llm_cli/cmd/repo/topics/_app.py b/src/llm_cli/cmd/repo/topics/_app.py index 3e7b71e..01c67eb 100644 --- a/src/llm_cli/cmd/repo/topics/_app.py +++ b/src/llm_cli/cmd/repo/topics/_app.py @@ -1,12 +1,14 @@ import asyncio -import typer +import typer_di -app: typer.Typer = typer.Typer(name="topics", no_args_is_help=True) +import llm_cli.utils as lcu + +app = typer_di.TyperDI(name="topics") @app.command() -def main() -> None: +def main(_: None = typer_di.Depends(lcu.get_config)) -> None: from ._main import main asyncio.run(main()) diff --git a/src/llm_cli/config/_router_config.py b/src/llm_cli/config/_router_config.py index eeef48c..a7c64d2 100644 --- a/src/llm_cli/config/_router_config.py +++ b/src/llm_cli/config/_router_config.py @@ -1,6 +1,7 @@ import functools import litellm +import pydantic import llm_cli.config as lcc @@ -55,11 +56,13 @@ def default_model_list() -> list[ModelConfig]: class RouterConfig(litellm.RouterConfig): - model_list: list[ModelConfig] = default_model_list() # pyright: ignore [reportIncompatibleVariableOverride] + model_list: list[ModelConfig] = pydantic.Field(default_factory=default_model_list) # pyright: ignore [reportIncompatibleVariableOverride] num_retries: int = 3 # pyright: ignore [reportIncompatibleVariableOverride] - fallbacks: list[dict[str, list[str]]] = [ # pyright: ignore [reportIncompatibleVariableOverride] # noqa: RUF012 - {"deepseek-chat": ["qwen-max", "qwen-plus", "qwen-turbo", "qwen-long"]} - ] + fallbacks: list[dict[str, list[str]]] = pydantic.Field( # pyright: ignore [reportIncompatibleVariableOverride] + default_factory=lambda: [ + {"deepseek-chat": ["qwen-max", "qwen-plus", "qwen-turbo", "qwen-long"]} + ] + ) @functools.cached_property def router(self) -> litellm.Router: diff --git a/src/llm_cli/logging/_fix_litellm.py b/src/llm_cli/logging/_fix_litellm.py index 4adf73a..e75b9a7 100644 --- a/src/llm_cli/logging/_fix_litellm.py +++ b/src/llm_cli/logging/_fix_litellm.py @@ -1,8 +1,8 @@ +import importlib import logging -import litellm # noqa: F401 - def fix_litellm() -> None: + importlib.import_module("litellm._logging") for name in ["LiteLLM Proxy", "LiteLLM Router", "LiteLLM"]: logging.getLogger(name).handlers.clear() diff --git a/src/llm_cli/logging/_init.py b/src/llm_cli/logging/_init.py index a8ef4c8..227767d 100644 --- a/src/llm_cli/logging/_init.py +++ b/src/llm_cli/logging/_init.py @@ -1,5 +1,8 @@ +import functools + import llm_cli as lc +@functools.cache def init() -> None: lc.logging.init_loguru() diff --git a/src/llm_cli/logging/_loguru.py b/src/llm_cli/logging/_loguru.py index d7fdb38..e331e4a 100644 --- a/src/llm_cli/logging/_loguru.py +++ b/src/llm_cli/logging/_loguru.py @@ -1,3 +1,4 @@ +import functools import inspect import logging import sys @@ -5,6 +6,8 @@ from loguru import logger +import llm_cli as lc + class InterceptHandler(logging.Handler): def emit(self, record: logging.LogRecord) -> None: @@ -28,6 +31,16 @@ def emit(self, record: logging.LogRecord) -> None: ) +@functools.cache def init_loguru() -> None: + lc.logging.fix_litellm() logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True) - logger.configure(handlers=[{"sink": sys.stderr, "level": "INFO"}]) + logger.configure( + handlers=[ + { + "sink": sys.stderr, + "level": "INFO", + "filter": {"httpx": "WARNING", "litellm": "WARNING"}, + } + ] + ) diff --git a/src/llm_cli/utils/__init__.pyi b/src/llm_cli/utils/__init__.pyi index d08a885..cf24368 100644 --- a/src/llm_cli/utils/__init__.pyi +++ b/src/llm_cli/utils/__init__.pyi @@ -3,6 +3,7 @@ from ._add_command import add_command from ._as_list import as_list from ._extract_between_tags import extract_between_tags from ._get_app_dir import get_app_dir +from ._get_config import get_config from ._get_prompt import get_prompt from ._ignore import get_ignore_patterns from ._repomix import repomix @@ -14,6 +15,7 @@ __all__ = [ "as_list", "extract_between_tags", "get_app_dir", + "get_config", "get_ignore_patterns", "get_prompt", "git", diff --git a/src/llm_cli/utils/_get_config.py b/src/llm_cli/utils/_get_config.py new file mode 100644 index 0000000..fdda27a --- /dev/null +++ b/src/llm_cli/utils/_get_config.py @@ -0,0 +1,13 @@ +from typing import Annotated + +import typer + +import llm_cli as lc +import llm_cli.config as lcc + + +def get_config(model: Annotated[str | None, typer.Option()] = None) -> None: + lc.logging.init() + cfg: lcc.Config = lcc.get_config() + if model: + cfg.completion.model = model diff --git a/uv.lock b/uv.lock index 66f3ada..698114e 100644 --- a/uv.lock +++ b/uv.lock @@ -490,6 +490,7 @@ dependencies = [ { name = "rich" }, { name = "transformers" }, { name = "typer" }, + { name = "typer-di" }, ] [package.dev-dependencies] @@ -511,6 +512,7 @@ requires-dist = [ { name = "rich", specifier = ">=13.9.4" }, { name = "transformers", specifier = ">=4.46.3" }, { name = "typer", specifier = ">=0.13.1" }, + { name = "typer-di", specifier = ">=0.1.2" }, ] [package.metadata.requires-dev] @@ -1161,6 +1163,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bb/d8/a3ab71d5587b42b832a7ef2e65b3e51a18f8da32b6ce169637d4d21995ed/typer-0.14.0-py3-none-any.whl", hash = "sha256:f476233a25770ab3e7b2eebf7c68f3bc702031681a008b20167573a4b7018f09", size = 44707 }, ] +[[package]] +name = "typer-di" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/68/6c50ecfc8c088db9055d652ed199899c67754211adf5e7abc8edf73700d7/typer_di-0.1.2.tar.gz", hash = "sha256:f66f60abe828239a1a2cd655f1f64873693f0c73d30222234da1ae93d3e7c049", size = 5993 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/9a/7b68544864f2e66c182f174f8b3cd1c2d8fe509cf28c21c3cf31ab76768c/typer_di-0.1.2-py3-none-any.whl", hash = "sha256:db3286976e80a829e13ed2deb6e42d4f26355a1bd09ecae32e8141b4c701f111", size = 7516 }, +] + [[package]] name = "typing-extensions" version = "4.12.2"