Coverage for agentos/marketplace/__init__.py: 0%

382 statements  

« prev     ^ index     » next       coverage.py v7.14.3, created at 2026-07-08 21:26 +0800

1""" 

2AgentOS Marketplace — Agent template registry and discovery hub. 

3 

4v1.14.4: Central marketplace for discovering, publishing, and installing 

5 agent templates, workflows, and plugins. 

6 

7Key features: 

8- Template registry with semantic search 

9- Versioned agent templates with dependency resolution 

10- Public + private registries 

11- One-click install from marketplace 

12- Agent ratings, reviews, and usage stats 

13- Template validation and compatibility checking 

14- CLI and programmatic API 

15""" 

16 

17import asyncio 

18import hashlib 

19import json 

20import logging 

21import time 

22from abc import ABC, abstractmethod 

23from dataclasses import dataclass, field 

24from enum import Enum 

25from pathlib import Path 

26from typing import Any 

27 

28logger = logging.getLogger(__name__) 

29 

30 

31# --------------------------------------------------------------------------- 

32# Data types 

33# --------------------------------------------------------------------------- 

34 

35 

36class TemplateCategory(Enum): 

37 CHAT = "chat" 

38 CODING = "coding" 

39 ANALYSIS = "analysis" 

40 AUTOMATION = "automation" 

41 RESEARCH = "research" 

42 CREATIVE = "creative" 

43 ENTERPRISE = "enterprise" 

44 UTILITY = "utility" 

45 OTHER = "other" 

46 

47 

48class TemplateStatus(Enum): 

49 DRAFT = "draft" 

50 PUBLISHED = "published" 

51 DEPRECATED = "deprecated" 

52 ARCHIVED = "archived" 

53 UNDER_REVIEW = "under_review" 

54 

55 

56@dataclass 

57class TemplateDependency: 

58 """A dependency required by a template.""" 

59 

60 name: str 

61 version_spec: str = "*" # PEP 440 version specifier 

62 optional: bool = False 

63 description: str = "" 

64 

65 

66@dataclass 

67class TemplateVersion: 

68 """A specific version of a template.""" 

69 

70 version: str # SemVer 

71 changelog: str = "" 

72 min_agentos_version: str = "1.0.0" 

73 files: dict[str, str] = field(default_factory=dict) # path → content 

74 dependencies: list[TemplateDependency] = field(default_factory=list) 

75 metadata: dict[str, Any] = field(default_factory=dict) 

76 published_at: float = 0.0 

77 download_count: int = 0 

78 

79 

80@dataclass 

81class TemplateReview: 

82 """User review of a template.""" 

83 

84 user_id: str 

85 rating: float # 1.0 - 5.0 

86 comment: str = "" 

87 timestamp: float = field(default_factory=time.time) 

88 helpful_count: int = 0 

89 

90 

91@dataclass 

92class AgentTemplate: 

93 """An agent template in the marketplace.""" 

94 

95 # Identity 

96 id: str 

97 name: str 

98 version: str 

99 author: str 

100 description: str = "" 

101 category: TemplateCategory = TemplateCategory.OTHER 

102 tags: list[str] = field(default_factory=list) 

103 icon_url: str = "" 

104 

105 # Status 

106 status: TemplateStatus = TemplateStatus.PUBLISHED 

107 

108 # Content 

109 versions: list[TemplateVersion] = field(default_factory=list) 

110 readme: str = "" 

111 license: str = "MIT" 

112 

113 # Engagement 

114 stars: int = 0 

115 downloads: int = 0 

116 reviews: list[TemplateReview] = field(default_factory=list) 

117 

118 # Compatibility 

119 compatible_agentos_versions: str = ">=1.0.0" 

120 requires: list[TemplateDependency] = field(default_factory=list) 

121 

122 # Metadata 

123 created_at: float = field(default_factory=time.time) 

124 updated_at: float = field(default_factory=time.time) 

125 source_url: str = "" 

126 documentation_url: str = "" 

127 metadata: dict[str, Any] = field(default_factory=dict) 

128 

129 @property 

130 def rating(self) -> float: 

131 """Average rating.""" 

132 if not self.reviews: 

133 return 0.0 

134 return sum(r.rating for r in self.reviews) / len(self.reviews) 

135 

136 @property 

137 def latest_version(self) -> TemplateVersion | None: 

138 """Get the latest published version.""" 

139 published = [v for v in self.versions if v.published_at > 0] 

140 if not published: 

141 return None 

142 return max(published, key=lambda v: v.published_at) 

143 

144 

145@dataclass 

146class MarketSearchQuery: 

147 """Search query for the marketplace.""" 

148 

149 keywords: str = "" 

150 category: TemplateCategory | None = None 

151 tags: list[str] = field(default_factory=list) 

152 min_rating: float = 0.0 

153 min_stars: int = 0 

154 author: str | None = None 

155 sort_by: str = "relevance" # relevance, downloads, rating, stars, updated 

156 sort_order: str = "desc" 

157 limit: int = 20 

158 offset: int = 0 

159 

160 

161@dataclass 

162class MarketSearchResult: 

163 """Search result from the marketplace.""" 

164 

165 template: AgentTemplate 

166 score: float = 0.0 

167 matched_tags: list[str] = field(default_factory=list) 

168 

169 

170# --------------------------------------------------------------------------- 

171# Registry backends 

172# --------------------------------------------------------------------------- 

173 

174 

175class MarketRegistryBackend(ABC): 

176 """Abstract backend for template storage and retrieval.""" 

177 

178 @abstractmethod 

179 async def list_templates( 

180 self, query: MarketSearchQuery | None = None 

181 ) -> list[MarketSearchResult]: ... 

182 

183 @abstractmethod 

184 async def get_template(self, template_id: str) -> AgentTemplate | None: ... 

185 

186 @abstractmethod 

187 async def publish_template(self, template: AgentTemplate) -> bool: ... 

188 

189 @abstractmethod 

190 async def unpublish_template(self, template_id: str) -> bool: ... 

191 

192 @abstractmethod 

193 async def add_review(self, template_id: str, review: TemplateReview) -> bool: ... 

194 

195 @abstractmethod 

196 async def get_stats(self) -> dict[str, Any]: ... 

197 

198 

199class InMemoryMarketBackend(MarketRegistryBackend): 

200 """In-memory registry for development and testing.""" 

201 

202 def __init__(self): 

203 self._templates: dict[str, AgentTemplate] = {} 

204 

205 async def list_templates( 

206 self, query: MarketSearchQuery | None = None 

207 ) -> list[MarketSearchResult]: 

208 results = [] 

209 for tpl in self._templates.values(): 

210 if tpl.status != TemplateStatus.PUBLISHED: 

211 continue 

212 

213 score = 0.0 

214 matched_tags = [] 

215 

216 if query: 

217 # Keyword search 

218 if query.keywords: 

219 kw_lower = query.keywords.lower() 

220 text = f"{tpl.name} {tpl.description} {' '.join(tpl.tags)}".lower() 

221 if kw_lower in text: 

222 score += 10.0 

223 

224 # Category filter 

225 if query.category and tpl.category != query.category: 

226 continue 

227 

228 # Tag filter 

229 if query.tags: 

230 matched_tags = [t for t in query.tags if t in tpl.tags] 

231 if not matched_tags: 

232 continue 

233 score += len(matched_tags) * 2.0 

234 

235 # Rating filter 

236 if query.min_rating > 0 and tpl.rating < query.min_rating: 

237 continue 

238 

239 # Stars filter 

240 if query.min_stars > 0 and tpl.stars < query.min_stars: 

241 continue 

242 

243 # Author filter 

244 if query.author and tpl.author != query.author: 

245 continue 

246 

247 results.append( 

248 MarketSearchResult( 

249 template=tpl, 

250 score=score, 

251 matched_tags=matched_tags, 

252 ) 

253 ) 

254 

255 # Sort 

256 if query: 

257 sort_key = query.sort_by 

258 reverse = query.sort_order == "desc" 

259 if sort_key == "downloads": 

260 results.sort(key=lambda r: r.template.downloads, reverse=reverse) 

261 elif sort_key == "rating": 

262 results.sort(key=lambda r: r.template.rating, reverse=reverse) 

263 elif sort_key == "stars": 

264 results.sort(key=lambda r: r.template.stars, reverse=reverse) 

265 elif sort_key == "updated": 

266 results.sort(key=lambda r: r.template.updated_at, reverse=reverse) 

267 else: # relevance 

268 results.sort(key=lambda r: r.score, reverse=reverse) 

269 

270 # Paginate 

271 results = results[query.offset : query.offset + query.limit] 

272 

273 return results 

274 

275 async def get_template(self, template_id: str) -> AgentTemplate | None: 

276 return self._templates.get(template_id) 

277 

278 async def publish_template(self, template: AgentTemplate) -> bool: 

279 template.updated_at = time.time() 

280 self._templates[template.id] = template 

281 return True 

282 

283 async def unpublish_template(self, template_id: str) -> bool: 

284 if template_id in self._templates: 

285 self._templates[template_id].status = TemplateStatus.ARCHIVED 

286 return True 

287 return False 

288 

289 async def add_review(self, template_id: str, review: TemplateReview) -> bool: 

290 tpl = self._templates.get(template_id) 

291 if not tpl: 

292 return False 

293 tpl.reviews.append(review) 

294 return True 

295 

296 async def get_stats(self) -> dict[str, Any]: 

297 total = len(self._templates) 

298 by_category = {} 

299 for tpl in self._templates.values(): 

300 cat = tpl.category.value 

301 by_category[cat] = by_category.get(cat, 0) + 1 

302 return { 

303 "total_templates": total, 

304 "total_downloads": sum(t.downloads for t in self._templates.values()), 

305 "by_category": by_category, 

306 } 

307 

308 

309class FileMarketBackend(MarketRegistryBackend): 

310 """JSON-file-based registry for local/CI usage.""" 

311 

312 def __init__(self, storage_dir: str | Path): 

313 self._dir = Path(storage_dir) 

314 self._dir.mkdir(parents=True, exist_ok=True) 

315 self._index_file = self._dir / "index.json" 

316 self._templates: dict[str, AgentTemplate] = {} 

317 self._load() 

318 

319 def _load(self) -> None: 

320 if self._index_file.exists(): 

321 with open(self._index_file) as f: 

322 data = json.load(f) 

323 for raw in data.get("templates", []): 

324 tpl = self._dict_to_template(raw) 

325 self._templates[tpl.id] = tpl 

326 

327 def _save(self) -> None: 

328 data = { 

329 "updated_at": time.time(), 

330 "templates": [self._template_to_dict(t) for t in self._templates.values()], 

331 } 

332 with open(self._index_file, "w") as f: 

333 json.dump(data, f, indent=2, default=str) 

334 

335 def _template_to_dict(self, tpl: AgentTemplate) -> dict[str, Any]: 

336 return { 

337 "id": tpl.id, 

338 "name": tpl.name, 

339 "version": tpl.version, 

340 "author": tpl.author, 

341 "description": tpl.description, 

342 "category": tpl.category.value, 

343 "tags": tpl.tags, 

344 "status": tpl.status.value, 

345 "stars": tpl.stars, 

346 "downloads": tpl.downloads, 

347 "rating": tpl.rating, 

348 "review_count": len(tpl.reviews), 

349 "compatible_agentos_versions": tpl.compatible_agentos_versions, 

350 "created_at": tpl.created_at, 

351 "updated_at": tpl.updated_at, 

352 } 

353 

354 def _dict_to_template(self, d: dict[str, Any]) -> AgentTemplate: 

355 return AgentTemplate( 

356 id=d["id"], 

357 name=d["name"], 

358 version=d.get("version", "1.0.0"), 

359 author=d["author"], 

360 description=d.get("description", ""), 

361 category=TemplateCategory(d.get("category", "other")), 

362 tags=d.get("tags", []), 

363 status=TemplateStatus(d.get("status", "published")), 

364 stars=d.get("stars", 0), 

365 downloads=d.get("downloads", 0), 

366 compatible_agentos_versions=d.get("compatible_agentos_versions", ">=1.0.0"), 

367 created_at=d.get("created_at", 0), 

368 updated_at=d.get("updated_at", 0), 

369 ) 

370 

371 # Delegate to in-memory backend 

372 async def list_templates(self, query=None): 

373 backend = InMemoryMarketBackend() 

374 backend._templates = dict(self._templates) 

375 return await backend.list_templates(query) 

376 

377 async def get_template(self, template_id): 

378 return self._templates.get(template_id) 

379 

380 async def publish_template(self, template): 

381 AgentTemplate( 

382 **{ 

383 k: v 

384 for k, v in template.__dict__.items() 

385 if k in AgentTemplate.__dataclass_fields__ 

386 } 

387 ) 

388 template.updated_at = time.time() 

389 self._templates[template.id] = template 

390 self._save() 

391 return True 

392 

393 async def unpublish_template(self, template_id): 

394 if template_id in self._templates: 

395 self._templates[template_id].status = TemplateStatus.ARCHIVED 

396 self._save() 

397 return True 

398 return False 

399 

400 async def add_review(self, template_id, review): 

401 tpl = self._templates.get(template_id) 

402 if not tpl: 

403 return False 

404 tpl.reviews.append(review) 

405 self._save() 

406 return True 

407 

408 async def get_stats(self): 

409 backend = InMemoryMarketBackend() 

410 backend._templates = dict(self._templates) 

411 return await backend.get_stats() 

412 

413 

414# --------------------------------------------------------------------------- 

415# Remote registry client 

416# --------------------------------------------------------------------------- 

417 

418 

419class RemoteMarketClient: 

420 """HTTP client for remote marketplace registries.""" 

421 

422 def __init__(self, base_url: str, api_key: str | None = None): 

423 self.base_url = base_url.rstrip("/") 

424 self.api_key = api_key 

425 

426 async def search(self, query: MarketSearchQuery) -> list[MarketSearchResult]: 

427 """Search the remote marketplace.""" 

428 import urllib.parse 

429 import urllib.request 

430 

431 params = {} 

432 if query.keywords: 

433 params["q"] = query.keywords 

434 if query.category: 

435 params["category"] = query.category.value 

436 if query.tags: 

437 params["tags"] = ",".join(query.tags) 

438 if query.limit: 

439 params["limit"] = str(query.limit) 

440 

441 url = f"{self.base_url}/api/v1/templates" 

442 if params: 

443 url += "?" + urllib.parse.urlencode(params) 

444 

445 # Use asyncio-compatible HTTP 

446 import aiohttp 

447 

448 async with aiohttp.ClientSession() as session: 

449 headers = {} 

450 if self.api_key: 

451 headers["Authorization"] = f"Bearer {self.api_key}" 

452 async with session.get(url, headers=headers) as resp: 

453 data = await resp.json() 

454 return [ 

455 MarketSearchResult( 

456 template=AgentTemplate(**item["template"]), 

457 score=item.get("score", 0), 

458 ) 

459 for item in data.get("results", []) 

460 ] 

461 

462 async def get_template(self, template_id: str) -> AgentTemplate | None: 

463 """Fetch a template from the remote registry.""" 

464 import aiohttp 

465 

466 async with aiohttp.ClientSession() as session: 

467 headers = {} 

468 if self.api_key: 

469 headers["Authorization"] = f"Bearer {self.api_key}" 

470 async with session.get( 

471 f"{self.base_url}/api/v1/templates/{template_id}", 

472 headers=headers, 

473 ) as resp: 

474 if resp.status == 404: 

475 return None 

476 data = await resp.json() 

477 return AgentTemplate(**data) 

478 

479 async def download_template(self, template_id: str, target_dir: str | Path) -> bool: 

480 """Download and extract a template to a local directory.""" 

481 import aiohttp 

482 

483 target = Path(target_dir) 

484 target.mkdir(parents=True, exist_ok=True) 

485 

486 async with aiohttp.ClientSession() as session: 

487 headers = {} 

488 if self.api_key: 

489 headers["Authorization"] = f"Bearer {self.api_key}" 

490 async with session.get( 

491 f"{self.base_url}/api/v1/templates/{template_id}/download", 

492 headers=headers, 

493 ) as resp: 

494 if resp.status != 200: 

495 return False 

496 

497 import io 

498 import tarfile 

499 

500 data = await resp.read() 

501 with tarfile.open(fileobj=io.BytesIO(data)) as tar: 

502 tar.extractall(path=target) 

503 return True 

504 

505 

506# --------------------------------------------------------------------------- 

507# Marketplace Manager 

508# --------------------------------------------------------------------------- 

509 

510 

511class MarketplaceManager: 

512 """Central marketplace management — search, install, publish.""" 

513 

514 def __init__( 

515 self, 

516 local_backend: MarketRegistryBackend | None = None, 

517 remote_clients: list[RemoteMarketClient] | None = None, 

518 install_dir: str | Path = "~/.agentos/templates", 

519 ): 

520 self.local = local_backend or InMemoryMarketBackend() 

521 self.remote_clients = remote_clients or [] 

522 self.install_dir = Path(install_dir).expanduser() 

523 self.install_dir.mkdir(parents=True, exist_ok=True) 

524 

525 async def search( 

526 self, query: MarketSearchQuery, include_remote: bool = True 

527 ) -> list[MarketSearchResult]: 

528 """Search local and remote registries.""" 

529 results = await self.local.list_templates(query) 

530 

531 if include_remote: 

532 for client in self.remote_clients: 

533 try: 

534 remote_results = await client.search(query) 

535 results.extend(remote_results) 

536 except Exception as e: 

537 logger.warning(f"Remote search failed: {e}") 

538 

539 # Deduplicate by template ID 

540 seen: set[str] = set() 

541 deduped = [] 

542 for r in results: 

543 if r.template.id not in seen: 

544 seen.add(r.template.id) 

545 deduped.append(r) 

546 

547 return deduped 

548 

549 async def install(self, template_id: str, version: str | None = None) -> Path: 

550 """Install a template from local or remote registry.""" 

551 # Check local first 

552 tpl = await self.local.get_template(template_id) 

553 

554 # Try remote 

555 if not tpl: 

556 for client in self.remote_clients: 

557 try: 

558 tpl = await client.get_template(template_id) 

559 if tpl: 

560 break 

561 except Exception: 

562 continue 

563 

564 if not tpl: 

565 raise ValueError(f"Template '{template_id}' not found in any registry") 

566 

567 # Install to local directory 

568 tpl_dir = self.install_dir / template_id 

569 if version: 

570 tpl_dir = tpl_dir / version 

571 

572 tpl_dir.mkdir(parents=True, exist_ok=True) 

573 

574 # Write template files 

575 latest = tpl.latest_version 

576 if latest: 

577 for filepath, content in latest.files.items(): 

578 full_path = tpl_dir / filepath 

579 full_path.parent.mkdir(parents=True, exist_ok=True) 

580 with open(full_path, "w") as f: 

581 f.write(content) 

582 

583 # Record installation 

584 tpl.downloads += 1 

585 tpl.updated_at = time.time() 

586 

587 return tpl_dir 

588 

589 async def publish( 

590 self, 

591 template: AgentTemplate, 

592 to_remote: bool = False, 

593 ) -> bool: 

594 """Publish a template to registries.""" 

595 # Always publish to local 

596 ok = await self.local.publish_template(template) 

597 if not ok: 

598 return False 

599 

600 # Optionally push to remote 

601 if to_remote: 

602 for client in self.remote_clients: 

603 try: 

604 # Remote publishing would use a POST endpoint 

605 pass 

606 except Exception as e: 

607 logger.error(f"Remote publish failed: {e}") 

608 

609 return True 

610 

611 async def get_stats(self) -> dict[str, Any]: 

612 """Get marketplace statistics.""" 

613 return await self.local.get_stats() 

614 

615 async def get_featured(self, limit: int = 10) -> list[AgentTemplate]: 

616 """Get featured/popular templates.""" 

617 query = MarketSearchQuery(sort_by="downloads", limit=limit) 

618 results = await self.local.list_templates(query) 

619 return [r.template for r in results] 

620 

621 async def get_by_category( 

622 self, category: TemplateCategory, limit: int = 20 

623 ) -> list[AgentTemplate]: 

624 """Get templates by category.""" 

625 query = MarketSearchQuery(category=category, limit=limit) 

626 results = await self.local.list_templates(query) 

627 return [r.template for r in results] 

628 

629 

630# --------------------------------------------------------------------------- 

631# Template builder 

632# --------------------------------------------------------------------------- 

633 

634 

635class TemplateBuilder: 

636 """Helper to build AgentTemplate objects programmatically.""" 

637 

638 def __init__(self, name: str, author: str, version: str = "1.0.0"): 

639 self._template = AgentTemplate( 

640 id=hashlib.sha256(f"{author}/{name}".encode()).hexdigest()[:16], 

641 name=name, 

642 version=version, 

643 author=author, 

644 ) 

645 

646 def description(self, text: str) -> "TemplateBuilder": 

647 self._template.description = text 

648 return self 

649 

650 def category(self, cat: TemplateCategory) -> "TemplateBuilder": 

651 self._template.category = cat 

652 return self 

653 

654 def tags(self, *tags: str) -> "TemplateBuilder": 

655 self._template.tags = list(tags) 

656 return self 

657 

658 def add_version( 

659 self, version: str, files: dict[str, str], changelog: str = "" 

660 ) -> "TemplateBuilder": 

661 tv = TemplateVersion( 

662 version=version, 

663 changelog=changelog, 

664 files=files, 

665 published_at=time.time(), 

666 ) 

667 self._template.versions.append(tv) 

668 return self 

669 

670 def add_dependency( 

671 self, name: str, version_spec: str = "*", optional: bool = False 

672 ) -> "TemplateBuilder": 

673 self._template.requires.append( 

674 TemplateDependency(name=name, version_spec=version_spec, optional=optional) 

675 ) 

676 return self 

677 

678 def add_review(self, user_id: str, rating: float, comment: str = "") -> "TemplateBuilder": 

679 self._template.reviews.append( 

680 TemplateReview(user_id=user_id, rating=rating, comment=comment) 

681 ) 

682 return self 

683 

684 def build(self) -> AgentTemplate: 

685 if not self._template.description: 

686 raise ValueError("Template must have a description") 

687 return self._template 

688 

689 

690# --------------------------------------------------------------------------- 

691# Pre-seeded templates 

692# --------------------------------------------------------------------------- 

693 

694 

695def seed_default_templates(manager: MarketplaceManager) -> None: 

696 """Seed the marketplace with default templates.""" 

697 templates = [ 

698 TemplateBuilder("Conversational Agent", "AgentOS Team") 

699 .description("General-purpose conversational agent with memory and tool use") 

700 .category(TemplateCategory.CHAT) 

701 .tags("chat", "conversation", "memory") 

702 .add_version( 

703 "1.0.0", 

704 { 

705 "agent.yaml": "name: conversational-agent\ntype: chat\nmemory: enabled", 

706 "main.py": "from agentos import Agent\n\nagent = Agent(...)", 

707 }, 

708 ) 

709 .build(), 

710 TemplateBuilder("Code Review Assistant", "AgentOS Team") 

711 .description("AI-powered code reviewer with PR integration") 

712 .category(TemplateCategory.CODING) 

713 .tags("code", "review", "github", "pr") 

714 .add_version( 

715 "1.0.0", 

716 { 

717 "agent.yaml": "name: code-reviewer\ntype: coding\n", 

718 "review.py": "async def review_pr(pr_url): ...", 

719 }, 

720 ) 

721 .build(), 

722 TemplateBuilder("Research Analyst", "AgentOS Team") 

723 .description("Multi-source research agent with deep analysis capabilities") 

724 .category(TemplateCategory.RESEARCH) 

725 .tags("research", "analysis", "web") 

726 .add_version( 

727 "1.0.0", 

728 { 

729 "agent.yaml": "name: research-analyst\ntype: research\n", 

730 "analyst.py": "async def deep_research(topic): ...", 

731 }, 

732 ) 

733 .build(), 

734 TemplateBuilder("Data Pipeline Agent", "AgentOS Team") 

735 .description("Automated ETL and data processing pipeline") 

736 .category(TemplateCategory.AUTOMATION) 

737 .tags("etl", "data", "pipeline", "automation") 

738 .add_version( 

739 "1.0.0", 

740 { 

741 "agent.yaml": "name: data-pipeline\ntype: automation\n", 

742 "pipeline.py": "async def run_pipeline(config): ...", 

743 }, 

744 ) 

745 .build(), 

746 TemplateBuilder("Document Writer", "AgentOS Team") 

747 .description("Professional document generation from outlines or templates") 

748 .category(TemplateCategory.CREATIVE) 

749 .tags("writing", "document", "report") 

750 .add_version( 

751 "1.0.0", 

752 { 

753 "agent.yaml": "name: doc-writer\ntype: creative\n", 

754 "writer.py": "async def generate_doc(outline): ...", 

755 }, 

756 ) 

757 .build(), 

758 ] 

759 

760 async def _seed(): 

761 for tpl in templates: 

762 await manager.local.publish_template(tpl) 

763 

764 try: 

765 asyncio.get_event_loop().run_until_complete(_seed()) 

766 except RuntimeError: 

767 asyncio.run(_seed()) 

768 

769 

770# --------------------------------------------------------------------------- 

771# Export 

772# --------------------------------------------------------------------------- 

773 

774__all__ = [ 

775 # Enums 

776 "TemplateCategory", 

777 "TemplateStatus", 

778 # Data types 

779 "TemplateDependency", 

780 "TemplateVersion", 

781 "TemplateReview", 

782 "AgentTemplate", 

783 "MarketSearchQuery", 

784 "MarketSearchResult", 

785 # Backends 

786 "MarketRegistryBackend", 

787 "InMemoryMarketBackend", 

788 "FileMarketBackend", 

789 "RemoteMarketClient", 

790 # Manager 

791 "MarketplaceManager", 

792 "TemplateBuilder", 

793 "seed_default_templates", 

794]