Skip to content

Python Analysis Service SDK

The scriptbee-analysis-sdk package is the Python implementation of the Analysis Service SDK. It lets you build a fully-featured Analysis Service in Python using FastAPI without writing any HTTP boilerplate.

Installation

bash
pip install scriptbee-analysis-sdk

Or with uv:

bash
uv add scriptbee-analysis-sdk

What the SDK provides

LayerWhat's included
EndpointsAll REST routes pre-wired with correct HTTP verbs, paths, and status codes
ContractsWeb* Pydantic request/response models matching the public REST API
ValidatorsPydantic field validators for all request models
AbstractionsAbstract base classes your service must implement
UtilitiesFileBundler for packaging file streams for loader plugins
Registrationcreate_analysis_sdk_router() to mount everything in one call

Architecture

┌────────────────┐   REST API    ┌────────────────────────────────────┐
│   Gateway      │ ─────────────▶│   Your FastAPI Analysis Service    │
│ (ScriptBee)    │               │                                    │
└────────────────┘               │  create_analysis_sdk_router()      │
                                 │  ┌────────────────────────────┐    │
                                 │  │  Your AnalysisSdkContainer  │    │
                                 │  │  (10 use-case properties)   │    │
                                 │  └────────────────────────────┘    │
                                 └────────────────────────────────────┘

Implementing the use cases

Implement each abstract base class from scriptbee_analysis_sdk.abstractions and expose them through an AnalysisSdkContainer-compatible object.

Abstract base classResponsibility
RunAnalysisUseCaseExecute a script against the loaded context
GetContextUseCaseReturn current context slices
GetContextGraphUseCaseReturn context as a graph (nodes + edges)
LoadContextUseCaseLoad files into context via a loader plugin
LinkContextUseCaseLink loaded context via a linker plugin
ClearContextUseCaseClear all loaded context
GenerateClassesUseCaseStream generated model class files
GetInstalledPluginsUseCaseList installed plugins
InstallPluginUseCaseInstall a plugin by id
UninstallPluginUseCaseUninstall a plugin by id

Example implementation

python
from scriptbee_analysis_sdk.abstractions import RunAnalysisUseCase, RunAnalysisCommand
from scriptbee_analysis_sdk.domain.analysis import AnalysisInfo


class Neo4jRunAnalysisService(RunAnalysisUseCase):
    def __init__(self, session):
        self._session = session

    async def run(self, command: RunAnalysisCommand) -> AnalysisInfo:
        # store results in Neo4j, return analysis info
        ...

Registration

Create a container that satisfies the AnalysisSdkContainer protocol, then call create_analysis_sdk_router() to get a pre-configured APIRouter:

python
from fastapi import FastAPI
from scriptbee_analysis_sdk.extensions import AnalysisSdkContainer, create_analysis_sdk_router


class MyContainer:
    @property
    def run_analysis(self):
        return Neo4jRunAnalysisService(session=...)

    @property
    def get_context(self):
        return Neo4jGetContextService(...)

    # ... all other use-case properties


app = FastAPI()
app.include_router(create_analysis_sdk_router(MyContainer()))

create_analysis_sdk_router() registers all endpoints under /api/ and wires each route to the corresponding use case resolved lazily from the container.

Container protocol

The AnalysisSdkContainer is a structural Protocol — any object with the right properties satisfies it without inheritance:

python
class AnalysisSdkContainer(Protocol):
    @property
    def run_analysis(self) -> RunAnalysisUseCase: ...
    @property
    def get_context(self) -> GetContextUseCase: ...
    @property
    def load_context(self) -> LoadContextUseCase: ...
    @property
    def link_context(self) -> LinkContextUseCase: ...
    @property
    def clear_context(self) -> ClearContextUseCase: ...
    @property
    def get_context_graph(self) -> GetContextGraphUseCase: ...
    @property
    def generate_classes(self) -> GenerateClassesUseCase: ...
    @property
    def get_installed_plugins(self) -> GetInstalledPluginsUseCase: ...
    @property
    def install_plugin(self) -> InstallPluginUseCase: ...
    @property
    def uninstall_plugin(self) -> UninstallPluginUseCase: ...

FileBundler

The FileBundler utility serialises a list of SampleCodeFile objects into the binary format consumed by the Gateway for the generate-classes endpoint.

python
import io
from scriptbee_analysis_sdk.file_bundler import FileBundler
from scriptbee_analysis_sdk.domain.code_generation import SampleCodeFile

files = [SampleCodeFile(name="Model.cs", content="public class Model {}")]

stream = io.BytesIO()
FileBundler().write_to_stream(files, stream)
stream.seek(0)
# stream now contains the binary-encoded file bundle

The wire format is: for each file, a big-endian uint32 path length, the UTF-8 encoded path, a big-endian uint32 content length, and the UTF-8 encoded content. A terminal uint32 of 0 signals end-of-stream.

Domain types

All domain types live under scriptbee_analysis_sdk.domain and are Pydantic BaseModels.

ModuleTypes
projectProjectId, ScriptId, FileId, InstanceId
contextContextSlice, ContextGraphNode, ContextGraphEdge, ContextGraphResult
analysisAnalysisId, AnalysisStatus, AnalysisInfo
code_generationSampleCodeFile
pluginsPluginId, PluginKind, PluginManifest, Plugin, *PluginExtensionPoint
scriptsScript, ScriptLanguage

Results SDK

The DefaultAnalysisResultService lets your analysis logic emit typed results (files, console output, errors, or custom types) without managing file IDs, byte encoding, or store calls directly.

Interface

python
class AnalysisResultService(Protocol):
    async def add_file(
        self,
        name: str,
        content: str | bytes,
        result_type: str = ResultType.FILE,
    ) -> ResultId: ...

    async def add_console(self, content: str, name: str = "ConsoleOutput") -> ResultId: ...

    async def add_error(self, message: str, name: str = "RunError") -> ResultId: ...

    async def add_result(self, name: str, result_type: str, content: str | bytes) -> ResultId: ...

Storage Decoupling

The SDK deliberately does not provide any in-memory or built-in storage implementation. The SDK only offers the basic abstractions — storage is something that the concrete implementer offers by implementing the ScriptResultsStore protocol.

python
class ScriptResultsStore(Protocol):
    async def upload_file(
        self,
        file_id: str,
        content: bytes,
        metadata: dict[str, str] | None = None,
    ) -> None: ...

Usage

python
from scriptbee_analysis_sdk.domain import ResultType
from scriptbee_analysis_sdk.results import DefaultAnalysisResultService, ScriptResultsStore


class FileSystemScriptResultsStore:
    async def upload_file(
        self,
        file_id: str,
        content: bytes,
        metadata: dict[str, str] | None = None,
    ) -> None:
        # concrete storage implementation (e.g. S3, disk, blob storage)
        ...


store = FileSystemScriptResultsStore()
results = DefaultAnalysisResultService(store=store)

# emit a text file result
result_id = await results.add_file("summary.json", '{"count": 42}')

# emit console output
await results.add_console("Analysis started")

# emit an error
await results.add_error("Script timed out")

# emit a custom result type
await results.add_result("chart.svg", "Chart", svg_bytes)

Result types

The built-in type constants are on ResultType:

ConstantValue
ResultType.FILE"File"
ResultType.CONSOLE"Console"
ResultType.RUN_ERROR"RunError"

You can pass any custom string as result_type in add_file or add_result to define your own result categories.

Callback hook

DefaultAnalysisResultService accepts an optional on_result_added callback that is invoked with a ResultSummary after each result is persisted. The host layer uses this to track results without leaking infrastructure concerns into the analysis logic:

python
summaries = []
results = DefaultAnalysisResultService(
    store=store,
    on_result_added=summaries.append,
)

Versioning & Changelog

The SDK follows Semantic Versioning. Releases are tagged analysis-sdk-python@<version> in the monorepo and published automatically to PyPI.

See the shared CHANGELOG.md for the full release history of all SDK implementations.

Released under the MIT License.