sycommon-python-lib 0.2.0b11__tar.gz → 0.2.0b12__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.
Files changed (104) hide show
  1. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/PKG-INFO +2 -1
  2. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/pyproject.toml +2 -1
  3. sycommon_python_lib-0.2.0b12/src/command/__init__.py +21 -0
  4. sycommon_python_lib-0.2.0b12/src/command/cli.py +171 -0
  5. sycommon_python_lib-0.2.0b12/src/command/console.py +36 -0
  6. sycommon_python_lib-0.2.0b12/src/command/models.py +28 -0
  7. sycommon_python_lib-0.2.0b12/src/command/project.py +234 -0
  8. sycommon_python_lib-0.2.0b12/src/command/utils.py +31 -0
  9. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/llm/get_llm.py +19 -4
  10. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/rabbitmq/rabbitmq_service_client_manager.py +43 -21
  11. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/rabbitmq/rabbitmq_service_producer_manager.py +19 -26
  12. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon_python_lib.egg-info/PKG-INFO +2 -1
  13. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon_python_lib.egg-info/SOURCES.txt +5 -0
  14. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon_python_lib.egg-info/requires.txt +1 -0
  15. sycommon_python_lib-0.2.0b11/src/command/cli.py +0 -417
  16. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/README.md +0 -0
  17. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/setup.cfg +0 -0
  18. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/__init__.py +0 -0
  19. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/config/Config.py +0 -0
  20. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/config/DatabaseConfig.py +0 -0
  21. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/config/EmbeddingConfig.py +0 -0
  22. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/config/LLMConfig.py +0 -0
  23. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/config/LangfuseConfig.py +0 -0
  24. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/config/MQConfig.py +0 -0
  25. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/config/RerankerConfig.py +0 -0
  26. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/config/SentryConfig.py +0 -0
  27. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/config/__init__.py +0 -0
  28. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/database/async_base_db_service.py +0 -0
  29. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/database/async_database_service.py +0 -0
  30. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/database/base_db_service.py +0 -0
  31. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/database/database_service.py +0 -0
  32. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/health/__init__.py +0 -0
  33. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/health/health_check.py +0 -0
  34. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/health/metrics.py +0 -0
  35. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/health/ping.py +0 -0
  36. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/llm/__init__.py +0 -0
  37. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/llm/embedding.py +0 -0
  38. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/llm/llm_logger.py +0 -0
  39. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/llm/llm_tokens.py +0 -0
  40. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/llm/struct_token.py +0 -0
  41. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/llm/sy_langfuse.py +0 -0
  42. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/llm/usage_token.py +0 -0
  43. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/logging/__init__.py +0 -0
  44. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/logging/async_sql_logger.py +0 -0
  45. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/logging/kafka_log.py +0 -0
  46. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/logging/logger_levels.py +0 -0
  47. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/logging/logger_wrapper.py +0 -0
  48. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/logging/sql_logger.py +0 -0
  49. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/middleware/__init__.py +0 -0
  50. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/middleware/context.py +0 -0
  51. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/middleware/cors.py +0 -0
  52. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/middleware/docs.py +0 -0
  53. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/middleware/exception.py +0 -0
  54. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/middleware/middleware.py +0 -0
  55. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/middleware/monitor_memory.py +0 -0
  56. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/middleware/mq.py +0 -0
  57. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/middleware/timeout.py +0 -0
  58. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/middleware/traceid.py +0 -0
  59. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/models/__init__.py +0 -0
  60. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/models/base_http.py +0 -0
  61. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/models/log.py +0 -0
  62. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/models/mqlistener_config.py +0 -0
  63. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/models/mqmsg_model.py +0 -0
  64. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/models/mqsend_config.py +0 -0
  65. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/models/sso_user.py +0 -0
  66. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/notice/__init__.py +0 -0
  67. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/notice/uvicorn_monitor.py +0 -0
  68. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/rabbitmq/rabbitmq_client.py +0 -0
  69. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/rabbitmq/rabbitmq_pool.py +0 -0
  70. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/rabbitmq/rabbitmq_service.py +0 -0
  71. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +0 -0
  72. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +0 -0
  73. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/rabbitmq/rabbitmq_service_core.py +0 -0
  74. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/sentry/__init__.py +0 -0
  75. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/sentry/sy_sentry.py +0 -0
  76. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/services.py +0 -0
  77. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/sse/__init__.py +0 -0
  78. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/sse/event.py +0 -0
  79. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/sse/sse.py +0 -0
  80. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/synacos/__init__.py +0 -0
  81. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/synacos/example.py +0 -0
  82. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/synacos/example2.py +0 -0
  83. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/synacos/feign.py +0 -0
  84. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/synacos/feign_client.py +0 -0
  85. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/synacos/nacos_client_base.py +0 -0
  86. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/synacos/nacos_config_manager.py +0 -0
  87. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/synacos/nacos_heartbeat_manager.py +0 -0
  88. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/synacos/nacos_service.py +0 -0
  89. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/synacos/nacos_service_discovery.py +0 -0
  90. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/synacos/nacos_service_registration.py +0 -0
  91. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/synacos/param.py +0 -0
  92. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/tests/test_email.py +0 -0
  93. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/tests/test_mq.py +0 -0
  94. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/tools/__init__.py +0 -0
  95. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/tools/async_utils.py +0 -0
  96. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/tools/docs.py +0 -0
  97. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/tools/env.py +0 -0
  98. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/tools/merge_headers.py +0 -0
  99. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/tools/snowflake.py +0 -0
  100. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/tools/syemail.py +0 -0
  101. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon/tools/timing.py +0 -0
  102. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
  103. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
  104. {sycommon_python_lib-0.2.0b11 → sycommon_python_lib-0.2.0b12}/src/sycommon_python_lib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.2.0b11
3
+ Version: 0.2.0b12
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -10,6 +10,7 @@ Requires-Dist: aiomysql>=0.3.2
10
10
  Requires-Dist: anyio>=4.12.1
11
11
  Requires-Dist: decorator>=5.2.1
12
12
  Requires-Dist: fastapi>=0.133.1
13
+ Requires-Dist: jinja2>=3.1.6
13
14
  Requires-Dist: kafka-python>=2.3.0
14
15
  Requires-Dist: langchain>=1.2.10
15
16
  Requires-Dist: langchain-core>=1.2.16
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sycommon-python-lib"
3
- version = "0.2.0b11"
3
+ version = "0.2.0b12"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -11,6 +11,7 @@ dependencies = [
11
11
  "anyio>=4.12.1",
12
12
  "decorator>=5.2.1",
13
13
  "fastapi>=0.133.1",
14
+ "jinja2>=3.1.6",
14
15
  "kafka-python>=2.3.0",
15
16
  "langchain>=1.2.10",
16
17
  "langchain-core>=1.2.16",
@@ -0,0 +1,21 @@
1
+ """
2
+ sycommon CLI 工具集
3
+ """
4
+ from .models import FeatureOptions
5
+ from .console import print_info, print_success, print_error, print_warning, Colors
6
+ from .utils import get_all_files_in_directory
7
+ from .project import init_project, interactive_feature_selection
8
+ from .cli import main
9
+
10
+ __all__ = [
11
+ "FeatureOptions",
12
+ "print_info",
13
+ "print_success",
14
+ "print_error",
15
+ "print_warning",
16
+ "Colors",
17
+ "get_all_files_in_directory",
18
+ "init_project",
19
+ "interactive_feature_selection",
20
+ "main",
21
+ ]
@@ -0,0 +1,171 @@
1
+ """
2
+ sycommon CLI 工具集
3
+
4
+ Usage:
5
+ sycommon init web my_project # 交互式创建Web项目
6
+ sycommon init agent my_project # 交互式创建Agent项目
7
+ sycommon init web my_project --with-db # 命令行指定功能
8
+ sycommon version # 显示版本
9
+ """
10
+ import argparse
11
+ import sys
12
+ from importlib.metadata import version, PackageNotFoundError
13
+
14
+ from .models import FeatureOptions
15
+ from .project import init_project
16
+ from .console import print_error, print_info
17
+
18
+
19
+ def get_version() -> str:
20
+ """获取已安装的库版本"""
21
+ try:
22
+ return version("sycommon-python-lib")
23
+ except PackageNotFoundError:
24
+ return "unknown (未安装)"
25
+
26
+
27
+ def create_init_parser(subparsers) -> None:
28
+ """创建 init 子命令解析器"""
29
+ init_parser = subparsers.add_parser(
30
+ "init",
31
+ help="创建Web/Agent类型项目模板",
32
+ formatter_class=argparse.RawTextHelpFormatter,
33
+ epilog="""
34
+ 示例:
35
+ uv pip install -e .
36
+ sycommon init web my_project # 交互式创建Web项目
37
+ sycommon init agent my_project # 交互式创建Agent项目
38
+ sycommon init web my_project --dry-run # 预览将要创建的文件
39
+ sycommon init web my_project --force # 强制覆盖已存在的目录
40
+ sycommon init web my_project -q # 静默模式(使用默认配置)
41
+ sycommon init web my_project --with-db --with-mq --with-redis
42
+
43
+ 项目类型说明:
44
+ web - 标准 FastAPI Web 服务,适合 RESTful API 开发
45
+ agent - AI Agent 服务,基于 langgraph 实现状态图
46
+
47
+ Agent 项目结构:
48
+ agent/ - Agent 核心 (main_agent, nodes, states, enums)
49
+ api/sse/ - SSE 流式接口
50
+ tools/ - 工具函数 (含 MQ 处理)
51
+ db/ - 数据库服务
52
+ model/ - 数据模型
53
+
54
+ Web 项目结构:
55
+ api/ - API 接口 (含 SSE)
56
+ tools/ - 工具函数
57
+ model/ - 数据模型
58
+ client/ - 外部服务客户端
59
+ """
60
+ )
61
+
62
+ # 位置参数
63
+ init_parser.add_argument(
64
+ "project_type",
65
+ choices=["web", "agent"],
66
+ help="项目类型:web / agent"
67
+ )
68
+ init_parser.add_argument(
69
+ "project_name",
70
+ help="工程名称"
71
+ )
72
+
73
+ # 功能选项
74
+ feature_group = init_parser.add_argument_group("功能选项")
75
+ feature_group.add_argument("--with-db", action="store_true", help="启用数据库")
76
+ feature_group.add_argument(
77
+ "--with-mq", action="store_true", help="启用 RabbitMQ")
78
+ feature_group.add_argument(
79
+ "--with-sse", action="store_true", help="启用 SSE 流式")
80
+ feature_group.add_argument(
81
+ "--with-redis", action="store_true", help="启用 Redis")
82
+ feature_group.add_argument(
83
+ "--no-nacos", action="store_true", help="禁用 Nacos")
84
+ feature_group.add_argument(
85
+ "--no-kafka", action="store_true", help="禁用 Kafka 日志")
86
+ feature_group.add_argument("--no-llm", action="store_true", help="禁用 LLM")
87
+
88
+ # 行为选项
89
+ behavior_group = init_parser.add_argument_group("行为选项")
90
+ behavior_group.add_argument(
91
+ "-n", "--dry-run", action="store_true", help="仅预览")
92
+ behavior_group.add_argument(
93
+ "-f", "--force", action="store_true", help="强制覆盖")
94
+ behavior_group.add_argument(
95
+ "-q", "--quiet", action="store_true", help="静默模式")
96
+ behavior_group.add_argument(
97
+ "-v", "--verbose", action="store_true", help="详细输出")
98
+
99
+
100
+ def handle_init_command(args) -> bool:
101
+ """处理 init 命令"""
102
+ # 判断是否使用命令行模式
103
+ use_cli_mode = any([
104
+ args.with_db, args.with_mq, args.with_sse, args.with_redis,
105
+ args.no_nacos, args.no_kafka, args.no_llm
106
+ ])
107
+
108
+ if use_cli_mode or args.quiet:
109
+ features = FeatureOptions(
110
+ database=args.with_db,
111
+ rabbitmq=args.with_mq,
112
+ nacos=not args.no_nacos,
113
+ kafka_log=not args.no_kafka,
114
+ sse=args.with_sse,
115
+ llm=not args.no_llm,
116
+ redis=args.with_redis,
117
+ )
118
+ else:
119
+ features = None # 交互式选择
120
+
121
+ verbose = args.verbose or not args.quiet
122
+
123
+ return init_project(
124
+ project_name=args.project_name,
125
+ project_type=args.project_type,
126
+ features=features,
127
+ dry_run=args.dry_run,
128
+ force=args.force,
129
+ verbose=verbose
130
+ )
131
+
132
+
133
+ def main() -> None:
134
+ """CLI 主入口"""
135
+ parser = argparse.ArgumentParser(
136
+ prog="sycommon",
137
+ description="sycommon 工具集 - 项目初始化工具",
138
+ formatter_class=argparse.RawTextHelpFormatter
139
+ )
140
+ subparsers = parser.add_subparsers(
141
+ dest="command", required=True, help="子命令")
142
+
143
+ # 注册子命令
144
+ create_init_parser(subparsers)
145
+ subparsers.add_parser("version", help="显示版本信息")
146
+
147
+ try:
148
+ args = parser.parse_args()
149
+
150
+ if args.command == "version":
151
+ print(f"sycommon {get_version()}")
152
+ return
153
+
154
+ if args.command == "init":
155
+ success = handle_init_command(args)
156
+ if not success:
157
+ sys.exit(1)
158
+
159
+ except argparse.ArgumentError as e:
160
+ print_error(str(e))
161
+ cmd = args.command if 'args' in locals() and hasattr(args, 'command') else ''
162
+ print_info(f"使用 {parser.prog} {cmd} -h 查看帮助")
163
+ sys.exit(1)
164
+ except KeyboardInterrupt:
165
+ print("\n")
166
+ print_info("操作已取消")
167
+ sys.exit(130)
168
+
169
+
170
+ if __name__ == "__main__":
171
+ main()
@@ -0,0 +1,36 @@
1
+ """
2
+ 控制台输出工具
3
+ """
4
+
5
+
6
+ class Colors:
7
+ """颜色输出"""
8
+ HEADER = '\033[95m'
9
+ OKBLUE = '\033[94m'
10
+ OKCYAN = '\033[96m'
11
+ OKGREEN = '\033[92m'
12
+ WARNING = '\033[93m'
13
+ FAIL = '\033[91m'
14
+ ENDC = '\033[0m'
15
+ BOLD = '\033[1m'
16
+
17
+
18
+ def print_info(msg: str, verbose: bool = True) -> None:
19
+ """打印信息"""
20
+ if verbose:
21
+ print(f"{Colors.OKCYAN}ℹ {msg}{Colors.ENDC}")
22
+
23
+
24
+ def print_success(msg: str) -> None:
25
+ """打印成功"""
26
+ print(f"{Colors.OKGREEN}✅ {msg}{Colors.ENDC}")
27
+
28
+
29
+ def print_error(msg: str) -> None:
30
+ """打印错误"""
31
+ print(f"{Colors.FAIL}❌ {msg}{Colors.ENDC}")
32
+
33
+
34
+ def print_warning(msg: str) -> None:
35
+ """打印警告"""
36
+ print(f"{Colors.WARNING}⚠️ {msg}{Colors.ENDC}")
@@ -0,0 +1,28 @@
1
+ """
2
+ CLI 数据模型
3
+ """
4
+ from dataclasses import dataclass
5
+
6
+
7
+ @dataclass
8
+ class FeatureOptions:
9
+ """功能模块选项"""
10
+ database: bool = False
11
+ rabbitmq: bool = False
12
+ nacos: bool = True # 默认开启
13
+ kafka_log: bool = True # 默认开启
14
+ sse: bool = False
15
+ llm: bool = True # 默认开启
16
+ redis: bool = False # Redis 缓存
17
+
18
+ def to_template_vars(self) -> dict:
19
+ """转换为模板变量"""
20
+ return {
21
+ "feature_database": str(self.database).lower(),
22
+ "feature_rabbitmq": str(self.rabbitmq).lower(),
23
+ "feature_nacos": str(self.nacos).lower(),
24
+ "feature_kafka_log": str(self.kafka_log).lower(),
25
+ "feature_sse": str(self.sse).lower(),
26
+ "feature_llm": str(self.llm).lower(),
27
+ "feature_redis": str(self.redis).lower(),
28
+ }
@@ -0,0 +1,234 @@
1
+ """
2
+ 项目初始化逻辑
3
+ """
4
+ import os
5
+ import re
6
+ import datetime
7
+ from pathlib import Path
8
+ from importlib.resources import files
9
+ from typing import Optional
10
+
11
+ from jinja2 import Environment, BaseLoader, TemplateSyntaxError
12
+
13
+ from .models import FeatureOptions
14
+ from .console import (
15
+ print_info, print_success, print_error, print_warning, Colors
16
+ )
17
+ from .utils import get_all_files_in_directory
18
+
19
+
20
+ def interactive_feature_selection() -> FeatureOptions:
21
+ """交互式功能选择"""
22
+ print(f"\n{Colors.HEADER}🔧 功能模块配置{Colors.ENDC}")
23
+ print("-" * 40)
24
+
25
+ def ask_yes_no(question: str, default: bool = False) -> bool:
26
+ default_str = "Y/n" if default else "y/N"
27
+ while True:
28
+ response = input(f" {question} [{default_str}]: ").strip().lower()
29
+ if not response:
30
+ return default
31
+ if response in ('y', 'yes', '是'):
32
+ return True
33
+ if response in ('n', 'no', '否'):
34
+ return False
35
+ print_warning("请输入 y/n")
36
+
37
+ return FeatureOptions(
38
+ database=ask_yes_no("启用数据库 (MySQL)?", default=False),
39
+ rabbitmq=ask_yes_no("启用 RabbitMQ 消息队列?", default=False),
40
+ nacos=ask_yes_no("启用 Nacos 服务注册?", default=True),
41
+ kafka_log=ask_yes_no("启用 Kafka 日志?", default=True),
42
+ sse=ask_yes_no("启用 SSE 流式响应?", default=False),
43
+ llm=ask_yes_no("启用 LLM 大模型支持?", default=True),
44
+ redis=ask_yes_no("启用 Redis 缓存?", default=False),
45
+ )
46
+
47
+
48
+ def init_project(
49
+ project_name: str,
50
+ project_type: str,
51
+ features: Optional[FeatureOptions] = None,
52
+ dry_run: bool = False,
53
+ force: bool = False,
54
+ verbose: bool = True
55
+ ) -> bool:
56
+ """
57
+ 初始化项目,自动读取模板文件并替换占位符
58
+
59
+ Args:
60
+ project_name: 项目名称
61
+ project_type: 项目类型 (web/agent)
62
+ features: 功能选项,为 None 时使用交互式选择
63
+ dry_run: 仅预览,不实际创建文件
64
+ force: 强制覆盖已存在的目录
65
+ verbose: 显示详细输出
66
+
67
+ Returns:
68
+ 是否成功
69
+ """
70
+ project_path = Path(os.getcwd()) / project_name
71
+
72
+ # 检查目录是否存在
73
+ if project_path.exists():
74
+ if not force:
75
+ print_error(f"工程 '{project_path}' 已存在")
76
+ print_info("使用 --force 参数强制覆盖", verbose)
77
+ return False
78
+ elif dry_run:
79
+ print_warning(f"[DRY-RUN] 将覆盖已存在的目录: {project_path}")
80
+ else:
81
+ print_warning(f"将覆盖已存在的目录: {project_path}")
82
+
83
+ # 交互式功能选择
84
+ if features is None:
85
+ try:
86
+ features = interactive_feature_selection()
87
+ except KeyboardInterrupt:
88
+ print("\n")
89
+ print_info("操作已取消")
90
+ return False
91
+
92
+ # 模板目录检查
93
+ template_root = files("command.templates")
94
+ if not template_root.is_dir():
95
+ print_error("未找到模板文件目录(command/templates)")
96
+ return False
97
+
98
+ # 处理项目名称
99
+ short_project_name = project_name.replace("shengye-platform-", "")
100
+ short_project_name_upper = short_project_name.upper()
101
+
102
+ # 定义模板变量
103
+ context = {
104
+ "project_name": project_name,
105
+ "short_project_name": short_project_name,
106
+ "short_project_name_upper": short_project_name_upper,
107
+ "project_type": project_type,
108
+ "create_time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
109
+ "author": os.getlogin(),
110
+ "default_port": 8080,
111
+ }
112
+ context.update(features.to_template_vars())
113
+
114
+ # 获取模板文件
115
+ base_dir = template_root / "base"
116
+ type_dir = template_root / project_type
117
+
118
+ base_files = get_all_files_in_directory(base_dir)
119
+ type_specific_files = get_all_files_in_directory(type_dir)
120
+ file_mappings = base_files + type_specific_files
121
+
122
+ # Dry run 模式
123
+ if dry_run:
124
+ return _dry_run(file_mappings, features, verbose)
125
+
126
+ # 创建目录
127
+ if not project_path.exists():
128
+ project_path.mkdir(parents=True)
129
+
130
+ # 渲染并写入文件
131
+ copied_files = _render_files(file_mappings, context, project_path, verbose)
132
+
133
+ if copied_files > 0:
134
+ _print_success_message(project_path, project_name, copied_files)
135
+ return True
136
+ else:
137
+ print_warning("未创建任何文件,可能是缺少模板文件或模板路径配置错误")
138
+ return False
139
+
140
+
141
+ def _dry_run(
142
+ file_mappings: list,
143
+ features: FeatureOptions,
144
+ verbose: bool
145
+ ) -> bool:
146
+ """预览模式"""
147
+ print_info(f"\n📋 预览将要创建的文件 (共 {len(file_mappings)} 个):", verbose)
148
+ print("-" * 40)
149
+ for _, target_rel_path in file_mappings:
150
+ if target_rel_path.endswith('.tpl'):
151
+ target_rel_path = target_rel_path[:-4]
152
+ print(f" 📄 {target_rel_path}")
153
+ print("-" * 40)
154
+ print_info("功能配置:", verbose)
155
+ print(f" 数据库: {'✓' if features.database else '✗'}")
156
+ print(f" RabbitMQ: {'✓' if features.rabbitmq else '✗'}")
157
+ print(f" Nacos: {'✓' if features.nacos else '✗'}")
158
+ print(f" Kafka日志: {'✓' if features.kafka_log else '✗'}")
159
+ print(f" SSE流式: {'✓' if features.sse else '✗'}")
160
+ print(f" LLM支持: {'✓' if features.llm else '✗'}")
161
+ print(f" Redis缓存: {'✓' if features.redis else '✗'}")
162
+ return True
163
+
164
+
165
+ def _render_files(
166
+ file_mappings: list,
167
+ context: dict,
168
+ project_path: Path,
169
+ verbose: bool
170
+ ) -> int:
171
+ """渲染并写入文件"""
172
+ jinja_env = Environment(
173
+ loader=BaseLoader(),
174
+ trim_blocks=True,
175
+ lstrip_blocks=True,
176
+ )
177
+
178
+ copied_files = 0
179
+
180
+ for template_file, target_rel_path in file_mappings:
181
+ try:
182
+ # 读取模板内容
183
+ template_content = template_file.read_text(encoding="utf-8")
184
+
185
+ # 使用 Jinja2 渲染
186
+ try:
187
+ jinja_template = jinja_env.from_string(template_content)
188
+ rendered_content = jinja_template.render(context)
189
+ except TemplateSyntaxError:
190
+ # 回退到简单替换
191
+ rendered_content = template_content
192
+ for key, value in context.items():
193
+ pattern = re.compile(rf'{{\s*{re.escape(key)}\s*}}')
194
+ rendered_content = pattern.sub(str(value), rendered_content)
195
+
196
+ # 清理引号
197
+ rendered_content = re.sub(
198
+ r'(\w+)\s*:\s*["\']([^"\']+)["\']',
199
+ r'\1: \2',
200
+ rendered_content
201
+ )
202
+
203
+ # 处理文件后缀
204
+ if target_rel_path.endswith('.tpl'):
205
+ target_rel_path = target_rel_path[:-4]
206
+
207
+ # 写入文件
208
+ target_file = project_path / target_rel_path
209
+ target_file.parent.mkdir(parents=True, exist_ok=True)
210
+ target_file.write_text(rendered_content, encoding="utf-8")
211
+
212
+ print_info(f"创建: {target_rel_path}", verbose)
213
+ copied_files += 1
214
+
215
+ except Exception as e:
216
+ print_error(f"处理模板 {template_file} 失败: {str(e)}")
217
+
218
+ return copied_files
219
+
220
+
221
+ def _print_success_message(
222
+ project_path: Path,
223
+ project_name: str,
224
+ copied_files: int
225
+ ) -> None:
226
+ """打印成功信息"""
227
+ print_success(f"模板工程 {project_name} 创建完成!")
228
+ print(f"📁 工程路径:{project_path}")
229
+ print(f"📊 共创建 {copied_files} 个文件")
230
+ print()
231
+ print_info("下一步操作:")
232
+ print(f" cd {project_name}")
233
+ print(f" pip install -r requirements.txt")
234
+ print(f" python app.py")
@@ -0,0 +1,31 @@
1
+ """
2
+ 工具函数
3
+ """
4
+ import os
5
+ from pathlib import Path
6
+
7
+
8
+ def get_all_files_in_directory(directory: Path) -> list[tuple[Path, str]]:
9
+ """
10
+ 获取目录下所有文件的相对路径(忽略__pycache__目录)
11
+
12
+ Args:
13
+ directory: 目录路径
14
+
15
+ Returns:
16
+ 元组列表 (模板文件路径, 相对目标路径)
17
+ """
18
+ file_mappings = []
19
+ if not directory.exists() or not directory.is_dir():
20
+ return file_mappings
21
+
22
+ for root, _, files in os.walk(directory):
23
+ if "__pycache__" in root:
24
+ continue
25
+
26
+ for file in files:
27
+ file_path = Path(root) / file
28
+ rel_path = file_path.relative_to(directory)
29
+ file_mappings.append((file_path, str(rel_path)))
30
+
31
+ return file_mappings
@@ -3,7 +3,8 @@ from langchain.chat_models import init_chat_model
3
3
  from sycommon.config.LLMConfig import LLMConfig
4
4
  from sycommon.llm.sy_langfuse import LangfuseInitializer
5
5
  from sycommon.llm.usage_token import LLMWithAutoTokenUsage
6
- from typing import Any
6
+ from typing import Any, Union
7
+ from langchain_core.language_models import BaseChatModel
7
8
 
8
9
 
9
10
  def get_llm(
@@ -13,8 +14,9 @@ def get_llm(
13
14
  temperature: float = 0.1,
14
15
  timeout: float = 180,
15
16
  max_retries: int = 3,
17
+ wrap_structured: bool = True,
16
18
  **kwargs: Any
17
- ) -> LLMWithAutoTokenUsage:
19
+ ) -> Union[LLMWithAutoTokenUsage, BaseChatModel]:
18
20
  """
19
21
  获取LLM实例
20
22
 
@@ -24,20 +26,29 @@ def get_llm(
24
26
  temperature: 温度参数
25
27
  timeout: 请求超时时间(秒),默认180秒
26
28
  max_retries: 最大重试次数,默认3次
29
+ wrap_structured: 是否包装为结构化输出实例,默认True
30
+ - True: 返回 LLMWithAutoTokenUsage,支持 with_structured_output()
31
+ - False: 返回原始 BaseChatModel,保留日志和 Langfuse 跟踪
27
32
  **kwargs: 其他透传参数,支持 langchain init_chat_model 所有参数
28
33
  - presence_penalty: 存在惩罚
29
34
  - extra_body: 额外请求体参数,如 {"top_k": 20, "chat_template_kwargs": {"enable_thinking": False}}
30
35
  - top_p: 核采样参数
31
36
  - frequency_penalty: 频率惩罚
32
37
  - model_kwargs: 模型特定参数
38
+ - summary_prompt: 结构化输出时的摘要提示词(仅 wrap_structured=True 时有效)
33
39
 
34
40
  Returns:
35
- LLMWithAutoTokenUsage: 包装后的LLM实例
41
+ LLMWithAutoTokenUsage | BaseChatModel: 根据 wrap_structured 参数返回对应类型
36
42
 
37
43
  Example:
38
44
  ```python
39
- # 基础用法
45
+ # 结构化输出(默认)
40
46
  llm = get_llm("Qwen2.5-72B")
47
+ chain = llm.with_structured_output(MyModel)
48
+
49
+ # 普通 LLM 调用,保留日志和 Langfuse
50
+ llm = get_llm("Qwen2.5-72B", wrap_structured=False)
51
+ response = await llm.ainvoke([HumanMessage(content="你好")])
41
52
 
42
53
  # 透传额外参数
43
54
  llm = get_llm(
@@ -82,6 +93,10 @@ def get_llm(
82
93
  if llm is None:
83
94
  raise Exception(f"初始化原始LLM实例失败:{model}")
84
95
 
96
+ # 如果不需要结构化输出包装,直接返回原始 LLM(仍保留日志和 Langfuse 跟踪)
97
+ if not wrap_structured:
98
+ return llm
99
+
85
100
  # 获取kwargs中summary_prompt参数
86
101
  summary_prompt: str = kwargs.get("summary_prompt")
87
102