repositories.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. from datetime import datetime
  2. from sqlalchemy import func, or_, select
  3. from sqlalchemy.orm import Session
  4. from core_domain import SkillInstallStatus, SkillRunStatus, SkillStatus
  5. from core_shared import JSONValue
  6. from app.db.models import SkillDefinition, SkillInstallation, SkillRun
  7. class SkillDefinitionRepository:
  8. def __init__(self, db: Session) -> None:
  9. self.db = db
  10. def create(
  11. self,
  12. *,
  13. code: str,
  14. name: str,
  15. skill_type: str,
  16. description: str | None,
  17. owner_user_id: str | None,
  18. runtime_type: str,
  19. entrypoint: str | None,
  20. parameter_schema_json: dict[str, JSONValue],
  21. output_schema_json: dict[str, JSONValue],
  22. implementation_json: dict[str, JSONValue],
  23. metadata_json: dict[str, JSONValue] | None) -> SkillDefinition:
  24. entity = SkillDefinition(
  25. code=code,
  26. name=name,
  27. skill_type=skill_type,
  28. description=description,
  29. owner_user_id=owner_user_id,
  30. runtime_type=runtime_type,
  31. entrypoint=entrypoint,
  32. parameter_schema_json=parameter_schema_json,
  33. output_schema_json=output_schema_json,
  34. implementation_json=implementation_json,
  35. metadata_json=metadata_json)
  36. self.db.add(entity)
  37. self.db.commit()
  38. self.db.refresh(entity)
  39. return entity
  40. def list_all(self) -> list[SkillDefinition]:
  41. stmt = (
  42. select(SkillDefinition)
  43. .order_by(SkillDefinition.created_time.desc())
  44. )
  45. return list(self.db.scalars(stmt))
  46. def list_filtered(
  47. self,
  48. *,
  49. keyword: str | None = None,
  50. status: SkillStatus | None = None,
  51. skill_type: str | None = None,
  52. category: str | None = None,
  53. offset: int = 0,
  54. limit: int = 20) -> tuple[list[SkillDefinition], int]:
  55. stmt = select(SkillDefinition)
  56. if status is not None:
  57. stmt = stmt.where(SkillDefinition.status == status)
  58. if skill_type is not None:
  59. stmt = stmt.where(SkillDefinition.skill_type == skill_type)
  60. if keyword:
  61. pattern = f"%{keyword.strip()}%"
  62. stmt = stmt.where(or_(
  63. SkillDefinition.name.ilike(pattern),
  64. SkillDefinition.description.ilike(pattern),
  65. SkillDefinition.code.ilike(pattern)))
  66. if category is not None:
  67. all_items = list(self.db.scalars(stmt.order_by(SkillDefinition.created_time.desc())))
  68. filtered_items = [
  69. item for item in all_items
  70. if isinstance(item.metadata_json, dict) and item.metadata_json.get("category") == category
  71. ]
  72. return filtered_items[offset:offset + limit], len(filtered_items)
  73. items = list(self.db.scalars(
  74. stmt.order_by(SkillDefinition.created_time.desc()).offset(offset).limit(limit)))
  75. total = self.db.scalar(select(func.count()).select_from(stmt.subquery())) or 0
  76. return items, total
  77. def get_by_id(self, *, skill_id: str) -> SkillDefinition | None:
  78. stmt = (
  79. select(SkillDefinition)
  80. .where(SkillDefinition.id == skill_id)
  81. )
  82. return self.db.scalar(stmt)
  83. def update_status(
  84. self,
  85. *,
  86. skill_id: str,
  87. status: SkillStatus) -> SkillDefinition | None:
  88. entity = self.get_by_id(skill_id=skill_id)
  89. if entity is None:
  90. return None
  91. entity.status = status
  92. self.db.commit()
  93. self.db.refresh(entity)
  94. return entity
  95. def update(
  96. self,
  97. *,
  98. skill_id: str,
  99. name: str | None = None,
  100. skill_type: str | None = None,
  101. description: str | None = None,
  102. owner_user_id: str | None = None,
  103. runtime_type: str | None = None,
  104. entrypoint: str | None = None,
  105. parameter_schema_json: dict[str, JSONValue] | None = None,
  106. output_schema_json: dict[str, JSONValue] | None = None,
  107. implementation_json: dict[str, JSONValue] | None = None,
  108. metadata_json: dict[str, JSONValue] | None = None) -> SkillDefinition | None:
  109. entity = self.get_by_id(skill_id=skill_id)
  110. if entity is None:
  111. return None
  112. if name is not None:
  113. entity.name = name
  114. if skill_type is not None:
  115. entity.skill_type = skill_type
  116. if description is not None:
  117. entity.description = description
  118. if owner_user_id is not None:
  119. entity.owner_user_id = owner_user_id
  120. if runtime_type is not None:
  121. entity.runtime_type = runtime_type
  122. if entrypoint is not None:
  123. entity.entrypoint = entrypoint
  124. if parameter_schema_json is not None:
  125. entity.parameter_schema_json = parameter_schema_json
  126. if output_schema_json is not None:
  127. entity.output_schema_json = output_schema_json
  128. if implementation_json is not None:
  129. entity.implementation_json = implementation_json
  130. if metadata_json is not None:
  131. entity.metadata_json = metadata_json
  132. self.db.commit()
  133. self.db.refresh(entity)
  134. return entity
  135. class SkillInstallationRepository:
  136. def __init__(self, db: Session) -> None:
  137. self.db = db
  138. def create(
  139. self,
  140. *,
  141. skill_id: str,
  142. install_scope: str,
  143. scope_id: str,
  144. config_json: dict[str, JSONValue],
  145. installed_by: str | None) -> SkillInstallation:
  146. entity = SkillInstallation(
  147. skill_id=skill_id,
  148. install_scope=install_scope,
  149. scope_id=scope_id,
  150. config_json=config_json,
  151. status="installed",
  152. installed_by=installed_by,
  153. installed_time=datetime.utcnow())
  154. self.db.add(entity)
  155. self.db.commit()
  156. self.db.refresh(entity)
  157. return entity
  158. def list_by_scope(
  159. self,
  160. *,
  161. install_scope: str | None = None,
  162. scope_id: str | None = None) -> list[SkillInstallation]:
  163. stmt = select(SkillInstallation)
  164. if install_scope is not None:
  165. stmt = stmt.where(SkillInstallation.install_scope == install_scope)
  166. if scope_id is not None:
  167. stmt = stmt.where(SkillInstallation.scope_id == scope_id)
  168. stmt = stmt.order_by(SkillInstallation.created_time.desc())
  169. return list(self.db.scalars(stmt))
  170. def list_filtered(
  171. self,
  172. *,
  173. install_scope: str | None = None,
  174. scope_id: str | None = None,
  175. status: SkillInstallStatus | None = None,
  176. offset: int = 0,
  177. limit: int = 20) -> tuple[list[SkillInstallation], int]:
  178. stmt = select(SkillInstallation)
  179. if install_scope is not None:
  180. stmt = stmt.where(SkillInstallation.install_scope == install_scope)
  181. if scope_id is not None:
  182. stmt = stmt.where(SkillInstallation.scope_id == scope_id)
  183. if status is not None:
  184. stmt = stmt.where(SkillInstallation.status == status)
  185. total = self.db.scalar(select(func.count()).select_from(stmt.subquery())) or 0
  186. items = list(self.db.scalars(
  187. stmt.order_by(SkillInstallation.created_time.desc()).offset(offset).limit(limit)))
  188. return items, total
  189. def get_by_id(self, *, installation_id: str) -> SkillInstallation | None:
  190. stmt = (
  191. select(SkillInstallation)
  192. .where(SkillInstallation.id == installation_id)
  193. )
  194. return self.db.scalar(stmt)
  195. def update_status(
  196. self,
  197. *,
  198. installation_id: str,
  199. status: SkillInstallStatus) -> SkillInstallation | None:
  200. entity = self.get_by_id(installation_id=installation_id)
  201. if entity is None:
  202. return None
  203. entity.status = status
  204. self.db.commit()
  205. self.db.refresh(entity)
  206. return entity
  207. class SkillRunRepository:
  208. def __init__(self, db: Session) -> None:
  209. self.db = db
  210. def create(
  211. self,
  212. *,
  213. skill_id: str,
  214. installation_id: str | None,
  215. input_json: dict[str, JSONValue]) -> SkillRun:
  216. entity = SkillRun(
  217. skill_id=skill_id,
  218. installation_id=installation_id,
  219. input_json=input_json,
  220. status="queued")
  221. self.db.add(entity)
  222. self.db.commit()
  223. self.db.refresh(entity)
  224. return entity
  225. def get_by_id(self, *, skill_run_id: str) -> SkillRun | None:
  226. stmt = (
  227. select(SkillRun)
  228. .where(SkillRun.id == skill_run_id)
  229. )
  230. return self.db.scalar(stmt)
  231. def update_status(
  232. self,
  233. *,
  234. skill_run_id: str,
  235. status: SkillRunStatus,
  236. worker_key: str | None = None,
  237. output_json: dict[str, JSONValue] | None = None,
  238. output_text: str | None = None,
  239. error_code: str | None = None,
  240. error_message: str | None = None) -> SkillRun | None:
  241. entity = self.db.get(SkillRun, skill_run_id)
  242. if entity is None:
  243. return None
  244. now = datetime.utcnow()
  245. entity.status = status
  246. entity.worker_key = worker_key
  247. entity.output_json = output_json
  248. entity.output_text = output_text
  249. entity.error_code = error_code
  250. entity.error_message = error_message
  251. if status == "running" and entity.started_time is None:
  252. entity.started_time = now
  253. if status in {"completed", "failed", "cancelled"}:
  254. entity.finished_time = now
  255. self.db.commit()
  256. self.db.refresh(entity)
  257. return entity