### Install Spakky Framework with Recommended Extras Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/INDEX.md Install Spakky with common extras like FastAPI, SQLAlchemy, and logging for a robust setup. ```bash # With recommended extras (FastAPI + SQLAlchemy + logging) pip install "spakky[recommended]" ``` -------------------------------- ### Spakky Application Initialization and Bean Retrieval Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/ARCHITECTURE-OVERVIEW.md This snippet shows the basic setup for a Spakky application, including scanning for beans, starting the application, and retrieving a service from the IoC container. ```python app = SpakkyApplication(ApplicationContext()).scan().start() container = app.container service = container.get(UserService) ``` -------------------------------- ### Install Spakky Framework with Specific Extras Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/INDEX.md Install Spakky with specific optional components as needed. ```bash # Specific extras pip install "spakky[web]" # FastAPI pip install "spakky[database-postgres]" # SQLAlchemy + PostgreSQL pip install "spakky[events-rabbitmq]" # RabbitMQ pip install "spakky[agent]" # Agentic workflows ``` -------------------------------- ### Install Spakky Framework with Full Extras Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/INDEX.md Install Spakky with all available plugins for a complete feature set. ```bash # Full installation (all plugins) pip install "spakky[full]" ``` -------------------------------- ### Install Spakky Framework with All Plugins Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/README.md Installs the Spakky framework with all available plugins enabled. This provides the most comprehensive set of features for development. ```bash # With all plugins pip install "spakky[full]" ``` -------------------------------- ### Install Spakky Framework with Recommended Extras Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/README.md Installs the Spakky framework along with recommended extras, including FastAPI and SQLAlchemy support. Use this for projects requiring web serving and database integration. ```bash # With recommended extras (FastAPI + SQLAlchemy) pip install "spakky[recommended]" ``` -------------------------------- ### Install Spakky Framework Core Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/README.md Installs the core Spakky framework package. This is the basic installation for using the framework's fundamental features. ```bash # Core framework pip install spakky ``` -------------------------------- ### Install Spakky-MCP Source: https://github.com/e5presso/spakky-framework/blob/main/plugins/spakky-mcp/README.md Install the spakky-mcp plugin using uv. Alternatively, install the full spakky[agent] bundle. ```bash uv add spakky-mcp # or through the agent bundle uv add "spakky[agent]" ``` -------------------------------- ### Example: Injecting Dependencies as a List Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/TYPES-REFERENCE.md Demonstrates injecting all matching EventHandler implementations as a list into a class constructor. ```python @Pod() class EventBus: # All EventHandler implementations injected as list def __init__(self, handlers: list[EventHandler]) -> None: self.handlers = handlers # Or as dict keyed by Pod name # def __init__(self, handlers: dict[str, EventHandler]) -> None: # Or as tuple # def __init__(self, handlers: tuple[EventHandler, ...]) -> None: ``` -------------------------------- ### Initialize and Start Spakky Application Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/CORE-API-REFERENCE.md Use this snippet to initialize a Spakky application with a basic context, scan for components, and start the application. The scan method auto-detects the caller's package. ```python from spakky.core.application.application import SpakkyApplication from spakky.core.application.application_context import ApplicationContext app = ( SpakkyApplication(ApplicationContext()) .scan() # Auto-detects caller's package .start() ) ``` -------------------------------- ### Bootstrap Spakky Application Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/README.md Initialize and start a Spakky application, enabling auto-detection of components. Retrieve services from the application container. ```python from spakky.core.application.application import SpakkyApplication from spakky.core.application.application_context import ApplicationContext app = ( SpakkyApplication(ApplicationContext()) .scan() # Auto-detect @Pod and @Aspect classes .start() ) user_service = app.container.get(UserService) ``` -------------------------------- ### Agent ToolKit Example Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/AUTH-TRACING-AGENT-API-REFERENCE.md Example of defining a toolkit with agent tools using the @agent_tool decorator. This demonstrates how to expose database operations as callable tools for agents. ```python from spakky.agent.stereotype.agent_tool import agent_tool from spakky.core.pod.annotations.pod import Pod # Assuming Database and other necessary imports are available # from your_database_module import Database @Pod() class DataToolKit: def __init__(self, db: Database) -> None: self.db = db @agent_tool(description="Find a user by ID") async def find_user(self, user_id: str) -> dict: """Retrieve user information by ID. Args: user_id: The UUID of the user to find. Returns: User data as dictionary. """ return await self.db.find_user(user_id) @agent_tool(description="List all users with optional filtering") async def list_users(self, limit: int = 10, offset: int = 0) -> list[dict]: """List users with pagination. Args: limit: Maximum number of users to return. offset: Number of users to skip. Returns: List of user dictionaries. """ return await self.db.list_users(limit=limit, offset=offset) @agent_tool(description="Create a new user") async def create_user(self, name: str, email: str) -> dict: """Create a new user. Args: name: User's full name. email: User's email address. Returns: Newly created user data. """ return await self.db.create_user(name=name, email=email) ``` -------------------------------- ### Start Spakky Application and Access Container Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/CORE-API-REFERENCE.md This snippet shows how to start a Spakky application after scanning and then access its IoC container. The container can be used for manual resolution of application components (Pods). ```python app = SpakkyApplication(ApplicationContext()).scan().start() container = app.container ``` -------------------------------- ### Implement a Command Handler for Creating Users Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/DOMAIN-API-REFERENCE.md Example of implementing the CommandHandler interface to handle the CreateUserCommand. This handler interacts with a UserRepository to save the new user. ```python from spakky.domain.cqrs.handler import CommandHandler from spakky.core.pod.annotations.pod import Pod @Pod() class CreateUserCommandHandler(CommandHandler[CreateUserCommand]): def __init__(self, repository: UserRepository) -> None: self.repository = repository def handle(self, command: CreateUserCommand) -> User: user = User( id=uuid4(), name=command.name, email=command.email, ) self.repository.save(user) return user ``` -------------------------------- ### Example: UserService with Generic Repository Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/TYPES-REFERENCE.md Shows a UserService using a generic IAsyncGenericRepository, where User is the aggregate root and UUID is its identity. ```python @Pod() class UserService: def __init__(self, repo: IAsyncGenericRepository[User, UUID]) -> None: self.repo = repo ``` -------------------------------- ### Async Pod Method Example Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/ARCHITECTURE-OVERVIEW.md Illustrates an asynchronous method within a Spakky pod, showcasing native support for async/await operations. ```python # Async pod methods @Pod() class UserService: async def get_user(self, id: UUID) -> User | None: return await self.repository.find_by_id(id) ``` -------------------------------- ### AggregateRoot Usage Example Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/DOMAIN-API-REFERENCE.md Demonstrates creating an AggregateRoot, raising a custom domain event upon initialization, and managing uncommitted events. ```python from spakky.domain.aggregate_root import AggregateRoot from spakky.domain.event import DomainEvent from uuid import UUID, uuid4 from dataclasses import dataclass @dataclass class UserCreatedEvent(DomainEvent): user_id: UUID name: str email: str class User(AggregateRoot): def __init__(self, id: UUID, name: str, email: str) -> None: super().__init__() self._id = id self.name = name self.email = email # Raise domain event self.raise_event(UserCreatedEvent( user_id=id, name=name, email=email, )) @property def identity(self) -> UUID: return self._id # Usage user = User(id=uuid4(), name="Jane Doe", email="jane@example.com") events = user.uncommitted_events # Access raised events user.clear_uncommitted_events() # Clear after publishing ``` -------------------------------- ### Entity Usage Example Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/DOMAIN-API-REFERENCE.md Demonstrates how to create and compare custom Entity objects using their unique identities. ```python from spakky.domain.entity import Entity from uuid import UUID, uuid4 class User(Entity): def __init__(self, id: UUID, name: str, email: str) -> None: self._id = id self.name = name self.email = email @property def identity(self) -> UUID: return self._id # Usage user1 = User(id=uuid4(), name="John Doe", email="john@example.com") user2 = User(id=user1.identity, name="John Smith", email="john.smith@example.com") assert user1.equals(user2) # Same identity, considered equal ``` -------------------------------- ### Usage of CacheEvict to Clear All Entries Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/EVENT-TASK-CACHE-API-REFERENCE.md Example showing how to use @CacheEvict with all_entries=True to clear the entire cache after clearing all users. ```python from spakky.cache.aspects.evict import CacheEvict @Pod() class UserRepository: @CacheEvict(all_entries=True) async def clear_all_users(self) -> None: # All user cache entries are cleared await self._delete_all_from_db() ``` -------------------------------- ### Initialize Spakky Application with Custom Context Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/CORE-API-REFERENCE.md This example shows the initialization of a Spakky application using a pre-configured ApplicationContext. This context can be customized before being passed to the SpakkyApplication constructor. ```python from spakky.core.application.application_context import ApplicationContext context = ApplicationContext() app = SpakkyApplication(context).scan().start() ``` -------------------------------- ### Example: Using AnnotatedType with Qualifier Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/TYPES-REFERENCE.md Shows how to use AnnotatedType with a Qualifier to specify a particular implementation of IUserRepository. ```python from typing import Annotated from spakky.core.pod.annotations.qualifier import Qualifier @Pod() class UserService: def __init__( self, primary_repo: Annotated[IUserRepository, Qualifier(name="primary")], ) -> None: self.repo = primary_repo ``` -------------------------------- ### CQRS Handler Usage Example Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/TYPES-REFERENCE.md Illustrates how to define command and query handlers using generic type hints for commands, queries, and return types. ```python @Pod() class CreateUserHandler(CommandHandler[CreateUserCommand]): def handle(self, command: CreateUserCommand) -> User: pass @Pod() class FindUserHandler(QueryHandler[FindUserQuery, User | None]): def handle(self, query: FindUserQuery) -> User | None: pass ``` -------------------------------- ### Register Typer App with Spakky Application Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/PLUGINS-FASTAPI-SQLALCHEMY.md Integrate a Typer application into the Spakky framework by scanning and starting the Spakky application. This setup is typically used in the main entry point of your application. ```python from spakky.core.application.application import SpakkyApplication from spakky.core.application.application_context import ApplicationContext import typer app = typer.Typer() spakky_app = ( SpakkyApplication(ApplicationContext()) .scan() .start() ) if __name__ == "__main__": app() ``` -------------------------------- ### Example: UserRepository with Generic Types Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/TYPES-REFERENCE.md Illustrates a UserRepository implementation using generic types for the aggregate root (User) and its identity (UUID). ```python from spakky.data.persistency.repository import IAsyncGenericRepository from uuid import UUID class UserRepository(IAsyncGenericRepository[User, UUID]): # User is the aggregate root # UUID is the identity type pass ``` -------------------------------- ### Agent Decorator and ResearchAgent Example Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/AUTH-TRACING-AGENT-API-REFERENCE.md Demonstrates how to use the @Agent decorator to define an agentic workflow component. This example shows a ResearchAgent that uses an LLM model and tools to perform research based on a query. ```python from spakky.agent.stereotype.agent import Agent from spakky.agent.spec import AgentExecutionSpec from spakky.agent.yield_ import AgentYield @Pod() @Agent() class ResearchAgent: def __init__( self, model: IAgentModel, tools: DataToolKit, ) -> None: self.model = model self.tools = tools async def research(self, query: str) -> str: """Run research workflow for a query. Args: query: The research question. Returns: Research findings as text. """ state = AgentState( signal=AgentSignal.START, context={"query": query}, history=[], ) spec = AgentExecutionSpec( instruction=f"Research the following: {query}", state=state, tools=[self.tools.find_user, self.tools.list_users], ) final_response = await self.model.invoke(spec) if isinstance(final_response, AgentResponse): return final_response.content raise ValueError("Unexpected agent response") ``` -------------------------------- ### Usage of CacheEvict for User Deletion Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/EVENT-TASK-CACHE-API-REFERENCE.md Example demonstrating how to use @CacheEvict with a key_builder to evict a specific user's cache entry after deletion. ```python from spakky.cache.aspects.evict import CacheEvict @Pod() class UserRepository: @CacheEvict(key_builder=lambda user_id, **_: [f"user:{user_id}"]) async def delete_user(self, user_id: UUID) -> None: # Cache entry is evicted after deletion await self._delete_from_db(user_id) ``` -------------------------------- ### Register Pod with PodType Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/TYPES-REFERENCE.md Example of using PodType to type-hint a parameter that can be either a class or a function. ```python from spakky.core.pod.annotations.pod import PodType def register_pod(pod: PodType) -> None: # pod is either a class or function if isclass(pod): # Handle class-based pod pass else: # Handle function-based pod pass ``` -------------------------------- ### SpakkyApplication Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/CORE-API-REFERENCE.md Class for configuring and bootstrapping a Spakky application. It manages the application context, scans for components, and starts the application lifecycle. ```APIDOC ## SpakkyApplication ### Description Class for configuring and bootstrapping a Spakky application. It manages the application context, scans for components, and starts the application lifecycle. ### Import Path spakky.core.application.application.SpakkyApplication ### Initialization ```python SpakkyApplication( context: IApplicationContext, include_plugin_search: bool = True, discovery_manifest_enabled: bool = True, startup_phase_recorder: IStartupPhaseRecorder | None = None, ) ``` ### Methods #### scan(path: str | ModuleType | None = None) -> Self Scans a package for annotated Pods and Aspects. Auto-detects caller's module if no path given. ##### Parameters - `path` (str | ModuleType | None): Optional. Package path or module to scan. If None, auto-detects caller's package. ##### Returns - `Self`: Returns self for chaining. ##### Throws - `CannotDetermineScanPathError`: If the scan path cannot be inferred. #### start() -> Self Starts the application, initializing all Pods and running startup hooks. ##### Returns - `Self`: Returns self for chaining. #### container() -> IContainer Returns the IoC container for manual Pod resolution. ##### Returns - `IContainer`: The IoC container instance. #### application_context() -> IApplicationContext Returns the underlying application context. ##### Returns - `IApplicationContext`: The application context instance. ### Example ```python from spakky.core.application.application import SpakkyApplication from spakky.core.application.application_context import ApplicationContext app = ( SpakkyApplication(ApplicationContext()) .scan() # Auto-detects caller's package .start() ) container = app.container ``` ``` -------------------------------- ### Specification Pattern Usage Example Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/DOMAIN-API-REFERENCE.md Demonstrates how to implement and use the Specification pattern with custom specifications for active users and valid emails. Shows composition of specifications using the 'and' method. ```python from uuid import uuid4 from spakky.domain.specification import Specification class User: def __init__(self, id: uuid4, email: str, is_active: bool) -> None: self.id = id self.email = email self.is_active = is_active class ActiveUserSpecification(Specification[User]): def is_satisfied_by(self, user: User) -> bool: return user.is_active class ValidEmailSpecification(Specification[User]): def is_satisfied_by(self, user: User) -> bool: return "@" in user.email # Usage active = ActiveUserSpecification() valid_email = ValidEmailSpecification() spec = active.and(valid_email) # Composite specification user = User(id=uuid4(), email="user@example.com", is_active=True) if spec.is_satisfied_by(user): print("User is active and has valid email") ``` -------------------------------- ### Implementing Aggregate Root with Event Sourcing Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/ARCHITECTURE-OVERVIEW.md Example of creating a User aggregate root that raises a DomainEvent upon initialization. Uncommitted events are stored and can be accessed and cleared. ```python class User(AggregateRoot): def __init__(self, id: UUID, name: str, email: str) -> None: super().__init__() self._id = id self.raise_event(UserCreatedEvent(...)) # Add to uncommitted_events # Access uncommitted events events = user.uncommitted_events # Clear after publishing user.clear_uncommitted_events() ``` -------------------------------- ### Usage Example: EventHandler Decorator with Methods Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/EVENT-TASK-CACHE-API-REFERENCE.md Demonstrates how to use the @EventHandler decorator on classes and define asynchronous methods (e.g., on_user_created) to handle specific events. This example shows updating user cache and sending welcome emails. ```python from spakky.event.stereotype.event_handler import EventHandler from spakky.domain.event import DomainEvent from spakky.core.pod.annotations.pod import Pod @dataclass class UserCreatedEvent(DomainEvent): user_id: UUID email: str @Pod() @EventHandler() class SendWelcomeEmailHandler: def __init__(self, email_service: EmailService) -> None: self.email_service = email_service async def on_user_created(self, event: UserCreatedEvent) -> None: await self.email_service.send_welcome(event.email) @Pod() @EventHandler() class UpdateUserCacheHandler: def __init__(self, cache: ICache) -> None: self.cache = cache async def on_user_created(self, event: UserCreatedEvent) -> None: await self.cache.set(f"user:{event.user_id}", {{}}) ``` -------------------------------- ### Celery Task Dispatcher Setup Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/PLUGINS-FASTAPI-SQLALCHEMY.md Illustrates how to set up a CeleryTaskDispatcher for handling asynchronous tasks. It includes a sample EmailService that uses the dispatcher to send emails via a Celery queue. ```python from spakky.plugins.celery import CeleryTaskDispatcher from spakky.task.stereotype.task_handler import task from spakky.core.pod.annotations.pod import Pod from celery import Celery celery_app = Celery("myapp", broker="redis://localhost:6379/0") @Pod() class EmailService: def __init__(self, dispatcher: CeleryTaskDispatcher) -> None: self.dispatcher = dispatcher @task(queue="email", max_retries=3, timeout=300) async def send_email(self, recipient: str, subject: str, body: str) -> None: """Send email via Celery task queue.""" # Implementation pass # Celery configuration @Pod() def celery_dispatcher() -> CeleryTaskDispatcher: return CeleryTaskDispatcher(celery_app=celery_app) ``` -------------------------------- ### W3CTracePropagator Usage Example Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/AUTH-TRACING-AGENT-API-REFERENCE.md Demonstrates how to use W3CTracePropagator to inject trace context into outgoing request headers and extract context from incoming request headers. ```python from spakky.tracing.propagation import W3CTracePropagator from spakky.tracing.context import TraceContext from uuid import uuid4 propagator = W3CTracePropagator() # Outgoing request trace_ctx = TraceContext( trace_id=str(uuid4()), span_id=str(uuid4()), ) headers = {} propagator.inject(trace_ctx, headers) # headers now contains: {"traceparent": "..."} await http_client.post("/api/users", headers=headers) # Incoming request incoming_headers = {"traceparent": "00-..."} extracted = propagator.extract(incoming_headers) if extracted: # Received trace context print(f"Trace ID: {extracted.trace_id}") ``` -------------------------------- ### Implement CQRS Pattern Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/README.md Apply the Command Query Responsibility Segregation (CQRS) pattern. This example defines a CreateUserCommand and its corresponding CommandHandler. ```python from spakky.domain.cqrs.command import Command from spakky.domain.cqrs.handler import CommandHandler @dataclass class CreateUserCommand(Command): name: str email: str @Pod() class CreateUserHandler(CommandHandler[CreateUserCommand]): def __init__(self, repository: UserRepository) -> None: self.repository = repository def handle(self, cmd: CreateUserCommand) -> User: user = User(id=uuid4(), name=cmd.name, email=cmd.email) self.repository.save(user) return user ``` -------------------------------- ### Configure External MCP Servers Source: https://github.com/e5presso/spakky-framework/blob/main/plugins/spakky-mcp/README.md Configure external MCP servers using the SPAKKY_MCP__SERVERS environment variable. This example sets up a 'weather' server using stdio transport. ```bash export SPAKKY_MCP__SERVERS='[ {"name": "weather", "transport": "stdio", "command": "weather-mcp-server"} ]' ``` -------------------------------- ### Async Repository Implementation Pattern Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/DATA-API-REFERENCE.md Demonstrates a pattern for implementing asynchronous repositories using Spakky's decorators for dependency injection (@Pod, @Repository) and transactional management (@Transactional). This example shows saving, finding by ID, deleting, and finding all users. ```python from spakky.data.persistency.repository import IAsyncGenericRepository from spakky.data.aspects.transactional import Transactional from spakky.core.pod.annotations.pod import Pod from spakky.data.stereotype.repository import Repository @Pod() @Repository() class UserRepository(IAsyncGenericRepository[User, UUID]): def __init__(self, session: AsyncSession) -> None: self.session = session @Transactional() async def save(self, user: User) -> None: self.session.merge(user) await self.session.flush() @Transactional(readonly=True) async def find_by_id(self, user_id: UUID) -> User | None: return await self.session.get(User, user_id) async def delete(self, user_id: UUID) -> None: user = await self.find_by_id(user_id) if user: await self.session.delete(user) async def find_all(self) -> list[User]: stmt = select(User) result = await self.session.execute(stmt) return result.scalars().all() ``` -------------------------------- ### Build Agentic Workflows with ResearchAgent Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/INDEX.md This example demonstrates building a ResearchAgent that uses an IAgentModel and a DataToolKit to perform research queries. It requires AgentExecutionSpec, AgentState, and AgentSignal. ```python @Pod() @Agent() class ResearchAgent: def __init__(self, model: IAgentModel, tools: DataToolKit) -> None: self.model = model self.tools = tools async def research(self, query: str) -> str: spec = AgentExecutionSpec( instruction=f"Research: {query}", state=AgentState(signal=AgentSignal.START, context={}, history=[]), tools=[self.tools.search, self.tools.fetch_page], ) response = await self.model.invoke(spec) return response.content if isinstance(response, AgentResponse) else "" ``` -------------------------------- ### ExchangeRateProxy Implementation and Usage Example Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/DATA-API-REFERENCE.md Demonstrates how to implement IExternalDataProxy to fetch and cache exchange rates using an HTTP client. It also shows how a CurrencyConverter class utilizes this proxy to perform currency conversions, prioritizing cached data. ```python from spakky.data.external.proxy import IExternalDataProxy from datetime import datetime from spakky.di import Pod @Pod() class ExchangeRateProxy(IExternalDataProxy[dict[str, float]]): def __init__(self, http_client) -> None: self.http_client = http_client self._cache = None self._cache_time = None async def fetch(self) -> dict[str, float]: response = await self.http_client.get( "https://api.example.com/rates" ) data = response.json() self._cache = data self._cache_time = datetime.now() return data async def get_cached( self, max_age_seconds: int | None = None ) -> dict[str, float] | None: if self._cache is None: return None if max_age_seconds is not None: age = (datetime.now() - self._cache_time).total_seconds() if age > max_age_seconds: return None return self._cache # Usage @Pod() class CurrencyConverter: def __init__(self, rates_proxy: ExchangeRateProxy) -> None: self.rates_proxy = rates_proxy async def convert(self, amount: float, from_currency: str, to_currency: str) -> float: # Try to use cached rates rates = await self.rates_proxy.get_cached(max_age_seconds=3600) # Fetch fresh if not cached if rates is None: rates = await self.rates_proxy.fetch() rate = rates.get(f"{from_currency}/{to_currency}", 1.0) return amount * rate ``` -------------------------------- ### Configure Spakky Application with OIDC Provider Source: https://github.com/e5presso/spakky-framework/blob/main/docs/guides/security.md Integrate the OIDC provider by loading necessary plugins and adding controllers. The application starts with a configured FastAPI instance. ```python from fastapi import FastAPI from spakky.auth import protected, require_scope from spakky.core.application.application import SpakkyApplication from spakky.core.application.application_context import ApplicationContext from spakky.plugins.fastapi.routes import get from spakky.plugins.fastapi.stereotypes.api_controller import ApiController import spakky.auth import spakky.plugins.fastapi import spakky.plugins.oidc @ApiController("/documents") class DocumentController: @get("/{document_id}") @require_scope("documents:read") @protected def read(self, document_id: str) -> dict[str, str]: return {"id": document_id} app = SpakkyApplication(ApplicationContext()) .load_plugins( include={ spakky.auth.PLUGIN_NAME, spakky.plugins.fastapi.PLUGIN_NAME, spakky.plugins.oidc.PLUGIN_NAME, } ) .add(DocumentController) .start() api = app.container.get(FastAPI) ``` -------------------------------- ### Implement Query Handlers for User Data Retrieval Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/DOMAIN-API-REFERENCE.md Examples of implementing the QueryHandler interface for retrieving user data. FindUserByIdQueryHandler fetches a user by ID, and ListAllUsersQueryHandler fetches all users. ```python @Pod() class FindUserByIdQueryHandler(QueryHandler[FindUserByIdQuery, User | None]): def __init__(self, repository: UserRepository) -> None: self.repository = repository def handle(self, query: FindUserByIdQuery) -> User | None: return self.repository.find_by_id(query.user_id) @Pod() class ListAllUsersQueryHandler(QueryHandler[ListAllUsersQuery, list[User]]): def __init__(self, repository: UserRepository) -> None: self.repository = repository def handle(self, query: ListAllUsersQuery) -> list[User]: return self.repository.list_all() ``` -------------------------------- ### Define a Command for Creating a User Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/DOMAIN-API-REFERENCE.md Example of defining a custom command that inherits from the base Command class to represent the intent of creating a user. This command includes user's name and email. ```python from spakky.domain.cqrs.command import Command from uuid import UUID from dataclasses import dataclass @dataclass class CreateUserCommand(Command): name: str email: str @dataclass class ChangeUserEmailCommand(Command): user_id: UUID new_email: str # Usage cmd = CreateUserCommand(aggregate_id=None, name="John", email="john@example.com") ``` -------------------------------- ### Build a REST API Controller Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/INDEX.md Define a REST API endpoint using the @ApiController decorator. This example shows how to create a user controller that fetches user data. ```python @Pod() @ApiController(prefix="/users") class UserController: def __init__(self, service: UserService) -> None: self.service = service async def get(self, user_id: str) -> UserResponse: user = await self.service.get_user(user_id) return UserResponse(**user.dict()) ``` -------------------------------- ### Inline MCP Server Declaration Source: https://github.com/e5presso/spakky-framework/blob/main/plugins/spakky-mcp/README.md This example shows how to declare an MCP server inline within `RunAgentInput`. It supports specifying the server name, transport protocol, URL, and authentication details. Use this for self-service connections. ```python RunAgentInput( state_id="run-2", instruction="inspect issue status", metadata={ "mcp": { "servers": [ { "name": "tenant-linear", "transport": "streamable_http", "url": "https://tenant.example.com/mcp", "auth": {"bearer_token": access_token}, } ] } }, ) ``` -------------------------------- ### Define Queries for Finding and Listing Users Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/DOMAIN-API-REFERENCE.md Examples of defining custom queries that inherit from the base Query class. FindUserByIdQuery retrieves a single user, while ListAllUsersQuery retrieves a list of all users. ```python from spakky.domain.cqrs.query import Query from uuid import UUID from dataclasses import dataclass @dataclass class FindUserByIdQuery(Query[User | None]): user_id: UUID @dataclass class ListAllUsersQuery(Query[list[User]]): pass # Usage query = FindUserByIdQuery(user_id=uuid4()) ``` -------------------------------- ### SQLAlchemy ORM Repository Pattern Implementation Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/PLUGINS-FASTAPI-SQLALCHEMY.md This snippet demonstrates the implementation of a user repository using SQLAlchemy's ORM and the Repository Pattern. It includes model definition, repository methods for CRUD operations, and application setup for Spakky. ```python from sqlalchemy import Column, String from sqlalchemy.orm import declarative_base from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine Base = declarative_base() class UserModel(Base): __tablename__ = "users" id: str = Column(String(36), primary_key=True) name: str = Column(String(255)) email: str = Column(String(255), unique=True) from spakky.data.persistency.repository import IAsyncGenericRepository from spakky.data.stereotype.repository import Repository from spakky.core.pod.annotations.pod import Pod from sqlalchemy import select @Pod() @Repository() class SQLAlchemyUserRepository(IAsyncGenericRepository[User, str]): def __init__(self, session: AsyncSession) -> None: self.session = session async def save(self, user: User) -> None: """Persist or update a user.""" model = UserModel(id=user.id, name=user.name, email=user.email) self.session.merge(model) await self.session.flush() async def find_by_id(self, user_id: str) -> User | None: """Find user by ID.""" stmt = select(UserModel).where(UserModel.id == user_id) result = await self.session.execute(stmt) model = result.scalars().first() if model: return User(id=model.id, name=model.name, email=model.email) return None async def delete(self, user_id: str) -> None: """Delete user by ID.""" stmt = select(UserModel).where(UserModel.id == user_id) result = await self.session.execute(stmt) model = result.scalars().first() if model: await self.session.delete(model) await self.session.flush() async def find_all(self) -> list[User]: """Retrieve all users.""" stmt = select(UserModel) result = await self.session.execute(stmt) models = result.scalars().all() return [User(id=m.id, name=m.name, email=m.email) for m in models] # Application setup from spakky.core.application.application import SpakkyApplication from spakky.core.application.application_context import ApplicationContext async def create_app(): # Create database engine engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/dbname") # Create tables async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) # Create async session async_session = AsyncSession(engine, expire_on_commit=False) # Start Spakky app context = ApplicationContext() app = ( SpakkyApplication(context) .scan() .start() ) # Inject session into container container = app.container # (session injection setup depends on specific DI configuration) return app ``` -------------------------------- ### Create FastAPI Controller Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/README.md Define an API controller for FastAPI using the @ApiController decorator. This example shows a UserController with GET and POST endpoints. ```python from spakky.plugins.fastapi.stereotypes.api_controller import ApiController @Pod() @ApiController(prefix="/users", tags=["users"]) class UserController: def __init__(self, service: UserService) -> None: self.service = service async def get(self, user_id: str) -> UserResponse: """GET /users/{user_id}""" user = await self.service.get_user(user_id) return UserResponse(**user.dict()) async def create(self, request: CreateUserRequest) -> UserResponse: """POST /users""" user = await self.service.create_user(**request.dict()) return UserResponse(**user.dict()) ``` -------------------------------- ### Domain Event Implementations (UserRegistered, UserEmailChanged) Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/DOMAIN-API-REFERENCE.md Example implementations of DomainEvent for user registration and email changes. These dataclasses capture specific event details like user ID and email addresses. ```python from spakky.domain.event import DomainEvent from uuid import UUID from dataclasses import dataclass @dataclass class UserRegisteredEvent(DomainEvent): user_id: UUID email: str name: str @dataclass class UserEmailChangedEvent(DomainEvent): user_id: UUID old_email: str new_email: str # Usage event = UserRegisteredEvent( aggregate_id=user_id, user_id=user_id, email="user@example.com", name="John Doe", ) ``` -------------------------------- ### Plugin Registration via pyproject.toml Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/ARCHITECTURE-OVERVIEW.md Plugins are registered using entry points in the pyproject.toml file. SpakkyApplication discovers and loads these plugins at startup. ```python # Plugins register via pyproject.toml entry points: [project.entry-points."spakky.plugins"] fastapi = "spakky.plugins.fastapi:main" sqlalchemy = "spakky.plugins.sqlalchemy:main" # At startup, SpakkyApplication discovers and loads plugins app = SpakkyApplication(context).scan().start() # → Plugin main() functions auto-register Pods/features ``` -------------------------------- ### Transaction Management Architecture Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/ARCHITECTURE-OVERVIEW.md Details the transaction management flow, starting with the @Transactional decorator and leading to the commit/rollback process. ```python @Transactional(isolation=..., readonly=...) ↓ AbstractAsyncTransaction / AbstractTransaction ↓ Begin → Flush → Commit/Rollback ``` -------------------------------- ### Implement Event-Driven Architecture with Event Publishing and Handling Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/INDEX.md Create a use case that publishes domain events and an event handler that reacts to a specific domain event. This illustrates event-driven patterns. ```python @Pod() class CreateUserUseCase: async def execute(self, cmd: CreateUserCommand) -> User: user = User(id=uuid4(), name=cmd.name, email=cmd.email) await self.repository.save(user) await self.event_publisher.publish_all(user.uncommitted_events) return user @Pod() @EventHandler() class UserNotificationHandler: @on_event(UserCreatedEvent) async def send_notification(self, event: UserCreatedEvent) -> None: await self.notification_service.notify(event.email) ``` -------------------------------- ### Implement Caching Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/README.md Add caching to a method using the @Cached decorator. This example caches the result of get_user for 1 hour. ```python from spakky.cache.aspects.cached import Cached @Pod() class UserService: @Cached(ttl=3600) # Cache for 1 hour async def get_user(self, user_id: UUID) -> User | None: return await self.repository.find_by_id(user_id) ``` -------------------------------- ### AgentSignal Enum Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/AUTH-TRACING-AGENT-API-REFERENCE.md An enumeration representing signals that control the agent execution flow, such as starting, continuing, resetting, or completing the agent. ```python from enum import Enum class AgentSignal(Enum): """Signals controlling agent execution flow.""" START = "start" """Initial signal to begin agent execution.""" CONTINUE = "continue" """Continue executing from current state.""" RESET = "reset" """Reset agent state and restart.""" COMPLETE = "complete" """Force agent completion.""" ``` -------------------------------- ### Dependency Injection with Constructor Injection Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/README.md Demonstrates how Spakky Framework automatically discovers and registers pods, resolving dependencies via constructor injection. Use this pattern for defining services that rely on other components. ```python @Pod() class Service: def __init__(self, dep1: Dependency1, dep2: Dependency2) -> None: self.dep1 = dep1 self.dep2 = dep2 ``` -------------------------------- ### TraceContext Initialization Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/TYPES-REFERENCE.md Demonstrates how to initialize a TraceContext with trace and span IDs using UUIDs. ```python from spakky.tracing.context import TraceContext, TraceId, SpanId from uuid import uuid4 trace = TraceContext( trace_id=str(uuid4()), span_id=str(uuid4()), ) ``` -------------------------------- ### ICache Interface Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/EVENT-TASK-CACHE-API-REFERENCE.md Defines the interface for synchronous application data caching operations, including get, set, delete, clear, and exists methods. ```APIDOC ## ICache Interface ### Description Interface for application-level data caching (synchronous). ### Methods - **get(key: str) -> object | None**: Retrieve a value from cache. - **set(key: str, value: object, ttl: int | None = None) -> None**: Store a value in cache. - **delete(key: str) -> None**: Remove a value from cache. - **clear() -> None**: Clear all cached values. - **exists(key: str) -> bool**: Check if a key exists in cache. ``` -------------------------------- ### Schedule Tasks Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/README.md Schedule a method to run at a specific time using the @schedule decorator with cron syntax. This example schedules a daily cleanup task. ```python from spakky.task.stereotype.schedule import schedule @Pod() class MaintenanceService: @schedule(cron="0 2 * * *") # Daily at 2 AM async def cleanup_old_logs(self) -> None: await self.db.delete_logs_before(datetime.now() - timedelta(days=30)) ``` -------------------------------- ### Implement Repository Pattern Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/README.md Define a data repository using the @Repository decorator, implementing an asynchronous generic repository interface. This example shows a UserRepository. ```python from spakky.data.persistency.repository import IAsyncGenericRepository from spakky.data.stereotype.repository import Repository @Pod() @Repository() class UserRepository(IAsyncGenericRepository[User, UUID]): async def save(self, user: User) -> None: ... async def find_by_id(self, user_id: UUID) -> User | None: ... async def delete(self, user_id: UUID) -> None: ... async def find_all(self) -> list[User]: ... ``` -------------------------------- ### Define an Aspect (AOP) Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/README.md Implement Aspect-Oriented Programming (AOP) with @Aspect and @Before/@After decorators. This example adds logging before and after method calls matching a pointcut. ```python from spakky.core.aop.aspect import Aspect, Aspect from spakky.core.aop.pointcut import Before, After @Pod() @Aspect() class LoggingAspect: @Before(pointcut="*.service.*.*(..)") def log_before(self, *args: object, **kwargs: object) -> None: print(f"Calling with args: {args}") @After(pointcut="*.service.*.*(..)") def log_after(self, result: object) -> None: print(f"Result: {result}") ``` -------------------------------- ### Set Up Distributed Tracing with TraceService Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/INDEX.md This snippet shows how to set up a TraceService to inject trace context into outgoing HTTP requests. It requires an ITracePropagator and an http_client. ```python @Pod() class TraceService: def __init__(self, propagator: ITracePropagator) -> None: self.propagator = propagator async def call_downstream(self, trace_ctx: TraceContext) -> None: headers = {} self.propagator.inject(trace_ctx, headers) await http_client.post("/api/endpoint", headers=headers) ``` -------------------------------- ### Execute CLI Command with Auth Token Source: https://github.com/e5presso/spakky-framework/blob/main/docs/guides/typer-advanced.md Demonstrates how to invoke a secured CLI command, either by providing the auth token via the --auth-token option or the SPAKKY_AUTH_TOKEN environment variable. ```bash python main.py documents read --document-id doc-1 --auth-token "$TOKEN" SPAKKY_AUTH_TOKEN="$TOKEN" python main.py documents read --document-id doc-1 ``` -------------------------------- ### Define a Pod (Managed Bean) Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/README.md Define a class as a managed bean (Pod) using the @Pod decorator. This example shows a UserService depending on a UserRepository. ```python from spakky.core.pod.annotations.pod import Pod @Pod() class UserService: def __init__(self, repository: UserRepository) -> None: self.repository = repository async def get_user(self, user_id: UUID) -> User | None: return await self.repository.find_by_id(user_id) ``` -------------------------------- ### Synchronous Cache Interface (ICache) Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/EVENT-TASK-CACHE-API-REFERENCE.md Defines the contract for synchronous application data caching operations like get, set, delete, clear, and exists. ```python class ICache(ABC): """Application data cache interface (synchronous).""" @abstractmethod def get(self, key: str) -> object | None: """Retrieve a value from cache. Args: key: Cache key. Returns: Cached value if exists, None otherwise. """ ... @abstractmethod def set(self, key: str, value: object, ttl: int | None = None) -> None: """Store a value in cache. Args: key: Cache key. value: Value to cache. ttl: Time-to-live in seconds. None for no expiration. """ ... @abstractmethod def delete(self, key: str) -> None: """Remove a value from cache. Args: key: Cache key to delete. """ ... @abstractmethod def clear(self) -> None: """Clear all cached values.""" ... @abstractmethod def exists(self, key: str) -> bool: """Check if a key exists in cache. Args: key: Cache key. Returns: True if key exists, False otherwise. """ ... ``` -------------------------------- ### Run Ruff Format Check Source: https://github.com/e5presso/spakky-framework/blob/main/plugins/spakky-mcp/README.md Formats code according to Ruff standards. Run from the package directory. ```bash uv run ruff format . ``` -------------------------------- ### Email Value Object Implementation Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/DOMAIN-API-REFERENCE.md An example implementation of a ValueObject for email addresses, including validation and value-based equality checks. Frozen dataclasses ensure immutability. ```python from spakky.domain.value_object import ValueObject from dataclasses import dataclass @dataclass(frozen=True) class Email(ValueObject): """Email value object with validation.""" value: str def __post_init__(self) -> None: if "@" not in self.value: raise ValueError("Invalid email") def equals(self, other: object) -> bool: if not isinstance(other, Email): return False return self.value == other.value # Usage email1 = Email("user@example.com") email2 = Email("user@example.com") assert email1.equals(email2) # Same value, considered equal class User: def __init__(self, name: str, email: Email) -> None: self.name = name self.email = email user = User("John", Email("john@example.com")) ``` -------------------------------- ### Task Scheduling and Execution Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/ARCHITECTURE-OVERVIEW.md Shows how to define background tasks using @task or scheduled tasks using @schedule, leading to TaskHandler execution. ```python @task(queue=..., max_retries=...) // Background task or @schedule(cron="...") // Scheduled task ↓ TaskHandler (executes in background/scheduler) ↓ Celery / Task Queue integration ``` -------------------------------- ### Implement Event Handler Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/README.md Create an event handler using the @EventHandler decorator to react to domain events. This example handles the UserCreatedEvent to send a welcome email. ```python from spakky.event.stereotype.event_handler import EventHandler @Pod() @EventHandler() class SendWelcomeEmailHandler: @on_event(UserCreatedEvent) async def handle_user_created(self, event: UserCreatedEvent) -> None: await self.email_service.send_welcome(event.email) ``` -------------------------------- ### Cache Service Usage with CacheKey Source: https://github.com/e5presso/spakky-framework/blob/main/_autodocs/TYPES-REFERENCE.md Demonstrates using the CacheKey type alias to construct cache keys and interact with a cache service for retrieving user data. ```python @Pod() class CacheService: async def get_user(self, user_id: UUID) -> User | None: key: CacheKey = f"user:{user_id}" cached = await self.cache.get(key) if cached: return User.model_validate(cached) return None ```