Initial artdag-common shared library
Shared components for L1 and L2 servers: - Jinja2 template system with base template and components - Middleware for auth and content negotiation - Pydantic models for requests/responses - Utility functions for pagination, media, formatting - Constants for Tailwind/HTMX/Cytoscape CDNs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
25
artdag_common/models/__init__.py
Normal file
25
artdag_common/models/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""
|
||||
Shared Pydantic models for Art-DAG servers.
|
||||
"""
|
||||
|
||||
from .requests import (
|
||||
PaginationParams,
|
||||
PublishRequest,
|
||||
StorageConfigRequest,
|
||||
MetadataUpdateRequest,
|
||||
)
|
||||
from .responses import (
|
||||
PaginatedResponse,
|
||||
ErrorResponse,
|
||||
SuccessResponse,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"PaginationParams",
|
||||
"PublishRequest",
|
||||
"StorageConfigRequest",
|
||||
"MetadataUpdateRequest",
|
||||
"PaginatedResponse",
|
||||
"ErrorResponse",
|
||||
"SuccessResponse",
|
||||
]
|
||||
74
artdag_common/models/requests.py
Normal file
74
artdag_common/models/requests.py
Normal file
@@ -0,0 +1,74 @@
|
||||
"""
|
||||
Request models shared across L1 and L2 servers.
|
||||
"""
|
||||
|
||||
from typing import Optional, List, Dict, Any
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from ..constants import DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE
|
||||
|
||||
|
||||
class PaginationParams(BaseModel):
|
||||
"""Common pagination parameters."""
|
||||
page: int = Field(default=1, ge=1, description="Page number (1-indexed)")
|
||||
limit: int = Field(
|
||||
default=DEFAULT_PAGE_SIZE,
|
||||
ge=1,
|
||||
le=MAX_PAGE_SIZE,
|
||||
description="Items per page"
|
||||
)
|
||||
|
||||
@property
|
||||
def offset(self) -> int:
|
||||
"""Calculate offset for database queries."""
|
||||
return (self.page - 1) * self.limit
|
||||
|
||||
|
||||
class PublishRequest(BaseModel):
|
||||
"""Request to publish content to L2/storage."""
|
||||
name: str = Field(..., min_length=1, max_length=255)
|
||||
description: Optional[str] = Field(default=None, max_length=2000)
|
||||
tags: List[str] = Field(default_factory=list)
|
||||
storage_id: Optional[str] = Field(default=None, description="Target storage provider")
|
||||
|
||||
|
||||
class MetadataUpdateRequest(BaseModel):
|
||||
"""Request to update content metadata."""
|
||||
name: Optional[str] = Field(default=None, max_length=255)
|
||||
description: Optional[str] = Field(default=None, max_length=2000)
|
||||
tags: Optional[List[str]] = Field(default=None)
|
||||
metadata: Optional[Dict[str, Any]] = Field(default=None)
|
||||
|
||||
|
||||
class StorageConfigRequest(BaseModel):
|
||||
"""Request to configure a storage provider."""
|
||||
provider_type: str = Field(..., description="Provider type (pinata, web3storage, local, etc.)")
|
||||
name: str = Field(..., min_length=1, max_length=100)
|
||||
api_key: Optional[str] = Field(default=None)
|
||||
api_secret: Optional[str] = Field(default=None)
|
||||
endpoint: Optional[str] = Field(default=None)
|
||||
config: Optional[Dict[str, Any]] = Field(default_factory=dict)
|
||||
is_default: bool = Field(default=False)
|
||||
|
||||
|
||||
class RecipeRunRequest(BaseModel):
|
||||
"""Request to run a recipe."""
|
||||
recipe_id: str = Field(..., description="Recipe content hash or ID")
|
||||
inputs: Dict[str, str] = Field(..., description="Map of input name to content hash")
|
||||
features: List[str] = Field(
|
||||
default=["beats", "energy"],
|
||||
description="Analysis features to extract"
|
||||
)
|
||||
|
||||
|
||||
class PlanRequest(BaseModel):
|
||||
"""Request to generate an execution plan."""
|
||||
recipe_yaml: str = Field(..., description="Recipe YAML content")
|
||||
input_hashes: Dict[str, str] = Field(..., description="Map of input name to content hash")
|
||||
features: List[str] = Field(default=["beats", "energy"])
|
||||
|
||||
|
||||
class ExecutePlanRequest(BaseModel):
|
||||
"""Request to execute a generated plan."""
|
||||
plan_json: str = Field(..., description="JSON-serialized execution plan")
|
||||
run_id: Optional[str] = Field(default=None, description="Optional run ID for tracking")
|
||||
96
artdag_common/models/responses.py
Normal file
96
artdag_common/models/responses.py
Normal file
@@ -0,0 +1,96 @@
|
||||
"""
|
||||
Response models shared across L1 and L2 servers.
|
||||
"""
|
||||
|
||||
from typing import Optional, List, Dict, Any, Generic, TypeVar
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class PaginatedResponse(BaseModel, Generic[T]):
|
||||
"""Generic paginated response."""
|
||||
data: List[Any] = Field(default_factory=list)
|
||||
pagination: Dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
cls,
|
||||
items: List[Any],
|
||||
page: int,
|
||||
limit: int,
|
||||
total: int,
|
||||
) -> "PaginatedResponse":
|
||||
"""Create a paginated response."""
|
||||
return cls(
|
||||
data=items,
|
||||
pagination={
|
||||
"page": page,
|
||||
"limit": limit,
|
||||
"total": total,
|
||||
"has_more": page * limit < total,
|
||||
"total_pages": (total + limit - 1) // limit,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ErrorResponse(BaseModel):
|
||||
"""Standard error response."""
|
||||
error: str = Field(..., description="Error message")
|
||||
detail: Optional[str] = Field(default=None, description="Detailed error info")
|
||||
code: Optional[str] = Field(default=None, description="Error code")
|
||||
|
||||
|
||||
class SuccessResponse(BaseModel):
|
||||
"""Standard success response."""
|
||||
success: bool = Field(default=True)
|
||||
message: Optional[str] = Field(default=None)
|
||||
data: Optional[Dict[str, Any]] = Field(default=None)
|
||||
|
||||
|
||||
class RunStatus(BaseModel):
|
||||
"""Run execution status."""
|
||||
run_id: str
|
||||
status: str = Field(..., description="pending, running, completed, failed")
|
||||
recipe: Optional[str] = None
|
||||
plan_id: Optional[str] = None
|
||||
output_hash: Optional[str] = None
|
||||
output_ipfs_cid: Optional[str] = None
|
||||
total_steps: int = 0
|
||||
cached_steps: int = 0
|
||||
completed_steps: int = 0
|
||||
error: Optional[str] = None
|
||||
|
||||
|
||||
class CacheItemResponse(BaseModel):
|
||||
"""Cached content item response."""
|
||||
content_hash: str
|
||||
media_type: Optional[str] = None
|
||||
size: Optional[int] = None
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
tags: List[str] = Field(default_factory=list)
|
||||
ipfs_cid: Optional[str] = None
|
||||
created_at: Optional[str] = None
|
||||
|
||||
|
||||
class RecipeResponse(BaseModel):
|
||||
"""Recipe response."""
|
||||
recipe_id: str
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
inputs: List[Dict[str, Any]] = Field(default_factory=list)
|
||||
outputs: List[str] = Field(default_factory=list)
|
||||
node_count: int = 0
|
||||
created_at: Optional[str] = None
|
||||
|
||||
|
||||
class StorageProviderResponse(BaseModel):
|
||||
"""Storage provider configuration response."""
|
||||
storage_id: str
|
||||
provider_type: str
|
||||
name: str
|
||||
is_default: bool = False
|
||||
is_connected: bool = False
|
||||
usage_bytes: Optional[int] = None
|
||||
pin_count: int = 0
|
||||
Reference in New Issue
Block a user