This commit is contained in:
2026-02-19 17:43:45 +03:00
parent c2a4c8062a
commit c8029ed309
28 changed files with 3369 additions and 1297 deletions

View File

@@ -0,0 +1,57 @@
# [DEF:Shot:FastAPI_Route:Example]
# @PURPOSE: Reference implementation of a task-based route using GRACE-Poly.
# @RELATION: IMPLEMENTS -> [DEF:Std:API_FastAPI]
from typing import List, Dict, Any, Optional
from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel
from ...core.logger import belief_scope
from ...core.task_manager import TaskManager, Task
from ...core.config_manager import ConfigManager
from ...dependencies import get_task_manager, get_config_manager, has_permission, get_current_user
router = APIRouter()
class CreateTaskRequest(BaseModel):
plugin_id: str
params: Dict[str, Any]
@router.post("/tasks", response_model=Task, status_code=status.HTTP_201_CREATED)
# [DEF:create_task:Function]
# @PURPOSE: Create and start a new task using TaskManager. Non-blocking.
# @PARAM: request (CreateTaskRequest) - Plugin and params.
# @PARAM: task_manager (TaskManager) - Async task executor.
# @PARAM: config (ConfigManager) - Centralized configuration.
# @PRE: plugin_id must exist; config must be initialized.
# @POST: A new task is spawned; Task ID returned immediately.
async def create_task(
request: CreateTaskRequest,
task_manager: TaskManager = Depends(get_task_manager),
config: ConfigManager = Depends(get_config_manager),
current_user = Depends(get_current_user)
):
# RBAC: Dynamic permission check
has_permission(f"plugin:{request.plugin_id}", "EXECUTE")(current_user)
with belief_scope("create_task"):
try:
# 1. Action: Resolve setting using ConfigManager (Example)
timeout = config.get("TASKS_DEFAULT_TIMEOUT", 3600)
# 2. Action: Spawn async task via TaskManager
# @RELATION: CALLS -> task_manager.create_task
task = await task_manager.create_task(
plugin_id=request.plugin_id,
params={**request.params, "timeout": timeout}
)
return task
except Exception as e:
# Evaluation: Proper error mapping and logging
# @UX_STATE: Error feedback to frontend
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Task creation failed: {str(e)}"
)
# [/DEF:create_task:Function]
# [/DEF:Shot:FastAPI_Route]

View File

@@ -0,0 +1,63 @@
<!-- [DEF:Shot:Svelte_Component:Example] -->
# @PURPOSE: Reference implementation of a task-spawning component using Constitution rules.
# @RELATION: IMPLEMENTS -> [DEF:Std:UI_Svelte]
<script>
/**
* @TIER: STANDARD
* @PURPOSE: Action button to spawn a new task.
* @LAYER: UI
* @SEMANTICS: Task, Creation, Button
* @RELATION: CALLS -> postApi
*
* @UX_STATE: Idle -> Button enabled with primary color.
* @UX_STATE: Loading -> Button disabled with spinner while postApi resolves.
* @UX_FEEDBACK: toast.success on completion; toast.error on failure.
* @UX_TEST: Idle -> {click: spawnTask, expected: loading state then success}
*/
import { postApi } from "$lib/api.js";
import { t } from "$lib/i18n";
import { toast } from "$lib/stores/toast";
export let plugin_id = "";
export let params = {};
let isLoading = false;
async def spawnTask() {
isLoading = true;
try {
// 1. Action: Constitution Rule - MUST use postApi wrapper
const response = await postApi("/api/tasks", {
plugin_id,
params
});
// 2. Feedback: UX state management
if (response.task_id) {
toast.success($t.tasks.spawned_success);
}
} catch (error) {
// 3. Recovery: Evaluation & UI reporting
toast.error(`${$t.errors.task_failed}: ${error.message}`);
} finally {
isLoading = false;
}
}
</script>
<button
on:click={spawnTask}
disabled={isLoading}
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center gap-2"
>
{#if isLoading}
<span class="animate-spin text-sm">🌀</span>
{/if}
<span>{$t.actions.start_task}</span>
</button>
<style>
/* Local styles minimized as per Constitution Rule III */
</style>
<!-- [/DEF:Shot:Svelte_Component] -->

View File

@@ -0,0 +1,67 @@
# [DEF:Shot:Plugin_Example:Example]
# @PURPOSE: Reference implementation of a plugin following GRACE standards.
# @RELATION: IMPLEMENTS -> [DEF:Std:Plugin]
from typing import Dict, Any, Optional
from ..core.plugin_base import PluginBase
from ..core.task_manager.context import TaskContext
class ExamplePlugin(PluginBase):
@property
def id(self) -> str:
return "example-plugin"
@property
def name(self) -> str:
return "Example Plugin"
@property
def description(self) -> str:
return "A simple plugin that demonstrates structured logging and progress tracking."
@property
def version(self) -> str:
return "1.0.0"
# [DEF:get_schema:Function]
# @PURPOSE: Defines input validation schema.
def get_schema(self) -> Dict[str, Any]:
return {
"type": "object",
"properties": {
"message": {
"type": "string",
"title": "Message",
"description": "A message to log.",
"default": "Hello, GRACE!",
}
},
"required": ["message"],
}
# [/DEF:get_schema:Function]
# [DEF:execute:Function]
# @PURPOSE: Core plugin logic with structured logging and progress reporting.
# @PARAM: params (Dict) - Validated input parameters.
# @PARAM: context (TaskContext) - Execution context with logging and progress tools.
async def execute(self, params: Dict[str, Any], context: Optional[TaskContext] = None):
message = params["message"]
# 1. Action: Structured Logging with Source Attribution
if context:
log = context.logger.with_source("example_plugin")
log.info(f"Starting execution with message: {message}")
# 2. Action: Progress Reporting
log.progress("Processing step 1...", percent=25)
# Simulating some async work...
# await some_async_op()
log.progress("Processing step 2...", percent=75)
log.info("Execution completed successfully.")
else:
# Fallback for manual/standalone execution
print(f"Standalone execution: {message}")
# [/DEF:execute:Function]
# [/DEF:Shot:Plugin_Example]