Compare commits
3 Commits
migration
...
006-config
| Author | SHA1 | Date | |
|---|---|---|---|
| cb7386f274 | |||
| 83e34e1799 | |||
| d197303b9f |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -61,3 +61,4 @@ keyring passwords.py
|
|||||||
*git*
|
*git*
|
||||||
*tech_spec*
|
*tech_spec*
|
||||||
dashboards
|
dashboards
|
||||||
|
backend/mappings.db
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ cd src; pytest; ruff check .
|
|||||||
Python 3.9+ (Backend), Node.js 18+ (Frontend Build): Follow standard conventions
|
Python 3.9+ (Backend), Node.js 18+ (Frontend Build): Follow standard conventions
|
||||||
|
|
||||||
## Recent Changes
|
## Recent Changes
|
||||||
|
- 006-configurable-belief-logs: Added Python 3.9+ + FastAPI (Backend), Pydantic (Config), Svelte (Frontend)
|
||||||
- 005-fix-ui-ws-validation: Added Python 3.9+ (Backend), Node.js 18+ (Frontend Build)
|
- 005-fix-ui-ws-validation: Added Python 3.9+ (Backend), Node.js 18+ (Frontend Build)
|
||||||
- 005-fix-ui-ws-validation: Added Python 3.9+, Node.js 18+ + FastAPI, SvelteKit, Tailwind CSS, Pydantic
|
- 005-fix-ui-ws-validation: Added Python 3.9+, Node.js 18+ + FastAPI, SvelteKit, Tailwind CSS, Pydantic
|
||||||
- 005-fix-ui-ws-validation: Added Python 3.9+, Node.js 18+ + FastAPI, SvelteKit, Tailwind CSS, Pydantic
|
|
||||||
|
|
||||||
|
|
||||||
<!-- MANUAL ADDITIONS START -->
|
<!-- MANUAL ADDITIONS START -->
|
||||||
|
|||||||
@@ -1,29 +1,68 @@
|
|||||||
# ss-tools Constitution
|
<!--
|
||||||
|
SYNC IMPACT REPORT
|
||||||
|
Version: 1.1.0 (Svelte Support)
|
||||||
|
Changes:
|
||||||
|
- Added Svelte Component semantic markup standards.
|
||||||
|
- Updated File Structure Standards to include `.svelte` files.
|
||||||
|
- Refined File Structure Standards to distinguish between Python Modules and Svelte Components.
|
||||||
|
Templates Status:
|
||||||
|
- .specify/templates/plan-template.md: ⚠ Pending (Needs update to include Component headers in checks).
|
||||||
|
- .specify/templates/spec-template.md: ✅ Aligned.
|
||||||
|
- .specify/templates/tasks-template.md: ⚠ Pending (Needs update to include Component definition tasks).
|
||||||
|
-->
|
||||||
|
# Semantic Code Generation Constitution
|
||||||
|
|
||||||
## Core Principles
|
## Core Principles
|
||||||
|
|
||||||
### I. SPA-First Architecture
|
### I. Causal Validity (Contracts First)
|
||||||
The frontend MUST be a Static Single Page Application (SPA) served by the Python backend. No Node.js server is permitted in production. The backend serves the `index.html` entry point for all non-API routes.
|
Semantic definitions (Contracts) must ALWAYS precede implementation code. Logic is downstream of definition. We define the structure and constraints (`[DEF]`, `@PRE`, `@POST`) before writing the executable logic. This ensures that the "what" and "why" govern the "how".
|
||||||
|
|
||||||
### II. API-Driven Communication
|
### II. Immutability of Architecture
|
||||||
All data retrieval and state changes MUST be performed via the backend REST API or WebSockets. The frontend should not access the database or filesystem directly.
|
Once defined, architectural decisions in the Module Header (`@LAYER`, `@INVARIANT`, `@CONSTRAINT`) are treated as immutable constraints for that module. Changes to these require an explicit refactoring step, not ad-hoc modification during implementation.
|
||||||
|
|
||||||
### III. Modern Stack Consistency
|
### III. Semantic Format Compliance
|
||||||
The project strictly uses SvelteKit (Frontend), FastAPI (Backend), and Tailwind CSS (Styling). New dependencies must be justified and approved.
|
All output must strictly follow the `[DEF]` / `[/DEF]` anchor syntax with specific Metadata Tags (`@KEY`) and Graph Relations (`@RELATION`). This structure is non-negotiable as it ensures the codebase remains machine-readable, fractal-structured, and optimized for Sparse Attention navigation by AI agents.
|
||||||
|
|
||||||
### IV. Semantic Protocol Adherence (GRACE-Poly)
|
### IV. Design by Contract (DbC)
|
||||||
All code generation and modification MUST adhere to the Semantic Protocol defined in `semantic_protocol.md`.
|
Contracts are the Source of Truth. Functions and Classes must define their purpose, specifications, and constraints (`@PRE`, `@POST`, `@THROW`) in the metadata block before implementation. Implementation must strictly satisfy these contracts.
|
||||||
- **Anchors**: Use `[DEF:id:Type]` and `[/DEF:id]` to define semantic boundaries.
|
|
||||||
- **Contracts**: Define `@PRE` and `@POST` conditions in headers.
|
### V. Belief State Logging
|
||||||
- **Logging**: Use structured logging with `[AnchorID][State]` format.
|
Logs must define the agent's internal state for debugging and coherence checks. We use a strict format: `logger.level(f"[{ANCHOR_ID}][{STATE}] {MESSAGE} context={...}")` to track transitions between `Entry`, `Validation`, `Action`, and `Coherence` states.
|
||||||
- **Immutability**: Respect architectural decisions in headers.
|
|
||||||
|
## File Structure Standards
|
||||||
|
|
||||||
|
### Python Modules
|
||||||
|
Every `.py` file must start with a Module definition header (`[DEF:module_name:Module]`) containing:
|
||||||
|
- `@SEMANTICS`: Keywords for vector search.
|
||||||
|
- `@PURPOSE`: Primary responsibility of the module.
|
||||||
|
- `@LAYER`: Architecture layer (Domain/Infra/UI).
|
||||||
|
- `@RELATION`: Dependencies.
|
||||||
|
- `@INVARIANT` & `@CONSTRAINT`: Immutable rules.
|
||||||
|
- `@PUBLIC_API`: Exported symbols.
|
||||||
|
|
||||||
|
### Svelte Components
|
||||||
|
Every `.svelte` file must start with a Component definition header (`[DEF:ComponentName:Component]`) wrapped in an HTML comment `<!-- ... -->` containing:
|
||||||
|
- `@SEMANTICS`: Keywords for vector search.
|
||||||
|
- `@PURPOSE`: Primary responsibility of the component.
|
||||||
|
- `@LAYER`: Architecture layer (UI/State/Layout).
|
||||||
|
- `@RELATION`: Child components, Stores used, API calls.
|
||||||
|
- `@PROPS`: Input properties.
|
||||||
|
- `@EVENTS`: Emitted events.
|
||||||
|
- `@INVARIANT`: Immutable UI/State rules.
|
||||||
|
|
||||||
|
## Generation Workflow
|
||||||
|
The development process follows a strict sequence:
|
||||||
|
1. **Analyze Request**: Identify target module and graph position.
|
||||||
|
2. **Define Structure**: Generate `[DEF]` anchors and Contracts FIRST.
|
||||||
|
3. **Implement Logic**: Write code satisfying Contracts.
|
||||||
|
4. **Validate**: If logic conflicts with Contract -> Stop -> Report Error.
|
||||||
|
|
||||||
## Governance
|
## Governance
|
||||||
|
This Constitution establishes the "Semantic Code Generation Protocol" as the supreme law of this repository.
|
||||||
|
|
||||||
### Compliance
|
- **Automated Enforcement**: All code generation tools and agents must parse and validate adherence to the `[DEF]` syntax and Contract requirements.
|
||||||
All Pull Requests and code modifications must be verified against this Constitution. Violations of Core Principles are considered critical defects.
|
- **Amendments**: Changes to the syntax or core principles require a formal amendment to this Constitution and a corresponding update to the constitution
|
||||||
|
- **Review**: Code reviews must verify that implementation matches the preceding contracts and that no "naked code" exists outside of semantic anchors.
|
||||||
|
- **Compliance**: Failure to adhere to the `[DEF]` / `[/DEF]` structure constitutes a build failure.
|
||||||
|
|
||||||
### Amendments
|
**Version**: 1.1.0 | **Ratified**: 2025-12-19 | **Last Amended**: 2025-12-19
|
||||||
Changes to this Constitution require a formal RFC process and approval from the project lead.
|
|
||||||
|
|
||||||
**Version**: 1.0.0 | **Ratified**: 2025-12-20
|
|
||||||
|
|||||||
Binary file not shown.
@@ -15,6 +15,7 @@ from backend.src.dependencies import get_config_manager
|
|||||||
from backend.src.core.superset_client import SupersetClient
|
from backend.src.core.superset_client import SupersetClient
|
||||||
from superset_tool.models import SupersetConfig
|
from superset_tool.models import SupersetConfig
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
from backend.src.core.logger import logger
|
||||||
# [/SECTION]
|
# [/SECTION]
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/environments", tags=["environments"])
|
router = APIRouter(prefix="/api/environments", tags=["environments"])
|
||||||
@@ -38,7 +39,9 @@ class DatabaseResponse(BaseModel):
|
|||||||
# @RETURN: List[EnvironmentResponse]
|
# @RETURN: List[EnvironmentResponse]
|
||||||
@router.get("", response_model=List[EnvironmentResponse])
|
@router.get("", response_model=List[EnvironmentResponse])
|
||||||
async def get_environments(config_manager=Depends(get_config_manager)):
|
async def get_environments(config_manager=Depends(get_config_manager)):
|
||||||
|
logger.info(f"[get_environments][Debug] Config path: {config_manager.config_path}")
|
||||||
envs = config_manager.get_environments()
|
envs = config_manager.get_environments()
|
||||||
|
logger.info(f"[get_environments][Debug] Found {len(envs)} environments")
|
||||||
return [EnvironmentResponse(id=e.id, name=e.name, url=e.url) for e in envs]
|
return [EnvironmentResponse(id=e.id, name=e.name, url=e.url) for e in envs]
|
||||||
# [/DEF:get_environments]
|
# [/DEF:get_environments]
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ from ...core.config_models import AppConfig, Environment, GlobalSettings
|
|||||||
from ...dependencies import get_config_manager
|
from ...dependencies import get_config_manager
|
||||||
from ...core.config_manager import ConfigManager
|
from ...core.config_manager import ConfigManager
|
||||||
from ...core.logger import logger
|
from ...core.logger import logger
|
||||||
from superset_tool.client import SupersetClient
|
from ...core.superset_client import SupersetClient
|
||||||
from superset_tool.models import SupersetConfig
|
from superset_tool.models import SupersetConfig
|
||||||
import os
|
import os
|
||||||
# [/SECTION]
|
# [/SECTION]
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import os
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
from .config_models import AppConfig, Environment, GlobalSettings
|
from .config_models import AppConfig, Environment, GlobalSettings
|
||||||
from .logger import logger
|
from .logger import logger, configure_logger
|
||||||
# [/SECTION]
|
# [/SECTION]
|
||||||
|
|
||||||
# [DEF:ConfigManager:Class]
|
# [DEF:ConfigManager:Class]
|
||||||
@@ -39,6 +39,9 @@ class ConfigManager:
|
|||||||
self.config_path = Path(config_path)
|
self.config_path = Path(config_path)
|
||||||
self.config: AppConfig = self._load_config()
|
self.config: AppConfig = self._load_config()
|
||||||
|
|
||||||
|
# Configure logger with loaded settings
|
||||||
|
configure_logger(self.config.settings.logging)
|
||||||
|
|
||||||
# 3. Runtime check of @POST
|
# 3. Runtime check of @POST
|
||||||
assert isinstance(self.config, AppConfig), "self.config must be an instance of AppConfig"
|
assert isinstance(self.config, AppConfig), "self.config must be an instance of AppConfig"
|
||||||
|
|
||||||
@@ -121,6 +124,9 @@ class ConfigManager:
|
|||||||
self.config.settings = settings
|
self.config.settings = settings
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
# Reconfigure logger with new settings
|
||||||
|
configure_logger(settings.logging)
|
||||||
|
|
||||||
logger.info(f"[update_global_settings][Exit] Settings updated")
|
logger.info(f"[update_global_settings][Exit] Settings updated")
|
||||||
# [/DEF:update_global_settings]
|
# [/DEF:update_global_settings]
|
||||||
|
|
||||||
|
|||||||
@@ -19,11 +19,22 @@ class Environment(BaseModel):
|
|||||||
is_default: bool = False
|
is_default: bool = False
|
||||||
# [/DEF:Environment]
|
# [/DEF:Environment]
|
||||||
|
|
||||||
|
# [DEF:LoggingConfig:DataClass]
|
||||||
|
# @PURPOSE: Defines the configuration for the application's logging system.
|
||||||
|
class LoggingConfig(BaseModel):
|
||||||
|
level: str = "INFO"
|
||||||
|
file_path: Optional[str] = "logs/app.log"
|
||||||
|
max_bytes: int = 10 * 1024 * 1024
|
||||||
|
backup_count: int = 5
|
||||||
|
enable_belief_state: bool = True
|
||||||
|
# [/DEF:LoggingConfig]
|
||||||
|
|
||||||
# [DEF:GlobalSettings:DataClass]
|
# [DEF:GlobalSettings:DataClass]
|
||||||
# @PURPOSE: Represents global application settings.
|
# @PURPOSE: Represents global application settings.
|
||||||
class GlobalSettings(BaseModel):
|
class GlobalSettings(BaseModel):
|
||||||
backup_path: str
|
backup_path: str
|
||||||
default_environment_id: Optional[str] = None
|
default_environment_id: Optional[str] = None
|
||||||
|
logging: LoggingConfig = Field(default_factory=LoggingConfig)
|
||||||
# [/DEF:GlobalSettings]
|
# [/DEF:GlobalSettings]
|
||||||
|
|
||||||
# [DEF:AppConfig:DataClass]
|
# [DEF:AppConfig:DataClass]
|
||||||
|
|||||||
@@ -4,12 +4,32 @@
|
|||||||
# @LAYER: Core
|
# @LAYER: Core
|
||||||
# @RELATION: Used by the main application and other modules to log events. The WebSocketLogHandler is used by the WebSocket endpoint in app.py.
|
# @RELATION: Used by the main application and other modules to log events. The WebSocketLogHandler is used by the WebSocket endpoint in app.py.
|
||||||
import logging
|
import logging
|
||||||
|
import threading
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict, Any, List, Optional
|
from typing import Dict, Any, List, Optional
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from logging.handlers import RotatingFileHandler
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
# Thread-local storage for belief state
|
||||||
|
_belief_state = threading.local()
|
||||||
|
|
||||||
|
# Global flag for belief state logging
|
||||||
|
_enable_belief_state = True
|
||||||
|
|
||||||
|
# [DEF:BeliefFormatter:Class]
|
||||||
|
# @PURPOSE: Custom logging formatter that adds belief state prefixes to log messages.
|
||||||
|
class BeliefFormatter(logging.Formatter):
|
||||||
|
def format(self, record):
|
||||||
|
msg = super().format(record)
|
||||||
|
anchor_id = getattr(_belief_state, 'anchor_id', None)
|
||||||
|
if anchor_id:
|
||||||
|
msg = f"[{anchor_id}][Action] {msg}"
|
||||||
|
return msg
|
||||||
|
# [/DEF:BeliefFormatter]
|
||||||
|
|
||||||
# Re-using LogEntry from task_manager for consistency
|
# Re-using LogEntry from task_manager for consistency
|
||||||
# [DEF:LogEntry:Class]
|
# [DEF:LogEntry:Class]
|
||||||
# @SEMANTICS: log, entry, record, pydantic
|
# @SEMANTICS: log, entry, record, pydantic
|
||||||
@@ -22,6 +42,81 @@ class LogEntry(BaseModel):
|
|||||||
|
|
||||||
# [/DEF]
|
# [/DEF]
|
||||||
|
|
||||||
|
# [DEF:BeliefScope:Function]
|
||||||
|
# @PURPOSE: Context manager for structured Belief State logging.
|
||||||
|
@contextmanager
|
||||||
|
def belief_scope(anchor_id: str, message: str = ""):
|
||||||
|
# Log Entry if enabled
|
||||||
|
if _enable_belief_state:
|
||||||
|
entry_msg = f"[{anchor_id}][Entry]"
|
||||||
|
if message:
|
||||||
|
entry_msg += f" {message}"
|
||||||
|
logger.info(entry_msg)
|
||||||
|
|
||||||
|
# Set thread-local anchor_id
|
||||||
|
old_anchor = getattr(_belief_state, 'anchor_id', None)
|
||||||
|
_belief_state.anchor_id = anchor_id
|
||||||
|
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
# Log Coherence OK and Exit
|
||||||
|
logger.info(f"[{anchor_id}][Coherence:OK]")
|
||||||
|
if _enable_belief_state:
|
||||||
|
logger.info(f"[{anchor_id}][Exit]")
|
||||||
|
except Exception as e:
|
||||||
|
# Log Coherence Failed
|
||||||
|
logger.info(f"[{anchor_id}][Coherence:Failed] {str(e)}")
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
# Restore old anchor
|
||||||
|
_belief_state.anchor_id = old_anchor
|
||||||
|
|
||||||
|
# [/DEF:BeliefScope]
|
||||||
|
|
||||||
|
# [DEF:ConfigureLogger:Function]
|
||||||
|
# @PURPOSE: Configures the logger with the provided logging settings.
|
||||||
|
# @PRE: config is a valid LoggingConfig instance.
|
||||||
|
# @POST: Logger level, handlers, and belief state flag are updated.
|
||||||
|
# @PARAM: config (LoggingConfig) - The logging configuration.
|
||||||
|
def configure_logger(config):
|
||||||
|
global _enable_belief_state
|
||||||
|
_enable_belief_state = config.enable_belief_state
|
||||||
|
|
||||||
|
# Set logger level
|
||||||
|
level = getattr(logging, config.level.upper(), logging.INFO)
|
||||||
|
logger.setLevel(level)
|
||||||
|
|
||||||
|
# Remove existing file handlers
|
||||||
|
handlers_to_remove = [h for h in logger.handlers if isinstance(h, RotatingFileHandler)]
|
||||||
|
for h in handlers_to_remove:
|
||||||
|
logger.removeHandler(h)
|
||||||
|
h.close()
|
||||||
|
|
||||||
|
# Add file handler if file_path is set
|
||||||
|
if config.file_path:
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
log_file = Path(config.file_path)
|
||||||
|
log_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
file_handler = RotatingFileHandler(
|
||||||
|
config.file_path,
|
||||||
|
maxBytes=config.max_bytes,
|
||||||
|
backupCount=config.backup_count
|
||||||
|
)
|
||||||
|
file_handler.setFormatter(BeliefFormatter(
|
||||||
|
'[%(asctime)s][%(levelname)s][%(name)s] %(message)s'
|
||||||
|
))
|
||||||
|
logger.addHandler(file_handler)
|
||||||
|
|
||||||
|
# Update existing handlers' formatters to BeliefFormatter
|
||||||
|
for handler in logger.handlers:
|
||||||
|
if not isinstance(handler, RotatingFileHandler):
|
||||||
|
handler.setFormatter(BeliefFormatter(
|
||||||
|
'[%(asctime)s][%(levelname)s][%(name)s] %(message)s'
|
||||||
|
))
|
||||||
|
# [/DEF:ConfigureLogger]
|
||||||
|
|
||||||
# [DEF:WebSocketLogHandler:Class]
|
# [DEF:WebSocketLogHandler:Class]
|
||||||
# @SEMANTICS: logging, handler, websocket, buffer
|
# @SEMANTICS: logging, handler, websocket, buffer
|
||||||
# @PURPOSE: A custom logging handler that captures log records into a buffer. It is designed to be extended for real-time log streaming over WebSockets.
|
# @PURPOSE: A custom logging handler that captures log records into a buffer. It is designed to be extended for real-time log streaming over WebSockets.
|
||||||
@@ -72,7 +167,7 @@ logger = logging.getLogger("superset_tools_app")
|
|||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
# Create a formatter
|
# Create a formatter
|
||||||
formatter = logging.Formatter(
|
formatter = BeliefFormatter(
|
||||||
'[%(asctime)s][%(levelname)s][%(name)s] %(message)s'
|
'[%(asctime)s][%(levelname)s][%(name)s] %(message)s'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,18 @@
|
|||||||
from typing import List, Dict, Optional, Tuple
|
from typing import List, Dict, Optional, Tuple
|
||||||
from superset_tool.client import SupersetClient as BaseSupersetClient
|
from superset_tool.client import SupersetClient as BaseSupersetClient
|
||||||
from superset_tool.models import SupersetConfig
|
from superset_tool.models import SupersetConfig
|
||||||
|
from backend.src.core.logger import logger
|
||||||
|
from superset_tool.utils.logger import SupersetLogger
|
||||||
# [/SECTION]
|
# [/SECTION]
|
||||||
|
|
||||||
# [DEF:SupersetClient:Class]
|
# [DEF:SupersetClient:Class]
|
||||||
# @PURPOSE: Extended SupersetClient for migration-specific operations.
|
# @PURPOSE: Extended SupersetClient for migration-specific operations.
|
||||||
class SupersetClient(BaseSupersetClient):
|
class SupersetClient(BaseSupersetClient):
|
||||||
|
def __init__(self, config: SupersetConfig):
|
||||||
|
# Initialize with the application's logger wrapped in SupersetLogger
|
||||||
|
# to ensure BeliefFormatter is used.
|
||||||
|
sl_logger = SupersetLogger(logger=logger)
|
||||||
|
super().__init__(config=config, logger=sl_logger)
|
||||||
|
|
||||||
# [DEF:SupersetClient.get_databases_summary:Function]
|
# [DEF:SupersetClient.get_databases_summary:Function]
|
||||||
# @PURPOSE: Fetch a summary of databases including uuid, name, and engine.
|
# @PURPOSE: Fetch a summary of databases including uuid, name, and engine.
|
||||||
|
|||||||
44
backend/tests/test_logger.py
Normal file
44
backend/tests/test_logger.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import pytest
|
||||||
|
from backend.src.core.logger import belief_scope, logger
|
||||||
|
|
||||||
|
|
||||||
|
def test_belief_scope_logs_entry_action_exit(caplog):
|
||||||
|
"""Test that belief_scope generates [ID][Entry], [ID][Action], and [ID][Exit] logs."""
|
||||||
|
caplog.set_level("INFO")
|
||||||
|
|
||||||
|
with belief_scope("TestFunction"):
|
||||||
|
logger.info("Doing something important")
|
||||||
|
|
||||||
|
# Check that the logs contain the expected patterns
|
||||||
|
log_messages = [record.message for record in caplog.records]
|
||||||
|
|
||||||
|
assert any("[TestFunction][Entry]" in msg for msg in log_messages), "Entry log not found"
|
||||||
|
assert any("[TestFunction][Action] Doing something important" in msg for msg in log_messages), "Action log not found"
|
||||||
|
assert any("[TestFunction][Exit]" in msg for msg in log_messages), "Exit log not found"
|
||||||
|
|
||||||
|
|
||||||
|
def test_belief_scope_error_handling(caplog):
|
||||||
|
"""Test that belief_scope logs Coherence:Failed on exception."""
|
||||||
|
caplog.set_level("INFO")
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
with belief_scope("FailingFunction"):
|
||||||
|
raise ValueError("Something went wrong")
|
||||||
|
|
||||||
|
log_messages = [record.message for record in caplog.records]
|
||||||
|
|
||||||
|
assert any("[FailingFunction][Entry]" in msg for msg in log_messages), "Entry log not found"
|
||||||
|
assert any("[FailingFunction][Coherence:Failed]" in msg for msg in log_messages), "Failed coherence log not found"
|
||||||
|
# Exit should not be logged on failure
|
||||||
|
|
||||||
|
|
||||||
|
def test_belief_scope_success_coherence(caplog):
|
||||||
|
"""Test that belief_scope logs Coherence:OK on success."""
|
||||||
|
caplog.set_level("INFO")
|
||||||
|
|
||||||
|
with belief_scope("SuccessFunction"):
|
||||||
|
pass
|
||||||
|
|
||||||
|
log_messages = [record.message for record in caplog.records]
|
||||||
|
|
||||||
|
assert any("[SuccessFunction][Coherence:OK]" in msg for msg in log_messages), "Success coherence log not found"
|
||||||
96
frontend/.svelte-kit/ambient.d.ts
vendored
96
frontend/.svelte-kit/ambient.d.ts
vendored
@@ -26,57 +26,85 @@
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
declare module '$env/static/private' {
|
declare module '$env/static/private' {
|
||||||
export const LESSOPEN: string;
|
|
||||||
export const USER: string;
|
export const USER: string;
|
||||||
export const npm_config_user_agent: string;
|
export const npm_config_user_agent: string;
|
||||||
|
export const XDG_SESSION_TYPE: string;
|
||||||
export const npm_node_execpath: string;
|
export const npm_node_execpath: string;
|
||||||
export const SHLVL: string;
|
export const SHLVL: string;
|
||||||
export const npm_config_noproxy: string;
|
export const npm_config_noproxy: string;
|
||||||
|
export const LESS: string;
|
||||||
export const HOME: string;
|
export const HOME: string;
|
||||||
export const OLDPWD: string;
|
export const OLDPWD: string;
|
||||||
|
export const DESKTOP_SESSION: string;
|
||||||
export const npm_package_json: string;
|
export const npm_package_json: string;
|
||||||
|
export const LSCOLORS: string;
|
||||||
|
export const ZSH: string;
|
||||||
|
export const GNOME_SHELL_SESSION_MODE: string;
|
||||||
|
export const GTK_MODULES: string;
|
||||||
|
export const PAGER: string;
|
||||||
export const PS1: string;
|
export const PS1: string;
|
||||||
export const npm_config_userconfig: string;
|
export const npm_config_userconfig: string;
|
||||||
export const npm_config_local_prefix: string;
|
export const npm_config_local_prefix: string;
|
||||||
|
export const SYSTEMD_EXEC_PID: string;
|
||||||
export const DBUS_SESSION_BUS_ADDRESS: string;
|
export const DBUS_SESSION_BUS_ADDRESS: string;
|
||||||
export const WSL_DISTRO_NAME: string;
|
export const COLORTERM: string;
|
||||||
export const COLOR: string;
|
export const COLOR: string;
|
||||||
|
export const npm_config_metrics_registry: string;
|
||||||
export const WAYLAND_DISPLAY: string;
|
export const WAYLAND_DISPLAY: string;
|
||||||
export const LOGNAME: string;
|
export const LOGNAME: string;
|
||||||
export const NAME: string;
|
export const SDKMAN_CANDIDATES_API: string;
|
||||||
export const WSL_INTEROP: string;
|
|
||||||
export const PULSE_SERVER: string;
|
|
||||||
export const _: string;
|
export const _: string;
|
||||||
export const npm_config_prefix: string;
|
export const npm_config_prefix: string;
|
||||||
export const npm_config_npm_version: string;
|
export const MEMORY_PRESSURE_WATCH: string;
|
||||||
|
export const XDG_SESSION_CLASS: string;
|
||||||
|
export const USERNAME: string;
|
||||||
export const TERM: string;
|
export const TERM: string;
|
||||||
export const npm_config_cache: string;
|
export const npm_config_cache: string;
|
||||||
|
export const GNOME_DESKTOP_SESSION_ID: string;
|
||||||
export const npm_config_node_gyp: string;
|
export const npm_config_node_gyp: string;
|
||||||
export const PATH: string;
|
export const PATH: string;
|
||||||
|
export const SDKMAN_CANDIDATES_DIR: string;
|
||||||
export const NODE: string;
|
export const NODE: string;
|
||||||
export const npm_package_name: string;
|
export const npm_package_name: string;
|
||||||
|
export const XDG_MENU_PREFIX: string;
|
||||||
|
export const SDKMAN_BROKER_API: string;
|
||||||
|
export const GNOME_TERMINAL_SCREEN: string;
|
||||||
|
export const GNOME_SETUP_DISPLAY: string;
|
||||||
export const XDG_RUNTIME_DIR: string;
|
export const XDG_RUNTIME_DIR: string;
|
||||||
export const DISPLAY: string;
|
export const DISPLAY: string;
|
||||||
export const LANG: string;
|
export const LANG: string;
|
||||||
|
export const XDG_CURRENT_DESKTOP: string;
|
||||||
export const VIRTUAL_ENV_PROMPT: string;
|
export const VIRTUAL_ENV_PROMPT: string;
|
||||||
|
export const XMODIFIERS: string;
|
||||||
|
export const XDG_SESSION_DESKTOP: string;
|
||||||
|
export const XAUTHORITY: string;
|
||||||
export const LS_COLORS: string;
|
export const LS_COLORS: string;
|
||||||
|
export const GNOME_TERMINAL_SERVICE: string;
|
||||||
|
export const SDKMAN_DIR: string;
|
||||||
|
export const SDKMAN_PLATFORM: string;
|
||||||
export const npm_lifecycle_script: string;
|
export const npm_lifecycle_script: string;
|
||||||
|
export const SSH_AUTH_SOCK: string;
|
||||||
export const SHELL: string;
|
export const SHELL: string;
|
||||||
export const npm_package_version: string;
|
export const npm_package_version: string;
|
||||||
export const npm_lifecycle_event: string;
|
export const npm_lifecycle_event: string;
|
||||||
|
export const QT_ACCESSIBILITY: string;
|
||||||
|
export const GDMSESSION: string;
|
||||||
export const GOOGLE_CLOUD_PROJECT: string;
|
export const GOOGLE_CLOUD_PROJECT: string;
|
||||||
export const LESSCLOSE: string;
|
export const GPG_AGENT_INFO: string;
|
||||||
export const VIRTUAL_ENV: string;
|
export const VIRTUAL_ENV: string;
|
||||||
|
export const QT_IM_MODULE: string;
|
||||||
export const npm_config_globalconfig: string;
|
export const npm_config_globalconfig: string;
|
||||||
export const npm_config_init_module: string;
|
export const npm_config_init_module: string;
|
||||||
|
export const JAVA_HOME: string;
|
||||||
export const PWD: string;
|
export const PWD: string;
|
||||||
|
export const npm_config_globalignorefile: string;
|
||||||
export const npm_execpath: string;
|
export const npm_execpath: string;
|
||||||
export const XDG_DATA_DIRS: string;
|
export const XDG_DATA_DIRS: string;
|
||||||
export const npm_config_global_prefix: string;
|
export const npm_config_global_prefix: string;
|
||||||
export const npm_command: string;
|
export const npm_command: string;
|
||||||
export const WSL2_GUI_APPS_ENABLED: string;
|
export const QT_IM_MODULES: string;
|
||||||
export const HOSTTYPE: string;
|
export const MEMORY_PRESSURE_WRITE: string;
|
||||||
export const WSLENV: string;
|
export const VTE_VERSION: string;
|
||||||
export const INIT_CWD: string;
|
export const INIT_CWD: string;
|
||||||
export const EDITOR: string;
|
export const EDITOR: string;
|
||||||
export const NODE_ENV: string;
|
export const NODE_ENV: string;
|
||||||
@@ -109,57 +137,85 @@ declare module '$env/static/public' {
|
|||||||
*/
|
*/
|
||||||
declare module '$env/dynamic/private' {
|
declare module '$env/dynamic/private' {
|
||||||
export const env: {
|
export const env: {
|
||||||
LESSOPEN: string;
|
|
||||||
USER: string;
|
USER: string;
|
||||||
npm_config_user_agent: string;
|
npm_config_user_agent: string;
|
||||||
|
XDG_SESSION_TYPE: string;
|
||||||
npm_node_execpath: string;
|
npm_node_execpath: string;
|
||||||
SHLVL: string;
|
SHLVL: string;
|
||||||
npm_config_noproxy: string;
|
npm_config_noproxy: string;
|
||||||
|
LESS: string;
|
||||||
HOME: string;
|
HOME: string;
|
||||||
OLDPWD: string;
|
OLDPWD: string;
|
||||||
|
DESKTOP_SESSION: string;
|
||||||
npm_package_json: string;
|
npm_package_json: string;
|
||||||
|
LSCOLORS: string;
|
||||||
|
ZSH: string;
|
||||||
|
GNOME_SHELL_SESSION_MODE: string;
|
||||||
|
GTK_MODULES: string;
|
||||||
|
PAGER: string;
|
||||||
PS1: string;
|
PS1: string;
|
||||||
npm_config_userconfig: string;
|
npm_config_userconfig: string;
|
||||||
npm_config_local_prefix: string;
|
npm_config_local_prefix: string;
|
||||||
|
SYSTEMD_EXEC_PID: string;
|
||||||
DBUS_SESSION_BUS_ADDRESS: string;
|
DBUS_SESSION_BUS_ADDRESS: string;
|
||||||
WSL_DISTRO_NAME: string;
|
COLORTERM: string;
|
||||||
COLOR: string;
|
COLOR: string;
|
||||||
|
npm_config_metrics_registry: string;
|
||||||
WAYLAND_DISPLAY: string;
|
WAYLAND_DISPLAY: string;
|
||||||
LOGNAME: string;
|
LOGNAME: string;
|
||||||
NAME: string;
|
SDKMAN_CANDIDATES_API: string;
|
||||||
WSL_INTEROP: string;
|
|
||||||
PULSE_SERVER: string;
|
|
||||||
_: string;
|
_: string;
|
||||||
npm_config_prefix: string;
|
npm_config_prefix: string;
|
||||||
npm_config_npm_version: string;
|
MEMORY_PRESSURE_WATCH: string;
|
||||||
|
XDG_SESSION_CLASS: string;
|
||||||
|
USERNAME: string;
|
||||||
TERM: string;
|
TERM: string;
|
||||||
npm_config_cache: string;
|
npm_config_cache: string;
|
||||||
|
GNOME_DESKTOP_SESSION_ID: string;
|
||||||
npm_config_node_gyp: string;
|
npm_config_node_gyp: string;
|
||||||
PATH: string;
|
PATH: string;
|
||||||
|
SDKMAN_CANDIDATES_DIR: string;
|
||||||
NODE: string;
|
NODE: string;
|
||||||
npm_package_name: string;
|
npm_package_name: string;
|
||||||
|
XDG_MENU_PREFIX: string;
|
||||||
|
SDKMAN_BROKER_API: string;
|
||||||
|
GNOME_TERMINAL_SCREEN: string;
|
||||||
|
GNOME_SETUP_DISPLAY: string;
|
||||||
XDG_RUNTIME_DIR: string;
|
XDG_RUNTIME_DIR: string;
|
||||||
DISPLAY: string;
|
DISPLAY: string;
|
||||||
LANG: string;
|
LANG: string;
|
||||||
|
XDG_CURRENT_DESKTOP: string;
|
||||||
VIRTUAL_ENV_PROMPT: string;
|
VIRTUAL_ENV_PROMPT: string;
|
||||||
|
XMODIFIERS: string;
|
||||||
|
XDG_SESSION_DESKTOP: string;
|
||||||
|
XAUTHORITY: string;
|
||||||
LS_COLORS: string;
|
LS_COLORS: string;
|
||||||
|
GNOME_TERMINAL_SERVICE: string;
|
||||||
|
SDKMAN_DIR: string;
|
||||||
|
SDKMAN_PLATFORM: string;
|
||||||
npm_lifecycle_script: string;
|
npm_lifecycle_script: string;
|
||||||
|
SSH_AUTH_SOCK: string;
|
||||||
SHELL: string;
|
SHELL: string;
|
||||||
npm_package_version: string;
|
npm_package_version: string;
|
||||||
npm_lifecycle_event: string;
|
npm_lifecycle_event: string;
|
||||||
|
QT_ACCESSIBILITY: string;
|
||||||
|
GDMSESSION: string;
|
||||||
GOOGLE_CLOUD_PROJECT: string;
|
GOOGLE_CLOUD_PROJECT: string;
|
||||||
LESSCLOSE: string;
|
GPG_AGENT_INFO: string;
|
||||||
VIRTUAL_ENV: string;
|
VIRTUAL_ENV: string;
|
||||||
|
QT_IM_MODULE: string;
|
||||||
npm_config_globalconfig: string;
|
npm_config_globalconfig: string;
|
||||||
npm_config_init_module: string;
|
npm_config_init_module: string;
|
||||||
|
JAVA_HOME: string;
|
||||||
PWD: string;
|
PWD: string;
|
||||||
|
npm_config_globalignorefile: string;
|
||||||
npm_execpath: string;
|
npm_execpath: string;
|
||||||
XDG_DATA_DIRS: string;
|
XDG_DATA_DIRS: string;
|
||||||
npm_config_global_prefix: string;
|
npm_config_global_prefix: string;
|
||||||
npm_command: string;
|
npm_command: string;
|
||||||
WSL2_GUI_APPS_ENABLED: string;
|
QT_IM_MODULES: string;
|
||||||
HOSTTYPE: string;
|
MEMORY_PRESSURE_WRITE: string;
|
||||||
WSLENV: string;
|
VTE_VERSION: string;
|
||||||
INIT_CWD: string;
|
INIT_CWD: string;
|
||||||
EDITOR: string;
|
EDITOR: string;
|
||||||
NODE_ENV: string;
|
NODE_ENV: string;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export const options = {
|
|||||||
app: ({ head, body, assets, nonce, env }) => "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<link rel=\"icon\" href=\"" + assets + "/favicon.png\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t" + head + "\n\t</head>\n\t<body data-sveltekit-preload-data=\"hover\">\n\t\t<div style=\"display: contents\">" + body + "</div>\n\t</body>\n</html>\n",
|
app: ({ head, body, assets, nonce, env }) => "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<link rel=\"icon\" href=\"" + assets + "/favicon.png\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t" + head + "\n\t</head>\n\t<body data-sveltekit-preload-data=\"hover\">\n\t\t<div style=\"display: contents\">" + body + "</div>\n\t</body>\n</html>\n",
|
||||||
error: ({ status, message }) => "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<title>" + message + "</title>\n\n\t\t<style>\n\t\t\tbody {\n\t\t\t\t--bg: white;\n\t\t\t\t--fg: #222;\n\t\t\t\t--divider: #ccc;\n\t\t\t\tbackground: var(--bg);\n\t\t\t\tcolor: var(--fg);\n\t\t\t\tfont-family:\n\t\t\t\t\tsystem-ui,\n\t\t\t\t\t-apple-system,\n\t\t\t\t\tBlinkMacSystemFont,\n\t\t\t\t\t'Segoe UI',\n\t\t\t\t\tRoboto,\n\t\t\t\t\tOxygen,\n\t\t\t\t\tUbuntu,\n\t\t\t\t\tCantarell,\n\t\t\t\t\t'Open Sans',\n\t\t\t\t\t'Helvetica Neue',\n\t\t\t\t\tsans-serif;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\theight: 100vh;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t.error {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tmax-width: 32rem;\n\t\t\t\tmargin: 0 1rem;\n\t\t\t}\n\n\t\t\t.status {\n\t\t\t\tfont-weight: 200;\n\t\t\t\tfont-size: 3rem;\n\t\t\t\tline-height: 1;\n\t\t\t\tposition: relative;\n\t\t\t\ttop: -0.05rem;\n\t\t\t}\n\n\t\t\t.message {\n\t\t\t\tborder-left: 1px solid var(--divider);\n\t\t\t\tpadding: 0 0 0 1rem;\n\t\t\t\tmargin: 0 0 0 1rem;\n\t\t\t\tmin-height: 2.5rem;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t}\n\n\t\t\t.message h1 {\n\t\t\t\tfont-weight: 400;\n\t\t\t\tfont-size: 1em;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t@media (prefers-color-scheme: dark) {\n\t\t\t\tbody {\n\t\t\t\t\t--bg: #222;\n\t\t\t\t\t--fg: #ddd;\n\t\t\t\t\t--divider: #666;\n\t\t\t\t}\n\t\t\t}\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<div class=\"error\">\n\t\t\t<span class=\"status\">" + status + "</span>\n\t\t\t<div class=\"message\">\n\t\t\t\t<h1>" + message + "</h1>\n\t\t\t</div>\n\t\t</div>\n\t</body>\n</html>\n"
|
error: ({ status, message }) => "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<title>" + message + "</title>\n\n\t\t<style>\n\t\t\tbody {\n\t\t\t\t--bg: white;\n\t\t\t\t--fg: #222;\n\t\t\t\t--divider: #ccc;\n\t\t\t\tbackground: var(--bg);\n\t\t\t\tcolor: var(--fg);\n\t\t\t\tfont-family:\n\t\t\t\t\tsystem-ui,\n\t\t\t\t\t-apple-system,\n\t\t\t\t\tBlinkMacSystemFont,\n\t\t\t\t\t'Segoe UI',\n\t\t\t\t\tRoboto,\n\t\t\t\t\tOxygen,\n\t\t\t\t\tUbuntu,\n\t\t\t\t\tCantarell,\n\t\t\t\t\t'Open Sans',\n\t\t\t\t\t'Helvetica Neue',\n\t\t\t\t\tsans-serif;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\theight: 100vh;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t.error {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tmax-width: 32rem;\n\t\t\t\tmargin: 0 1rem;\n\t\t\t}\n\n\t\t\t.status {\n\t\t\t\tfont-weight: 200;\n\t\t\t\tfont-size: 3rem;\n\t\t\t\tline-height: 1;\n\t\t\t\tposition: relative;\n\t\t\t\ttop: -0.05rem;\n\t\t\t}\n\n\t\t\t.message {\n\t\t\t\tborder-left: 1px solid var(--divider);\n\t\t\t\tpadding: 0 0 0 1rem;\n\t\t\t\tmargin: 0 0 0 1rem;\n\t\t\t\tmin-height: 2.5rem;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t}\n\n\t\t\t.message h1 {\n\t\t\t\tfont-weight: 400;\n\t\t\t\tfont-size: 1em;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t@media (prefers-color-scheme: dark) {\n\t\t\t\tbody {\n\t\t\t\t\t--bg: #222;\n\t\t\t\t\t--fg: #ddd;\n\t\t\t\t\t--divider: #666;\n\t\t\t\t}\n\t\t\t}\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<div class=\"error\">\n\t\t\t<span class=\"status\">" + status + "</span>\n\t\t\t<div class=\"message\">\n\t\t\t\t<h1>" + message + "</h1>\n\t\t\t</div>\n\t\t</div>\n\t</body>\n</html>\n"
|
||||||
},
|
},
|
||||||
version_hash: "1pvaiah"
|
version_hash: "n7gbte"
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function get_hooks() {
|
export async function get_hooks() {
|
||||||
|
|||||||
@@ -16,6 +16,12 @@
|
|||||||
>
|
>
|
||||||
Dashboard
|
Dashboard
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
href="/migration"
|
||||||
|
class="text-gray-600 hover:text-blue-600 font-medium {$page.url.pathname.startsWith('/migration') ? 'text-blue-600 border-b-2 border-blue-600' : ''}"
|
||||||
|
>
|
||||||
|
Migration
|
||||||
|
</a>
|
||||||
<a
|
<a
|
||||||
href="/settings"
|
href="/settings"
|
||||||
class="text-gray-600 hover:text-blue-600 font-medium {$page.url.pathname === '/settings' ? 'text-blue-600 border-b-2 border-blue-600' : ''}"
|
class="text-gray-600 hover:text-blue-600 font-medium {$page.url.pathname === '/settings' ? 'text-blue-600 border-b-2 border-blue-600' : ''}"
|
||||||
|
|||||||
@@ -21,7 +21,14 @@
|
|||||||
environments: [],
|
environments: [],
|
||||||
settings: {
|
settings: {
|
||||||
backup_path: '',
|
backup_path: '',
|
||||||
default_environment_id: null
|
default_environment_id: null,
|
||||||
|
logging: {
|
||||||
|
level: 'INFO',
|
||||||
|
file_path: 'logs/app.log',
|
||||||
|
max_bytes: 10485760,
|
||||||
|
backup_count: 5,
|
||||||
|
enable_belief_state: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -180,10 +187,43 @@
|
|||||||
<label for="backup_path" class="block text-sm font-medium text-gray-700">Backup Storage Path</label>
|
<label for="backup_path" class="block text-sm font-medium text-gray-700">Backup Storage Path</label>
|
||||||
<input type="text" id="backup_path" bind:value={settings.settings.backup_path} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
<input type="text" id="backup_path" bind:value={settings.settings.backup_path} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||||
</div>
|
</div>
|
||||||
<button on:click={handleSaveGlobal} class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 w-max">
|
|
||||||
Save Global Settings
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h3 class="text-lg font-medium mb-4 mt-6">Logging Configuration</h3>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label for="log_level" class="block text-sm font-medium text-gray-700">Log Level</label>
|
||||||
|
<select id="log_level" bind:value={settings.settings.logging.level} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2">
|
||||||
|
<option value="DEBUG">DEBUG</option>
|
||||||
|
<option value="INFO">INFO</option>
|
||||||
|
<option value="WARNING">WARNING</option>
|
||||||
|
<option value="ERROR">ERROR</option>
|
||||||
|
<option value="CRITICAL">CRITICAL</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="log_file_path" class="block text-sm font-medium text-gray-700">Log File Path</label>
|
||||||
|
<input type="text" id="log_file_path" bind:value={settings.settings.logging.file_path} placeholder="logs/app.log" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="log_max_bytes" class="block text-sm font-medium text-gray-700">Max File Size (MB)</label>
|
||||||
|
<input type="number" id="log_max_bytes" bind:value={settings.settings.logging.max_bytes} min="1" step="1" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="log_backup_count" class="block text-sm font-medium text-gray-700">Backup Count</label>
|
||||||
|
<input type="number" id="log_backup_count" bind:value={settings.settings.logging.backup_count} min="1" step="1" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||||
|
</div>
|
||||||
|
<div class="md:col-span-2">
|
||||||
|
<label class="flex items-center">
|
||||||
|
<input type="checkbox" id="enable_belief_state" bind:checked={settings.settings.logging.enable_belief_state} class="h-4 w-4 text-blue-600 border-gray-300 rounded" />
|
||||||
|
<span class="ml-2 block text-sm text-gray-900">Enable Belief State Logging</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button on:click={handleSaveGlobal} class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 w-max mt-4">
|
||||||
|
Save Global Settings
|
||||||
|
</button>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="mb-8 bg-white p-6 rounded shadow">
|
<section class="mb-8 bg-white p-6 rounded shadow">
|
||||||
|
|||||||
@@ -103,16 +103,25 @@
|
|||||||
<p class="mt-1 text-sm text-gray-500">Regular expression to filter dashboards to migrate.</p>
|
<p class="mt-1 text-sm text-gray-500">Regular expression to filter dashboards to migrate.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center mb-8">
|
<div class="flex items-center justify-between mb-8">
|
||||||
<input
|
<div class="flex items-center">
|
||||||
id="replace-db"
|
<input
|
||||||
type="checkbox"
|
id="replace-db"
|
||||||
bind:checked={replaceDb}
|
type="checkbox"
|
||||||
class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded"
|
bind:checked={replaceDb}
|
||||||
/>
|
class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded"
|
||||||
<label for="replace-db" class="ml-2 block text-sm text-gray-900">
|
/>
|
||||||
Replace Database (Apply Mappings)
|
<label for="replace-db" class="ml-2 block text-sm text-gray-900">
|
||||||
</label>
|
Replace Database (Apply Mappings)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="/migration/mappings"
|
||||||
|
class="text-sm font-medium text-indigo-600 hover:text-indigo-500"
|
||||||
|
>
|
||||||
|
Manage Mappings →
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Specification Quality Checklist: Configurable Belief State Logging
|
||||||
|
|
||||||
|
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||||
|
**Created**: 2025-12-26
|
||||||
|
**Feature**: [specs/006-configurable-belief-logs/spec.md](../spec.md)
|
||||||
|
|
||||||
|
## Content Quality
|
||||||
|
|
||||||
|
- [x] No implementation details (languages, frameworks, APIs)
|
||||||
|
- [x] Focused on user value and business needs
|
||||||
|
- [x] Written for non-technical stakeholders
|
||||||
|
- [x] All mandatory sections completed
|
||||||
|
|
||||||
|
## Requirement Completeness
|
||||||
|
|
||||||
|
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||||
|
- [x] Requirements are testable and unambiguous
|
||||||
|
- [x] Success criteria are measurable
|
||||||
|
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||||
|
- [x] All acceptance scenarios are defined
|
||||||
|
- [x] Edge cases are identified
|
||||||
|
- [x] Scope is clearly bounded
|
||||||
|
- [x] Dependencies and assumptions identified
|
||||||
|
|
||||||
|
## Feature Readiness
|
||||||
|
|
||||||
|
- [x] All functional requirements have clear acceptance criteria
|
||||||
|
- [x] User scenarios cover primary flows
|
||||||
|
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||||
|
- [x] No implementation details leak into specification
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Items marked incomplete require spec updates before `/speckit.clarify` or `/speckit.plan`
|
||||||
56
specs/006-configurable-belief-logs/contracts/api.md
Normal file
56
specs/006-configurable-belief-logs/contracts/api.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# API Contract: Settings Update
|
||||||
|
|
||||||
|
## PATCH /api/settings/global
|
||||||
|
|
||||||
|
Updates the global application settings, including the new logging configuration.
|
||||||
|
|
||||||
|
### Request Body
|
||||||
|
|
||||||
|
**Content-Type**: `application/json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"backup_path": "string",
|
||||||
|
"default_environment_id": "string (optional)",
|
||||||
|
"logging": {
|
||||||
|
"level": "string (DEBUG, INFO, WARNING, ERROR, CRITICAL)",
|
||||||
|
"file_path": "string (optional)",
|
||||||
|
"max_bytes": "integer (default: 10485760)",
|
||||||
|
"backup_count": "integer (default: 5)",
|
||||||
|
"enable_belief_state": "boolean (default: true)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Response
|
||||||
|
|
||||||
|
**Status**: `200 OK`
|
||||||
|
**Content-Type**: `application/json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"backup_path": "string",
|
||||||
|
"default_environment_id": "string (optional)",
|
||||||
|
"logging": {
|
||||||
|
"level": "string",
|
||||||
|
"file_path": "string",
|
||||||
|
"max_bytes": "integer",
|
||||||
|
"backup_count": "integer",
|
||||||
|
"enable_belief_state": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
**Request**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"backup_path": "backups",
|
||||||
|
"logging": {
|
||||||
|
"level": "DEBUG",
|
||||||
|
"file_path": "logs/app.log",
|
||||||
|
"enable_belief_state": true
|
||||||
|
}
|
||||||
|
}
|
||||||
74
specs/006-configurable-belief-logs/data-model.md
Normal file
74
specs/006-configurable-belief-logs/data-model.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# Data Model: Configurable Belief State Logging
|
||||||
|
|
||||||
|
## 1. Configuration Models
|
||||||
|
|
||||||
|
These models extend the existing `ConfigModels` in `backend/src/core/config_models.py`.
|
||||||
|
|
||||||
|
### 1.1. LoggingConfig
|
||||||
|
|
||||||
|
Defines the configuration for the application's logging system.
|
||||||
|
|
||||||
|
| Field | Type | Default | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `level` | `str` | `"INFO"` | The logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL). |
|
||||||
|
| `file_path` | `Optional[str]` | `"logs/app.log"` | Path to the log file. If None, file logging is disabled. |
|
||||||
|
| `max_bytes` | `int` | `10485760` (10MB) | Maximum size of a log file before rotation. |
|
||||||
|
| `backup_count` | `int` | `5` | Number of backup files to keep. |
|
||||||
|
| `enable_belief_state` | `bool` | `True` | Whether to enable structured Belief State logging (Entry/Exit). |
|
||||||
|
|
||||||
|
```python
|
||||||
|
class LoggingConfig(BaseModel):
|
||||||
|
level: str = "INFO"
|
||||||
|
file_path: Optional[str] = "logs/app.log"
|
||||||
|
max_bytes: int = 10 * 1024 * 1024
|
||||||
|
backup_count: int = 5
|
||||||
|
enable_belief_state: bool = True
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2. GlobalSettings (Updated)
|
||||||
|
|
||||||
|
Updates the existing `GlobalSettings` to include `LoggingConfig`.
|
||||||
|
|
||||||
|
| Field | Type | Default | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `logging` | `LoggingConfig` | `LoggingConfig()` | The logging configuration object. |
|
||||||
|
|
||||||
|
```python
|
||||||
|
class GlobalSettings(BaseModel):
|
||||||
|
backup_path: str
|
||||||
|
default_environment_id: Optional[str] = None
|
||||||
|
logging: LoggingConfig = Field(default_factory=LoggingConfig)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Logger Entities
|
||||||
|
|
||||||
|
These entities are part of the `backend/src/core/logger.py` module.
|
||||||
|
|
||||||
|
### 2.1. LogEntry (Existing)
|
||||||
|
|
||||||
|
Represents a single log record.
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `timestamp` | `datetime` | UTC timestamp of the log. |
|
||||||
|
| `level` | `str` | Log level. |
|
||||||
|
| `message` | `str` | Log message. |
|
||||||
|
| `context` | `Optional[Dict[str, Any]]` | Additional context data. |
|
||||||
|
|
||||||
|
### 2.2. BeliefState (Concept)
|
||||||
|
|
||||||
|
Represents the state of execution in the "Belief State" model.
|
||||||
|
|
||||||
|
- **Entry**: Entering a logical block.
|
||||||
|
- **Action**: Performing a core action within the block.
|
||||||
|
- **Coherence**: Verifying the state (OK or Failed).
|
||||||
|
- **Exit**: Exiting the logical block.
|
||||||
|
|
||||||
|
Format: `[{ANCHOR_ID}][{STATE}] {Message}`
|
||||||
|
|
||||||
|
## 3. Relationships
|
||||||
|
|
||||||
|
- `AppConfig` contains `GlobalSettings`.
|
||||||
|
- `GlobalSettings` contains `LoggingConfig`.
|
||||||
|
- `ConfigManager` reads/writes `AppConfig`.
|
||||||
|
- `ConfigManager` configures the global `logger` based on `LoggingConfig`.
|
||||||
81
specs/006-configurable-belief-logs/plan.md
Normal file
81
specs/006-configurable-belief-logs/plan.md
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# Implementation Plan: Configurable Belief State Logging
|
||||||
|
|
||||||
|
**Branch**: `006-configurable-belief-logs` | **Date**: 2025-12-26 | **Spec**: specs/006-configurable-belief-logs/spec.md
|
||||||
|
**Input**: Feature specification from `/specs/006-configurable-belief-logs/spec.md`
|
||||||
|
|
||||||
|
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/templates/commands/plan.md` for the execution workflow.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Implement a configurable logging system with "Belief State" support (Entry, Action, Coherence, Exit).
|
||||||
|
Approach: Use Python's `logging` module with a custom Context Manager (`belief_scope`) and extend `GlobalSettings` with a `LoggingConfig` model.
|
||||||
|
|
||||||
|
## Technical Context
|
||||||
|
|
||||||
|
**Language/Version**: Python 3.9+
|
||||||
|
**Primary Dependencies**: FastAPI (Backend), Pydantic (Config), Svelte (Frontend)
|
||||||
|
**Storage**: Filesystem (for log files), JSON (for configuration persistence)
|
||||||
|
**Testing**: pytest (Backend), manual verification (Frontend)
|
||||||
|
**Target Platform**: Linux server (primary), cross-platform compatible
|
||||||
|
**Project Type**: Web application (Backend + Frontend)
|
||||||
|
**Performance Goals**: Low overhead logging (<1ms per log entry), non-blocking for main thread (mostly)
|
||||||
|
**Constraints**: Must use standard library `logging` where possible; Log rotation to prevent disk overflow
|
||||||
|
**Scale/Scope**: Configurable log levels and retention policies
|
||||||
|
|
||||||
|
## Constitution Check
|
||||||
|
|
||||||
|
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||||
|
|
||||||
|
- **Library-First**: N/A (Core infrastructure)
|
||||||
|
- **CLI Interface**: N/A (Configured via UI/API/JSON)
|
||||||
|
- **Test-First**: Will require unit tests for `belief_scope` and config updates.
|
||||||
|
- **Integration Testing**: Will require testing the settings API.
|
||||||
|
- **Observability**: This feature *is* the observability enhancement.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
### Documentation (this feature)
|
||||||
|
|
||||||
|
```text
|
||||||
|
specs/[###-feature]/
|
||||||
|
├── plan.md # This file (/speckit.plan command output)
|
||||||
|
├── research.md # Phase 0 output (/speckit.plan command)
|
||||||
|
├── data-model.md # Phase 1 output (/speckit.plan command)
|
||||||
|
├── quickstart.md # Phase 1 output (/speckit.plan command)
|
||||||
|
├── contracts/ # Phase 1 output (/speckit.plan command)
|
||||||
|
└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Source Code (repository root)
|
||||||
|
|
||||||
|
```text
|
||||||
|
backend/
|
||||||
|
├── src/
|
||||||
|
│ ├── core/
|
||||||
|
│ │ ├── config_models.py # Add LoggingConfig
|
||||||
|
│ │ ├── config_manager.py # Update config loading/saving
|
||||||
|
│ │ └── logger.py # Add belief_scope and configure_logger
|
||||||
|
│ └── api/
|
||||||
|
│ └── routes/
|
||||||
|
│ └── settings.py # Update GlobalSettings endpoint
|
||||||
|
└── tests/
|
||||||
|
└── test_logger.py # New tests for logger logic
|
||||||
|
|
||||||
|
frontend/
|
||||||
|
├── src/
|
||||||
|
│ ├── pages/
|
||||||
|
│ │ └── Settings.svelte # Add logging UI controls
|
||||||
|
│ └── lib/
|
||||||
|
│ └── api.js # Update API calls if needed
|
||||||
|
```
|
||||||
|
|
||||||
|
**Structure Decision**: enhancing existing Backend/Frontend structure.
|
||||||
|
|
||||||
|
## Complexity Tracking
|
||||||
|
|
||||||
|
> **Fill ONLY if Constitution Check has violations that must be justified**
|
||||||
|
|
||||||
|
| Violation | Why Needed | Simpler Alternative Rejected Because |
|
||||||
|
|-----------|------------|-------------------------------------|
|
||||||
|
| [e.g., 4th project] | [current need] | [why 3 projects insufficient] |
|
||||||
|
| [e.g., Repository pattern] | [specific problem] | [why direct DB access insufficient] |
|
||||||
104
specs/006-configurable-belief-logs/quickstart.md
Normal file
104
specs/006-configurable-belief-logs/quickstart.md
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
# Quickstart: Configurable Belief State Logging
|
||||||
|
|
||||||
|
## 1. Configuration
|
||||||
|
|
||||||
|
The logging system is configured via the `GlobalSettings` in `config.json` or through the Settings UI.
|
||||||
|
|
||||||
|
### 1.1. Default Configuration
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"environments": [],
|
||||||
|
"settings": {
|
||||||
|
"backup_path": "backups",
|
||||||
|
"logging": {
|
||||||
|
"level": "INFO",
|
||||||
|
"file_path": "logs/app.log",
|
||||||
|
"max_bytes": 10485760,
|
||||||
|
"backup_count": 5,
|
||||||
|
"enable_belief_state": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2. Changing Log Level
|
||||||
|
|
||||||
|
To change the log level to `DEBUG`, update the `logging.level` field in `config.json` or use the API:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X PATCH http://localhost:8000/api/settings/global \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"backup_path": "backups",
|
||||||
|
"logging": {
|
||||||
|
"level": "DEBUG",
|
||||||
|
"file_path": "logs/app.log",
|
||||||
|
"max_bytes": 10485760,
|
||||||
|
"backup_count": 5,
|
||||||
|
"enable_belief_state": true
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Using Belief State Logging
|
||||||
|
|
||||||
|
### 2.1. Basic Usage
|
||||||
|
|
||||||
|
Use the `belief_scope` context manager to automatically log Entry, Exit, and Coherence states.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from backend.src.core.logger import logger, belief_scope
|
||||||
|
|
||||||
|
def my_function():
|
||||||
|
with belief_scope("MyFunction"):
|
||||||
|
# Logs: [MyFunction][Entry]
|
||||||
|
|
||||||
|
logger.info("Doing something important")
|
||||||
|
# Logs: [MyFunction][Action] Doing something important
|
||||||
|
|
||||||
|
# ... logic ...
|
||||||
|
|
||||||
|
# Logs: [MyFunction][Coherence:OK]
|
||||||
|
# Logs: [MyFunction][Exit]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2. Error Handling
|
||||||
|
|
||||||
|
If an exception occurs within the scope, it is caught, logged as a failure, and re-raised.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def failing_function():
|
||||||
|
with belief_scope("FailingFunc"):
|
||||||
|
raise ValueError("Something went wrong")
|
||||||
|
|
||||||
|
# Logs: [FailingFunc][Entry]
|
||||||
|
# Logs: [FailingFunc][Coherence:Failed] Something went wrong
|
||||||
|
# Re-raises ValueError
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3. Custom Messages
|
||||||
|
|
||||||
|
You can provide an optional message to `belief_scope`.
|
||||||
|
|
||||||
|
```python
|
||||||
|
with belief_scope("DataProcessor", "Processing batch #1"):
|
||||||
|
# Logs: [DataProcessor][Entry] Processing batch #1
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Log Output Format
|
||||||
|
|
||||||
|
### 3.1. Standard Log
|
||||||
|
|
||||||
|
```text
|
||||||
|
[2025-12-26 10:00:00,000][INFO][superset_tools_app] System initialized
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2. Belief State Log
|
||||||
|
|
||||||
|
```text
|
||||||
|
[2025-12-26 10:00:01,000][INFO][superset_tools_app] [MyFunction][Entry]
|
||||||
|
[2025-12-26 10:00:01,050][INFO][superset_tools_app] [MyFunction][Action] Processing data
|
||||||
|
[2025-12-26 10:00:01,100][INFO][superset_tools_app] [MyFunction][Coherence:OK]
|
||||||
|
[2025-12-26 10:00:01,100][INFO][superset_tools_app] [MyFunction][Exit]
|
||||||
109
specs/006-configurable-belief-logs/research.md
Normal file
109
specs/006-configurable-belief-logs/research.md
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
# Research: Configurable Belief State Logging
|
||||||
|
|
||||||
|
## 1. Introduction
|
||||||
|
|
||||||
|
This research explores the implementation of a configurable logging system that supports "Belief State" logging (Entry, Action, Coherence, Exit) and allows users to customize log levels, formats, and file persistence.
|
||||||
|
|
||||||
|
## 2. Analysis of Existing System
|
||||||
|
|
||||||
|
- **Language**: Python 3.9+ (Backend)
|
||||||
|
- **Framework**: FastAPI (inferred from context, though not explicitly seen in snippets, `uvicorn` mentioned)
|
||||||
|
- **Logging**: Standard Python `logging` module.
|
||||||
|
- **Configuration**: Pydantic models (`ConfigModels`) managed by `ConfigManager`, persisting to `config.json`.
|
||||||
|
- **Current Logger**:
|
||||||
|
- `backend/src/core/logger.py`:
|
||||||
|
- Uses `logging.getLogger("superset_tools_app")`
|
||||||
|
- Has `StreamHandler` (console) and `WebSocketLogHandler` (streaming).
|
||||||
|
- `WebSocketLogHandler` buffers logs in a `deque`.
|
||||||
|
|
||||||
|
## 3. Requirements Analysis
|
||||||
|
|
||||||
|
- **Belief State**: Need a structured way to log `[ANCHOR_ID][STATE] Message`.
|
||||||
|
- **Context Manager**: Need a `with belief_scope("ID"):` pattern.
|
||||||
|
- **Configuration**: Need to add `LoggingConfig` to `GlobalSettings`.
|
||||||
|
- **File Logging**: Need `RotatingFileHandler` with size limits.
|
||||||
|
- **Dynamic Reconfiguration**: Need to update logger handlers/levels when config changes.
|
||||||
|
|
||||||
|
## 4. Proposed Solution
|
||||||
|
|
||||||
|
### 4.1. Data Model (`LoggingConfig`)
|
||||||
|
|
||||||
|
We will add a `LoggingConfig` model to `backend/src/core/config_models.py`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
class LoggingConfig(BaseModel):
|
||||||
|
level: str = "INFO" # DEBUG, INFO, WARNING, ERROR, CRITICAL
|
||||||
|
file_path: Optional[str] = "logs/app.log"
|
||||||
|
max_bytes: int = 10 * 1024 * 1024 # 10MB
|
||||||
|
backup_count: int = 5
|
||||||
|
enable_belief_state: bool = True
|
||||||
|
```
|
||||||
|
|
||||||
|
And update `GlobalSettings`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
class GlobalSettings(BaseModel):
|
||||||
|
backup_path: str
|
||||||
|
default_environment_id: Optional[str] = None
|
||||||
|
logging: LoggingConfig = Field(default_factory=LoggingConfig)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2. Context Manager (`belief_scope`)
|
||||||
|
|
||||||
|
We will implement a context manager in `backend/src/core/logger.py`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def belief_scope(anchor_id: str, message: str = ""):
|
||||||
|
# ... logic to log [Entry] ...
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
# ... logic to log [Coherence:OK] and [Exit] ...
|
||||||
|
except Exception as e:
|
||||||
|
# ... logic to log [Coherence:Failed] ...
|
||||||
|
raise
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3. Logger Reconfiguration
|
||||||
|
|
||||||
|
We will add a `configure_logger(config: LoggingConfig)` function in `backend/src/core/logger.py` that:
|
||||||
|
1. Sets the logger level.
|
||||||
|
2. Removes old file handlers.
|
||||||
|
3. Adds a new `RotatingFileHandler` if `file_path` is set.
|
||||||
|
4. Updates a global flag for `enable_belief_state`.
|
||||||
|
|
||||||
|
`ConfigManager` will call this function whenever settings are updated.
|
||||||
|
|
||||||
|
### 4.4. Belief State Filtering
|
||||||
|
|
||||||
|
If `enable_belief_state` is False:
|
||||||
|
- `Entry`/`Exit` logs are skipped.
|
||||||
|
- `Action`/`Coherence` logs are logged as standard messages (maybe without the `[ANCHOR_ID]` prefix if desired, but retaining it is usually better for consistency).
|
||||||
|
|
||||||
|
## 5. Alternatives Considered
|
||||||
|
|
||||||
|
- **Alternative A**: Use a separate logger for Belief State.
|
||||||
|
- *Pros*: Cleaner separation.
|
||||||
|
- *Cons*: Harder to interleave with standard logs in the same stream/file.
|
||||||
|
- *Decision*: Rejected. We want a unified log stream.
|
||||||
|
|
||||||
|
- **Alternative B**: Use structlog.
|
||||||
|
- *Pros*: Powerful structured logging.
|
||||||
|
- *Cons*: Adds a new dependency.
|
||||||
|
- *Decision*: Rejected. Standard `logging` is sufficient and already used.
|
||||||
|
|
||||||
|
## 6. Implementation Steps
|
||||||
|
|
||||||
|
1. **Modify `backend/src/core/config_models.py`**: Add `LoggingConfig` and update `GlobalSettings`.
|
||||||
|
2. **Modify `backend/src/core/logger.py`**:
|
||||||
|
- Add `configure_logger` function.
|
||||||
|
- Implement `belief_scope` context manager.
|
||||||
|
- Implement `RotatingFileHandler`.
|
||||||
|
3. **Modify `backend/src/core/config_manager.py`**: Call `configure_logger` on init and update.
|
||||||
|
4. **Frontend**: Update Settings page to allow editing `LoggingConfig`.
|
||||||
|
|
||||||
|
## 7. Conclusion
|
||||||
|
|
||||||
|
The proposed solution leverages the existing Pydantic/ConfigManager architecture and standard Python logging, minimizing disruption while meeting all requirements.
|
||||||
88
specs/006-configurable-belief-logs/spec.md
Normal file
88
specs/006-configurable-belief-logs/spec.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# Feature Specification: Configurable Belief State Logging
|
||||||
|
|
||||||
|
**Feature Branch**: `006-configurable-belief-logs`
|
||||||
|
**Created**: 2025-12-26
|
||||||
|
**Status**: Draft
|
||||||
|
**Input**: User description: "Меня не устраивает текущее состояния логов проекта, которые я получаю после запуска run.sh. Нужно продумать систему хранения логов, которая будет настраиваемой, включать логирование belief state агента разработки (смотри semantic_protocol.md) и гибко настраиваемой."
|
||||||
|
|
||||||
|
## Clarifications
|
||||||
|
|
||||||
|
### Session 2025-12-26
|
||||||
|
- Q: Log rotation policy? → A: Size-based rotation (e.g., 10MB limit, 5 backups).
|
||||||
|
- Q: Belief State disabled behavior? → A: Smart Filter (Suppress Entry/Exit; keep Action/Coherence as standard logs).
|
||||||
|
- Q: Implementation pattern? → A: Context Manager (e.g., `with belief_scope("ID"):`) to auto-handle Entry/Exit/Error.
|
||||||
|
|
||||||
|
## User Scenarios & Testing
|
||||||
|
|
||||||
|
### User Story 1 - Structured Belief State Logging (Priority: P1)
|
||||||
|
|
||||||
|
As a developer or AI agent, I want logs to clearly indicate the execution flow and internal state ("Belief State") of the system using a standardized format, so that I can easily debug logic errors and verify semantic coherence.
|
||||||
|
|
||||||
|
**Why this priority**: This is the core requirement to align with the `semantic_protocol.md` and improve debugging capabilities for the AI agent.
|
||||||
|
|
||||||
|
**Independent Test**: Trigger an action in the system (e.g., running a migration) and verify that the logs contain entries formatted as `[ANCHOR_ID][STATE] Message`, representing the entry, action, and exit states of the code blocks.
|
||||||
|
|
||||||
|
**Acceptance Scenarios**:
|
||||||
|
|
||||||
|
1. **Given** a function wrapped with a Belief State logger, **When** the function is called, **Then** a log entry `[ANCHOR_ID][Entry] ...` is generated.
|
||||||
|
2. **Given** a function execution, **When** the core logic is executed, **Then** a log entry `[ANCHOR_ID][Action] ...` is generated.
|
||||||
|
3. **Given** a function execution completes successfully, **When** it returns, **Then** a log entry `[ANCHOR_ID][Coherence:OK]` and `[ANCHOR_ID][Exit]` are generated.
|
||||||
|
4. **Given** a function execution fails, **When** an exception is raised, **Then** a log entry `[ANCHOR_ID][Coherence:Failed]` is generated.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### User Story 2 - Configurable Logging Settings (Priority: P2)
|
||||||
|
|
||||||
|
As a user, I want to be able to configure log levels, formats, and destinations via the application settings, so that I can control the verbosity and storage of logs without changing code.
|
||||||
|
|
||||||
|
**Why this priority**: Provides the "flexible" and "customizable" aspect of the requirement, allowing users to adapt logging to their needs (dev vs prod).
|
||||||
|
|
||||||
|
**Independent Test**: Change the log level in the settings (e.g., from INFO to DEBUG) and verify that debug messages start appearing in the output.
|
||||||
|
|
||||||
|
**Acceptance Scenarios**:
|
||||||
|
|
||||||
|
1. **Given** the application is running, **When** I update the log level to "DEBUG" in the settings, **Then** debug-level logs are captured and displayed.
|
||||||
|
2. **Given** the application is running, **When** I enable "File Logging" in settings, **Then** logs are written to the specified file path.
|
||||||
|
3. **Given** the application is running, **When** I disable "Belief State" logs in settings, **Then** "Entry" and "Exit" logs are suppressed, while "Action" and "Coherence" logs are retained as standard log entries.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Edge Cases
|
||||||
|
|
||||||
|
- **Invalid Configuration**: What happens if the user provides an invalid log level (e.g., "SUPER_LOUD")? The system should fallback to a safe default (e.g., "INFO") and log a warning.
|
||||||
|
- **File System Errors**: What happens if the configured log file path is not writable? The system should fallback to console logging and alert the user.
|
||||||
|
- **High Volume**: What happens if "Belief State" logging generates too much noise? The system should remain performant, and users should be able to toggle it off easily.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
### Functional Requirements
|
||||||
|
|
||||||
|
- **FR-001**: The system MUST support a `LoggingConfig` structure within the global configuration to store settings for level, format, file path, and belief state enablement.
|
||||||
|
- **FR-002**: The logging system MUST implement a standard format for "Belief State" logs as defined in the system protocols: `[{ANCHOR_ID}][{STATE}] {Message}`.
|
||||||
|
- **FR-003**: The system MUST provide a standard mechanism to easily generate Belief State logs without repetitive boilerplate code, specifically using a **Context Manager** pattern.
|
||||||
|
- **FR-004**: The supported Belief States MUST include: `Entry`, `Action`, `Coherence:OK`, `Coherence:Failed`, `Exit`.
|
||||||
|
- **FR-005**: The system MUST allow dynamic reconfiguration of the logger (e.g., changing levels) when settings are updated.
|
||||||
|
- **FR-006**: The real-time log stream MUST preserve the structured format of Belief State logs so the frontend can potentially render them specially.
|
||||||
|
- **FR-007**: Logs MUST optionally be persisted to a file if configured.
|
||||||
|
- **FR-008**: The file logging system MUST implement size-based rotation (default: 10MB limit, 5 backups) to prevent disk saturation.
|
||||||
|
- **FR-009**: When `enable_belief_state` is False, the logger MUST suppress `Entry` and `Exit` states but retain `Action` and `Coherence` states (stripping the structured prefix if necessary or keeping it as standard info).
|
||||||
|
- **FR-010**: The Context Manager MUST automatically log `[Entry]` on start, `[Exit]` on success, and `[Coherence:Failed]` if an exception occurs (re-raising the exception).
|
||||||
|
|
||||||
|
### Key Entities
|
||||||
|
|
||||||
|
- **LoggingConfig**: A configuration object defining `level` (text), `file_path` (text), `format` (structure), `enable_belief_state` (boolean).
|
||||||
|
- **BeliefStateAdapter**: A component that enforces the semantic protocol format for log entries.
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
### Measurable Outcomes
|
||||||
|
|
||||||
|
- **SC-001**: Developers can trace the execution flow of a specific task using only the `[ANCHOR_ID]` filtered logs.
|
||||||
|
- **SC-002**: Changing the log level in the configuration file or API immediately (or upon restart) reflects in the log output.
|
||||||
|
- **SC-003**: All "Belief State" logs strictly follow the `[ID][State]` format, allowing for regex parsing.
|
||||||
|
- **SC-004**: System supports logging to both console and file simultaneously if configured.
|
||||||
|
|
||||||
|
## Assumptions
|
||||||
|
|
||||||
|
- The existing real-time streaming mechanism can be adapted or chained with the new logging configuration.
|
||||||
|
- The system protocol definition of states is the source of truth.
|
||||||
61
specs/006-configurable-belief-logs/tasks.md
Normal file
61
specs/006-configurable-belief-logs/tasks.md
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# Tasks: Configurable Belief State Logging
|
||||||
|
|
||||||
|
**Spec**: `specs/006-configurable-belief-logs/spec.md`
|
||||||
|
**Plan**: `specs/006-configurable-belief-logs/plan.md`
|
||||||
|
**Status**: Completed
|
||||||
|
|
||||||
|
## Phase 1: Setup
|
||||||
|
*Goal: Initialize project structure for logging.*
|
||||||
|
|
||||||
|
- [x] T001 Ensure logs directory exists at `logs/` (relative to project root)
|
||||||
|
|
||||||
|
## Phase 2: Foundational
|
||||||
|
*Goal: Define data models and infrastructure required for logging configuration.*
|
||||||
|
|
||||||
|
- [x] T002 Define `LoggingConfig` model in `backend/src/core/config_models.py`
|
||||||
|
- [x] T003 Update `GlobalSettings` model to include `logging` field in `backend/src/core/config_models.py`
|
||||||
|
- [x] T004 Update `ConfigManager` to handle logging configuration persistence in `backend/src/core/config_manager.py`
|
||||||
|
|
||||||
|
## Phase 3: User Story 1 - Structured Belief State Logging
|
||||||
|
*Goal: Implement the core "Belief State" logging logic with context managers.*
|
||||||
|
*Priority: P1*
|
||||||
|
|
||||||
|
**Independent Test**: Run `pytest backend/tests/test_logger.py` and verify `belief_scope` generates `[ID][Entry]`, `[ID][Action]`, and `[ID][Exit]` logs.
|
||||||
|
|
||||||
|
- [x] T005 [US1] Create unit tests for belief state logging in `backend/tests/test_logger.py`
|
||||||
|
- [x] T006 [US1] Implement `belief_scope` context manager in `backend/src/core/logger.py`
|
||||||
|
- [x] T007 [US1] Implement log formatting and smart filtering (suppress Entry/Exit if disabled) in `backend/src/core/logger.py`
|
||||||
|
|
||||||
|
## Phase 4: User Story 2 - Configurable Logging Settings
|
||||||
|
*Goal: Expose logging configuration to the user via API and UI.*
|
||||||
|
*Priority: P2*
|
||||||
|
|
||||||
|
**Independent Test**: Update settings via API/UI and verify log level changes (e.g., DEBUG logs appear) and file rotation is active.
|
||||||
|
|
||||||
|
- [x] T008 [US2] Implement `configure_logger` function to apply settings (level, file, rotation) in `backend/src/core/logger.py`
|
||||||
|
- [x] T009 [US2] Update settings API endpoint to handle `logging` updates in `backend/src/api/routes/settings.py`
|
||||||
|
- [x] T010 [P] [US2] Add Logging configuration section (Level, File Path, Enable Belief State) to `frontend/src/pages/Settings.svelte`
|
||||||
|
|
||||||
|
## Final Phase: Polish & Cross-Cutting Concerns
|
||||||
|
*Goal: Verify system stability and cleanup.*
|
||||||
|
|
||||||
|
- [x] T011 Verify log rotation works by generating dummy logs (manual verification)
|
||||||
|
- [x] T012 Ensure default configuration provides a sensible out-of-the-box experience
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
Setup[Phase 1: Setup] --> Foundational[Phase 2: Foundational]
|
||||||
|
Foundational --> US1[Phase 3: US1 Belief State]
|
||||||
|
US1 --> US2[Phase 4: US2 Configurable Settings]
|
||||||
|
US2 --> Polish[Final Phase: Polish]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parallel Execution Opportunities
|
||||||
|
|
||||||
|
- **US2**: Frontend task (T010) can be implemented in parallel with Backend tasks (T008, T009) once the API contract is finalized.
|
||||||
|
|
||||||
|
## Implementation Strategy
|
||||||
|
1. **MVP**: Complete Phase 1, 2, and 3 to enable structured logging for the agent.
|
||||||
|
2. **Full Feature**: Complete Phase 4 to allow users to control the verbosity and storage.
|
||||||
@@ -28,7 +28,11 @@ class SupersetLogger:
|
|||||||
# @PARAM: log_dir (Optional[Path]) - Директория для сохранения лог-файлов.
|
# @PARAM: log_dir (Optional[Path]) - Директория для сохранения лог-файлов.
|
||||||
# @PARAM: level (int) - Уровень логирования (e.g., `logging.INFO`).
|
# @PARAM: level (int) - Уровень логирования (e.g., `logging.INFO`).
|
||||||
# @PARAM: console (bool) - Флаг для включения вывода в консоль.
|
# @PARAM: console (bool) - Флаг для включения вывода в консоль.
|
||||||
def __init__(self, name: str = "superset_tool", log_dir: Optional[Path] = None, level: int = logging.INFO, console: bool = True) -> None:
|
def __init__(self, name: str = "superset_tool", log_dir: Optional[Path] = None, level: int = logging.INFO, console: bool = True, logger: Optional[logging.Logger] = None) -> None:
|
||||||
|
if logger:
|
||||||
|
self.logger = logger
|
||||||
|
return
|
||||||
|
|
||||||
self.logger = logging.getLogger(name)
|
self.logger = logging.getLogger(name)
|
||||||
self.logger.setLevel(level)
|
self.logger.setLevel(level)
|
||||||
self.logger.propagate = False
|
self.logger.propagate = False
|
||||||
|
|||||||
Reference in New Issue
Block a user