htmlgen-mcp 0.3.3__py3-none-any.whl → 0.3.4__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 htmlgen-mcp might be problematic. Click here for more details.

@@ -1,414 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- 集群状态管理模块 - 支持分布式部署
5
- """
6
-
7
- import json
8
- import time
9
- from typing import Optional, List, Dict, Any
10
- from datetime import datetime
11
- from enum import Enum
12
-
13
-
14
- class TaskStatus(Enum):
15
- """任务状态枚举"""
16
- PENDING = "pending"
17
- RUNNING = "running"
18
- SUCCESS = "success"
19
- FAILED = "failed"
20
- CANCELLED = "cancelled"
21
-
22
-
23
- class StateManager:
24
- """
25
- 抽象状态管理器基类
26
- 可以有多种实现:RedisStateManager, PostgreSQLStateManager, MongoDBStateManager
27
- """
28
-
29
- def create_task(self, task_id: str, user_input: str, project_directory: str, metadata: dict = None) -> bool:
30
- """创建新任务"""
31
- raise NotImplementedError
32
-
33
- def update_task_status(self, task_id: str, status: TaskStatus, message: str = None) -> bool:
34
- """更新任务状态"""
35
- raise NotImplementedError
36
-
37
- def get_task_status(self, task_id: str) -> Optional[dict]:
38
- """获取任务状态"""
39
- raise NotImplementedError
40
-
41
- def add_created_file(self, task_id: str, file_path: str, size: int = 0) -> bool:
42
- """记录创建的文件"""
43
- raise NotImplementedError
44
-
45
- def get_created_files(self, task_id: str) -> List[str]:
46
- """获取任务创建的所有文件"""
47
- raise NotImplementedError
48
-
49
- def add_execution_step(self, task_id: str, step: dict) -> bool:
50
- """添加执行步骤记录"""
51
- raise NotImplementedError
52
-
53
- def get_execution_history(self, task_id: str) -> List[dict]:
54
- """获取执行历史"""
55
- raise NotImplementedError
56
-
57
- def acquire_lock(self, resource_id: str, timeout: int = 10) -> bool:
58
- """获取分布式锁"""
59
- raise NotImplementedError
60
-
61
- def release_lock(self, resource_id: str) -> bool:
62
- """释放分布式锁"""
63
- raise NotImplementedError
64
-
65
-
66
- class RedisStateManager(StateManager):
67
- """基于 Redis 的状态管理器"""
68
-
69
- def __init__(self, redis_url: str = "redis://localhost:6379/0"):
70
- try:
71
- import redis
72
- from redis.lock import Lock
73
- except ImportError:
74
- raise ImportError("请安装 redis: pip install redis")
75
-
76
- self.redis = redis.from_url(redis_url, decode_responses=True)
77
- self.Lock = Lock
78
-
79
- def create_task(self, task_id: str, user_input: str, project_directory: str, metadata: dict = None) -> bool:
80
- """创建新任务"""
81
- task_data = {
82
- "task_id": task_id,
83
- "user_input": user_input,
84
- "project_directory": project_directory,
85
- "status": TaskStatus.PENDING.value,
86
- "created_at": datetime.now().isoformat(),
87
- "updated_at": datetime.now().isoformat(),
88
- "metadata": metadata or {}
89
- }
90
-
91
- # 使用 Hash 存储任务基本信息
92
- self.redis.hset(f"task:{task_id}", mapping={
93
- k: json.dumps(v) if isinstance(v, (dict, list)) else str(v)
94
- for k, v in task_data.items()
95
- })
96
-
97
- # 设置过期时间(7天)
98
- self.redis.expire(f"task:{task_id}", 7 * 24 * 3600)
99
-
100
- return True
101
-
102
- def update_task_status(self, task_id: str, status: TaskStatus, message: str = None) -> bool:
103
- """更新任务状态"""
104
- updates = {
105
- "status": status.value,
106
- "updated_at": datetime.now().isoformat()
107
- }
108
- if message:
109
- updates["message"] = message
110
-
111
- self.redis.hset(f"task:{task_id}", mapping=updates)
112
- return True
113
-
114
- def get_task_status(self, task_id: str) -> Optional[dict]:
115
- """获取任务状态"""
116
- data = self.redis.hgetall(f"task:{task_id}")
117
- if not data:
118
- return None
119
-
120
- # 反序列化 JSON 字段
121
- for key in ["metadata"]:
122
- if key in data:
123
- try:
124
- data[key] = json.loads(data[key])
125
- except (json.JSONDecodeError, TypeError):
126
- pass
127
-
128
- return data
129
-
130
- def add_created_file(self, task_id: str, file_path: str, size: int = 0) -> bool:
131
- """记录创建的文件"""
132
- file_info = {
133
- "path": file_path,
134
- "size": size,
135
- "created_at": datetime.now().isoformat()
136
- }
137
-
138
- # 使用 List 存储文件列表
139
- self.redis.rpush(f"task:{task_id}:files", json.dumps(file_info))
140
- self.redis.expire(f"task:{task_id}:files", 7 * 24 * 3600)
141
-
142
- return True
143
-
144
- def get_created_files(self, task_id: str) -> List[str]:
145
- """获取任务创建的所有文件"""
146
- files_json = self.redis.lrange(f"task:{task_id}:files", 0, -1)
147
- files = []
148
- for file_json in files_json:
149
- try:
150
- file_info = json.loads(file_json)
151
- files.append(file_info["path"])
152
- except (json.JSONDecodeError, KeyError):
153
- continue
154
- return files
155
-
156
- def add_execution_step(self, task_id: str, step: dict) -> bool:
157
- """添加执行步骤记录"""
158
- step_data = {
159
- **step,
160
- "timestamp": datetime.now().isoformat()
161
- }
162
-
163
- # 使用 List 存储执行历史
164
- self.redis.rpush(f"task:{task_id}:history", json.dumps(step_data))
165
- self.redis.expire(f"task:{task_id}:history", 7 * 24 * 3600)
166
-
167
- return True
168
-
169
- def get_execution_history(self, task_id: str) -> List[dict]:
170
- """获取执行历史"""
171
- history_json = self.redis.lrange(f"task:{task_id}:history", 0, -1)
172
- history = []
173
- for step_json in history_json:
174
- try:
175
- history.append(json.loads(step_json))
176
- except json.JSONDecodeError:
177
- continue
178
- return history
179
-
180
- def acquire_lock(self, resource_id: str, timeout: int = 10) -> bool:
181
- """获取分布式锁"""
182
- lock = self.redis.lock(f"lock:{resource_id}", timeout=timeout)
183
- return lock.acquire(blocking=True, blocking_timeout=timeout)
184
-
185
- def release_lock(self, resource_id: str) -> bool:
186
- """释放分布式锁"""
187
- try:
188
- lock = self.redis.lock(f"lock:{resource_id}")
189
- lock.release()
190
- return True
191
- except Exception:
192
- return False
193
-
194
-
195
- class DatabaseStateManager(StateManager):
196
- """基于 PostgreSQL/MySQL 的状态管理器"""
197
-
198
- def __init__(self, db_url: str):
199
- try:
200
- from sqlalchemy import create_engine, Column, String, Integer, Text, DateTime, JSON
201
- from sqlalchemy.ext.declarative import declarative_base
202
- from sqlalchemy.orm import sessionmaker
203
- except ImportError:
204
- raise ImportError("请安装 sqlalchemy: pip install sqlalchemy psycopg2-binary")
205
-
206
- self.engine = create_engine(db_url)
207
- Base = declarative_base()
208
-
209
- # 定义数据模型
210
- class Task(Base):
211
- __tablename__ = 'agent_tasks'
212
-
213
- task_id = Column(String(64), primary_key=True)
214
- user_input = Column(Text)
215
- project_directory = Column(String(512))
216
- status = Column(String(20))
217
- message = Column(Text, nullable=True)
218
- metadata = Column(JSON, nullable=True)
219
- created_at = Column(DateTime, default=datetime.now)
220
- updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
221
-
222
- class CreatedFile(Base):
223
- __tablename__ = 'agent_files'
224
-
225
- id = Column(Integer, primary_key=True, autoincrement=True)
226
- task_id = Column(String(64), index=True)
227
- file_path = Column(String(1024))
228
- size = Column(Integer, default=0)
229
- created_at = Column(DateTime, default=datetime.now)
230
-
231
- class ExecutionStep(Base):
232
- __tablename__ = 'agent_execution_history'
233
-
234
- id = Column(Integer, primary_key=True, autoincrement=True)
235
- task_id = Column(String(64), index=True)
236
- step = Column(Integer)
237
- tool = Column(String(100))
238
- status = Column(String(20))
239
- result = Column(Text)
240
- duration = Column(Integer, default=0)
241
- created_at = Column(DateTime, default=datetime.now)
242
-
243
- Base.metadata.create_all(self.engine)
244
-
245
- self.Session = sessionmaker(bind=self.engine)
246
- self.Task = Task
247
- self.CreatedFile = CreatedFile
248
- self.ExecutionStep = ExecutionStep
249
-
250
- def create_task(self, task_id: str, user_input: str, project_directory: str, metadata: dict = None) -> bool:
251
- """创建新任务"""
252
- session = self.Session()
253
- try:
254
- task = self.Task(
255
- task_id=task_id,
256
- user_input=user_input,
257
- project_directory=project_directory,
258
- status=TaskStatus.PENDING.value,
259
- metadata=metadata
260
- )
261
- session.add(task)
262
- session.commit()
263
- return True
264
- except Exception as e:
265
- session.rollback()
266
- print(f"创建任务失败: {e}")
267
- return False
268
- finally:
269
- session.close()
270
-
271
- def update_task_status(self, task_id: str, status: TaskStatus, message: str = None) -> bool:
272
- """更新任务状态"""
273
- session = self.Session()
274
- try:
275
- task = session.query(self.Task).filter_by(task_id=task_id).first()
276
- if task:
277
- task.status = status.value
278
- task.updated_at = datetime.now()
279
- if message:
280
- task.message = message
281
- session.commit()
282
- return True
283
- return False
284
- except Exception as e:
285
- session.rollback()
286
- print(f"更新任务状态失败: {e}")
287
- return False
288
- finally:
289
- session.close()
290
-
291
- def get_task_status(self, task_id: str) -> Optional[dict]:
292
- """获取任务状态"""
293
- session = self.Session()
294
- try:
295
- task = session.query(self.Task).filter_by(task_id=task_id).first()
296
- if not task:
297
- return None
298
-
299
- return {
300
- "task_id": task.task_id,
301
- "user_input": task.user_input,
302
- "project_directory": task.project_directory,
303
- "status": task.status,
304
- "message": task.message,
305
- "metadata": task.metadata,
306
- "created_at": task.created_at.isoformat() if task.created_at else None,
307
- "updated_at": task.updated_at.isoformat() if task.updated_at else None
308
- }
309
- finally:
310
- session.close()
311
-
312
- def add_created_file(self, task_id: str, file_path: str, size: int = 0) -> bool:
313
- """记录创建的文件"""
314
- session = self.Session()
315
- try:
316
- file_record = self.CreatedFile(
317
- task_id=task_id,
318
- file_path=file_path,
319
- size=size
320
- )
321
- session.add(file_record)
322
- session.commit()
323
- return True
324
- except Exception as e:
325
- session.rollback()
326
- print(f"记录文件失败: {e}")
327
- return False
328
- finally:
329
- session.close()
330
-
331
- def get_created_files(self, task_id: str) -> List[str]:
332
- """获取任务创建的所有文件"""
333
- session = self.Session()
334
- try:
335
- files = session.query(self.CreatedFile).filter_by(task_id=task_id).all()
336
- return [f.file_path for f in files]
337
- finally:
338
- session.close()
339
-
340
- def add_execution_step(self, task_id: str, step: dict) -> bool:
341
- """添加执行步骤记录"""
342
- session = self.Session()
343
- try:
344
- step_record = self.ExecutionStep(
345
- task_id=task_id,
346
- step=step.get("step", 0),
347
- tool=step.get("tool", ""),
348
- status=step.get("status", ""),
349
- result=json.dumps(step.get("result", "")),
350
- duration=step.get("duration", 0)
351
- )
352
- session.add(step_record)
353
- session.commit()
354
- return True
355
- except Exception as e:
356
- session.rollback()
357
- print(f"记录执行步骤失败: {e}")
358
- return False
359
- finally:
360
- session.close()
361
-
362
- def get_execution_history(self, task_id: str) -> List[dict]:
363
- """获取执行历史"""
364
- session = self.Session()
365
- try:
366
- steps = session.query(self.ExecutionStep).filter_by(task_id=task_id).order_by(self.ExecutionStep.step).all()
367
- return [
368
- {
369
- "step": s.step,
370
- "tool": s.tool,
371
- "status": s.status,
372
- "result": s.result,
373
- "duration": s.duration,
374
- "created_at": s.created_at.isoformat() if s.created_at else None
375
- }
376
- for s in steps
377
- ]
378
- finally:
379
- session.close()
380
-
381
- def acquire_lock(self, resource_id: str, timeout: int = 10) -> bool:
382
- """获取分布式锁(需要配合数据库锁实现)"""
383
- # PostgreSQL 可以使用 advisory lock
384
- # MySQL 可以使用 GET_LOCK
385
- # 这里简化实现,实际应使用数据库特定的锁机制
386
- return True
387
-
388
- def release_lock(self, resource_id: str) -> bool:
389
- """释放分布式锁"""
390
- return True
391
-
392
-
393
- def create_state_manager(backend: str = "redis", connection_string: str = None) -> StateManager:
394
- """
395
- 工厂函数:根据配置创建状态管理器
396
-
397
- Args:
398
- backend: 后端类型,支持 'redis', 'postgresql', 'mysql'
399
- connection_string: 连接字符串
400
- - Redis: redis://localhost:6379/0
401
- - PostgreSQL: postgresql://user:pass@localhost/dbname
402
- - MySQL: mysql://user:pass@localhost/dbname
403
-
404
- Returns:
405
- StateManager 实例
406
- """
407
- if backend == "redis":
408
- return RedisStateManager(connection_string or "redis://localhost:6379/0")
409
- elif backend in ["postgresql", "mysql"]:
410
- if not connection_string:
411
- raise ValueError(f"{backend} 需要提供 connection_string")
412
- return DatabaseStateManager(connection_string)
413
- else:
414
- raise ValueError(f"不支持的后端类型: {backend}")