Skip to content

refactor: decompose large monolithic modules into focused packages#27

Merged
dimakis merged 1 commit intomainfrom
refactor/large-file-decomposition
Feb 14, 2026
Merged

refactor: decompose large monolithic modules into focused packages#27
dimakis merged 1 commit intomainfrom
refactor/large-file-decomposition

Conversation

@dimakis
Copy link
Owner

@dimakis dimakis commented Feb 14, 2026

Summary

  • Decompose 8 oversized Python modules (architect.py, data_scientist.py, llm.py, mlflow.py, specialist_tools.py, openai_compat, ha_registry, agents routes) into well-organized packages with single-responsibility modules
  • Extract UI components into dedicated directories (diagnostics tabs, architecture panels, dashboard cards, inline-assistant, chat hooks)
  • Resolve all mypy type errors introduced by decomposition (92 -> 0) with proper type annotations, generics, and facade re-exports for backward compatibility

Motivation

Several core modules had grown beyond 800-1200 lines, making navigation, testing, and code review difficult. This refactor improves maintainability without changing any behavior.

Type of Change

  • refactor — Code restructuring (no feature or fix)

Changes

Backend decomposition (8 modules -> packages):

  • src/agents/architect.py -> src/agents/architect/ (agent, workflow, proposals, review, tools, entity_context, stream_event)
  • src/agents/data_scientist.py -> src/agents/data_scientist/ (agent, collectors, constants, prompts, suggestions, workflow)
  • src/llm.py -> src/llm/ (factory, resilient, circuit_breaker, usage)
  • src/tracing/mlflow.py -> src/tracing/ (init, tracer, spans, runs, logging, feedback)
  • src/tools/specialist_tools.py -> split into routing, runners, strategies, consult modules
  • src/api/routes/openai_compat.py -> package (handlers, streaming_filter, schemas, utils)
  • src/api/routes/ha_registry.py -> package (automations, scenes, scripts, services, summary, sync, helpers)
  • src/api/routes/agents.py -> package (core, config_versions, prompt_versions, schemas, seed, serializers)

Additional extractions:

  • Analyst mixins (config, discussion, persistence) from base_analyst
  • Graph state split into submodules (base, conversation, analysis, dashboard, discovery, enums, etc.)
  • HA automation schema split into triggers, conditions, actions

UI decomposition:

  • Diagnostics page -> 5 tab components
  • Architecture page -> graph + panel components
  • Dashboard -> card components
  • InlineAssistant -> hook + input + messages + types
  • Chat -> header + hooks

Backward compatibility:

  • All original module paths preserved as facade re-exports (__init__.py files re-export public symbols)
  • No import changes required by consumers

Type safety:

  • Fixed 92 mypy errors across 25 files introduced by decomposition
  • Added return type to _safe_import_mlflow (eliminated 23 cascade errors)
  • Added type stubs to analyst mixins
  • Unified StreamEvent via canonical re-export
  • Proper generic type params, enum conversions, and casts throughout

Testing

  • Unit tests added/updated
  • make ci-local passes (ruff format, ruff check, mypy 0 errors/269 files, bandit, 2717 tests pass)

Security Considerations

  • No security implications

This is a pure structural refactor. No behavioral changes, no new endpoints, no auth/crypto changes.

Checklist

  • My code follows the project's style (ruff, mypy pass)
  • I've written tests that prove my fix/feature works
  • I've updated documentation if needed
  • I've added conventional commit messages
  • Coverage has not decreased
  • CI passed locally (make ci-local)
  • Branch commits squashed into a single conventional commit
  • Ready for rebase-merge

"""
if body.stream:
return StreamingResponse(
_stream_chat_completion(body),

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

Copilot Autofix

AI about 7 hours ago

In general, the fix is to avoid sending raw exception messages to the client. Instead, log the detailed error server-side and return a generic, sanitized message over SSE that does not leak internal details. This keeps observability for developers while protecting users from seeing stack traces or sensitive provider/backend messages.

Concretely, the best change with minimal behavioral impact is:

  • Keep the existing _format_sse_error helper and the SSE error shape.
  • Change _stream_chat_completion’s except block so it:
    • Logs the full exception (including stack trace) using logging.exception.
    • Sends a generic error string (e.g. "An internal error occurred during streaming.") to _format_sse_error instead of str(e).

This only affects:

  • src/api/routes/openai_compat/handlers.py: update the except Exception as e: block at the end of _stream_chat_completion and add an import for logging.
    No changes are needed in routes.py or utils.py beyond this, because _format_sse_error will no longer receive sensitive messages.

To implement:

  • Add import logging alongside the existing imports in handlers.py.
  • Change the except block:
    except Exception as e:
        import logging

        logging.exception("Unhandled error in _stream_chat_completion")
        yield _format_sse_error("An internal error occurred during streaming.")

This preserves the SSE structure (data: { "error": { "message": ..., "type": "api_error" } }) and the client contract, but replaces the sensitive content with a safe, generic message.

Suggested changeset 1
src/api/routes/openai_compat/handlers.py
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/api/routes/openai_compat/handlers.py b/src/api/routes/openai_compat/handlers.py
--- a/src/api/routes/openai_compat/handlers.py
+++ b/src/api/routes/openai_compat/handlers.py
@@ -6,6 +6,7 @@
 import time
 from typing import TYPE_CHECKING, Any
 from uuid import uuid4
+import logging
 
 from fastapi import HTTPException
 from langchain_core.messages import AIMessage
@@ -446,4 +447,6 @@
                     await session.commit()
 
     except Exception as e:
-        yield _format_sse_error(str(e))
+        # Log full exception server-side, but send a generic message to the client
+        logging.exception("Unhandled error in _stream_chat_completion")
+        yield _format_sse_error("An internal error occurred during streaming.")
EOF
@@ -6,6 +6,7 @@
import time
from typing import TYPE_CHECKING, Any
from uuid import uuid4
import logging

from fastapi import HTTPException
from langchain_core.messages import AIMessage
@@ -446,4 +447,6 @@
await session.commit()

except Exception as e:
yield _format_sse_error(str(e))
# Log full exception server-side, but send a generic message to the client
logging.exception("Unhandled error in _stream_chat_completion")
yield _format_sse_error("An internal error occurred during streaming.")
Copilot is powered by AI and may make mistakes. Always verify output.
@dimakis dimakis force-pushed the refactor/large-file-decomposition branch from ffb1d02 to fb556fe Compare February 14, 2026 03:34
@dimakis dimakis force-pushed the refactor/large-file-decomposition branch from fb556fe to bb96107 Compare February 14, 2026 04:19
@dimakis dimakis changed the title refactor: decompose large files into focused modules refactor: decompose large monolithic modules into focused packages Feb 14, 2026
Breaks down 8 oversized modules (architect.py, data_scientist.py, llm.py,
mlflow.py, specialist_tools.py, openai_compat route, ha_registry route,
agents route) into well-organized packages with clear single-responsibility
modules. Extracts UI components into dedicated directories. Resolves all
mypy type errors introduced by decomposition (92 -> 0).

Key changes:
- architect.py -> src/agents/architect/ (7 modules)
- data_scientist.py -> src/agents/data_scientist/ (2 modules)
- llm.py -> src/llm/ (factory, resilient, circuit_breaker, usage)
- mlflow.py -> src/tracing/ (init, tracer, spans, runs, logging, feedback)
- specialist_tools.py -> split into routing, runners, strategies, consult
- openai_compat route -> package with handlers, streaming_filter, schemas
- ha_registry route -> package with sub-routers
- agents route -> package with sub-routers
- Extract analyst mixins (config, discussion, persistence)
- Extract graph state into package (base, conversation, analysis, etc.)
- Decompose UI pages (diagnostics, architecture, dashboard, inline-assistant)
- All facade modules preserved for backward-compatible imports

Co-authored-by: Cursor <cursoragent@cursor.com>
@dimakis dimakis force-pushed the refactor/large-file-decomposition branch from bb96107 to 10a50b3 Compare February 14, 2026 04:32
@dimakis dimakis merged commit 4768f33 into main Feb 14, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant