pro-craft 0.1.1__py3-none-any.whl

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.

Potentially problematic release.


This version of pro-craft might be problematic. Click here for more details.

pro_craft/__init__.py ADDED
File without changes
pro_craft/core.py ADDED
File without changes
pro_craft/log.py ADDED
@@ -0,0 +1,118 @@
1
+ import logging
2
+ import os
3
+ from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
4
+
5
+ class Logger:
6
+ """log
7
+
8
+ Returns:
9
+ _type_: single
10
+ """
11
+ _instance = None
12
+
13
+ def __new__(cls, *args, **kwargs):
14
+ if cls._instance is None:
15
+ cls._instance = super().__new__(cls)
16
+ return cls._instance
17
+
18
+ def __init__(self,level = 'debug',log_file_name = "app.log"):
19
+ """
20
+ Level (级别): 定义日志的严重程度。从低到高依次是:
21
+ DEBUG: 详细的调试信息,通常只在开发阶段使用。
22
+ INFO: 确认程序按预期运行。
23
+ WARNING: 发生了意外,或将来可能出现问题,但程序仍在正常运行。
24
+ ERROR: 发生了严重错误,程序可能无法执行某些功能。
25
+ CRITICAL: 发生了严重错误,程序可能无法继续运行。
26
+ # --- 1. 定义日志级别 ---
27
+ # 建议在开发环境使用 DEBUG,生产环境使用 INFO 或 WARNING
28
+
29
+ """
30
+ if not hasattr(self, 'initialized'):
31
+ self.initialized = True
32
+
33
+ if level == 'debug':
34
+ self.LOG_LEVEL = logging.DEBUG # 开发阶段
35
+ elif level == 'info':
36
+ self.LOG_LEVEL = logging.INFO # 生产阶段
37
+ elif level == 'warning':
38
+ self.LOG_LEVEL = logging.WARNING
39
+ elif level == 'error':
40
+ self.LOG_LEVEL = logging.ERROR
41
+ elif level == 'critical':
42
+ self.LOG_LEVEL = logging.CRITICAL
43
+ else:
44
+ self.LOG_LEVEL = logging.INFO # 默认级别
45
+
46
+ # --- 2. 定义日志文件路径和名称 ---
47
+ self.LOG_DIR = "logs"
48
+ self.LOG_FILE_NAME = log_file_name
49
+ self.LOG_FILE_PATH = os.path.join(self.LOG_DIR, self.LOG_FILE_NAME)
50
+
51
+ # 确保日志目录存在
52
+ os.makedirs(self.LOG_DIR, exist_ok=True)
53
+ self.logger = None
54
+ self.setup_logging()
55
+ self.env = 'dev'
56
+
57
+ def reset_level(self,level = 'debug',env = 'dev'):
58
+ if level == 'debug':
59
+ self.LOG_LEVEL = logging.DEBUG # 开发阶段
60
+ elif level == 'info':
61
+ self.LOG_LEVEL = logging.INFO # 生产阶段
62
+ elif level == 'warning':
63
+ self.LOG_LEVEL = logging.WARNING
64
+ elif level == 'error':
65
+ self.LOG_LEVEL = logging.ERROR
66
+ elif level == 'critical':
67
+ self.LOG_LEVEL = logging.CRITICAL
68
+ else:
69
+ self.LOG_LEVEL = logging.INFO # 默认级别
70
+
71
+ self.setup_logging()
72
+ self.env = env
73
+
74
+ def setup_logging(self):
75
+ """# --- 3. 配置 Logger ---
76
+ """
77
+ # 获取根 Logger (也可以创建自定义的 Logger: logging.getLogger('my_app'))
78
+ logger = logging.getLogger()
79
+ logger.setLevel(self.LOG_LEVEL)
80
+
81
+ # 避免重复添加 Handler (如果多次调用 setup_logging)
82
+ if not logger.handlers:
83
+ # --- 4. 配置 Formatter (格式化器) ---
84
+ # 常见格式:时间 - 日志级别 - Logger名称 - 模块名 - 行号 - 消息
85
+ formatter = logging.Formatter(
86
+ '%(asctime)s - %(levelname)s - %(name)s - %(module)s:%(lineno)d - %(message)s'
87
+ )
88
+
89
+ # --- 5. 配置 Handler (处理器) ---
90
+
91
+ # 5.1 控制台处理器 (StreamHandler)
92
+ console_handler = logging.StreamHandler()
93
+ console_handler.setLevel(logging.INFO) # 控制台只显示 INFO 及以上级别的日志
94
+ console_handler.setFormatter(formatter)
95
+ logger.addHandler(console_handler)
96
+
97
+ # 5.2 文件处理器 (RotatingFileHandler 或 TimedRotatingFileHandler)
98
+
99
+ file_handler = RotatingFileHandler(
100
+ self.LOG_FILE_PATH,
101
+ maxBytes=10 * 1024 * 1024, # 10 MB
102
+ backupCount=5,
103
+ encoding='utf-8'
104
+ )
105
+ file_handler.setLevel(self.LOG_LEVEL) # 文件中显示所有指定级别的日志
106
+ file_handler.setFormatter(formatter)
107
+ logger.addHandler(file_handler)
108
+
109
+ self.logger = logger
110
+
111
+
112
+ Log = Logger(log_file_name = "app.log")
113
+ del Logger
114
+
115
+ """
116
+ from .log import Log
117
+ logger = Log.logger
118
+ """
File without changes
@@ -0,0 +1,160 @@
1
+
2
+ from fastapi import FastAPI, HTTPException, Header
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ from fastapi import FastAPI, Depends
5
+ from fastapi_users import FastAPIUsers
6
+ from contextlib import asynccontextmanager
7
+ from .utils import create_db_and_tables
8
+ from .utils import get_user_manager, auth_backend
9
+ from .models.models import User,UserCreate, UserRead,UserUpdate
10
+ from .routers import admin_router,user_router
11
+ from pydantic import BaseModel, Field, model_validator
12
+ import argparse
13
+ import uvicorn
14
+ import uuid
15
+ from umanager.log import Log
16
+
17
+ # 应用程序生命周期事件
18
+ @asynccontextmanager
19
+ async def lifespan(app: FastAPI):
20
+ # 应用启动时创建数据库表
21
+ await create_db_and_tables()
22
+ yield
23
+
24
+
25
+ default=8008
26
+
27
+ app = FastAPI(
28
+ lifespan=lifespan,
29
+ title="LLM Service",
30
+ description="Provides an OpenAI-compatible API for custom large language models.",
31
+ version="1.0.1",
32
+ )
33
+
34
+ fastapi_users = FastAPIUsers[User, uuid.UUID](
35
+ get_user_manager,
36
+ [auth_backend],
37
+ )
38
+
39
+
40
+ # --- Configure CORS ---
41
+ origins = [
42
+ "*", # Allows all origins (convenient for development, insecure for production)
43
+ # Add the specific origin of your "别的调度" tool/frontend if known
44
+ # e.g., "http://localhost:5173" for a typical Vite frontend dev server
45
+ # e.g., "http://127.0.0.1:5173"
46
+ ]
47
+
48
+ app.add_middleware(
49
+ CORSMiddleware,
50
+ allow_origins=origins, # Specifies the allowed origins
51
+ allow_credentials=True, # Allows cookies/authorization headers
52
+ allow_methods=["*"], # Allows all methods (GET, POST, OPTIONS, etc.)
53
+ allow_headers=["*"], # Allows all headers (Content-Type, Authorization, etc.)
54
+ )
55
+ # --- End CORS Configuration ---
56
+
57
+ # 注册认证和用户管理路由
58
+ app.include_router(
59
+ fastapi_users.get_auth_router(auth_backend),
60
+ prefix="/auth/jwt",
61
+ tags=["auth"],
62
+ )
63
+ app.include_router(
64
+ fastapi_users.get_register_router(UserRead, UserCreate),
65
+ prefix="/auth",
66
+ tags=["auth"],
67
+ )
68
+ app.include_router(
69
+ fastapi_users.get_reset_password_router(),
70
+ prefix="/auth",
71
+ tags=["auth"],
72
+ )
73
+ app.include_router(
74
+ fastapi_users.get_verify_router(UserRead),
75
+ prefix="/auth",
76
+ tags=["auth"],
77
+ )
78
+ app.include_router(
79
+ fastapi_users.get_users_router(UserRead, UserUpdate),
80
+ prefix="/users",
81
+ tags=["users"],
82
+ )
83
+
84
+
85
+ # -------------------- 集成待办事项路由 --------------------
86
+ # app.include_router(admin_router, prefix="/")
87
+ # app.include_router(user_router, prefix="/")
88
+
89
+ app.include_router(admin_router,prefix="/admin")
90
+ app.include_router(user_router,prefix="/users")
91
+
92
+
93
+ @app.get("/")
94
+ async def root():
95
+ """ x """
96
+ return {"message": "LLM Service is running."}
97
+
98
+
99
+
100
+
101
+ if __name__ == "__main__":
102
+
103
+ parser = argparse.ArgumentParser(
104
+ description="Start a simple HTTP server similar to http.server."
105
+ )
106
+ parser.add_argument(
107
+ 'port',
108
+ metavar='PORT',
109
+ type=int,
110
+ nargs='?', # 端口是可选的
111
+ default=default,
112
+ help=f'Specify alternate port [default: {default}]'
113
+ )
114
+ # 创建一个互斥组用于环境选择
115
+ group = parser.add_mutually_exclusive_group()
116
+
117
+ # 添加 --dev 选项
118
+ group.add_argument(
119
+ '--dev',
120
+ action='store_true', # 当存在 --dev 时,该值为 True
121
+ help='Run in development mode (default).'
122
+ )
123
+
124
+ # 添加 --prod 选项
125
+ group.add_argument(
126
+ '--prod',
127
+ action='store_true', # 当存在 --prod 时,该值为 True
128
+ help='Run in production mode.'
129
+ )
130
+ args = parser.parse_args()
131
+
132
+ if args.prod:
133
+ env = "prod"
134
+ else:
135
+ # 如果 --prod 不存在,默认就是 dev
136
+ env = "dev"
137
+
138
+ port = args.port
139
+ if env == "dev":
140
+ port += 100
141
+ Log.reset_level('debug',env = env)
142
+ reload = True
143
+ app_import_string = f"{__package__}.__main__:app" # <--- 关键修改:传递导入字符串
144
+ elif env == "prod":
145
+ Log.reset_level('info',env = env)# ['debug', 'info', 'warning', 'error', 'critical']
146
+ reload = False
147
+ app_import_string = app
148
+ else:
149
+ reload = False
150
+ app_import_string = app
151
+
152
+
153
+ # 使用 uvicorn.run() 来启动服务器
154
+ # 参数对应于命令行选项
155
+ uvicorn.run(
156
+ app_import_string,
157
+ host="0.0.0.0",
158
+ port=port,
159
+ reload=reload # 启用热重载
160
+ )
@@ -0,0 +1,5 @@
1
+ # uv run mcp dev src/obsidian_sdk/mcp.py
2
+ # TODO 开发mcp 的stdio 格式, 使得MCP 更加好用
3
+ # Create an MCP server
4
+
5
+
@@ -0,0 +1,113 @@
1
+
2
+ from mcp.server.fastmcp import Context, FastMCP, Icon
3
+ from prompt_writing_assistant.file_manager import ContentManager, TextType
4
+ from mcp.server.fastmcp import FastMCP
5
+ from mcp.server.fastmcp.prompts import base
6
+ from pydantic import BaseModel, Field
7
+ from prompt_writing_assistant.prompt_helper import IntellectType,Intel
8
+
9
+
10
+ content_manager = ContentManager()
11
+ intels = Intel()
12
+
13
+ mcp = FastMCP("Content")
14
+
15
+ @mcp.prompt(title="Code Review")
16
+ def review_code(code: str) -> str:
17
+ return f"Please review this code:\n\n{code}"
18
+
19
+
20
+ @mcp.prompt(title="Debug Assistant")
21
+ def debug_error(error: str) -> list[base.Message]:
22
+ return [
23
+ base.UserMessage("I'm seeing this error:"),
24
+ base.UserMessage(error),
25
+ base.AssistantMessage("I'll help debug that. What have you tried so far?"),
26
+ ]
27
+
28
+
29
+ @mcp.tool()
30
+ def save_content(text:str)->str:
31
+ """
32
+ text : 需要存储的内容
33
+ return : 返回是否存储成功的信息
34
+ """
35
+ result =content_manager.save_content_auto(
36
+ text = text)
37
+ return result
38
+
39
+ @mcp.tool()
40
+ def similarit_content(text: str,limit: int):
41
+ """
42
+ text : 待查询的文字, 进行匹配
43
+ limit : 查询的数量
44
+ """
45
+
46
+ result = content_manager.similarity(
47
+ content = text,
48
+ limit = limit
49
+ )
50
+ return result
51
+
52
+ @mcp.tool()
53
+ def get_prompts(prompt_id: str,version: str = "1.0"):
54
+ """
55
+ prompt_id : 待查询的文字, 进行匹配
56
+ limit : 查询的数量
57
+ """
58
+ result = intels.get_prompts_from_sql(
59
+ table_name = "prompts_data",
60
+ prompt_id = prompt_id,
61
+ version=version,
62
+ )
63
+ return result
64
+
65
+ @mcp.tool()
66
+ def list_cities() -> list[str]:
67
+ """Get a list of cities"""
68
+ return ["London", "Paris", "Tokyo"]
69
+
70
+
71
+
72
+
73
+
74
+
75
+ class WeatherData(BaseModel):
76
+ """Weather information structure."""
77
+
78
+ temperature: float = Field(description="Temperature in Celsius")
79
+ humidity: float = Field(description="Humidity percentage")
80
+ condition: str
81
+ wind_speed: float
82
+
83
+
84
+ @mcp.tool() # icons=[icon]
85
+ def get_weather(city: str) -> WeatherData:
86
+ """Get weather for a city - returns structured data."""
87
+ # Simulated weather data
88
+ return WeatherData(
89
+ temperature=22.5,
90
+ humidity=45.0,
91
+ condition="sunny",
92
+ wind_speed=5.2,
93
+ )
94
+
95
+
96
+ from PIL import Image as PILImage
97
+ from mcp.server.fastmcp import FastMCP, Image
98
+
99
+ @mcp.tool()
100
+ def create_thumbnail(image_path: str) -> Image:
101
+ """Create a thumbnail from an image"""
102
+ img = PILImage.open(image_path)
103
+ img.thumbnail((100, 100))
104
+ return Image(data=img.tobytes(), format="png")
105
+
106
+
107
+
108
+ if __name__ == "__main__":
109
+ mcp.run(transport="streamable-http")
110
+ # mcp.run(transport="sse")
111
+ # search_mcp.run(transport="sse", mount_path="/search")
112
+
113
+
@@ -0,0 +1,33 @@
1
+ from mcp.server.fastmcp import FastMCP
2
+
3
+
4
+ # region MCP Math
5
+ mcp = FastMCP("Math")
6
+
7
+ @mcp.tool(description="A simple add tool")
8
+ def add(a: int, b: int) -> int:
9
+ """Adds two integers.
10
+ Args:
11
+ a: The first integer.
12
+ b: The second integer.
13
+ """
14
+ return a + b
15
+
16
+
17
+ @mcp.tool()
18
+ def multiply(a: int, b: int) -> int:
19
+ """Multiply two integers.
20
+ Args:
21
+ a: The first integer.
22
+ b: The second integer.
23
+ """
24
+ return a * b
25
+ # endregion
26
+
27
+
28
+ if __name__ == "__main__":
29
+ mcp.run(transport="streamable-http")
30
+ # mcp.run(transport="sse")
31
+ # search_mcp.run(transport="sse", mount_path="/search")
32
+
33
+
@@ -0,0 +1,35 @@
1
+
2
+ from mcp.server.fastmcp import Context, FastMCP
3
+
4
+
5
+ # region MCP Math
6
+ mcp = FastMCP("resource")
7
+
8
+
9
+ @mcp.resource("config://settings")
10
+ def get_settings() -> str:
11
+ """Get application settings."""
12
+ return """{
13
+ "theme": "dark",
14
+ "language": "en",
15
+ "debug": false
16
+ }"""
17
+
18
+ @mcp.resource("file://documents/{name}")
19
+ def read_document(name: str) -> str:
20
+ """Read a document by name."""
21
+ # This would normally read from disk
22
+ path = "/Users/zhaoxuefeng/GitHub/prompt_writing_assistant/tests/temp_file/"
23
+ path += name
24
+ with open(path,'r') as f:
25
+ return f.read()
26
+
27
+ @mcp.resource("file://notes/{name}")
28
+ def read_notes(name:str)-> str:
29
+ """ 阅读备忘录"""
30
+
31
+ return "你要做王子"
32
+
33
+ if __name__ == "__main__":
34
+ mcp.run(transport="streamable-http")
35
+
@@ -0,0 +1,22 @@
1
+ from datetime import datetime
2
+ from mcp.server.fastmcp import FastMCP
3
+
4
+ # region MCP Weather
5
+ mcp = FastMCP("Weather")
6
+
7
+ @mcp.tool()
8
+ def get_weather(location: str) -> str:
9
+ return "Cloudy"
10
+
11
+ @mcp.tool()
12
+ def get_time() -> str:
13
+ return datetime.now().strftime('%Y-%m-%d %H:%M:%S')
14
+ # endregion
15
+
16
+
17
+ if __name__ == "__main__":
18
+ mcp.run(transport="streamable-http")
19
+ # mcp.run(transport="sse")
20
+ # search_mcp.run(transport="sse", mount_path="/search")
21
+
22
+
File without changes
@@ -0,0 +1,48 @@
1
+
2
+ import uuid
3
+ from typing import Optional
4
+
5
+ from sqlalchemy import String, Boolean, Column
6
+ from sqlalchemy.dialects.mysql import CHAR # Use CHAR for UUID to store as fixed length string
7
+ from fastapi_users.db import SQLAlchemyBaseUserTableUUID
8
+ from pydantic import BaseModel, Field
9
+
10
+ from umanager.server.utils import Base # 引入Base
11
+
12
+ # SQLAlchemy 用户模型
13
+ # 使用 SQLAlchemyBaseUserTableUUID 来自动处理 UUID 主键
14
+ class User(SQLAlchemyBaseUserTableUUID, Base):
15
+ __tablename__ = "users" # 表名
16
+
17
+ # 可以在这里添加额外的用户属性
18
+ first_name: str = Column(String(255), nullable=True)
19
+ last_name: str = Column(String(255), nullable=True)
20
+ is_admin: bool = Column(Boolean, default=False)
21
+
22
+ # Pydantic 模型 (用于请求和响应)
23
+ class UserRead(BaseModel):
24
+ id: uuid.UUID
25
+ email: str
26
+ is_active: bool = True
27
+ is_superuser: bool = False
28
+ is_verified: bool = False
29
+ first_name: Optional[str] = None
30
+ last_name: Optional[str] = None
31
+ is_admin: bool = False
32
+
33
+ class Config:
34
+ from_attributes = True # updated from orm_mode = True
35
+
36
+ class UserCreate(BaseModel):
37
+ email: str
38
+ password: str
39
+ first_name: Optional[str] = None
40
+ last_name: Optional[str] = None
41
+
42
+ class UserUpdate(UserCreate):
43
+ is_active: Optional[bool] = None
44
+ is_superuser: Optional[bool] = None
45
+ is_verified: Optional[bool] = None
46
+ first_name: Optional[str] = None
47
+ last_name: Optional[str] = None
48
+ is_admin: Optional[bool] = None
@@ -0,0 +1,2 @@
1
+ from .admin import router as admin_router
2
+ from .user import router as user_router
@@ -0,0 +1,42 @@
1
+
2
+ # app/routers/admin.py
3
+ from fastapi import APIRouter, Depends, HTTPException, status
4
+ from fastapi import FastAPI, HTTPException, Header
5
+
6
+
7
+ # 模拟获取当前用户的依赖项
8
+ async def get_current_user(x_token: str = Header(...)):
9
+ if x_token != "valid-secret-token":
10
+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid X-Token header")
11
+ return {"username": "admin", "roles": ["user", "admin"]}
12
+
13
+ # 模拟一个管理员权限检查的依赖项
14
+ async def verify_admin_role(user: dict = Depends(get_current_user)):
15
+ if "admin" not in user.get("roles", []):
16
+ raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions")
17
+ return user
18
+
19
+
20
+ router = APIRouter(
21
+ tags=["Admin"],
22
+ dependencies=[Depends(get_current_user), Depends(verify_admin_role)] # 统一的依赖项列表
23
+ )
24
+
25
+
26
+
27
+ @router.get("/dashboard/")
28
+ async def get_admin_dashboard_data(user):
29
+ # 这个函数会自动接收到 get_current_user 和 verify_admin_role 的结果(如果需要)
30
+ # 但我们在这里不需要显式接收它们,因为它们只是做权限检查
31
+ print(user,'user')
32
+ return {"message": "Welcome to the admin dashboard!"}
33
+
34
+ @router.post("/settings/")
35
+ async def update_admin_settings(settings: dict):
36
+ # 这个函数也会自动执行 get_current_user 和 verify_admin_role
37
+ return {"message": "Admin settings updated", "settings": settings}
38
+
39
+ # 这个API也需要认证和管理员权限
40
+ @router.delete("/users/{user_id}")
41
+ async def delete_user(user_id: int):
42
+ return {"message": f"User {user_id} deleted by admin."}
@@ -0,0 +1,24 @@
1
+
2
+ # app/routers/admin.py
3
+ from fastapi import APIRouter, Depends, HTTPException, status
4
+ from fastapi import FastAPI, Header
5
+
6
+
7
+ # 模拟获取当前用户的依赖项
8
+ async def get_current_user(x_token: str = Header(...)):
9
+ if x_token not in ["1234",
10
+ "5678"]:
11
+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid X-Token header")
12
+ return {"username": "admin", "roles": ["user", "admin"]}
13
+
14
+
15
+ router = APIRouter(
16
+ tags=["users"],
17
+ dependencies=[Depends(get_current_user)] # 统一的依赖项列表
18
+ )
19
+
20
+
21
+ @router.get("/dashboard")
22
+ async def get_user(user_info :dict = Depends(get_current_user)):
23
+ pass
24
+ return {"message": "Welcome to the admin dashboard!"}
@@ -0,0 +1,3 @@
1
+ from .database import Base, create_db_and_tables
2
+ from .auth_backends import auth_backend
3
+ from .user_manager import get_user_manager
@@ -0,0 +1,15 @@
1
+ # auth_backends.py
2
+ from fastapi_users.authentication import BearerTransport, JWTStrategy, AuthenticationBackend
3
+
4
+ from .user_manager import SECRET # 从 user_manager 引入 SECRET
5
+
6
+ bearer_transport = BearerTransport(tokenUrl="auth/jwt/login")
7
+
8
+ def get_jwt_strategy() -> JWTStrategy:
9
+ return JWTStrategy(secret=SECRET, lifetime_seconds=3600) # JWT有效期1小时
10
+
11
+ auth_backend = AuthenticationBackend(
12
+ name="jwt",
13
+ transport=bearer_transport,
14
+ get_strategy=get_jwt_strategy,
15
+ )
@@ -0,0 +1,27 @@
1
+ # database.py
2
+ from typing import AsyncGenerator
3
+
4
+ from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
5
+ from sqlalchemy.orm import sessionmaker, DeclarativeBase
6
+
7
+ # 您的 MySQL 连接字符串
8
+ # 格式:mysql+aiomysql://user:password@host:port/database
9
+ # 注意:fastapi_users 的 sqlalchemy 适配器通常需要异步驱动,
10
+ # 所以我们使用 `aiomysql` (虽然你安装的是 mysql-connector-python,
11
+ # 但为了异步,我们实际会用 aiomysql,请确保安装它:pip install aiomysql)
12
+ DATABASE_URL = "mysql+aiomysql://root:1234@localhost:3306/prompts"
13
+
14
+ class Base(DeclarativeBase):
15
+ pass
16
+
17
+ engine = create_async_engine(DATABASE_URL)
18
+ async_session_maker = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
19
+
20
+ async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
21
+ async with async_session_maker() as session:
22
+ yield session
23
+
24
+ # 创建所有定义的表(在应用启动时调用)
25
+ async def create_db_and_tables():
26
+ async with engine.begin() as conn:
27
+ await conn.run_sync(Base.metadata.create_all)
@@ -0,0 +1,87 @@
1
+ # user_manager.py
2
+ import uuid
3
+ from typing import Optional
4
+
5
+ from fastapi import Depends, Request
6
+ from fastapi_users import BaseUserManager, UUIDIDMixin
7
+ # from fastapi_users_sqlalchemy import SQLAlchemyUserDatabase
8
+ from fastapi_users.db import SQLAlchemyUserDatabase
9
+ from .database import get_async_session
10
+
11
+ from fastapi import Depends, Request, HTTPException, status # <-- 确保 HTTPException 在这里
12
+ from sqlalchemy.exc import IntegrityError # <-- 确保这一行存在
13
+
14
+ from umanager.server.models.models import User, UserCreate # <--- 在这里添加 UserCreate 的导入
15
+
16
+ from passlib.context import CryptContext # 确保这里引入了 CryptContext
17
+
18
+ # ---------- 确保 pwd_context 在模块级别定义 ----------
19
+ # 它应该在任何使用它的类或函数之外
20
+ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
21
+
22
+ # 替换为您的安全密钥,最好从环境变量中获取
23
+ SECRET = "YOUR_SUPER_SECRET_KEY_REPLACE_ME" # !!! 重要:在生产环境中,请务必使用环境变量
24
+
25
+
26
+ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]):
27
+ # ... (reset_password_token_secret, verification_token_secret 保持不变)
28
+
29
+ async def create(
30
+ self,
31
+ user_create: UserCreate,
32
+ safe: bool = False,
33
+ request: Optional[Request] = None,
34
+ ) -> User:
35
+ existing_user = await self.user_db.get_by_email(user_create.email)
36
+ if existing_user is not None:
37
+ raise HTTPException(
38
+ status_code=status.HTTP_400_BAD_REQUEST,
39
+ detail="A user with this email already exists."
40
+ )
41
+
42
+ hashed_password = pwd_context.hash(user_create.password)
43
+
44
+ # 使用 model_dump 获取 Pydantic V2 的字典表示
45
+ user_data = user_create.model_dump(exclude_unset=True)
46
+ user_data["hashed_password"] = hashed_password
47
+ user_data["id"] = uuid.uuid4()
48
+
49
+ if "password" in user_data: # 确保移除明文密码
50
+ del user_data["password"]
51
+
52
+ # 创建 SQLAlchemy User 实例
53
+ # 这里的 self.user_db.user_table 就是 models.User
54
+ new_user = self.user_db.user_table(**user_data)
55
+
56
+ # 确保默认字段被设置
57
+ new_user.is_active = True
58
+ new_user.is_superuser = False
59
+ new_user.is_verified = False
60
+
61
+ # created_user = await self.user_db.create(new_user)
62
+ try:
63
+ self.user_db.session.add(new_user)
64
+ await self.user_db.session.commit()
65
+ await self.user_db.session.refresh(new_user) # 刷新以获取可能由数据库生成的字段(如默认值)
66
+ except IntegrityError:
67
+ await self.user_db.session.rollback()
68
+ raise HTTPException(
69
+ status_code=status.HTTP_400_BAD_REQUEST,
70
+ detail="A database integrity error occurred (e.g., duplicate email)."
71
+ )
72
+ # await self.on_after_register(created_user, request)
73
+ # return created_user
74
+ await self.on_after_register(new_user, request) # 这里也是 new_user
75
+ return new_user
76
+
77
+ async def get_user_db(session = Depends(get_async_session)):
78
+ yield SQLAlchemyUserDatabase(session, User)
79
+
80
+
81
+
82
+ async def get_user_manager(user_db: SQLAlchemyUserDatabase = Depends(get_user_db)):
83
+ yield UserManager(user_db)
84
+
85
+
86
+ # uv venv .venv --python 3.11
87
+ # python -c "import pydantic; print(pydantic.VERSION)"
pro_craft/server.py ADDED
@@ -0,0 +1,100 @@
1
+
2
+ from fastapi import FastAPI, HTTPException, Header
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ from pydantic import BaseModel, Field, model_validator
5
+ import argparse
6
+ import uvicorn
7
+ from .log import Log
8
+
9
+ default=8008
10
+
11
+ app = FastAPI(
12
+ title="LLM Service",
13
+ description="Provides an OpenAI-compatible API for custom large language models.",
14
+ version="1.0.1",
15
+ )
16
+
17
+ # --- Configure CORS ---
18
+ origins = [
19
+ "*", # Allows all origins (convenient for development, insecure for production)
20
+ # Add the specific origin of your "别的调度" tool/frontend if known
21
+ # e.g., "http://localhost:5173" for a typical Vite frontend dev server
22
+ # e.g., "http://127.0.0.1:5173"
23
+ ]
24
+
25
+ app.add_middleware(
26
+ CORSMiddleware,
27
+ allow_origins=origins, # Specifies the allowed origins
28
+ allow_credentials=True, # Allows cookies/authorization headers
29
+ allow_methods=["*"], # Allows all methods (GET, POST, OPTIONS, etc.)
30
+ allow_headers=["*"], # Allows all headers (Content-Type, Authorization, etc.)
31
+ )
32
+ # --- End CORS Configuration ---
33
+
34
+
35
+ @app.get("/")
36
+ async def root():
37
+ """ x """
38
+ return {"message": "LLM Service is running."}
39
+
40
+
41
+ if __name__ == "__main__":
42
+
43
+ parser = argparse.ArgumentParser(
44
+ description="Start a simple HTTP server similar to http.server."
45
+ )
46
+ parser.add_argument(
47
+ 'port',
48
+ metavar='PORT',
49
+ type=int,
50
+ nargs='?', # 端口是可选的
51
+ default=default,
52
+ help=f'Specify alternate port [default: {default}]'
53
+ )
54
+ # 创建一个互斥组用于环境选择
55
+ group = parser.add_mutually_exclusive_group()
56
+
57
+ # 添加 --dev 选项
58
+ group.add_argument(
59
+ '--dev',
60
+ action='store_true', # 当存在 --dev 时,该值为 True
61
+ help='Run in development mode (default).'
62
+ )
63
+
64
+ # 添加 --prod 选项
65
+ group.add_argument(
66
+ '--prod',
67
+ action='store_true', # 当存在 --prod 时,该值为 True
68
+ help='Run in production mode.'
69
+ )
70
+ args = parser.parse_args()
71
+
72
+ if args.prod:
73
+ env = "prod"
74
+ else:
75
+ # 如果 --prod 不存在,默认就是 dev
76
+ env = "dev"
77
+
78
+ port = args.port
79
+ if env == "dev":
80
+ port += 100
81
+ Log.reset_level('debug',env = env)
82
+ reload = True
83
+ app_import_string = f"{__package__}.server:app" # <--- 关键修改:传递导入字符串
84
+ elif env == "prod":
85
+ Log.reset_level('info',env = env)# ['debug', 'info', 'warning', 'error', 'critical']
86
+ reload = False
87
+ app_import_string = app
88
+ else:
89
+ reload = False
90
+ app_import_string = app
91
+
92
+
93
+ # 使用 uvicorn.run() 来启动服务器
94
+ # 参数对应于命令行选项
95
+ uvicorn.run(
96
+ app_import_string,
97
+ host="0.0.0.0",
98
+ port=port,
99
+ reload=reload # 启用热重载
100
+ )
pro_craft/utils.py ADDED
@@ -0,0 +1 @@
1
+ from utils_tool import *
@@ -0,0 +1,14 @@
1
+ Metadata-Version: 2.4
2
+ Name: pro-craft
3
+ Version: 0.1.1
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.13
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: anyio>=4.11.0
8
+ Requires-Dist: fastapi>=0.119.0
9
+ Requires-Dist: pytest>=8.4.2
10
+ Requires-Dist: pytest-asyncio>=1.2.0
11
+ Requires-Dist: pytest-tornasync>=0.6.0.post2
12
+ Requires-Dist: toml>=0.10.2
13
+ Requires-Dist: utils-tool==0.1.3
14
+ Requires-Dist: uvicorn>=0.38.0
@@ -0,0 +1,25 @@
1
+ pro_craft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ pro_craft/core.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ pro_craft/log.py,sha256=MZf9jCZsiRoAq8v4FxVnJqeSXxgzAiiKf7mxz6bFtwM,4263
4
+ pro_craft/server.py,sha256=fPAosQIU0d7gxICiALl8u6QwbLI4cawVFyoRYebRES0,2827
5
+ pro_craft/utils.py,sha256=Pi6-tconaDYaYV5E-Bb-gt9jDasqcVnNBAjpdXtGY5I,25
6
+ pro_craft/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ pro_craft/server/__main__.py,sha256=e4Ygx01-YHkgk0AJTHdf7N--q1_7XwxtfS2ba5yPViM,4331
8
+ pro_craft/server/mcp/__init__.py,sha256=4dbl-lFcm0r2tkOP04OxqiZG2jR-rqF181qi2AfU6UA,123
9
+ pro_craft/server/mcp/content.py,sha256=DombC1DlasvEl_lzy4u6soVR7rxgmyWFlaEvTSdCiOU,2745
10
+ pro_craft/server/mcp/math.py,sha256=OOzGXx64nK4bOVlu33PtVddcCQ9ilqA3Em9yxjSX9cg,641
11
+ pro_craft/server/mcp/resource.py,sha256=z94jP3qZofO-1lZCM3TuOfLajw41HARs1ojXab1ymas,776
12
+ pro_craft/server/mcp/weather.py,sha256=RAGuf4sgjlTQSfRRZ1Fo18JnuMQRS_Db9p6AqBQrl8E,455
13
+ pro_craft/server/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ pro_craft/server/models/models.py,sha256=VlYy3VYuIAZ8p8vd5Qcnyjzy8-EnQQbbVPl6YN1mCjg,1491
15
+ pro_craft/server/routers/__init__.py,sha256=mnJ7XmAzyZePf6CxusxbTZZF0xbqnDqYg3Z929rbSqQ,81
16
+ pro_craft/server/routers/admin.py,sha256=4flQtIMhVFoEf4z3rxC3W0k70Gtpupbv_JrM_AqWZnc,1608
17
+ pro_craft/server/routers/user.py,sha256=SHNXnJDSM2hKVpKQVUnjlBN-sVWuANlrAF26TWpDCR8,723
18
+ pro_craft/server/utils/__init__.py,sha256=ziKn9B14SbHdwnRH9cxiA-rXHuzjtPDM1mAIdRc9p-I,131
19
+ pro_craft/server/utils/auth_backends.py,sha256=7eZ2tC7P6Tw1wBN3X5RWt1FNJKtzpZutmMg5rRbDbNQ,490
20
+ pro_craft/server/utils/database.py,sha256=t2cTLmTT0yD9ObXZxpJrLo-d38C_QRtHTVrtLcgem3o,1083
21
+ pro_craft/server/utils/user_manager.py,sha256=tmvmoCfblMpBQtvhYsy5YS0ANy2mSW3vvS-rsajhzVw,3380
22
+ pro_craft-0.1.1.dist-info/METADATA,sha256=T9zezfPzqEvV39r8sEbjoIPvEx92F0iEvAM7POFnfXI,416
23
+ pro_craft-0.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
+ pro_craft-0.1.1.dist-info/top_level.txt,sha256=yqYDHArnYMWpeCxkmGRwlL6sJtxiOUnYylLDx9EOgFg,10
25
+ pro_craft-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ pro_craft