Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5ce2ed0
feat: implement auxiliary tool categories -- design, communication, a…
Aureliolo Apr 8, 2026
d3073eb
fix: resolve mypy type errors in test fixtures and notification dispa…
Aureliolo Apr 8, 2026
12ced79
fix: add design to event module discovery and new config defaults
Aureliolo Apr 8, 2026
b387f95
fix: address review findings -- validation, security, docs, tests
Aureliolo Apr 8, 2026
f030b9a
fix: address 21 PR review items from Copilot, Gemini, CodeRabbit, Cod…
Aureliolo Apr 8, 2026
e60fac4
fix: add smtp_conn type annotation to satisfy mypy
Aureliolo Apr 8, 2026
e05fe23
fix: address 10 additional review items from external reviewers
Aureliolo Apr 8, 2026
799f3e4
fix: address 6 review findings -- logging, test quality, base64 valid…
Aureliolo Apr 8, 2026
ac6f0a3
fix: add type annotation for mypy strict mode
Aureliolo Apr 8, 2026
1c4bf6f
fix: add missing logs, test assertions, and parametrize attribute tests
Aureliolo Apr 8, 2026
2254c78
fix: address 13 review findings -- CLAUDE.md brevity, NaN guard, allo…
Aureliolo Apr 8, 2026
50e656b
fix: input validation, DOT fix, format_report guard, from_config tests
Aureliolo Apr 8, 2026
76be6bb
fix: metric action type, generic error messages, bcc privacy, Validat…
Aureliolo Apr 8, 2026
409eac1
fix: generic SMTP error, schema minLength, unit validation, test rename
Aureliolo Apr 8, 2026
0f884c4
fix: generic error messages, validation logging, dimension bounds, te…
Aureliolo Apr 8, 2026
6f3e9f5
fix: tags type filtering, smtp_timeout tests, image validation tests,…
Aureliolo Apr 8, 2026
7309eb3
fix: asset_id type check, defensive tag filtering, provider-not-calle…
Aureliolo Apr 8, 2026
75109eb
fix: use Any type for parametrized kwargs to satisfy mypy
Aureliolo Apr 8, 2026
29421df
fix: query strip, register_asset validation, parametrize enum and max…
Aureliolo Apr 9, 2026
6e36ece
fix: remove unreachable type checks flagged by mypy
Aureliolo Apr 9, 2026
ee3d58e
fix: reject whitespace-only asset_id in get and delete handlers
Aureliolo Apr 9, 2026
7e2c0a7
fix: docs VERSION_CONTROL wording, parametrize 6 test files, mock_sen…
Aureliolo Apr 9, 2026
c39dbb9
fix: add type args for mypy strict mode
Aureliolo Apr 9, 2026
8871f29
fix: docs category wording, mock assertions, parametrize invalid-inpu…
Aureliolo Apr 9, 2026
e78aaf8
fix: date type guard, email header strings, sink call count, template…
Aureliolo Apr 9, 2026
9c89c82
fix: add negative assertions for backend-gated tools in from_config t…
Aureliolo Apr 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ See `web/CLAUDE.md` for the full component inventory, design token rules, and po
- **Every module** with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`
- **Never** use `import logging` / `logging.getLogger()` / `print()` in application code (exception: `observability/setup.py`, `observability/sinks.py`, `observability/syslog_handler.py`, `observability/http_handler.py`, and `observability/otlp_handler.py` may use stdlib `logging` and `print(..., file=sys.stderr)` for handler construction, bootstrap, and error reporting code that runs before or during logging system configuration)
- **Variable name**: always `logger` (not `_logger`, not `log`)
- **Event names**: always use constants from the domain-specific module under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`, `GIT_COMMAND_START` from `events.git`, `CONTEXT_BUDGET_FILL_UPDATED`, `CONTEXT_BUDGET_COMPACTION_STARTED`, `CONTEXT_BUDGET_COMPACTION_COMPLETED`, `CONTEXT_BUDGET_COMPACTION_FAILED`, `CONTEXT_BUDGET_COMPACTION_SKIPPED`, `CONTEXT_BUDGET_COMPACTION_FALLBACK`, `CONTEXT_BUDGET_INDICATOR_INJECTED`, `CONTEXT_BUDGET_AGENT_COMPACTION_REQUESTED`, `CONTEXT_BUDGET_EPISTEMIC_MARKERS_PRESERVED` from `events.context_budget`, `BACKUP_STARTED` from `events.backup`, `SETUP_COMPLETED` from `events.setup`, `ROUTING_CANDIDATE_SELECTED` from `events.routing`, `SHIPPING_HTTP_BATCH_SENT` from `events.shipping`, `EVAL_REPORT_COMPUTED` from `events.evaluation`, `PROMPT_PROFILE_SELECTED` from `events.prompt`, `PROCEDURAL_MEMORY_START` from `events.procedural_memory`, `PERF_LLM_JUDGE_STARTED` from `events.performance`, `TASK_ENGINE_OBSERVER_FAILED` from `events.task_engine`, `TASK_ASSIGNMENT_PROJECT_FILTERED` and `TASK_ASSIGNMENT_PROJECT_NO_ELIGIBLE` from `events.task_assignment`, `EXECUTION_SHUTDOWN_IMMEDIATE_CANCEL`, `EXECUTION_SHUTDOWN_TOOL_WAIT`, `EXECUTION_SHUTDOWN_CHECKPOINT_SAVE`, `EXECUTION_SHUTDOWN_CHECKPOINT_FAILED`, and `EXECUTION_PROJECT_VALIDATION_FAILED` from `events.execution`, `WORKFLOW_EXEC_COMPLETED` from `events.workflow_execution`, `BLUEPRINT_INSTANTIATE_START` from `events.blueprint`, `WORKFLOW_DEF_ROLLED_BACK` from `events.workflow_definition`, `WORKFLOW_VERSION_SAVED` from `events.workflow_version`, `MEMORY_FINE_TUNE_STARTED`, `MEMORY_SELF_EDIT_TOOL_EXECUTE`, `MEMORY_SELF_EDIT_CORE_READ`, `MEMORY_SELF_EDIT_CORE_WRITE`, `MEMORY_SELF_EDIT_CORE_WRITE_REJECTED`, `MEMORY_SELF_EDIT_ARCHIVAL_SEARCH`, `MEMORY_SELF_EDIT_ARCHIVAL_WRITE`, `MEMORY_SELF_EDIT_RECALL_READ`, `MEMORY_SELF_EDIT_RECALL_WRITE`, `MEMORY_SELF_EDIT_WRITE_FAILED` from `events.memory`, `REPORTING_GENERATION_STARTED` from `events.reporting`, `RISK_BUDGET_SCORE_COMPUTED` from `events.risk_budget`, `BUDGET_PROJECT_COST_QUERIED`, `BUDGET_PROJECT_RECORDS_QUERIED`, `BUDGET_PROJECT_BUDGET_EXCEEDED`, `BUDGET_PROJECT_ENFORCEMENT_CHECK`, `BUDGET_PROJECT_COST_AGGREGATED`, `BUDGET_PROJECT_COST_AGGREGATION_FAILED`, and `BUDGET_PROJECT_BASELINE_SOURCE` from `events.budget`, `LLM_STRATEGY_SYNTHESIZED` and `DISTILLATION_CAPTURED` from `events.consolidation`, `MEMORY_DIVERSITY_RERANKED`, `MEMORY_DIVERSITY_RERANK_FAILED`, and `MEMORY_REFORMULATION_ROUND` from `events.memory`, `NOTIFICATION_DISPATCHED` and `NOTIFICATION_DISPATCH_FAILED` from `events.notification`, `QUALITY_STEP_CLASSIFIED` from `events.quality`, `HEALTH_TICKET_EMITTED` from `events.health`, `TRAJECTORY_SCORING_START` from `events.trajectory`, `COORD_METRICS_AMDAHL_COMPUTED` from `events.coordination_metrics`, `COORDINATION_STARTED`, `COORDINATION_COMPLETED`, `COORDINATION_FAILED`, `COORDINATION_PHASE_STARTED`, `COORDINATION_PHASE_COMPLETED`, `COORDINATION_PHASE_FAILED`, `COORDINATION_WAVE_STARTED`, `COORDINATION_WAVE_COMPLETED`, `COORDINATION_TOPOLOGY_RESOLVED`, `COORDINATION_CLEANUP_STARTED`, `COORDINATION_CLEANUP_COMPLETED`, `COORDINATION_CLEANUP_FAILED`, `COORDINATION_WAVE_BUILT`, `COORDINATION_FACTORY_BUILT`, and `COORDINATION_ATTRIBUTION_BUILT` from `events.coordination`, `WEB_REQUEST_START` and `WEB_SSRF_BLOCKED` from `events.web`, `DB_QUERY_START` and `DB_WRITE_BLOCKED` from `events.database`, `TERMINAL_COMMAND_START` and `TERMINAL_COMMAND_BLOCKED` from `events.terminal`, `SUB_CONSTRAINT_RESOLVED` and `SUB_CONSTRAINT_DENIED` from `events.sub_constraint`, `VERSION_SAVED`, `VERSION_SNAPSHOT_FAILED`, `VERSION_LISTED`, and `VERSION_NOT_FOUND` from `events.versioning`, `ANALYTICS_AGGREGATION_COMPUTED` and `ANALYTICS_RETRY_RATE_ALERT` from `events.analytics`, `CALL_CLASSIFICATION_COMPUTED` from `events.call_classification`, `QUOTA_THRESHOLD_ALERT` and `QUOTA_POLL_FAILED` from `events.quota`, `CONFLICT_DEBATE_EVALUATOR_FAILED` from `events.conflict`, `DELEGATION_LOOP_CIRCUIT_BACKOFF` and `DELEGATION_LOOP_CIRCUIT_PERSIST_FAILED` from `events.delegation`, `MEETING_EVENT_COOLDOWN_SKIPPED` and `MEETING_TASKS_CAPPED` from `events.meeting`, `PERSISTENCE_CIRCUIT_BREAKER_SAVED`, `PERSISTENCE_CIRCUIT_BREAKER_SAVE_FAILED`, `PERSISTENCE_CIRCUIT_BREAKER_LOADED`, `PERSISTENCE_CIRCUIT_BREAKER_LOAD_FAILED`, `PERSISTENCE_CIRCUIT_BREAKER_DELETED`, `PERSISTENCE_CIRCUIT_BREAKER_DELETE_FAILED`, `PERSISTENCE_PROJECT_COST_AGG_INCREMENTED`, `PERSISTENCE_PROJECT_COST_AGG_INCREMENT_FAILED`, `PERSISTENCE_PROJECT_COST_AGG_FETCHED`, `PERSISTENCE_PROJECT_COST_AGG_FETCH_FAILED`, and `PERSISTENCE_PROJECT_COST_AGG_DESERIALIZE_FAILED` from `events.persistence`, `METRICS_SCRAPE_COMPLETED`, `METRICS_SCRAPE_FAILED`, `METRICS_COLLECTOR_INITIALIZED`, `METRICS_COORDINATION_RECORDED`, `METRICS_OTLP_EXPORT_COMPLETED` and `METRICS_OTLP_FLUSHER_STOPPED` from `events.metrics`, `ORG_MEMORY_QUERY_START`, `ORG_MEMORY_QUERY_COMPLETE`, `ORG_MEMORY_QUERY_FAILED`, `ORG_MEMORY_WRITE_START`, `ORG_MEMORY_WRITE_COMPLETE`, `ORG_MEMORY_WRITE_DENIED`, `ORG_MEMORY_WRITE_FAILED`, `ORG_MEMORY_POLICIES_LISTED`, `ORG_MEMORY_BACKEND_CREATED`, `ORG_MEMORY_CONNECT_FAILED`, `ORG_MEMORY_DISCONNECT_FAILED`, `ORG_MEMORY_NOT_CONNECTED`, `ORG_MEMORY_ROW_PARSE_FAILED`, `ORG_MEMORY_CONFIG_INVALID`, `ORG_MEMORY_MODEL_INVALID`, `ORG_MEMORY_MVCC_PUBLISH_APPENDED`, `ORG_MEMORY_MVCC_RETRACT_APPENDED`, `ORG_MEMORY_MVCC_SNAPSHOT_AT_QUERIED`, and `ORG_MEMORY_MVCC_LOG_QUERIED` from `events.org_memory`). Each domain has its own module -- see `src/synthorg/observability/events/` for the full inventory of constants. Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`
- **Event names**: always use constants from the domain-specific module under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Each domain has its own module -- see `src/synthorg/observability/events/` for the full inventory of constants. Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`
- **Structured kwargs**: always `logger.info(EVENT, key=value)` -- never `logger.info("msg %s", val)`
- **All error paths** must log at WARNING or ERROR with context before raising
- **All state transitions** must log at INFO
Expand Down
10 changes: 8 additions & 2 deletions docs/design/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -691,8 +691,14 @@ Per-category backend selection is implemented in `tools/sandbox/factory.py` via
`build_sandbox_backends` (instantiates only the backends referenced by config),
`resolve_sandbox_for_category` (looks up the correct backend for a `ToolCategory`), and
`cleanup_sandbox_backends` (parallel cleanup with error isolation). The tool factory
(`build_default_tools_from_config`) wires `VERSION_CONTROL` category; other categories will
be wired as their tool builders are added.
(`build_default_tools_from_config`) wires tool categories. Core tools
(`FILE_SYSTEM`, `VERSION_CONTROL`, web, etc.) are part of the default toolset
and always registered. The
auxiliary categories `DESIGN`, `COMMUNICATION`, and `ANALYTICS` are opt-in: tools
are only registered when the corresponding config section is present, and some
individual tools additionally require a runtime dependency (e.g. image tools
require an ``ImageProvider``, notification tools require a dispatcher, analytics
query/metric tools require a provider or sink).

Docker is optional -- only required when code execution, terminal, web, or database tools are
enabled. File system and git tools work out of the box with subprocess isolation. This keeps
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/claude-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ src/synthorg/
settings/ # Runtime-editable settings (DB > env > YAML > code), Fernet encryption, ConfigResolver, definitions/, subscribers/ (SecuritySubscriber for discovery allowlist hot-reload)
security/ # Rule engine, audit log, output scanner, progressive trust, autonomy levels, timeout policies, LLM fallback evaluator, custom policy rules, risk scoring (pluggable RiskScorer protocol, multi-dimensional RiskScore, DefaultRiskScorer), enforcement modes (active/shadow/disabled via SecurityEnforcementMode), risk override (SecOps risk tier reclassification via RiskTierOverride + SecOpsRiskClassifier), SSRF violation tracking (SsrfViolation model, pending/allowed/denied status for self-healing discovery allowlist)
templates/ # Pre-built company templates (inheritance tree), template merge engine, personality presets, preset discovery/CRUD service, model requirements, tier-to-model matching, locale-aware name generation, workflow config rendering, pack_loader (additive team packs), packs/ (built-in pack YAMLs), uses_packs composition
tools/ # Tool registry, built-in tools, git SSRF prevention, MCP bridge, sandbox factory (gVisor default overrides via merge_gvisor_defaults), invocation tracking, network_validator (shared SSRF), sub_constraints (per-level constraint models), sub_constraint_enforcer (granular enforcement), web/ (HTTP requests, HTML parsing, web search), database/ (SQL query, schema inspection), terminal/ (sandboxed shell commands), sandbox/ (4-domain SandboxPolicy model (filesystem/network/process/inference), SandboxRuntimeResolver (gVisor probe + per-category runtime resolution with fallback), SandboxCredentialManager (env var credential stripping), SandboxAuthProxy (LLM traffic auth proxy stub))
tools/ # Tool registry, built-in tools, git SSRF prevention, MCP bridge, sandbox factory (gVisor default overrides via merge_gvisor_defaults), invocation tracking, network_validator (shared SSRF), sub_constraints (per-level constraint models), sub_constraint_enforcer (granular enforcement), web/ (HTTP requests, HTML parsing, web search), database/ (SQL query, schema inspection), terminal/ (sandboxed shell commands), design/ (image generation via ImageProvider protocol, diagram DSL generation, asset management), communication/ (SMTP email sending, notification dispatch via NotificationDispatcherProtocol, Jinja2 template formatting), analytics/ (data aggregation via AnalyticsProvider protocol, report generation, metric collection via MetricSink protocol), sandbox/ (4-domain SandboxPolicy model (filesystem/network/process/inference), SandboxRuntimeResolver (gVisor probe + per-category runtime resolution with fallback), SandboxCredentialManager (env var credential stripping), SandboxAuthProxy (LLM traffic auth proxy stub))

web/src/ # React 19 dashboard (see web/CLAUDE.md for full structure)
cli/ # Go CLI binary (see cli/CLAUDE.md for full structure)
Expand Down
3 changes: 3 additions & 0 deletions src/synthorg/config/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,7 @@ def default_config_dict() -> dict[str, object]:
"web": None,
"database": None,
"terminal": None,
"design_tools": None,
"communication_tools": None,
"analytics_tools": None,
}
20 changes: 20 additions & 0 deletions src/synthorg/config/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@
from synthorg.providers.enums import AuthType
from synthorg.security.config import SecurityConfig
from synthorg.security.trust.config import TrustConfig
from synthorg.tools.analytics.config import AnalyticsToolsConfig # noqa: TC001
from synthorg.tools.communication.config import CommunicationToolsConfig # noqa: TC001
from synthorg.tools.database.config import DatabaseConfig # noqa: TC001
from synthorg.tools.design.config import DesignToolsConfig # noqa: TC001
from synthorg.tools.git_url_validator import GitCloneNetworkPolicy
from synthorg.tools.mcp.config import MCPConfig
from synthorg.tools.sandbox.sandboxing_config import SandboxingConfig
Expand Down Expand Up @@ -592,6 +595,11 @@ class RootConfig(BaseModel):
tools).
terminal: Terminal tool configuration (``None`` = default
terminal config).
design_tools: Design tool configuration (``None`` = disabled).
communication_tools: Communication tool configuration
(``None`` = disabled).
analytics_tools: Analytics tool configuration
(``None`` = disabled).
"""

model_config = ConfigDict(frozen=True, allow_inf_nan=False)
Expand Down Expand Up @@ -739,6 +747,18 @@ class RootConfig(BaseModel):
default=None,
description="Terminal tool configuration (None = default terminal config)",
)
design_tools: DesignToolsConfig | None = Field(
default=None,
description="Design tool configuration (None = disabled)",
)
communication_tools: CommunicationToolsConfig | None = Field(
default=None,
description="Communication tool configuration (None = disabled)",
)
analytics_tools: AnalyticsToolsConfig | None = Field(
default=None,
description="Analytics tool configuration (None = disabled)",
)

@model_validator(mode="after")
def _validate_unique_agent_names(self) -> Self:
Expand Down
20 changes: 20 additions & 0 deletions src/synthorg/observability/events/analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,23 @@
ANALYTICS_RETRY_RATE_ALERT: Final[str] = "analytics.retry_rate_alert"
ANALYTICS_ORCHESTRATION_ALERT: Final[str] = "analytics.orchestration_alert"
ANALYTICS_SERVICE_CREATED: Final[str] = "analytics.service_created"

# Tool: data aggregation queries
ANALYTICS_TOOL_QUERY_START: Final[str] = "analytics.tool.query_start"
ANALYTICS_TOOL_QUERY_SUCCESS: Final[str] = "analytics.tool.query_success"
ANALYTICS_TOOL_QUERY_FAILED: Final[str] = "analytics.tool.query_failed"

# Tool: report generation
ANALYTICS_TOOL_REPORT_START: Final[str] = "analytics.tool.report_start"
ANALYTICS_TOOL_REPORT_SUCCESS: Final[str] = "analytics.tool.report_success"
ANALYTICS_TOOL_REPORT_FAILED: Final[str] = "analytics.tool.report_failed"

# Tool: metric collection
ANALYTICS_TOOL_METRIC_RECORDED: Final[str] = "analytics.tool.metric_recorded"
ANALYTICS_TOOL_METRIC_RECORD_FAILED: Final[str] = "analytics.tool.metric_record_failed"
ANALYTICS_TOOL_METRIC_NOT_ALLOWED: Final[str] = "analytics.tool.metric_not_allowed"

# Tool: provider
ANALYTICS_TOOL_PROVIDER_NOT_CONFIGURED: Final[str] = (
"analytics.tool.provider_not_configured"
)
31 changes: 31 additions & 0 deletions src/synthorg/observability/events/communication.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,34 @@

# Shutdown
COMM_BUS_SHUTDOWN_SIGNAL: Final[str] = "communication.bus.shutdown_signal"

# Tool: email sending
COMM_TOOL_EMAIL_SEND_START: Final[str] = "communication.tool.email.send_start"
COMM_TOOL_EMAIL_SEND_SUCCESS: Final[str] = "communication.tool.email.send_success"
COMM_TOOL_EMAIL_SEND_FAILED: Final[str] = "communication.tool.email.send_failed"
COMM_TOOL_EMAIL_VALIDATION_FAILED: Final[str] = (
"communication.tool.email.validation_failed"
)

# Tool: notification sending
COMM_TOOL_NOTIFICATION_SEND_START: Final[str] = (
"communication.tool.notification.send_start"
)
COMM_TOOL_NOTIFICATION_SEND_SUCCESS: Final[str] = (
"communication.tool.notification.send_success"
)
COMM_TOOL_NOTIFICATION_SEND_FAILED: Final[str] = (
"communication.tool.notification.send_failed"
)

# Tool: template rendering
COMM_TOOL_TEMPLATE_RENDER_START: Final[str] = "communication.tool.template.render_start"
COMM_TOOL_TEMPLATE_RENDER_SUCCESS: Final[str] = (
"communication.tool.template.render_success"
)
COMM_TOOL_TEMPLATE_RENDER_FAILED: Final[str] = (
"communication.tool.template.render_failed"
)
COMM_TOOL_TEMPLATE_RENDER_INVALID: Final[str] = (
"communication.tool.template.render_invalid"
)
25 changes: 25 additions & 0 deletions src/synthorg/observability/events/design.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Design tool event constants."""

from typing import Final

# Image generation
DESIGN_IMAGE_GENERATION_START: Final[str] = "design.image.generation_start"
DESIGN_IMAGE_GENERATION_SUCCESS: Final[str] = "design.image.generation_success"
DESIGN_IMAGE_GENERATION_FAILED: Final[str] = "design.image.generation_failed"
DESIGN_IMAGE_GENERATION_TIMEOUT: Final[str] = "design.image.generation_timeout"

# Diagram generation
DESIGN_DIAGRAM_GENERATION_START: Final[str] = "design.diagram.generation_start"
DESIGN_DIAGRAM_GENERATION_SUCCESS: Final[str] = "design.diagram.generation_success"
DESIGN_DIAGRAM_GENERATION_FAILED: Final[str] = "design.diagram.generation_failed"

# Asset management
DESIGN_ASSET_STORED: Final[str] = "design.asset.stored"
DESIGN_ASSET_RETRIEVED: Final[str] = "design.asset.retrieved"
DESIGN_ASSET_DELETED: Final[str] = "design.asset.deleted"
DESIGN_ASSET_LISTED: Final[str] = "design.asset.listed"
DESIGN_ASSET_SEARCHED: Final[str] = "design.asset.searched"
DESIGN_ASSET_VALIDATION_FAILED: Final[str] = "design.asset.validation_failed"

# Provider
DESIGN_PROVIDER_NOT_CONFIGURED: Final[str] = "design.provider.not_configured"
23 changes: 23 additions & 0 deletions src/synthorg/tools/analytics/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Built-in analytics tools for data aggregation, reporting, and metrics."""

from synthorg.tools.analytics.base_analytics_tool import BaseAnalyticsTool
from synthorg.tools.analytics.config import AnalyticsToolsConfig
from synthorg.tools.analytics.data_aggregator import (
AnalyticsProvider,
DataAggregatorTool,
)
from synthorg.tools.analytics.metric_collector import (
MetricCollectorTool,
MetricSink,
)
from synthorg.tools.analytics.report_generator import ReportGeneratorTool

__all__ = [
"AnalyticsProvider",
"AnalyticsToolsConfig",
"BaseAnalyticsTool",
"DataAggregatorTool",
"MetricCollectorTool",
"MetricSink",
"ReportGeneratorTool",
]
67 changes: 67 additions & 0 deletions src/synthorg/tools/analytics/base_analytics_tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""Base class for analytics tools.

Provides the common ``ToolCategory.ANALYTICS`` category, a
shared configuration reference, and a metric-name validation
helper.
"""

from abc import ABC
from typing import Any

from synthorg.core.enums import ToolCategory
from synthorg.tools.analytics.config import AnalyticsToolsConfig
from synthorg.tools.base import BaseTool

Comment on lines +8 to +14
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add required module logger initialization.

This business-logic module should initialize the standard module logger.

♻️ Proposed fix
 from abc import ABC
 from typing import Any
 
 from synthorg.core.enums import ToolCategory
+from synthorg.observability import get_logger
 from synthorg.tools.analytics.config import AnalyticsToolsConfig
 from synthorg.tools.base import BaseTool
 
+logger = get_logger(__name__)
+
 
 class BaseAnalyticsTool(BaseTool, ABC):

As per coding guidelines: src/synthorg/**/*.py: Every Python module with business logic MUST import from synthorg.observability import get_logger and define logger = get_logger(__name__).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/tools/analytics/base_analytics_tool.py` around lines 8 - 14,
This module is missing the standard module logger; import get_logger from
synthorg.observability and initialize module-level logger by adding "logger =
get_logger(__name__)" near the top of the file so the module exposes the
required logger variable used by other code and follows the project guideline;
update imports to include "from synthorg.observability import get_logger" and
add the "logger = get_logger(__name__)" assignment after the imports.


class BaseAnalyticsTool(BaseTool, ABC):
"""Abstract base for all analytics tools.

Sets ``category=ToolCategory.ANALYTICS`` and holds a shared
``AnalyticsToolsConfig``.
"""

def __init__(
self,
*,
name: str,
description: str = "",
parameters_schema: dict[str, Any] | None = None,
action_type: str | None = None,
config: AnalyticsToolsConfig | None = None,
) -> None:
"""Initialize an analytics tool with configuration.

Args:
name: Tool name.
description: Human-readable description.
parameters_schema: JSON Schema for tool parameters.
action_type: Security action type override.
config: Analytics tool configuration.
"""
super().__init__(
name=name,
description=description,
category=ToolCategory.ANALYTICS,
parameters_schema=parameters_schema,
action_type=action_type,
)
self._config = config or AnalyticsToolsConfig()

@property
def config(self) -> AnalyticsToolsConfig:
"""The analytics tool configuration."""
return self._config

def _is_metric_allowed(self, metric_name: str) -> bool:
"""Check if a metric name is allowed by the whitelist.

Args:
metric_name: Name of the metric to check.

Returns:
``True`` if the metric is allowed (or no whitelist
is configured).
"""
if self._config.allowed_metrics is None:
return True
return metric_name in self._config.allowed_metrics
Loading
Loading