Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
51 changes: 46 additions & 5 deletions src/smallestai/atoms/crew/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,50 @@
chatbots, and other interactive AI applications.

Usage:
from smallestai.atoms.crew.server import AtomsCrewApp
from smallestai.atoms.crew.session import CrewSession
from smallestai.atoms.crew.nodes import OutputCrewNode, BackgroundCrewNode
from smallestai.atoms.crew.clients.openai import OpenAIClient
from smallestai.atoms.crew.tools import function_tool, ToolRegistry
from smallestai.atoms.crew import AtomsCrewApp
from smallestai.atoms.crew import CrewSession
from smallestai.atoms.crew import OutputCrewNode, BackgroundCrewNode
from smallestai.atoms.crew import OpenAIClient
from smallestai.atoms.crew import function_tool, ToolRegistry

The submodule paths (e.g. ``smallestai.atoms.crew.server``) also work.
"""

from typing import TYPE_CHECKING

# Public API. Imported lazily via __getattr__ (PEP 562) so that importing this
# package does not eagerly pull in the crew runtime deps (fastapi, uvicorn, ...)
# that `server` and friends need. Access a name and it is imported on demand.
_LAZY_EXPORTS = {
"AtomsCrewApp": "smallestai.atoms.crew.server",
"CrewSession": "smallestai.atoms.crew.session",
"OutputCrewNode": "smallestai.atoms.crew.nodes",
"BackgroundCrewNode": "smallestai.atoms.crew.nodes",
"OpenAIClient": "smallestai.atoms.crew.clients.openai",
"function_tool": "smallestai.atoms.crew.tools",
"ToolRegistry": "smallestai.atoms.crew.tools",
}

__all__ = list(_LAZY_EXPORTS.keys())

if TYPE_CHECKING:
from smallestai.atoms.crew.clients.openai import OpenAIClient as OpenAIClient
from smallestai.atoms.crew.nodes import BackgroundCrewNode as BackgroundCrewNode
from smallestai.atoms.crew.nodes import OutputCrewNode as OutputCrewNode
from smallestai.atoms.crew.server import AtomsCrewApp as AtomsCrewApp
from smallestai.atoms.crew.session import CrewSession as CrewSession
from smallestai.atoms.crew.tools import ToolRegistry as ToolRegistry
from smallestai.atoms.crew.tools import function_tool as function_tool


def __getattr__(name: str):
module_path = _LAZY_EXPORTS.get(name)
if module_path is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib

return getattr(importlib.import_module(module_path), name)


def __dir__():
return sorted(list(globals().keys()) + __all__)
57 changes: 57 additions & 0 deletions tests/custom/test_public_imports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""Guards the documented public import surface of `smallestai.atoms.crew`.

A customer hit `ImportError` on `from smallestai.atoms.crew import AtomsCrewApp`
because `crew/__init__` exported nothing (the imports there lived only in the
docstring). Both the module docstring and the docs advertise this short path, so
lock it here: if the package stops exporting a public symbol, this test fails in
CI rather than in a customer's notebook.
"""

import importlib

CREW_PUBLIC = [
"AtomsCrewApp",
"CrewSession",
"OutputCrewNode",
"BackgroundCrewNode",
"OpenAIClient",
"function_tool",
"ToolRegistry",
]


def test_crew_all_advertises_public_api():
mod = importlib.import_module("smallestai.atoms.crew")
assert set(CREW_PUBLIC) <= set(mod.__all__)


def test_crew_public_symbols_resolve_from_short_path():
from smallestai.atoms.crew import ( # noqa: F401
AtomsCrewApp,
BackgroundCrewNode,
CrewSession,
OpenAIClient,
OutputCrewNode,
ToolRegistry,
function_tool,
)

for sym in (
AtomsCrewApp,
CrewSession,
OutputCrewNode,
BackgroundCrewNode,
OpenAIClient,
ToolRegistry,
function_tool,
):
assert sym is not None


def test_unknown_attr_raises_attributeerror():
mod = importlib.import_module("smallestai.atoms.crew")
try:
mod.DefinitelyNotAThing
except AttributeError:
return
raise AssertionError("expected AttributeError for an unknown attribute")
Loading