From d241455da6963c82edc0856d00d2c9400469f225 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Sat, 14 Feb 2026 19:31:32 +0530 Subject: [PATCH] feat(skills): consolidate skill-related models and update imports --- backend/app/api/gateways.py | 2 +- backend/app/api/skills_marketplace.py | 4 +- backend/app/models/__init__.py | 4 +- .../app/models/gateway_installed_skills.py | 33 ------- backend/app/models/marketplace_skills.py | 42 --------- backend/app/models/skill_packs.py | 40 --------- backend/app/models/skills.py | 88 +++++++++++++++++++ backend/app/services/organizations.py | 2 +- backend/tests/test_organizations_service.py | 2 +- backend/tests/test_skills_marketplace_api.py | 4 +- 10 files changed, 94 insertions(+), 127 deletions(-) delete mode 100644 backend/app/models/gateway_installed_skills.py delete mode 100644 backend/app/models/marketplace_skills.py delete mode 100644 backend/app/models/skill_packs.py create mode 100644 backend/app/models/skills.py diff --git a/backend/app/api/gateways.py b/backend/app/api/gateways.py index 31a102f1..f6b328e3 100644 --- a/backend/app/api/gateways.py +++ b/backend/app/api/gateways.py @@ -14,8 +14,8 @@ from app.db import crud from app.db.pagination import paginate from app.db.session import get_session from app.models.agents import Agent -from app.models.gateway_installed_skills import GatewayInstalledSkill from app.models.gateways import Gateway +from app.models.skills import GatewayInstalledSkill from app.schemas.common import OkResponse from app.schemas.gateways import ( GatewayCreate, diff --git a/backend/app/api/skills_marketplace.py b/backend/app/api/skills_marketplace.py index 32243559..d6e552c2 100644 --- a/backend/app/api/skills_marketplace.py +++ b/backend/app/api/skills_marketplace.py @@ -20,10 +20,8 @@ from sqlmodel import col, select from app.api.deps import require_org_admin from app.core.time import utcnow from app.db.session import get_session -from app.models.gateway_installed_skills import GatewayInstalledSkill from app.models.gateways import Gateway -from app.models.marketplace_skills import MarketplaceSkill -from app.models.skill_packs import SkillPack +from app.models.skills import GatewayInstalledSkill, MarketplaceSkill, SkillPack from app.schemas.common import OkResponse from app.schemas.skills_marketplace import ( MarketplaceSkillActionResponse, diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py index e3f8f71f..2d1df559 100644 --- a/backend/app/models/__init__.py +++ b/backend/app/models/__init__.py @@ -11,15 +11,13 @@ from app.models.board_onboarding import BoardOnboardingSession from app.models.board_webhook_payloads import BoardWebhookPayload from app.models.board_webhooks import BoardWebhook from app.models.boards import Board -from app.models.gateway_installed_skills import GatewayInstalledSkill from app.models.gateways import Gateway -from app.models.marketplace_skills import MarketplaceSkill from app.models.organization_board_access import OrganizationBoardAccess from app.models.organization_invite_board_access import OrganizationInviteBoardAccess from app.models.organization_invites import OrganizationInvite from app.models.organization_members import OrganizationMember from app.models.organizations import Organization -from app.models.skill_packs import SkillPack +from app.models.skills import GatewayInstalledSkill, MarketplaceSkill, SkillPack from app.models.tag_assignments import TagAssignment from app.models.tags import Tag from app.models.task_custom_fields import ( diff --git a/backend/app/models/gateway_installed_skills.py b/backend/app/models/gateway_installed_skills.py deleted file mode 100644 index 2222395f..00000000 --- a/backend/app/models/gateway_installed_skills.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Gateway-to-skill installation state records.""" - -from __future__ import annotations - -from datetime import datetime -from uuid import UUID, uuid4 - -from sqlalchemy import UniqueConstraint -from sqlmodel import Field - -from app.core.time import utcnow -from app.models.base import QueryModel - -RUNTIME_ANNOTATION_TYPES = (datetime,) - - -class GatewayInstalledSkill(QueryModel, table=True): - """Marks that a marketplace skill is installed for a specific gateway.""" - - __tablename__ = "gateway_installed_skills" # pyright: ignore[reportAssignmentType] - __table_args__ = ( - UniqueConstraint( - "gateway_id", - "skill_id", - name="uq_gateway_installed_skills_gateway_id_skill_id", - ), - ) - - id: UUID = Field(default_factory=uuid4, primary_key=True) - gateway_id: UUID = Field(foreign_key="gateways.id", index=True) - skill_id: UUID = Field(foreign_key="marketplace_skills.id", index=True) - created_at: datetime = Field(default_factory=utcnow) - updated_at: datetime = Field(default_factory=utcnow) diff --git a/backend/app/models/marketplace_skills.py b/backend/app/models/marketplace_skills.py deleted file mode 100644 index e4139ff6..00000000 --- a/backend/app/models/marketplace_skills.py +++ /dev/null @@ -1,42 +0,0 @@ -"""Organization-scoped skill catalog entries for the skills marketplace.""" - -from __future__ import annotations - -from datetime import datetime -from uuid import UUID, uuid4 - -from sqlalchemy import JSON, Column, UniqueConstraint -from sqlmodel import Field - -from app.core.time import utcnow -from app.models.tenancy import TenantScoped - -RUNTIME_ANNOTATION_TYPES = (datetime,) - - -class MarketplaceSkill(TenantScoped, table=True): - """A marketplace skill entry that can be installed onto one or more gateways.""" - - __tablename__ = "marketplace_skills" # pyright: ignore[reportAssignmentType] - __table_args__ = ( - UniqueConstraint( - "organization_id", - "source_url", - name="uq_marketplace_skills_org_source_url", - ), - ) - - id: UUID = Field(default_factory=uuid4, primary_key=True) - organization_id: UUID = Field(foreign_key="organizations.id", index=True) - name: str - description: str | None = Field(default=None) - category: str | None = Field(default=None) - risk: str | None = Field(default=None) - source: str | None = Field(default=None) - source_url: str - metadata_: dict[str, object] = Field( - default_factory=dict, - sa_column=Column("metadata", JSON, nullable=False), - ) - created_at: datetime = Field(default_factory=utcnow) - updated_at: datetime = Field(default_factory=utcnow) diff --git a/backend/app/models/skill_packs.py b/backend/app/models/skill_packs.py deleted file mode 100644 index 99b641ca..00000000 --- a/backend/app/models/skill_packs.py +++ /dev/null @@ -1,40 +0,0 @@ -"""Organization-scoped skill pack sources.""" - -from __future__ import annotations - -from datetime import datetime -from uuid import UUID, uuid4 - -from sqlalchemy import JSON, Column, UniqueConstraint -from sqlmodel import Field - -from app.core.time import utcnow -from app.models.tenancy import TenantScoped - -RUNTIME_ANNOTATION_TYPES = (datetime,) - - -class SkillPack(TenantScoped, table=True): - """A pack repository URL that can be synced into marketplace skills.""" - - __tablename__ = "skill_packs" # pyright: ignore[reportAssignmentType] - __table_args__ = ( - UniqueConstraint( - "organization_id", - "source_url", - name="uq_skill_packs_org_source_url", - ), - ) - - id: UUID = Field(default_factory=uuid4, primary_key=True) - organization_id: UUID = Field(foreign_key="organizations.id", index=True) - name: str - description: str | None = Field(default=None) - source_url: str - branch: str = Field(default="main") - metadata_: dict[str, object] = Field( - default_factory=dict, - sa_column=Column("metadata", JSON, nullable=False), - ) - created_at: datetime = Field(default_factory=utcnow) - updated_at: datetime = Field(default_factory=utcnow) diff --git a/backend/app/models/skills.py b/backend/app/models/skills.py new file mode 100644 index 00000000..0672707c --- /dev/null +++ b/backend/app/models/skills.py @@ -0,0 +1,88 @@ +"""Skill-related SQLModel tables for marketplace, packs, and installations.""" + +from __future__ import annotations + +from datetime import datetime +from uuid import UUID, uuid4 + +from sqlalchemy import JSON, Column, UniqueConstraint +from sqlmodel import Field + +from app.core.time import utcnow +from app.models.base import QueryModel +from app.models.tenancy import TenantScoped + +RUNTIME_ANNOTATION_TYPES = (datetime,) + + +class MarketplaceSkill(TenantScoped, table=True): + """A marketplace skill entry that can be installed onto one or more gateways.""" + + __tablename__ = "marketplace_skills" # pyright: ignore[reportAssignmentType] + __table_args__ = ( + UniqueConstraint( + "organization_id", + "source_url", + name="uq_marketplace_skills_org_source_url", + ), + ) + + id: UUID = Field(default_factory=uuid4, primary_key=True) + organization_id: UUID = Field(foreign_key="organizations.id", index=True) + name: str + description: str | None = Field(default=None) + category: str | None = Field(default=None) + risk: str | None = Field(default=None) + source: str | None = Field(default=None) + source_url: str + metadata_: dict[str, object] = Field( + default_factory=dict, + sa_column=Column("metadata", JSON, nullable=False), + ) + created_at: datetime = Field(default_factory=utcnow) + updated_at: datetime = Field(default_factory=utcnow) + + +class SkillPack(TenantScoped, table=True): + """A pack repository URL that can be synced into marketplace skills.""" + + __tablename__ = "skill_packs" # pyright: ignore[reportAssignmentType] + __table_args__ = ( + UniqueConstraint( + "organization_id", + "source_url", + name="uq_skill_packs_org_source_url", + ), + ) + + id: UUID = Field(default_factory=uuid4, primary_key=True) + organization_id: UUID = Field(foreign_key="organizations.id", index=True) + name: str + description: str | None = Field(default=None) + source_url: str + branch: str = Field(default="main") + metadata_: dict[str, object] = Field( + default_factory=dict, + sa_column=Column("metadata", JSON, nullable=False), + ) + created_at: datetime = Field(default_factory=utcnow) + updated_at: datetime = Field(default_factory=utcnow) + + +class GatewayInstalledSkill(QueryModel, table=True): + """Marks that a marketplace skill is installed for a specific gateway.""" + + __tablename__ = "gateway_installed_skills" # pyright: ignore[reportAssignmentType] + __table_args__ = ( + UniqueConstraint( + "gateway_id", + "skill_id", + name="uq_gateway_installed_skills_gateway_id_skill_id", + ), + ) + + id: UUID = Field(default_factory=uuid4, primary_key=True) + gateway_id: UUID = Field(foreign_key="gateways.id", index=True) + skill_id: UUID = Field(foreign_key="marketplace_skills.id", index=True) + created_at: datetime = Field(default_factory=utcnow) + updated_at: datetime = Field(default_factory=utcnow) diff --git a/backend/app/services/organizations.py b/backend/app/services/organizations.py index 0e4d14c0..8093e3fb 100644 --- a/backend/app/services/organizations.py +++ b/backend/app/services/organizations.py @@ -21,7 +21,7 @@ from app.models.organization_invite_board_access import OrganizationInviteBoardA from app.models.organization_invites import OrganizationInvite from app.models.organization_members import OrganizationMember from app.models.organizations import Organization -from app.models.skill_packs import SkillPack +from app.models.skills import SkillPack from app.models.users import User if TYPE_CHECKING: diff --git a/backend/tests/test_organizations_service.py b/backend/tests/test_organizations_service.py index ea3c940f..503a8ffc 100644 --- a/backend/tests/test_organizations_service.py +++ b/backend/tests/test_organizations_service.py @@ -17,7 +17,7 @@ from app.models.organization_invite_board_access import OrganizationInviteBoardA from app.models.organization_invites import OrganizationInvite from app.models.organization_members import OrganizationMember from app.models.organizations import Organization -from app.models.skill_packs import SkillPack +from app.models.skills import SkillPack from app.models.users import User from app.schemas.organizations import OrganizationBoardAccessSpec, OrganizationMemberAccessUpdate from app.services import organizations diff --git a/backend/tests/test_skills_marketplace_api.py b/backend/tests/test_skills_marketplace_api.py index b9c5b114..782ed06c 100644 --- a/backend/tests/test_skills_marketplace_api.py +++ b/backend/tests/test_skills_marketplace_api.py @@ -23,12 +23,10 @@ from app.api.skills_marketplace import ( ) from app.api.skills_marketplace import router as skills_marketplace_router from app.db.session import get_session -from app.models.gateway_installed_skills import GatewayInstalledSkill from app.models.gateways import Gateway -from app.models.marketplace_skills import MarketplaceSkill from app.models.organization_members import OrganizationMember from app.models.organizations import Organization -from app.models.skill_packs import SkillPack +from app.models.skills import GatewayInstalledSkill, MarketplaceSkill, SkillPack from app.services.organizations import OrganizationContext