├── src └── roboto │ ├── py.typed │ ├── version │ ├── autogen_version.py │ └── __init__.py │ ├── domain │ ├── __init__.py │ ├── tokens │ │ ├── __init__.py │ │ ├── record.py │ │ └── operations.py │ ├── users │ │ ├── __init__.py │ │ ├── record.py │ │ └── operations.py │ ├── layouts │ │ ├── __init__.py │ │ ├── record.py │ │ └── operations.py │ ├── topics │ │ ├── parquet │ │ │ ├── __init__.py │ │ │ └── ingestion.py │ │ ├── __init__.py │ │ ├── json_decoder_factory.py │ │ └── topic_reader.py │ ├── secrets │ │ └── __init__.py │ ├── collections │ │ ├── __init__.py │ │ ├── operations.py │ │ └── record.py │ ├── comments │ │ ├── __init__.py │ │ └── operations.py │ ├── orgs │ │ └── __init__.py │ ├── devices │ │ ├── __init__.py │ │ ├── record.py │ │ └── operations.py │ ├── datasets │ │ └── __init__.py │ ├── events │ │ └── __init__.py │ ├── files │ │ ├── __init__.py │ │ └── lazy_lookup_file.py │ └── actions │ │ └── scheduled_trigger_operations.py │ ├── upload_agent │ ├── __init__.py │ └── exceptions.py │ ├── cli │ ├── datasets │ │ ├── shared_helpdoc.py │ │ ├── __init__.py │ │ ├── show.py │ │ ├── delete_dataset.py │ │ ├── commands.py │ │ ├── delete_files.py │ │ ├── list_files.py │ │ ├── download_files.py │ │ ├── import_external_file.py │ │ ├── upload_files.py │ │ └── create.py │ ├── orgs │ │ └── __init__.py │ ├── actions │ │ ├── __init__.py │ │ ├── invoke │ │ │ ├── __init__.py │ │ │ └── validation.py │ │ ├── commands.py │ │ ├── delete.py │ │ ├── show.py │ │ └── init.py │ ├── cache │ │ ├── __init__.py │ │ ├── commands.py │ │ ├── where.py │ │ ├── size.py │ │ └── clear.py │ ├── images │ │ ├── __init__.py │ │ ├── commands.py │ │ ├── delete_image.py │ │ ├── list_repos.py │ │ ├── delete_repo.py │ │ ├── list_images.py │ │ └── login.py │ ├── tokens │ │ └── __init__.py │ ├── triggers │ │ └── __init__.py │ ├── users │ │ └── __init__.py │ ├── __init__.py │ ├── chat │ │ └── __init__.py │ ├── extension │ │ └── __init__.py │ ├── command │ │ └── __init__.py │ ├── secrets │ │ ├── __init__.py │ │ ├── read.py │ │ ├── delete.py │ │ ├── list.py │ │ └── write.py │ ├── terminal.py │ ├── invocations │ │ ├── __init__.py │ │ ├── show.py │ │ ├── cancel.py │ │ └── status.py │ ├── collections │ │ ├── __init__.py │ │ ├── shared_helpdoc.py │ │ ├── list.py │ │ ├── delete.py │ │ ├── list_all.py │ │ ├── changes.py │ │ └── show.py │ ├── common_args │ │ ├── __init__.py │ │ └── orgs.py │ ├── argparse.py │ ├── context.py │ ├── validation.py │ └── config.py │ ├── file_infra │ ├── __init__.py │ └── object_store │ │ ├── __init__.py │ │ ├── object_store.py │ │ └── registry.py │ ├── types.py │ ├── BUILD │ ├── logging.py │ ├── query │ ├── signal_similarity │ │ ├── __init__.py │ │ └── match.py │ ├── __init__.py │ ├── visitor.py │ └── precanned.py │ ├── exceptions │ ├── ingestion.py │ ├── http.py │ └── __init__.py │ ├── serde │ ├── arrays.py │ ├── __init__.py │ ├── paths.py │ └── dicts.py │ ├── analytics │ ├── __init__.py │ └── signal_similarity │ │ └── __init__.py │ ├── ai │ ├── core │ │ ├── __init__.py │ │ └── context.py │ ├── __init__.py │ ├── record.py │ └── chat │ │ ├── __init__.py │ │ └── event.py │ ├── pydantic │ ├── __init__.py │ ├── serializers.py │ └── validators.py │ ├── compat │ ├── __init__.py │ └── _strenum.py │ ├── auth │ ├── __init__.py │ ├── permissions.py │ ├── fga.py │ └── scope.py │ ├── warnings.py │ ├── regionalization.py │ ├── action_runtime │ ├── action_input │ │ └── __init__.py │ ├── exceptions.py │ ├── __init__.py │ └── exit_codes.py │ ├── notifications │ ├── http_resources.py │ ├── __init__.py │ └── record.py │ ├── image_registry │ ├── record.py │ ├── __init__.py │ └── http_resources.py │ ├── collection_utils.py │ ├── http │ ├── retry.py │ ├── url_builder.py │ ├── headers.py │ ├── constants.py │ ├── __init__.py │ ├── testing_util.py │ └── requester.py │ ├── api_version.py │ └── waiters.py ├── .python-version ├── vendor └── robotpajamas.pants.sci │ ├── experimental │ ├── scie │ │ ├── __init__.py │ │ ├── BUILD │ │ ├── CHANGELOG.md │ │ ├── register.py │ │ ├── subsystems.py │ │ └── config.py │ └── __init__.py │ └── BUILD.pants ├── RELEASE_NOTES.md ├── cli_entrypoint.py ├── agent_entrypoint.py ├── roboto-cli-lift.toml ├── .gitignore ├── roboto-agent-lift.toml ├── pants.toml ├── examples ├── BUILD └── README.md ├── .github └── workflows │ └── sdk.yml ├── pyproject.toml └── BUILD.pants /src/roboto/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.13 2 | -------------------------------------------------------------------------------- /vendor/robotpajamas.pants.sci/experimental/scie/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/roboto/version/autogen_version.py: -------------------------------------------------------------------------------- 1 | AUTOGEN_VERSION = "0.33.1" 2 | 3 | __all__= ("AUTOGEN_VERSION",) 4 | -------------------------------------------------------------------------------- /vendor/robotpajamas.pants.sci/experimental/__init__.py: -------------------------------------------------------------------------------- 1 | # Empty file to make this directory a Python package 2 | 3 | -------------------------------------------------------------------------------- /RELEASE_NOTES.md: -------------------------------------------------------------------------------- 1 | # 0.33.1 2 | ## Bugs Fixed 3 | - Added configurable HTTP timeout (default 30 seconds) with automatic retry for idempotent requests to prevent indefinite blocking on network issues. 4 | 5 | -------------------------------------------------------------------------------- /vendor/robotpajamas.pants.sci/experimental/scie/BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Pants project contributors (see CONTRIBUTORS.md). 2 | # Licensed under the Apache License, Version 2.0 (see LICENSE). 3 | 4 | python_sources() 5 | -------------------------------------------------------------------------------- /src/roboto/domain/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | __all__ = () 8 | -------------------------------------------------------------------------------- /src/roboto/upload_agent/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | # Intentionally left blank 8 | -------------------------------------------------------------------------------- /cli_entrypoint.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | from roboto.cli import entry 7 | 8 | if __name__ == "__main__": 9 | entry() 10 | -------------------------------------------------------------------------------- /src/roboto/upload_agent/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | 8 | class UploadAgentException(Exception): 9 | pass 10 | -------------------------------------------------------------------------------- /src/roboto/cli/datasets/shared_helpdoc.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | DATASET_ID_HELP = "A unique ID used to reference a single dataset" 8 | -------------------------------------------------------------------------------- /src/roboto/cli/orgs/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .commands import command_set 8 | 9 | __all__ = ["command_set"] 10 | -------------------------------------------------------------------------------- /src/roboto/cli/actions/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .commands import command_set 8 | 9 | __all__ = ("command_set",) 10 | -------------------------------------------------------------------------------- /src/roboto/cli/cache/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .commands import command_set 8 | 9 | __all__ = ["command_set"] 10 | -------------------------------------------------------------------------------- /src/roboto/cli/datasets/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .commands import command_set 8 | 9 | __all__ = ["command_set"] 10 | -------------------------------------------------------------------------------- /src/roboto/cli/images/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .commands import command_set 8 | 9 | __all__ = ("command_set",) 10 | -------------------------------------------------------------------------------- /src/roboto/cli/tokens/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .commands import command_set 8 | 9 | __all__ = ["command_set"] 10 | -------------------------------------------------------------------------------- /src/roboto/cli/triggers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .commands import command_set 8 | 9 | __all__ = ["command_set"] 10 | -------------------------------------------------------------------------------- /src/roboto/cli/users/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .commands import command_set 8 | 9 | __all__ = ["command_set"] 10 | -------------------------------------------------------------------------------- /src/roboto/file_infra/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .file_service import FileService 8 | 9 | __all__ = ("FileService",) 10 | -------------------------------------------------------------------------------- /agent_entrypoint.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | from roboto.upload_agent.__main__ import main 7 | 8 | if __name__ == "__main__": 9 | main() 10 | -------------------------------------------------------------------------------- /src/roboto/types.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import typing 8 | 9 | JsonablePrimitive = typing.Union[int, str, float, bool] 10 | UserMetadata = dict[str, JsonablePrimitive] 11 | -------------------------------------------------------------------------------- /src/roboto/BUILD: -------------------------------------------------------------------------------- 1 | python_sources( 2 | name="roboto-src", 3 | sources=["*.py", "**/*.py"], 4 | ) 5 | 6 | resource( 7 | name="py_typed", 8 | source="py.typed", 9 | ) 10 | 11 | pex_binary( 12 | name="admin-agent", 13 | dependencies=[ 14 | ":roboto-src", 15 | ], 16 | entry_point="roboto.upload_agent.__main__:main", 17 | execution_mode="venv", 18 | output_path="bin/roboto-agent-admin" 19 | ) -------------------------------------------------------------------------------- /src/roboto/logging.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import logging 8 | 9 | LOGGER_NAME = "roboto" 10 | 11 | 12 | def default_logger(): 13 | return logging.getLogger(LOGGER_NAME) 14 | -------------------------------------------------------------------------------- /src/roboto/query/signal_similarity/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .match import Match 8 | from .query_signal import QuerySignal 9 | 10 | __all__ = ("Match", "QuerySignal") 11 | -------------------------------------------------------------------------------- /src/roboto/cli/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .entry import construct_parser, entry 8 | 9 | __version__ = "0.0.1" 10 | 11 | __all__ = ["__version__", "entry", "construct_parser"] 12 | -------------------------------------------------------------------------------- /src/roboto/exceptions/ingestion.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | 8 | class IngestionException(Exception): 9 | pass 10 | 11 | 12 | class TimestampFieldNotFoundException(IngestionException): 13 | pass 14 | -------------------------------------------------------------------------------- /roboto-cli-lift.toml: -------------------------------------------------------------------------------- 1 | [lift] 2 | name = "roboto" 3 | description = "Roboto CLI" 4 | platforms = ["linux-aarch64", "linux-x86_64", "macos-aarch64", "macos-x86_64"] 5 | 6 | [[lift.interpreters]] 7 | id = "cpython" 8 | provider = "PythonBuildStandalone" 9 | release = "20251209" 10 | version = "3.13.11" 11 | lazy = true 12 | 13 | [[lift.files]] 14 | name = ":roboto-cli" 15 | 16 | [[lift.commands]] 17 | exe = "#{cpython:python}" 18 | args = ["{:roboto-cli}"] 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/build/ 2 | .aws-sam/ 3 | .idea/ 4 | .roboto/ 5 | .env 6 | .env.production 7 | **/node_modules/ 8 | **/cdk.out/ 9 | cdk/**/*.d.ts 10 | cdk/**/*.js 11 | cdk/cdk.context.json 12 | .DS_Store 13 | 14 | # Pants Build 15 | /.pants.d 16 | /dist/ 17 | 18 | # IDEs 19 | .vscode 20 | 21 | # Python 22 | **/*.egg-info 23 | **/.mypy_cache/ 24 | **/.venv/ 25 | **/__pycache__/ 26 | **/.pytest_cache 27 | **/dist/ 28 | **/venv/ 29 | **/.ipynb_checkpoints 30 | 31 | -------------------------------------------------------------------------------- /roboto-agent-lift.toml: -------------------------------------------------------------------------------- 1 | [lift] 2 | name = "roboto-agent" 3 | description = "Roboto Agent" 4 | platforms = ["linux-aarch64", "linux-x86_64", "macos-aarch64", "macos-x86_64"] 5 | 6 | [[lift.interpreters]] 7 | id = "cpython" 8 | provider = "PythonBuildStandalone" 9 | release = "20251209" 10 | version = "3.13.11" 11 | lazy = true 12 | 13 | [[lift.files]] 14 | name = ":upload-agent" 15 | 16 | [[lift.commands]] 17 | exe = "#{cpython:python}" 18 | args = ["{:upload-agent}"] 19 | -------------------------------------------------------------------------------- /src/roboto/serde/arrays.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | 8 | def safe_access_array(array, index): 9 | if array is not None and isinstance(array, list) and len(array) > index: 10 | return array[index] 11 | return None 12 | -------------------------------------------------------------------------------- /src/roboto/analytics/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .signal_similarity import ( 8 | Match, 9 | MatchContext, 10 | find_similar_signals, 11 | ) 12 | 13 | __all__ = ("Match", "MatchContext", "find_similar_signals") 14 | -------------------------------------------------------------------------------- /src/roboto/ai/core/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | """ 8 | Core AI abstractions usable by any other submodule within module `roboto.ai`. 9 | """ 10 | 11 | from .context import RobotoLLMContext 12 | 13 | __all__ = [ 14 | "RobotoLLMContext", 15 | ] 16 | -------------------------------------------------------------------------------- /src/roboto/analytics/signal_similarity/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .match import Match, MatchContext 8 | from .signal_similarity import ( 9 | find_similar_signals, 10 | ) 11 | 12 | __all__ = ("Match", "MatchContext", "find_similar_signals") 13 | -------------------------------------------------------------------------------- /src/roboto/ai/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .record import ( 8 | PromptRequest, 9 | SetSummaryRequest, 10 | ) 11 | from .summary import AISummary 12 | 13 | __all__ = [ 14 | "AISummary", 15 | "PromptRequest", 16 | "SetSummaryRequest", 17 | ] 18 | -------------------------------------------------------------------------------- /src/roboto/pydantic/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .validators import ( 8 | remove_non_noneable_init_args, 9 | validate_nonzero_gitpath_specs, 10 | ) 11 | 12 | __all__ = [ 13 | "remove_non_noneable_init_args", 14 | "validate_nonzero_gitpath_specs", 15 | ] 16 | -------------------------------------------------------------------------------- /src/roboto/cli/chat/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from ..command import RobotoCommandSet 8 | from .start import start_command 9 | 10 | command_set = RobotoCommandSet( 11 | name="chat", 12 | help="Talk with your data, using natural language.", 13 | commands=[start_command], 14 | ) 15 | -------------------------------------------------------------------------------- /src/roboto/domain/tokens/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .operations import CreateTokenRequest 8 | from .record import TokenContext, TokenRecord 9 | from .token import Token 10 | 11 | __all__ = [ 12 | "CreateTokenRequest", 13 | "Token", 14 | "TokenRecord", 15 | "TokenContext", 16 | ] 17 | -------------------------------------------------------------------------------- /src/roboto/compat/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from ._optional import ( 8 | IMPORT_ERROR_MSG_TEMPLATE, 9 | import_optional_dependency, 10 | ) 11 | from ._strenum import StrEnum 12 | 13 | __all__ = ( 14 | "IMPORT_ERROR_MSG_TEMPLATE", 15 | "import_optional_dependency", 16 | "StrEnum", 17 | ) 18 | -------------------------------------------------------------------------------- /src/roboto/cli/actions/invoke/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | """Action invocation commands for hosted and local execution.""" 8 | 9 | from .invoke_hosted_cmd import ( 10 | invoke_hosted_command, 11 | ) 12 | from .invoke_local_cmd import invoke_local_command 13 | 14 | __all__ = ["invoke_hosted_command", "invoke_local_command"] 15 | -------------------------------------------------------------------------------- /src/roboto/auth/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .fga import ( 8 | AuthZTupleRecord, 9 | EditAccessRequest, 10 | GetAccessResponse, 11 | ) 12 | from .permissions import Permissions 13 | 14 | __all__ = ( 15 | "Permissions", 16 | "AuthZTupleRecord", 17 | "EditAccessRequest", 18 | "GetAccessResponse", 19 | ) 20 | -------------------------------------------------------------------------------- /src/roboto/domain/users/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .operations import ( 8 | CreateUserRequest, 9 | UpdateUserRequest, 10 | ) 11 | from .record import UserRecord 12 | from .user import User 13 | 14 | __all__ = [ 15 | "CreateUserRequest", 16 | "User", 17 | "UserRecord", 18 | "UpdateUserRequest", 19 | ] 20 | -------------------------------------------------------------------------------- /src/roboto/warnings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import warnings 8 | 9 | 10 | def roboto_default_warning_behavior(): 11 | # https://github.com/boto/botocore/issues/619 12 | warnings.filterwarnings( 13 | "ignore", 14 | module="botocore.vendored.requests.packages.urllib3.connectionpool", 15 | message=".*", 16 | ) 17 | -------------------------------------------------------------------------------- /src/roboto/compat/_strenum.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import enum 8 | import sys 9 | 10 | if sys.version_info >= (3, 11): 11 | from enum import StrEnum 12 | else: 13 | 14 | class StrEnum(str, enum.Enum): 15 | """Compatibility StrEnum for Python < 3.11""" 16 | 17 | def __str__(self) -> str: 18 | return self.value 19 | -------------------------------------------------------------------------------- /src/roboto/cli/extension/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .cli_extension import ( 8 | RobotoCLIExtension, 9 | apply_roboto_cli_command_extensions, 10 | apply_roboto_cli_context_extensions, 11 | ) 12 | 13 | __all__ = [ 14 | "RobotoCLIExtension", 15 | "apply_roboto_cli_command_extensions", 16 | "apply_roboto_cli_context_extensions", 17 | ] 18 | -------------------------------------------------------------------------------- /src/roboto/domain/layouts/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .operations import ( 8 | CreateLayoutRequest, 9 | UpdateLayoutRequest, 10 | ) 11 | from .record import ( 12 | LayoutAccessibility, 13 | LayoutRecord, 14 | ) 15 | 16 | __all__ = [ 17 | "LayoutAccessibility", 18 | "LayoutRecord", 19 | "CreateLayoutRequest", 20 | "UpdateLayoutRequest", 21 | ] 22 | -------------------------------------------------------------------------------- /vendor/robotpajamas.pants.sci/BUILD.pants: -------------------------------------------------------------------------------- 1 | # Upstream: https://github.com/sureshjoshi/pants-plugins 2 | # Vendored at commit: 228cd5ec43cfd6923281c7c153d6ad85860626cf 3 | # On: 2025-12-11 4 | # Because: 5 | # The science tool (used by this plugin) needs ldd to detect the libc version at module import time on Linux, 6 | # (see https://github.com/a-scie/lift/blob/24af473e116eb780350719f61bcdaa01b40ccf19/science/platform.py#L155-L167) 7 | # but Pants runs all processes in hermetic sandboxes with env -i (empty environment), which clears PATH. 8 | # Without PATH, the ldd command cannot be found, causing the build to fail. 9 | 10 | pants_requirements(name="pants") 11 | -------------------------------------------------------------------------------- /src/roboto/cli/command/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .args import ( 8 | ExistingPathlibPath, 9 | JsonFileOrStrType, 10 | KeyValuePairsAction, 11 | ) 12 | from .roboto_command import ( 13 | RobotoCommand, 14 | RobotoCommandSet, 15 | ) 16 | 17 | __all__ = [ 18 | "ExistingPathlibPath", 19 | "JsonFileOrStrType", 20 | "KeyValuePairsAction", 21 | "RobotoCommand", 22 | "RobotoCommandSet", 23 | ] 24 | -------------------------------------------------------------------------------- /src/roboto/file_infra/object_store/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .object_store import ( 8 | CredentialProvider, 9 | Credentials, 10 | FutureLike, 11 | ObjectStore, 12 | ) 13 | from .registry import StoreRegistry 14 | from .s3 import S3Store 15 | 16 | __all__ = ( 17 | "Credentials", 18 | "CredentialProvider", 19 | "FutureLike", 20 | "ObjectStore", 21 | "StoreRegistry", 22 | "S3Store", 23 | ) 24 | -------------------------------------------------------------------------------- /src/roboto/cli/cache/commands.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from ..command import RobotoCommandSet 8 | from .clear import clear_command 9 | from .size import size_command 10 | from .where import where_command 11 | 12 | commands = [ 13 | clear_command, 14 | size_command, 15 | where_command, 16 | ] 17 | 18 | command_set = RobotoCommandSet( 19 | name="cache", 20 | help="Manage the local Roboto cache.", 21 | commands=commands, 22 | ) 23 | -------------------------------------------------------------------------------- /src/roboto/regionalization.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .compat import StrEnum 8 | 9 | 10 | class RobotoRegion(StrEnum): 11 | """ 12 | The geographic region of a Roboto resource. Used when configuring org-level default behavior for data storage, in 13 | order to ensure that data is close to your users. 14 | """ 15 | 16 | US_WEST = "us-west" 17 | US_GOV_WEST = "us-gov-west" 18 | US_EAST = "us-east" 19 | EU_CENTRAL = "eu-central" 20 | -------------------------------------------------------------------------------- /vendor/robotpajamas.pants.sci/experimental/scie/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [0.1.1] - 2025-11-05 6 | 7 | - Required was incorrectly set to 3.14 8 | 9 | ## [0.1.0] - 2025-11-05 10 | 11 | - Migrated to call-by-name 12 | - Updated science to 0.15.1 13 | - Updated PBS to 20251031 14 | - Requires Python >= 3.11 15 | 16 | ## [0.0.5] - 2024-12-18 17 | 18 | - Expanded `requires` to being greater than 3.9 19 | 20 | ## [0.0.4] - 2024-12-18 21 | 22 | - Bumped `science` to 0.9.0 23 | - No assertion on Python 3.12 or 3.13 24 | 25 | ## [0.0.3] - 2024-05-09 26 | 27 | - Bumped `science` to 0.3.4 28 | - Migrated to Pants 2.20 (removed `@rule_helper`) 29 | 30 | ## [0.0.2] - 2023-05-22 31 | 32 | First release -------------------------------------------------------------------------------- /src/roboto/cli/secrets/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from ..command import RobotoCommandSet 8 | from .delete import delete_command 9 | from .list import list_command 10 | from .read import read_command 11 | from .write import write_command 12 | 13 | command_set = RobotoCommandSet( 14 | name="secrets", 15 | help="Manage secure secrets registered with Roboto. These are typically 3rd party API keys to power integrations.", 16 | commands=[delete_command, read_command, write_command, list_command], 17 | ) 18 | -------------------------------------------------------------------------------- /src/roboto/version/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import importlib.metadata 8 | 9 | try: 10 | from .autogen_version import AUTOGEN_VERSION 11 | 12 | __version__ = AUTOGEN_VERSION 13 | except ImportError: 14 | __version__ = "0.0.0" 15 | 16 | 17 | def roboto_version() -> str: 18 | try: 19 | return importlib.metadata.version("roboto") 20 | except importlib.metadata.PackageNotFoundError: 21 | return "version_not_found" 22 | 23 | 24 | __all__ = ["__version__", "roboto_version"] 25 | -------------------------------------------------------------------------------- /src/roboto/ai/record.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import pydantic 8 | 9 | 10 | class PromptRequest(pydantic.BaseModel): 11 | """ 12 | A generic request intended for a natural-language powered endpoint which accepts a human-readable prompt. 13 | """ 14 | 15 | prompt: str 16 | """The prompt to send to the AI model.""" 17 | 18 | 19 | class SetSummaryRequest(pydantic.BaseModel): 20 | """ 21 | A request to set the summary of an entity. 22 | """ 23 | 24 | summary: str 25 | """The summary to set.""" 26 | -------------------------------------------------------------------------------- /src/roboto/action_runtime/action_input/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .action_input import ( 8 | DEFAULT_INPUT_FILE, 9 | ActionInput, 10 | ActionInputRecord, 11 | ) 12 | from .file_resolver import InputFileResolver 13 | from .input_resolver import ActionInputResolver 14 | from .topic_resolver import InputTopicResolver 15 | 16 | __all__ = ( 17 | "DEFAULT_INPUT_FILE", 18 | "ActionInput", 19 | "ActionInputRecord", 20 | "ActionInputResolver", 21 | "InputFileResolver", 22 | "InputTopicResolver", 23 | ) 24 | -------------------------------------------------------------------------------- /src/roboto/notifications/http_resources.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from typing import Optional 8 | 9 | import pydantic 10 | 11 | from .record import ( 12 | NotificationChannel, 13 | ReadStatus, 14 | ) 15 | from .validator import LifecycleStatusValidator 16 | 17 | 18 | class UpdateNotificationRequest(pydantic.BaseModel, LifecycleStatusValidator): 19 | """Request payload to update a notification""" 20 | 21 | notification_id: str 22 | read_status: Optional[ReadStatus] = None 23 | lifecycle_status: Optional[dict[NotificationChannel, str]] = None 24 | -------------------------------------------------------------------------------- /src/roboto/pydantic/serializers.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import decimal 8 | import typing 9 | 10 | from roboto.types import UserMetadata 11 | 12 | 13 | def field_serializer_user_metadata(value: dict[str, typing.Any]) -> UserMetadata: 14 | for k, v in value.items(): 15 | if type(v) in [bool, int, float, str]: 16 | continue 17 | elif type(v) is decimal.Decimal: 18 | value[k] = float(v) 19 | else: 20 | raise ValueError(f"Illegal metadata element with key '{k}', type {type(v)}") 21 | 22 | return value 23 | -------------------------------------------------------------------------------- /src/roboto/domain/topics/parquet/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .arrow_to_roboto import ( 8 | field_to_message_path_request, 9 | ) 10 | from .ingestion import ( 11 | make_topic_filename_safe, 12 | upload_representation_file, 13 | ) 14 | from .parquet_parser import ParquetParser 15 | from .parquet_topic_reader import ( 16 | ParquetTopicReader, 17 | ) 18 | 19 | __all__ = ( 20 | "field_to_message_path_request", 21 | "make_topic_filename_safe", 22 | "ParquetParser", 23 | "ParquetTopicReader", 24 | "upload_representation_file", 25 | ) 26 | -------------------------------------------------------------------------------- /vendor/robotpajamas.pants.sci/experimental/scie/register.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Pants project contributors (see CONTRIBUTORS.md). 2 | # Licensed under the Apache License, Version 2.0 (see LICENSE). 3 | 4 | """A Self Contained Interpreted Executable Launcher. 5 | 6 | See https://github.com/a-scie/jump for details. 7 | """ 8 | 9 | from __future__ import annotations 10 | 11 | from collections.abc import Iterable 12 | 13 | from experimental.scie.rules import rules as scie_rules 14 | from experimental.scie.target_types import ScieTarget 15 | from pants.engine.rules import Rule 16 | from pants.engine.target import Target 17 | from pants.engine.unions import UnionRule 18 | 19 | 20 | def rules() -> Iterable[Rule | UnionRule]: 21 | return (*scie_rules(),) 22 | 23 | 24 | def target_types() -> Iterable[type[Target]]: 25 | return (ScieTarget,) 26 | -------------------------------------------------------------------------------- /src/roboto/cli/cache/where.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from ..command import RobotoCommand 10 | from ..context import CLIContext 11 | 12 | 13 | def where(args, context: CLIContext, parser: argparse.ArgumentParser): 14 | cache_dir = context.roboto_config.get_cache_dir() 15 | print(cache_dir) 16 | 17 | 18 | def where_setup_parser(parser): 19 | pass 20 | 21 | 22 | where_command = RobotoCommand( 23 | name="where", 24 | logic=where, 25 | setup_parser=where_setup_parser, 26 | command_kwargs={"help": "Prints the path to the Roboto cache directory."}, 27 | ) 28 | -------------------------------------------------------------------------------- /src/roboto/image_registry/record.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import pydantic 8 | 9 | 10 | class ContainerImageRepositoryRecord(pydantic.BaseModel): 11 | """ 12 | A wire-transmissible representation of a container image repository. 13 | """ 14 | 15 | org_id: str 16 | repository_name: str 17 | repository_uri: str 18 | arn: str 19 | 20 | 21 | class ContainerImageRecord(pydantic.BaseModel): 22 | """ 23 | A wire-transmissible representation of a container image. 24 | """ 25 | 26 | org_id: str 27 | repository_name: str 28 | image_tag: str 29 | image_uri: str 30 | -------------------------------------------------------------------------------- /src/roboto/cli/terminal.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import sys 8 | import typing 9 | 10 | from ..compat import StrEnum 11 | 12 | 13 | class AnsiColor(StrEnum): 14 | RED = "\033[0;31m" 15 | GREEN = "\033[0;32m" 16 | BLUE = "\033[0;34m" 17 | END = "\033[0m" 18 | 19 | 20 | def print_error_and_exit(msg: typing.Union[str, list[str]]) -> typing.NoReturn: 21 | if isinstance(msg, list): 22 | for line in msg: 23 | print(f"{AnsiColor.RED}[error]{AnsiColor.END} {line}", file=sys.stderr) 24 | else: 25 | print(f"{AnsiColor.RED}[error]{AnsiColor.END} {msg}", file=sys.stderr) 26 | sys.exit(1) 27 | -------------------------------------------------------------------------------- /src/roboto/collection_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import collections.abc 8 | import functools 9 | import typing 10 | 11 | 12 | def get_by_path( 13 | target: collections.abc.Mapping[typing.Any, typing.Any], 14 | key_path: collections.abc.Sequence[typing.Any], 15 | ) -> typing.Any: 16 | """ 17 | Access a key path in a mapping. 18 | Returns ``None`` if any part of the path is not found or traverses through an object that is not a mapping. 19 | """ 20 | try: 21 | return functools.reduce(lambda d, key: d.get(key, None), key_path, target) 22 | except (TypeError, AttributeError): 23 | return None 24 | -------------------------------------------------------------------------------- /src/roboto/action_runtime/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .exit_codes import ExitCode 8 | 9 | 10 | class ActionRuntimeException(Exception): 11 | """ 12 | Base class for all exceptions raised by the action_runtime submodule. 13 | """ 14 | 15 | 16 | class PrepareEnvException(ActionRuntimeException): 17 | exit_code: ExitCode 18 | reason: str 19 | 20 | def __init__(self, exit_code: ExitCode, reason: str): 21 | super().__init__() 22 | self.exit_code = exit_code 23 | self.reason = reason 24 | 25 | def __str__(self) -> str: 26 | return f"{self.reason} (Exit code {self.exit_code})" 27 | -------------------------------------------------------------------------------- /src/roboto/ai/chat/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .chat import Chat 8 | from .record import ( 9 | ChatContent, 10 | ChatErrorContent, 11 | ChatMessage, 12 | ChatMessageStatus, 13 | ChatRecord, 14 | ChatRole, 15 | ChatStatus, 16 | ChatTextContent, 17 | SendMessageRequest, 18 | StartChatRequest, 19 | ) 20 | 21 | __all__ = [ 22 | "Chat", 23 | "ChatContent", 24 | "ChatErrorContent", 25 | "ChatMessage", 26 | "ChatMessageStatus", 27 | "ChatRecord", 28 | "ChatRole", 29 | "ChatStatus", 30 | "ChatTextContent", 31 | "SendMessageRequest", 32 | "StartChatRequest", 33 | ] 34 | -------------------------------------------------------------------------------- /src/roboto/domain/secrets/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .record import ( 8 | AwsSecretRetrievalLocation, 9 | AwsSecretsManagerAccessCreds, 10 | CreateSecretRequest, 11 | GetSecretAccessCredsResponse, 12 | SecretAccessCreds, 13 | SecretRecord, 14 | SecretStoreType, 15 | ) 16 | from .secret import Secret, is_secret_uri 17 | 18 | __all__ = [ 19 | "is_secret_uri", 20 | "AwsSecretsManagerAccessCreds", 21 | "AwsSecretRetrievalLocation", 22 | "CreateSecretRequest", 23 | "GetSecretAccessCredsResponse", 24 | "SecretRecord", 25 | "SecretStoreType", 26 | "SecretAccessCreds", 27 | "Secret", 28 | ] 29 | -------------------------------------------------------------------------------- /src/roboto/auth/permissions.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import enum 8 | 9 | 10 | class Permissions(enum.Enum): 11 | """ 12 | Enum for permission levels of a Roboto resource. 13 | It is a best practice to only request/use the minimum permissions required for a given operation. 14 | 15 | For example: 16 | - When listing files associated with a dataset or pulling a container image hosted in Roboto's registry, 17 | use ``ReadOnly`` permissions. 18 | - When adding files to a dataset or pushing a container image to Roboto's registry, use ``ReadWrite`` permissions. 19 | """ 20 | 21 | ReadOnly = "ReadOnly" 22 | ReadWrite = "ReadWrite" 23 | -------------------------------------------------------------------------------- /src/roboto/cli/invocations/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from ..command import RobotoCommandSet 8 | from .cancel import cancel_command 9 | from .cancel_all import cancel_all_command 10 | from .logs import get_logs_command 11 | from .show import show_command 12 | from .status import status_command 13 | 14 | commands = [ 15 | cancel_command, 16 | cancel_all_command, 17 | get_logs_command, 18 | show_command, 19 | status_command, 20 | ] 21 | 22 | command_set = RobotoCommandSet( 23 | name="invocations", 24 | help=("Get logs and status history from invoked actions, including the option to cancel them."), 25 | commands=commands, 26 | ) 27 | -------------------------------------------------------------------------------- /src/roboto/notifications/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .http_client import NotificationsClient 8 | from .http_resources import ( 9 | UpdateNotificationRequest, 10 | ) 11 | from .record import NotificationRecord 12 | from .validator import ( 13 | EmailLifecycleStatus, 14 | NotificationChannel, 15 | NotificationType, 16 | ReadStatus, 17 | WebUiLifecycleStatus, 18 | ) 19 | 20 | __all__ = [ 21 | "NotificationType", 22 | "NotificationChannel", 23 | "NotificationsClient", 24 | "NotificationRecord", 25 | "ReadStatus", 26 | "UpdateNotificationRequest", 27 | "EmailLifecycleStatus", 28 | "WebUiLifecycleStatus", 29 | ] 30 | -------------------------------------------------------------------------------- /src/roboto/cli/collections/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from ..command import RobotoCommandSet 8 | from .changes import changes_command 9 | from .create import create_command 10 | from .delete import delete_command 11 | from .list import list_command 12 | from .show import show_command 13 | from .update import update_command 14 | 15 | command_set = RobotoCommandSet( 16 | name="collections", 17 | help="Curate collections of datasets and other data types.", 18 | commands=[ 19 | changes_command, 20 | create_command, 21 | delete_command, 22 | list_command, 23 | show_command, 24 | update_command, 25 | ], 26 | ) 27 | -------------------------------------------------------------------------------- /src/roboto/cli/collections/shared_helpdoc.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | 8 | COLLECTION_ID_HELP = "A unique ID used to reference a collection of Roboto resources." 9 | COLLECTION_VERSION_HELP = ( 10 | "An optional version for the provided collection ID." 11 | + " Allows caller to ensure that they're referencing an immutable representation of a given collection." 12 | ) 13 | CONTENT_MODE_HELP = ( 14 | "The type of content to return for a collection or set of collections. ``summary_only`` returns " 15 | + "only metadata, ``references`` will return each id in the collection, " 16 | + "and ``full`` will retrieve the full content of each resource in the collection." 17 | ) 18 | -------------------------------------------------------------------------------- /src/roboto/cli/collections/list.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from ...domain.collections import Collection 10 | from ..command import RobotoCommand 11 | from ..common_args import add_org_arg 12 | from ..context import CLIContext 13 | 14 | 15 | def list(args, context: CLIContext, parser: argparse.ArgumentParser): 16 | for collection in Collection.list_all( 17 | owner_org_id=args.org, 18 | roboto_client=context.roboto_client, 19 | ): 20 | print(collection.record.model_dump_json(indent=2)) 21 | 22 | 23 | list_command = RobotoCommand( 24 | name="list", 25 | logic=list, 26 | setup_parser=add_org_arg, 27 | command_kwargs={"help": "List existing collections."}, 28 | ) 29 | -------------------------------------------------------------------------------- /src/roboto/domain/collections/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .collection import Collection 8 | from .operations import ( 9 | CreateCollectionRequest, 10 | UpdateCollectionRequest, 11 | ) 12 | from .record import ( 13 | CollectionChangeRecord, 14 | CollectionChangeSet, 15 | CollectionContentMode, 16 | CollectionRecord, 17 | CollectionResourceRef, 18 | CollectionResourceType, 19 | ) 20 | 21 | __all__ = [ 22 | "Collection", 23 | "CollectionChangeRecord", 24 | "CollectionChangeSet", 25 | "CollectionContentMode", 26 | "CollectionRecord", 27 | "CollectionResourceRef", 28 | "CollectionResourceType", 29 | "CreateCollectionRequest", 30 | "UpdateCollectionRequest", 31 | ] 32 | -------------------------------------------------------------------------------- /src/roboto/http/retry.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import random 8 | import typing 9 | 10 | import tenacity.wait 11 | 12 | RetryWaitFn = typing.Callable[[tenacity.RetryCallState, typing.Optional[BaseException]], float] 13 | 14 | 15 | def default_retry_wait_ms(retry_state: tenacity.RetryCallState, _exc: typing.Optional[BaseException]) -> float: 16 | """ 17 | Returns sleep time in ms using exponential backoff with full jitter, as described in: 18 | https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ 19 | """ 20 | base = 500 21 | cap = 30_000 22 | 23 | exponential_wait = min(cap, pow(2, retry_state.attempt_number) * base) 24 | jittered = random.uniform(0, exponential_wait) 25 | return jittered 26 | -------------------------------------------------------------------------------- /pants.toml: -------------------------------------------------------------------------------- 1 | [GLOBAL] 2 | pants_version = "2.30.0" 3 | 4 | pythonpath = ["%(buildroot)s/vendor/robotpajamas.pants.sci"] 5 | 6 | backend_packages = [ 7 | "experimental.scie", 8 | "pants.backend.python", 9 | "pants.backend.python.typecheck.mypy", 10 | "pants.backend.plugin_development", 11 | ] 12 | 13 | build_patterns = ["BUILD.pants", "BUILD"] 14 | 15 | [source] 16 | root_patterns = [ 17 | "/", 18 | "vendor/robotpajamas.pants.sci", 19 | ] 20 | 21 | [python] 22 | enable_resolves = true 23 | default_resolve = "python-default" 24 | resolver_manylinux = "manylinux_2_28" 25 | interpreter_constraints = ["==3.13.*"] 26 | 27 | [python.resolves] 28 | python-default = "build-support/lockfiles/python-default.lock" 29 | 30 | [mypy] 31 | args = [ 32 | "--check-untyped-defs", 33 | "--follow-imports silent", 34 | ] 35 | config = "build-support/requirements/tools/pyproject.toml" 36 | install_from_resolve = "python-default" 37 | 38 | [pytest] 39 | install_from_resolve = "python-default" -------------------------------------------------------------------------------- /src/roboto/domain/tokens/record.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import datetime 8 | from typing import Optional 9 | 10 | import pydantic 11 | 12 | from roboto.auth.scope import ApiScope 13 | 14 | 15 | class TokenContext(pydantic.BaseModel): 16 | """Context for token usage""" 17 | 18 | token_id: str 19 | name: str 20 | description: Optional[str] = None 21 | expires: datetime.datetime 22 | last_used: Optional[datetime.datetime] = None 23 | api_scopes: set[ApiScope] 24 | enabled: bool = True 25 | 26 | 27 | class TokenRecord(pydantic.BaseModel): 28 | """A wire-transmissible representation of a token.""" 29 | 30 | secret: Optional[str] = None 31 | user_id: Optional[str] = None 32 | context: Optional[TokenContext] = None 33 | -------------------------------------------------------------------------------- /src/roboto/domain/tokens/operations.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import typing 8 | 9 | import pydantic 10 | 11 | from roboto.auth.scope import ApiScope 12 | 13 | 14 | class CreateTokenRequest(pydantic.BaseModel): 15 | """Request payload to create a new token""" 16 | 17 | api_scopes: typing.Optional[set[ApiScope]] = pydantic.Field( 18 | default=None, description="Optional set of API scopes to grant this token." 19 | ) 20 | description: typing.Optional[str] = pydantic.Field( 21 | default=None, description="An optional longer description for this token." 22 | ) 23 | expiry_days: int = pydantic.Field(description="Number of days until the token expires") 24 | name: str = pydantic.Field(description="A human-readable name for this token.") 25 | -------------------------------------------------------------------------------- /src/roboto/cli/images/commands.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from ..command import RobotoCommandSet 8 | from .delete_image import delete_image_command 9 | from .delete_repo import delete_repo_command 10 | from .list_images import ls_images_command 11 | from .list_repos import ls_repos_command 12 | from .login import login_command 13 | from .pull import pull_command 14 | from .push import push_command 15 | 16 | commands = [ 17 | pull_command, 18 | push_command, 19 | ls_images_command, 20 | ls_repos_command, 21 | delete_image_command, 22 | delete_repo_command, 23 | login_command, 24 | ] 25 | 26 | command_set = RobotoCommandSet( 27 | name="images", 28 | help=("Push and pull images from Roboto's private container registry."), 29 | commands=commands, 30 | ) 31 | -------------------------------------------------------------------------------- /src/roboto/cli/invocations/show.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from ...domain import actions 10 | from ..command import RobotoCommand 11 | from ..context import CLIContext 12 | 13 | 14 | def show(args: argparse.Namespace, context: CLIContext, parser: argparse.ArgumentParser) -> None: 15 | invocation = actions.Invocation.from_id( 16 | args.invocation_id, 17 | roboto_client=context.roboto_client, 18 | ) 19 | print(str(invocation)) 20 | return 21 | 22 | 23 | def show_parser(parser: argparse.ArgumentParser): 24 | parser.add_argument("invocation_id") 25 | 26 | 27 | show_command = RobotoCommand( 28 | name="show", 29 | logic=show, 30 | setup_parser=show_parser, 31 | command_kwargs={"help": "Show invocation details."}, 32 | ) 33 | -------------------------------------------------------------------------------- /src/roboto/http/url_builder.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import typing 8 | import urllib.parse 9 | 10 | 11 | class UrlBuilder: 12 | __endpoint: str 13 | 14 | def __init__(self, endpoint: str): 15 | self.__endpoint = endpoint.rstrip("/") 16 | 17 | def build(self, path: str, query: typing.Optional[dict[str, typing.Any]] = None): 18 | return UrlBuilder.url(endpoint=self.__endpoint, path=path, query=query) 19 | 20 | @staticmethod 21 | def url(endpoint: str, path: str, query: typing.Optional[dict[str, typing.Any]] = None) -> str: 22 | normalized_path = path.lstrip("/") 23 | 24 | if query is None: 25 | return f"{endpoint}/{normalized_path}" 26 | else: 27 | return f"{endpoint}/{normalized_path}?{urllib.parse.urlencode(query)}" 28 | -------------------------------------------------------------------------------- /src/roboto/cli/datasets/show.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | import json 9 | 10 | from ...domain.datasets import Dataset 11 | from ..command import RobotoCommand 12 | from ..context import CLIContext 13 | from .shared_helpdoc import DATASET_ID_HELP 14 | 15 | 16 | def show(args, context: CLIContext, parser: argparse.ArgumentParser): 17 | record = Dataset.from_id(args.dataset_id, context.roboto_client) 18 | print(json.dumps(record.to_dict(), indent=2)) 19 | 20 | 21 | def show_setup_parser(parser): 22 | parser.add_argument("-d", "--dataset-id", type=str, required=True, help=DATASET_ID_HELP) 23 | 24 | 25 | show_command = RobotoCommand( 26 | name="show", 27 | logic=show, 28 | setup_parser=show_setup_parser, 29 | command_kwargs={"help": "Display detailed information about a dataset."}, 30 | ) 31 | -------------------------------------------------------------------------------- /src/roboto/cli/invocations/cancel.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from ...domain import actions 10 | from ..command import RobotoCommand 11 | from ..context import CLIContext 12 | 13 | 14 | def cancel(args: argparse.Namespace, context: CLIContext, parser: argparse.ArgumentParser) -> None: 15 | invocation = actions.Invocation.from_id( 16 | args.invocation_id, 17 | roboto_client=context.roboto_client, 18 | ) 19 | invocation.cancel() 20 | print("Invocation cancelled.") 21 | return 22 | 23 | 24 | def cancel_parser(parser: argparse.ArgumentParser): 25 | parser.add_argument("invocation_id") 26 | 27 | 28 | cancel_command = RobotoCommand( 29 | name="cancel", 30 | logic=cancel, 31 | setup_parser=cancel_parser, 32 | command_kwargs={"help": "Cancel invocation."}, 33 | ) 34 | -------------------------------------------------------------------------------- /src/roboto/cli/collections/delete.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from ...domain.collections import Collection 10 | from ..command import RobotoCommand 11 | from ..context import CLIContext 12 | from .shared_helpdoc import COLLECTION_ID_HELP 13 | 14 | 15 | def delete(args, context: CLIContext, parser: argparse.ArgumentParser): 16 | Collection.from_id( 17 | collection_id=args.collection_id, 18 | roboto_client=context.roboto_client, 19 | ).delete() 20 | print(f"Deleted collection {args.collection_id}") 21 | 22 | 23 | def delete_setup_parser(parser): 24 | parser.add_argument("collection_id", type=str, help=COLLECTION_ID_HELP) 25 | 26 | 27 | delete_command = RobotoCommand( 28 | name="delete", 29 | logic=delete, 30 | setup_parser=delete_setup_parser, 31 | command_kwargs={"help": "Delete a collection."}, 32 | ) 33 | -------------------------------------------------------------------------------- /src/roboto/notifications/record.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import datetime 8 | 9 | import pydantic 10 | 11 | from .validator import ( 12 | LifecycleStatusValidator, 13 | NotificationChannel, 14 | NotificationType, 15 | ReadStatus, 16 | ) 17 | 18 | 19 | class NotificationRecord(pydantic.BaseModel, LifecycleStatusValidator): 20 | """ 21 | A wire-transmissible representation of a notification. 22 | """ 23 | 24 | notification_id: str 25 | org_id: str 26 | user_id: str 27 | notifier_id: str 28 | notification_type: NotificationType 29 | channels: list[NotificationChannel] = pydantic.Field(default_factory=list) 30 | lifecycle_status: dict[NotificationChannel, str] = pydantic.Field(default_factory=dict) 31 | read_status: ReadStatus = ReadStatus.Unread 32 | created: datetime.datetime 33 | modified: datetime.datetime 34 | -------------------------------------------------------------------------------- /src/roboto/cli/common_args/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .actions import ( 8 | ActionParameterArg, 9 | ActionReferenceParser, 10 | ActionTimeoutArg, 11 | add_action_reference_arg, 12 | add_compute_requirements_args, 13 | add_container_parameters_args, 14 | parse_action_reference_string, 15 | parse_compute_requirements, 16 | parse_container_overrides, 17 | ) 18 | from .orgs import ( 19 | add_org_arg, 20 | get_defaulted_org_id, 21 | ) 22 | 23 | __all__ = ( 24 | "ActionParameterArg", 25 | "ActionReferenceParser", 26 | "ActionTimeoutArg", 27 | "add_action_reference_arg", 28 | "add_compute_requirements_args", 29 | "add_container_parameters_args", 30 | "add_org_arg", 31 | "get_defaulted_org_id", 32 | "parse_action_reference_string", 33 | "parse_compute_requirements", 34 | "parse_container_overrides", 35 | ) 36 | -------------------------------------------------------------------------------- /src/roboto/cli/datasets/delete_dataset.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from ...domain.datasets import Dataset 10 | from ..command import RobotoCommand 11 | from ..context import CLIContext 12 | from .shared_helpdoc import DATASET_ID_HELP 13 | 14 | 15 | def delete_dataset(args, context: CLIContext, parser: argparse.ArgumentParser): 16 | dataset = Dataset.from_id(args.dataset_id, context.roboto_client) 17 | dataset.delete() 18 | print(f"Deleted dataset {args.dataset_id}") 19 | 20 | 21 | def delete_dataset_setup_parser(parser): 22 | parser.add_argument("-d", "--dataset-id", type=str, required=True, help=DATASET_ID_HELP) 23 | 24 | 25 | delete_dataset_command = RobotoCommand( 26 | name="delete", 27 | logic=delete_dataset, 28 | setup_parser=delete_dataset_setup_parser, 29 | command_kwargs={"help": "Delete a dataset and all contained resources."}, 30 | ) 31 | -------------------------------------------------------------------------------- /src/roboto/auth/fga.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import pydantic 8 | 9 | 10 | class AuthZTupleRecord(pydantic.BaseModel): 11 | """ 12 | Fully qualified record of (user has relation to obj) 13 | """ 14 | 15 | user: str 16 | relation: str 17 | obj: str 18 | 19 | 20 | class EditAccessRequest(pydantic.BaseModel): 21 | """ 22 | Request payload to add or remove fine-grained access to a Roboto resource 23 | """ 24 | 25 | add: list[AuthZTupleRecord] = pydantic.Field(default_factory=list) 26 | remove: list[AuthZTupleRecord] = pydantic.Field(default_factory=list) 27 | 28 | 29 | class GetAccessResponse(pydantic.BaseModel): 30 | """ 31 | Response payload for a request to describe fine-grained access to a Roboto resource 32 | """ 33 | 34 | relations: list[AuthZTupleRecord] 35 | group_permissions: dict[str, list[str]] = pydantic.Field(default_factory=dict) 36 | -------------------------------------------------------------------------------- /src/roboto/serde/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | 8 | """ 9 | SerDe: Serialization + Deserialization 10 | 11 | This module contains utility functions for common serialization and deserialization scenarios used 12 | throughout the Roboto python codebase. 13 | """ 14 | 15 | from .arrays import safe_access_array 16 | from .dicts import ( 17 | case_insensitive_get, 18 | pydantic_jsonable_dict, 19 | pydantic_jsonable_dict_map, 20 | pydantic_jsonable_dicts, 21 | safe_dict_drill, 22 | ) 23 | from .paths import ( 24 | exclude_patterns_to_spec, 25 | git_paths_match, 26 | git_paths_to_spec, 27 | ) 28 | 29 | __all__ = [ 30 | "pydantic_jsonable_dict", 31 | "pydantic_jsonable_dicts", 32 | "pydantic_jsonable_dict_map", 33 | "safe_dict_drill", 34 | "case_insensitive_get", 35 | "exclude_patterns_to_spec", 36 | "git_paths_to_spec", 37 | "git_paths_match", 38 | "safe_access_array", 39 | ] 40 | -------------------------------------------------------------------------------- /src/roboto/ai/core/context.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from typing import Any, Optional 8 | 9 | import pydantic 10 | 11 | 12 | class RobotoLLMContext(pydantic.BaseModel): 13 | """Contextual information about what a user is trying to do. May be passed along to Roboto LLM based code paths 14 | in order to enrich results""" 15 | 16 | dataset_ids: list[str] = pydantic.Field(default_factory=list) 17 | """IDs of datasets that are relevant to the user's query.""" 18 | 19 | file_ids: list[str] = pydantic.Field(default_factory=list) 20 | """IDs of files that are relevant to the user's query.""" 21 | 22 | visualizer_state: Optional[dict[str, Any]] = None 23 | """State of the visualizer, if a request is being made from the visualizer. This is expected to be a relatively 24 | opaque JSON blob""" 25 | 26 | misc_context: Optional[dict[str, Any]] = None 27 | """Miscellaneous context that is relevant to the user's query.""" 28 | -------------------------------------------------------------------------------- /src/roboto/cli/actions/commands.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from ..command import RobotoCommandSet 8 | from .create import create_command 9 | from .delete import delete_command 10 | from .init import init_command 11 | from .invoke import ( 12 | invoke_hosted_command, 13 | invoke_local_command, 14 | ) 15 | from .list_invocations import ( 16 | list_invocations_command, 17 | ) 18 | from .search import search_command 19 | from .show import show_command 20 | from .update import update_command 21 | 22 | commands = [ 23 | create_command, 24 | delete_command, 25 | update_command, 26 | search_command, 27 | show_command, 28 | invoke_hosted_command, 29 | invoke_local_command, 30 | list_invocations_command, 31 | init_command, 32 | ] 33 | 34 | command_set = RobotoCommandSet( 35 | name="actions", 36 | help=("Create, edit and invoke reusable actions that run containerized code on your datasets."), 37 | commands=commands, 38 | ) 39 | -------------------------------------------------------------------------------- /src/roboto/domain/comments/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | """Comments module for the Roboto SDK. 8 | 9 | This module provides functionality for creating, retrieving, updating, and deleting 10 | comments on various Roboto platform entities such as datasets, files, actions, 11 | invocations, triggers, and collections. 12 | 13 | Comments support `@mention` syntax to notify users and can be queried by entity, 14 | entity type, user, or organization. The module includes both the high-level 15 | :py:class:`Comment` domain entity and supporting data structures for API operations. 16 | """ 17 | 18 | from .comment import Comment 19 | from .operations import ( 20 | CreateCommentRequest, 21 | UpdateCommentRequest, 22 | ) 23 | from .record import ( 24 | CommentEntityType, 25 | CommentRecord, 26 | ) 27 | 28 | __all__ = ( 29 | "Comment", 30 | "CommentRecord", 31 | "CreateCommentRequest", 32 | "CommentEntityType", 33 | "UpdateCommentRequest", 34 | ) 35 | -------------------------------------------------------------------------------- /src/roboto/action_runtime/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .action_input import ( 8 | ActionInput, 9 | ActionInputResolver, 10 | ) 11 | from .exceptions import ( 12 | ActionRuntimeException, 13 | PrepareEnvException, 14 | ) 15 | from .exit_codes import ExitCode 16 | from .file_changeset import ( 17 | FilesChangesetFileManager, 18 | ) 19 | from .invocation_context import ( 20 | ActionRuntime, 21 | InvocationContext, 22 | ) 23 | from .prepare import ( 24 | prepare_invocation_input_data, 25 | prepare_invocation_parameters, 26 | prepare_metadata_changeset_manifest, 27 | ) 28 | 29 | __all__ = ( 30 | "ActionInput", 31 | "ActionInputResolver", 32 | "ActionRuntime", 33 | "ActionRuntimeException", 34 | "ExitCode", 35 | "FilesChangesetFileManager", 36 | "InvocationContext", 37 | "PrepareEnvException", 38 | "prepare_invocation_input_data", 39 | "prepare_invocation_parameters", 40 | "prepare_metadata_changeset_manifest", 41 | ) 42 | -------------------------------------------------------------------------------- /src/roboto/domain/orgs/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .org import Org 8 | from .org_invite import OrgInvite 9 | from .org_operations import ( 10 | BindEmailDomainRequest, 11 | CreateOrgRequest, 12 | InviteUserRequest, 13 | ModifyRoleForUserRequest, 14 | OrgRecordUpdates, 15 | RemoveUserFromOrgRequest, 16 | UpdateOrgRequest, 17 | UpdateOrgUserRequest, 18 | ) 19 | from .org_records import ( 20 | OrgInviteRecord, 21 | OrgRecord, 22 | OrgRoleName, 23 | OrgStatus, 24 | OrgTier, 25 | OrgUserRecord, 26 | ) 27 | 28 | __all__ = [ 29 | "BindEmailDomainRequest", 30 | "CreateOrgRequest", 31 | "InviteUserRequest", 32 | "ModifyRoleForUserRequest", 33 | "Org", 34 | "OrgInvite", 35 | "OrgInviteRecord", 36 | "OrgRecord", 37 | "OrgRecordUpdates", 38 | "OrgRoleName", 39 | "OrgStatus", 40 | "OrgTier", 41 | "OrgUserRecord", 42 | "RemoveUserFromOrgRequest", 43 | "UpdateOrgRequest", 44 | "UpdateOrgUserRequest", 45 | ] 46 | -------------------------------------------------------------------------------- /src/roboto/domain/comments/operations.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import pydantic 8 | 9 | from .record import CommentEntityType 10 | 11 | 12 | class CreateCommentRequest(pydantic.BaseModel): 13 | """Request payload for creating a new comment. 14 | 15 | This model defines the required information to create a comment 16 | on a Roboto platform entity. 17 | """ 18 | 19 | entity_type: CommentEntityType 20 | """Type of entity to attach the comment to.""" 21 | 22 | entity_id: str 23 | """Unique identifier of the entity to attach the comment to.""" 24 | 25 | comment_text: str 26 | """Text content of the comment, may include `@mention` syntax.""" 27 | 28 | 29 | class UpdateCommentRequest(pydantic.BaseModel): 30 | """Request payload for updating an existing comment. 31 | 32 | This model defines the information that can be modified when 33 | updating a comment. 34 | """ 35 | 36 | comment_text: str 37 | """Updated text content of the comment, may include `@mention` syntax.""" 38 | -------------------------------------------------------------------------------- /src/roboto/action_runtime/exit_codes.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import enum 8 | 9 | 10 | class ExitCode(enum.IntEnum): 11 | """ 12 | Defined exit codes used by the action runtime. 13 | Exception codes are adapted from /usr/include/sysexits.h 14 | """ 15 | 16 | Success = 0 17 | 18 | UsageError = 64 19 | """ 20 | From /usr/include/sysexits.h: 21 | > EX_USAGE -- The command was used incorrectly, e.g., with 22 | > the wrong number of arguments, a bad flag, a bad 23 | > syntax in a parameter, or whatever. 24 | """ 25 | 26 | InternalError = 70 27 | """ 28 | From /usr/include/sysexits.h: 29 | > EX_SOFTWARE -- An internal software error has been detected. 30 | > This should be limited to non-operating system related 31 | > errors as possible. 32 | """ 33 | 34 | ConfigurationError = 78 35 | """ 36 | From /usr/include/sysexits.h: 37 | > EX_CONFIG 78 /* configuration error */ 38 | """ 39 | -------------------------------------------------------------------------------- /src/roboto/domain/layouts/record.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import datetime 8 | import typing 9 | 10 | import pydantic 11 | 12 | from ...compat import StrEnum 13 | 14 | 15 | class LayoutAccessibility(StrEnum): 16 | """ 17 | Controls who can view a layout. 18 | """ 19 | 20 | Organization = "organization" 21 | """All members of the organization owning the layout can view the layout.""" 22 | 23 | User = "user" 24 | """Just the user who created the layout can view it.""" 25 | 26 | 27 | class LayoutRecord(pydantic.BaseModel): 28 | """A wire-transmissible representation of a layout""" 29 | 30 | accessibility: LayoutAccessibility = LayoutAccessibility.User 31 | created: datetime.datetime 32 | created_by: str 33 | layout_definition: dict[str, typing.Any] 34 | layout_id: str 35 | modified: datetime.datetime 36 | modified_by: str 37 | name: str 38 | org_id: str 39 | 40 | # Layout definition schema version 41 | schema_version: int 42 | tags: list[str] = pydantic.Field(default_factory=list) 43 | -------------------------------------------------------------------------------- /examples/BUILD: -------------------------------------------------------------------------------- 1 | python_sources( 2 | name="notebook-utils", 3 | dependencies=[ 4 | # build-support[sdk_examples] extras 5 | "//build-support:3rdparty-dev-tools#ipython", 6 | "//build-support:3rdparty-dev-tools#jupyter", 7 | "//build-support:3rdparty-dev-tools#matplotlib", 8 | "//build-support:3rdparty-dev-tools#pillow", 9 | ], 10 | ) 11 | 12 | resources( 13 | name="notebooks", 14 | sources=["*.ipynb"], 15 | ) 16 | 17 | # To run: 18 | # pants run packages/roboto/examples:jupyter 19 | pex_binary( 20 | name="jupyter", 21 | dependencies=[ 22 | ":notebooks", 23 | ":notebook-utils", 24 | "//packages/roboto/src/roboto:roboto-src", 25 | 26 | # roboto extras (optional deps) 27 | "//build-support:3rdparty#fsspec", 28 | "//build-support:3rdparty#numpy", 29 | "//build-support:3rdparty#pandas", 30 | "//build-support:3rdparty#pyarrow", 31 | "//build-support:3rdparty#stumpy", 32 | 33 | # build-support[sdk_examples] extras 34 | "//build-support:3rdparty-dev-tools#ipython", 35 | "//build-support:3rdparty-dev-tools#jupyter", 36 | "//build-support:3rdparty-dev-tools#matplotlib", 37 | "//build-support:3rdparty-dev-tools#pillow", 38 | ], 39 | script="jupyter", 40 | args=["lab", f"--notebook-dir='{build_file_dir()}'"], 41 | execution_mode="venv", 42 | ) -------------------------------------------------------------------------------- /src/roboto/cli/actions/delete.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from ...domain import actions 10 | from ..command import RobotoCommand 11 | from ..common_args import add_org_arg 12 | from ..context import CLIContext 13 | 14 | 15 | def delete(args: argparse.Namespace, context: CLIContext, parser: argparse.ArgumentParser) -> None: 16 | action = actions.Action.from_name( 17 | name=args.action, 18 | owner_org_id=args.org, 19 | roboto_client=context.roboto_client, 20 | ) 21 | action.delete() 22 | print(f"Deleted action '{args.action}'") 23 | 24 | 25 | def delete_parser(parser: argparse.ArgumentParser): 26 | parser.add_argument( 27 | "action", 28 | metavar="action_reference: ", 29 | help="Exact name of action to delete.", 30 | ) 31 | add_org_arg(parser=parser) 32 | 33 | 34 | delete_command = RobotoCommand( 35 | name="delete", 36 | logic=delete, 37 | setup_parser=delete_parser, 38 | command_kwargs={"help": "Delete action and all of its related subresources."}, 39 | ) 40 | -------------------------------------------------------------------------------- /src/roboto/cli/images/delete_image.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from ...image_registry import ImageRegistry 10 | from ..command import RobotoCommand 11 | from ..common_args import add_org_arg 12 | from ..context import CLIContext 13 | 14 | 15 | def delete_image(args: argparse.Namespace, context: CLIContext, parser: argparse.ArgumentParser) -> None: 16 | image_registry = ImageRegistry(context.roboto_client) 17 | image_registry.delete_image(args.remote_image, org_id=args.org) 18 | 19 | 20 | def delete_image_parser(parser: argparse.ArgumentParser) -> None: 21 | add_org_arg(parser) 22 | 23 | parser.add_argument( 24 | "remote_image", 25 | action="store", 26 | help="Specify the remote image to delete, in the format ``:``.", 27 | ) 28 | 29 | 30 | delete_image_command = RobotoCommand( 31 | name="delete-image", 32 | logic=delete_image, 33 | setup_parser=delete_image_parser, 34 | command_kwargs={"help": ("Delete a container image hosted in Roboto's image registry. Requires Docker CLI.")}, 35 | ) 36 | -------------------------------------------------------------------------------- /src/roboto/cli/secrets/read.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from roboto.cli.command import RobotoCommand 10 | from roboto.cli.common_args import add_org_arg 11 | from roboto.cli.context import CLIContext 12 | from roboto.domain.secrets import Secret 13 | 14 | 15 | def read_logic(args, context: CLIContext, parser: argparse.ArgumentParser): 16 | org_id = args.org 17 | secret_name = args.secret_name 18 | 19 | secret = Secret.from_name( 20 | name=secret_name, 21 | org_id=org_id, 22 | roboto_client=context.roboto_client, 23 | ) 24 | 25 | print(secret.read_value().get_secret_value()) 26 | 27 | 28 | def read_setup_parser(parser): 29 | add_org_arg(parser) 30 | parser.add_argument( 31 | "secret_name", 32 | help="The name of the secret to read.", 33 | ) 34 | 35 | 36 | read_command = RobotoCommand( 37 | name="read", 38 | logic=read_logic, 39 | setup_parser=read_setup_parser, 40 | command_kwargs={"help": "Retrieve a secret's value from secure storage, and reveal it to the user."}, 41 | ) 42 | -------------------------------------------------------------------------------- /src/roboto/cli/actions/show.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | import json 9 | 10 | from ...domain import actions 11 | from ..command import RobotoCommand 12 | from ..common_args import ( 13 | add_action_reference_arg, 14 | add_org_arg, 15 | ) 16 | from ..context import CLIContext 17 | 18 | 19 | def show(args: argparse.Namespace, context: CLIContext, parser: argparse.ArgumentParser) -> None: 20 | owner_org_id = args.action.owner if args.action.owner else args.org 21 | action = actions.Action.from_name( 22 | name=args.action.name, 23 | digest=args.action.digest, 24 | owner_org_id=owner_org_id, 25 | roboto_client=context.roboto_client, 26 | ) 27 | print(json.dumps(action.to_dict(), indent=2)) 28 | 29 | 30 | def show_parser(parser: argparse.ArgumentParser): 31 | add_action_reference_arg(parser) 32 | add_org_arg(parser=parser) 33 | 34 | 35 | show_command = RobotoCommand( 36 | name="show", 37 | logic=show, 38 | setup_parser=show_parser, 39 | command_kwargs={"help": "Show details for an action."}, 40 | ) 41 | -------------------------------------------------------------------------------- /src/roboto/query/signal_similarity/match.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from __future__ import annotations 8 | 9 | import dataclasses 10 | import typing 11 | 12 | if typing.TYPE_CHECKING: 13 | import numpy.typing # pants: no-infer-dep 14 | 15 | 16 | @dataclasses.dataclass(frozen=True) 17 | class Match: 18 | """ 19 | A subsequence of a target signal that is similar to a query signal. 20 | """ 21 | 22 | start_idx: int 23 | """ 24 | The start index in the target signal of this match. 25 | """ 26 | 27 | end_idx: int 28 | """ 29 | The end index in the target signal of this match. 30 | """ 31 | 32 | distance: float 33 | """ 34 | Unitless measure of similarity between a query signal 35 | and the subsequence of the target signal this Match represents. 36 | A smaller distance relative to a larger distance indicates a "closer" match. 37 | """ 38 | 39 | subsequence: numpy.typing.NDArray 40 | """ 41 | The subsequence of the target signal this Match represents. 42 | It is equivalent to ``target_sequence[start_idx:end_idx]``. 43 | """ 44 | -------------------------------------------------------------------------------- /src/roboto/cli/cache/size.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | import sys 9 | 10 | from ..command import RobotoCommand 11 | from ..context import CLIContext 12 | 13 | 14 | def get_directory_size(path) -> int: 15 | """Calculate the total size of a directory in bytes.""" 16 | total = 0 17 | for entry in path.rglob("*"): 18 | if entry.is_file(): 19 | total += entry.stat().st_size 20 | return total 21 | 22 | 23 | def size(args, context: CLIContext, parser: argparse.ArgumentParser): 24 | cache_dir = context.roboto_config.get_cache_dir() 25 | 26 | if not cache_dir.exists(): 27 | print(f"Cache directory does not exist: {cache_dir}", file=sys.stderr) 28 | print(0) 29 | return 30 | 31 | total_bytes = get_directory_size(cache_dir) 32 | print(total_bytes) 33 | 34 | 35 | def size_setup_parser(parser): 36 | pass 37 | 38 | 39 | size_command = RobotoCommand( 40 | name="size", 41 | logic=size, 42 | setup_parser=size_setup_parser, 43 | command_kwargs={"help": "Returns the total size in bytes of the Roboto cache directory."}, 44 | ) 45 | -------------------------------------------------------------------------------- /src/roboto/domain/devices/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | """Device management for the Roboto platform. 8 | 9 | This module provides functionality for managing devices - non-human entities that can 10 | interact with Roboto on behalf of organizations. Devices are typically robots or 11 | other systems that upload data to the platform. 12 | 13 | The main components are: 14 | - :py:class:`Device`: Core device entity for registration, token management, and operations 15 | - :py:class:`DeviceRecord`: Wire-transmissible representation of device data 16 | - :py:class:`CreateDeviceRequest`: Request payload for device registration 17 | 18 | Devices are identified by unique device IDs within their organization and can be 19 | assigned API tokens for authentication. They serve as the primary mechanism for 20 | automated data ingestion and platform interaction. 21 | """ 22 | 23 | from .device import Device 24 | from .operations import ( 25 | CreateDeviceRequest, 26 | UpdateDeviceRequest, 27 | ) 28 | from .record import DeviceRecord 29 | 30 | __all__ = ["CreateDeviceRequest", "Device", "DeviceRecord", "UpdateDeviceRequest"] 31 | -------------------------------------------------------------------------------- /src/roboto/cli/datasets/commands.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from ..command import RobotoCommandSet 8 | from .create import create_command 9 | from .delete_dataset import delete_dataset_command 10 | from .delete_files import delete_files_command 11 | from .download_files import download_files_command 12 | from .import_external_file import ( 13 | import_external_file_command, 14 | ) 15 | from .list_files import list_files_command 16 | from .search import search_command 17 | from .show import show_command 18 | from .update import update_command 19 | from .upload_files import upload_files_command 20 | 21 | commands = [ 22 | create_command, 23 | delete_dataset_command, 24 | delete_files_command, 25 | download_files_command, 26 | import_external_file_command, 27 | list_files_command, 28 | show_command, 29 | search_command, 30 | update_command, 31 | upload_files_command, 32 | ] 33 | 34 | command_set = RobotoCommandSet( 35 | name="datasets", 36 | help="Manage datasets, used to store files from a robot run or drone flight. Includes file upload and download.", 37 | commands=commands, 38 | ) 39 | -------------------------------------------------------------------------------- /src/roboto/cli/secrets/delete.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | import sys 9 | 10 | from roboto.cli.command import RobotoCommand 11 | from roboto.cli.common_args import add_org_arg 12 | from roboto.cli.context import CLIContext 13 | from roboto.domain.secrets import Secret 14 | 15 | 16 | def delete_logic(args, context: CLIContext, parser: argparse.ArgumentParser): 17 | org_id = args.org 18 | secret_name = args.secret_name 19 | 20 | secret = Secret.from_name( 21 | name=secret_name, 22 | org_id=org_id, 23 | roboto_client=context.roboto_client, 24 | ) 25 | 26 | secret.delete() 27 | print(f"Deleted secret {secret_name}", file=sys.stderr) 28 | 29 | 30 | def delete_setup_parser(parser): 31 | add_org_arg(parser) 32 | parser.add_argument( 33 | "secret_name", 34 | help="The name of the secret to delete.", 35 | ) 36 | 37 | 38 | delete_command = RobotoCommand( 39 | name="delete", 40 | logic=delete_logic, 41 | setup_parser=delete_setup_parser, 42 | command_kwargs={"help": "Deletes a secret from the Roboto platform. This operation cannot be undone."}, 43 | ) 44 | -------------------------------------------------------------------------------- /src/roboto/http/headers.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from typing import Optional 8 | 9 | from ..api_version import RobotoApiVersion 10 | from .constants import ( 11 | API_VERSION_HEADER, 12 | ORG_OVERRIDE_HEADER, 13 | RESOURCE_OWNER_OVERRIDE_HEADER, 14 | USER_OVERRIDE_HEADER, 15 | ) 16 | 17 | CONTENT_TYPE_JSON_HEADER = {"Content-Type": "application/json"} 18 | 19 | 20 | def roboto_headers( 21 | org_id: Optional[str] = None, 22 | user_id: Optional[str] = None, 23 | resource_owner_id: Optional[str] = None, 24 | additional_headers: Optional[dict[str, str]] = None, 25 | api_version: RobotoApiVersion = RobotoApiVersion.latest(), 26 | ): 27 | headers: dict[str, str] = {API_VERSION_HEADER: api_version} 28 | 29 | if org_id is not None: 30 | headers[ORG_OVERRIDE_HEADER] = org_id 31 | 32 | if user_id is not None: 33 | headers[USER_OVERRIDE_HEADER] = user_id 34 | 35 | if resource_owner_id is not None: 36 | headers[RESOURCE_OWNER_OVERRIDE_HEADER] = resource_owner_id 37 | 38 | if additional_headers is not None: 39 | headers.update(additional_headers) 40 | 41 | return headers 42 | -------------------------------------------------------------------------------- /src/roboto/cli/argparse.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | from operator import attrgetter 9 | import typing 10 | 11 | 12 | class DeprecationAction(argparse.Action): 13 | """An action that triggers a deprecation error.""" 14 | 15 | __deprecation_msg: typing.Optional[str] = None 16 | 17 | def __init__( 18 | self, 19 | option_strings, 20 | dest, 21 | help=None, 22 | deprecation_msg: typing.Optional[str] = None, 23 | **kwargs, 24 | ): 25 | super(DeprecationAction, self).__init__(option_strings, dest, nargs=0, help=help, **kwargs) 26 | self.__deprecation_msg = deprecation_msg 27 | 28 | def __call__(self, parser, namespace, values, option_string=None): 29 | deprecation_msg = self.__deprecation_msg if self.__deprecation_msg else f"Deprecated option: {option_string}" 30 | parser.error(deprecation_msg) 31 | 32 | 33 | class SortingHelpFormatter(argparse.RawTextHelpFormatter): 34 | def add_arguments(self, actions): 35 | actions = sorted(actions, key=attrgetter("option_strings")) 36 | super(SortingHelpFormatter, self).add_arguments(actions) 37 | -------------------------------------------------------------------------------- /src/roboto/auth/scope.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from __future__ import annotations 8 | 9 | from ..compat import StrEnum 10 | 11 | 12 | class ApiScope(StrEnum): 13 | """ 14 | Scopes define the set of APIs a credential holder can access. 15 | """ 16 | 17 | ApiEverythingElse = "api.everything_else" 18 | """Holder has access to all other APIs not covered by other scopes. 19 | 20 | A developer API token will likely want to include this scope, whereas an upload-only device token will likely 21 | want to omit it for principle of least privilege.""" 22 | 23 | DatasetsCreate = "datasets.create" 24 | """Holder can create new datasets.""" 25 | 26 | FilesImport = "files.import" 27 | """Holder can import existing files from an external object store into Roboto.""" 28 | 29 | FilesUpload = "files.upload" 30 | """Holder can upload new files to Roboto's managed storage.""" 31 | 32 | @classmethod 33 | def all(cls) -> set[ApiScope]: 34 | return set(x for x in cls) 35 | 36 | @classmethod 37 | def minimal_uploader(cls) -> set[ApiScope]: 38 | return {cls.FilesImport, cls.FilesUpload, cls.DatasetsCreate} 39 | -------------------------------------------------------------------------------- /src/roboto/image_registry/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .http_resources import ( 8 | ContainerUploadCredentials, 9 | CreateImageRepositoryRequest, 10 | CreateImageRepositoryResponse, 11 | DeleteImageRepositoryRequest, 12 | DeleteImageRequest, 13 | RepositoryContainsImageResponse, 14 | SetImageRepositoryImmutableTagsRequest, 15 | ) 16 | from .image_registry import ( 17 | ContainerCredentials, 18 | ImageRegistry, 19 | ImageRepository, 20 | RepositoryPurpose, 21 | RepositoryTag, 22 | ) 23 | from .record import ( 24 | ContainerImageRecord, 25 | ContainerImageRepositoryRecord, 26 | ) 27 | 28 | __all__ = ( 29 | "ContainerCredentials", 30 | "ContainerImageRecord", 31 | "ContainerImageRepositoryRecord", 32 | "ContainerUploadCredentials", 33 | "CreateImageRepositoryRequest", 34 | "CreateImageRepositoryResponse", 35 | "DeleteImageRequest", 36 | "DeleteImageRepositoryRequest", 37 | "ImageRegistry", 38 | "ImageRepository", 39 | "RepositoryContainsImageResponse", 40 | "RepositoryPurpose", 41 | "RepositoryTag", 42 | "SetImageRepositoryImmutableTagsRequest", 43 | ) 44 | -------------------------------------------------------------------------------- /src/roboto/cli/cache/clear.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | import shutil 9 | import sys 10 | 11 | from ..command import RobotoCommand 12 | from ..context import CLIContext 13 | 14 | 15 | def clear(args, context: CLIContext, parser: argparse.ArgumentParser): 16 | cache_dir = context.roboto_config.get_cache_dir() 17 | 18 | if not cache_dir.exists(): 19 | print(f"Cache directory does not exist: {cache_dir}", file=sys.stderr) 20 | return 21 | 22 | if args.dry_run: 23 | print(f"Would clear cache directory: {cache_dir}", file=sys.stderr) 24 | return 25 | 26 | shutil.rmtree(cache_dir) 27 | print(f"Cleared cache directory: {cache_dir}", file=sys.stderr) 28 | 29 | 30 | def clear_setup_parser(parser): 31 | parser.add_argument( 32 | "--dry-run", 33 | action="store_true", 34 | help="Print location of cache directory that would be deleted, without performing the delete.", 35 | ) 36 | 37 | 38 | clear_command = RobotoCommand( 39 | name="clear", 40 | logic=clear, 41 | setup_parser=clear_setup_parser, 42 | command_kwargs={"help": "Clears the local Roboto cache directory."}, 43 | ) 44 | -------------------------------------------------------------------------------- /src/roboto/domain/collections/operations.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from typing import Optional, Union 8 | 9 | import pydantic 10 | from pydantic import ConfigDict 11 | 12 | from roboto.sentinels import NotSet, NotSetType 13 | 14 | from .record import CollectionResourceRef 15 | 16 | 17 | class CreateCollectionRequest(pydantic.BaseModel): 18 | """Request payload to create a collection""" 19 | 20 | description: Optional[str] = None 21 | name: Optional[str] = None 22 | resources: Optional[list[CollectionResourceRef]] = None 23 | tags: Optional[list[str]] = None 24 | 25 | 26 | class UpdateCollectionRequest(pydantic.BaseModel): 27 | """Request payload to update a collection""" 28 | 29 | add_resources: Union[list[CollectionResourceRef], NotSetType] = NotSet 30 | add_tags: Union[list[str], NotSetType] = NotSet 31 | description: Optional[Union[NotSetType, str]] = NotSet 32 | name: Optional[Union[NotSetType, str]] = NotSet 33 | remove_resources: Union[list[CollectionResourceRef], NotSetType] = NotSet 34 | remove_tags: Union[list[str], NotSetType] = NotSet 35 | 36 | model_config = ConfigDict(json_schema_extra=NotSetType.openapi_schema_modifier) 37 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | SDK usage examples 2 | ================== 3 | 4 | This directory contains Jupyter notebooks demonstrating the use and features of the Roboto Python SDK. 5 | 6 | ## Setup 7 | 8 | 1. Setup your environment for [programmatic access to the Roboto platform](http://docs.roboto.ai/getting-started/programmatic-access.html) 9 | 10 | 2. Create a virtual environment using Python >= 3.10: 11 | ```bash 12 | python -m venv .venv 13 | ``` 14 | 15 | 3. Install the SDK with relevant pip extras: 16 | 17 | _This assumes you're running this command from the root of the repository._ 18 | 19 | ```bash 20 | .venv/bin/python -m pip install .[analytics,examples] 21 | ``` 22 | 23 | ## Running the examples 24 | 25 | Start the Jupyter server and run the cells in notebooks of interest: 26 | ```bash 27 | .venv/bin/jupyter lab --notebook-dir=examples 28 | ``` 29 | 30 | _If you're running the `jupyter lab` command from the `examples` directory itself, there's no need to specify the `--notebook-dir` argument. It's only necessary if you're running it from the root of the repository._ 31 | 32 | ## Learn more 33 | 34 | For more information, check out: 35 | * [General Docs](https://docs.roboto.ai/) 36 | * [User Guides](https://docs.roboto.ai/user-guides/index.html) 37 | * [SDK Reference](https://docs.roboto.ai/reference/python-sdk.html) 38 | * [CLI Reference](https://docs.roboto.ai/reference/cli.html) 39 | * [About Roboto](https://www.roboto.ai/about) 40 | -------------------------------------------------------------------------------- /src/roboto/domain/datasets/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .dataset import Dataset 8 | from .operations import ( 9 | BeginManifestTransactionRequest, 10 | BeginManifestTransactionResponse, 11 | BeginSingleFileUploadRequest, 12 | BeginSingleFileUploadResponse, 13 | CreateDatasetIfNotExistsRequest, 14 | CreateDatasetRequest, 15 | CreateDirectoryRequest, 16 | DeleteDirectoriesRequest, 17 | QueryDatasetFilesRequest, 18 | QueryDatasetsRequest, 19 | RenameDirectoryRequest, 20 | ReportTransactionProgressRequest, 21 | UpdateDatasetRequest, 22 | ) 23 | from .record import DatasetRecord 24 | 25 | __all__ = ( 26 | "BeginManifestTransactionRequest", 27 | "BeginManifestTransactionResponse", 28 | "BeginSingleFileUploadRequest", 29 | "BeginSingleFileUploadResponse", 30 | "CreateDatasetRequest", 31 | "CreateDatasetIfNotExistsRequest", 32 | "CreateDirectoryRequest", 33 | "Dataset", 34 | "DatasetRecord", 35 | "DeleteDirectoriesRequest", 36 | "QueryDatasetFilesRequest", 37 | "QueryDatasetsRequest", 38 | "RenameDirectoryRequest", 39 | "ReportTransactionProgressRequest", 40 | "UpdateDatasetRequest", 41 | ) 42 | -------------------------------------------------------------------------------- /src/roboto/cli/context.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import typing 8 | 9 | from ..config import RobotoConfig 10 | from ..http import HttpClient, RobotoClient 11 | 12 | 13 | class CLIContext: 14 | __roboto_service_base_url: typing.Optional[str] 15 | __http: typing.Optional[HttpClient] 16 | extensions: dict[str, typing.Any] 17 | roboto_client: RobotoClient 18 | roboto_config: RobotoConfig 19 | 20 | @property 21 | def roboto_service_base_url(self) -> str: 22 | if self.__roboto_service_base_url is None: 23 | raise ValueError("roboto_service_base_url is unset") 24 | 25 | return self.__roboto_service_base_url 26 | 27 | @roboto_service_base_url.setter 28 | def roboto_service_base_url(self, roboto_service_base_url: str) -> None: 29 | self.__roboto_service_base_url = roboto_service_base_url 30 | 31 | @property 32 | def http_client(self) -> HttpClient: 33 | # Necessary since http is lazy set after parsing args 34 | if self.__http is None: 35 | raise ValueError("Unset HTTP client!") 36 | 37 | return self.__http 38 | 39 | @http_client.setter 40 | def http_client(self, http: HttpClient) -> None: 41 | self.__http = http 42 | -------------------------------------------------------------------------------- /src/roboto/cli/datasets/delete_files.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from ...domain.datasets import Dataset 10 | from ..command import RobotoCommand 11 | from ..context import CLIContext 12 | from .shared_helpdoc import DATASET_ID_HELP 13 | 14 | 15 | def delete_files(args, context: CLIContext, parser: argparse.ArgumentParser): 16 | dataset = Dataset.from_id(args.dataset_id, context.roboto_client) 17 | 18 | dataset.delete_files(include_patterns=args.include, exclude_patterns=args.exclude) 19 | 20 | 21 | def delete_files_setup_parser(parser): 22 | parser.add_argument("-d", "--dataset-id", type=str, required=True, help=DATASET_ID_HELP) 23 | parser.add_argument( 24 | "-i", 25 | "--include", 26 | type=str, 27 | nargs="*", 28 | help="Zero or more include filters", 29 | ) 30 | parser.add_argument( 31 | "-x", 32 | "--exclude", 33 | type=str, 34 | nargs="*", 35 | help="Zero or more exclude filters", 36 | ) 37 | 38 | 39 | delete_files_command = RobotoCommand( 40 | name="delete-files", 41 | logic=delete_files, 42 | setup_parser=delete_files_setup_parser, 43 | command_kwargs={"help": "Delete files from a dataset."}, 44 | ) 45 | -------------------------------------------------------------------------------- /src/roboto/domain/events/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | """Events domain module for the Roboto SDK. 8 | 9 | This module provides functionality for creating and managing events, which are time-anchored 10 | annotations that relate Roboto entities (datasets, files, topics, and message paths) to 11 | specific time periods. Events enable temporal analysis, data annotation, and correlation 12 | of activities across different data sources. 13 | 14 | The events domain includes: 15 | 16 | - Event creation and management 17 | - Association with datasets, files, topics, and message paths 18 | - Time-based data retrieval and analysis 19 | - Display options and metadata management 20 | - Event querying and filtering capabilities 21 | """ 22 | 23 | from .event import Event 24 | from .operations import ( 25 | CreateEventRequest, 26 | EventDisplayOptions, 27 | EventDisplayOptionsChangeset, 28 | QueryEventsForAssociationsRequest, 29 | UpdateEventRequest, 30 | ) 31 | from .record import EventRecord 32 | 33 | __all__ = [ 34 | "CreateEventRequest", 35 | "Event", 36 | "EventDisplayOptions", 37 | "EventDisplayOptionsChangeset", 38 | "EventRecord", 39 | "QueryEventsForAssociationsRequest", 40 | "UpdateEventRequest", 41 | ] 42 | -------------------------------------------------------------------------------- /src/roboto/cli/collections/list_all.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from ...domain.collections import ( 10 | Collection, 11 | CollectionContentMode, 12 | ) 13 | from ..command import RobotoCommand 14 | from ..common_args import add_org_arg 15 | from ..context import CLIContext 16 | from .shared_helpdoc import CONTENT_MODE_HELP 17 | 18 | 19 | def list_all(args, context: CLIContext, parser: argparse.ArgumentParser): 20 | for collection in Collection.list_all( 21 | owner_org_id=args.org, 22 | roboto_client=context.roboto_client, 23 | content_mode=args.content_mode, 24 | ): 25 | print(collection.record.model_dump_json(indent=2)) 26 | 27 | 28 | def list_all_setup_parser(parser: argparse.ArgumentParser): 29 | parser.add_argument( 30 | "--content-mode", 31 | type=str, 32 | choices=[mode.value for mode in CollectionContentMode], 33 | help=CONTENT_MODE_HELP, 34 | default=CollectionContentMode.References.value, 35 | ) 36 | add_org_arg(parser) 37 | 38 | 39 | list_all_command = RobotoCommand( 40 | name="list-all", 41 | logic=list_all, 42 | setup_parser=list_all_setup_parser, 43 | command_kwargs={"help": "List all collections."}, 44 | ) 45 | -------------------------------------------------------------------------------- /src/roboto/cli/images/list_repos.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | import json 9 | 10 | from ...image_registry import ImageRegistry 11 | from ..command import RobotoCommand 12 | from ..common_args import add_org_arg 13 | from ..context import CLIContext 14 | 15 | 16 | def list_repos(args: argparse.Namespace, context: CLIContext, parser: argparse.ArgumentParser) -> None: 17 | image_registry = ImageRegistry(context.roboto_client) 18 | paginated_results = image_registry.list_repositories(org_id=args.org) 19 | while True: 20 | for repo in paginated_results.items: 21 | print(json.dumps(repo.model_dump(), indent=2)) 22 | if paginated_results.next_token: 23 | paginated_results = image_registry.list_repositories( 24 | page_token=paginated_results.next_token, org_id=args.org 25 | ) 26 | else: 27 | break 28 | 29 | 30 | def list_repos_parser(parser: argparse.ArgumentParser) -> None: 31 | add_org_arg(parser) 32 | 33 | 34 | ls_repos_command = RobotoCommand( 35 | name="list-repos", 36 | logic=list_repos, 37 | setup_parser=list_repos_parser, 38 | command_kwargs={"help": "List image repositories hosted in Roboto's image registry."}, 39 | ) 40 | -------------------------------------------------------------------------------- /src/roboto/cli/common_args/orgs.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | import os 9 | import typing 10 | 11 | from ...domain import orgs 12 | from ...exceptions import ( 13 | RobotoNoOrgProvidedException, 14 | ) 15 | 16 | ORG_ARG_HELP = ( 17 | "The calling organization ID. Gets set implicitly if in a single org. " 18 | + "The `ROBOTO_ORG_ID` environment variable can be set to control the default value." 19 | ) 20 | 21 | DEFAULT_ORG_ID = os.getenv("ROBOTO_ORG_ID") 22 | 23 | 24 | def add_org_arg(parser: argparse.ArgumentParser, arg_help: str = ORG_ARG_HELP): 25 | parser.add_argument("--org", required=False, type=str, help=arg_help, default=DEFAULT_ORG_ID) 26 | 27 | 28 | def get_defaulted_org_id(org_id: typing.Optional[str]) -> str: 29 | if org_id is not None: 30 | return org_id 31 | 32 | user_orgs = orgs.Org.for_self() 33 | if len(user_orgs) == 0: 34 | raise RobotoNoOrgProvidedException("Current user is not a member of any orgs, and did not provide a --org") 35 | elif len(user_orgs) > 1: 36 | raise RobotoNoOrgProvidedException( 37 | f"Current user is a member of {len(user_orgs)} orgs, and must specify one with --org" 38 | ) 39 | else: 40 | return user_orgs[0].org_id 41 | -------------------------------------------------------------------------------- /src/roboto/http/constants.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | RESOURCE_OWNER_OVERRIDE_HEADER = "X-Roboto-Resource-Owner-Id" 8 | """Header to specify the organization that owns the resource being accessed.""" 9 | 10 | RESOURCE_OWNER_OVERRIDE_QUERY_PARAM = "robotoResourceOwnerId" 11 | """Query parameter to specify the organization that owns the resource being accessed.""" 12 | 13 | ORG_OVERRIDE_HEADER = "X-Roboto-Org-Id" 14 | """Header to specify the organization that the user is acting on behalf of.""" 15 | 16 | ORG_OVERRIDE_QUERY_PARAM = "robotoOrgId" 17 | """Query parameter to specify the organization that the user is acting on behalf of.""" 18 | 19 | USER_OVERRIDE_HEADER = "X-Roboto-User-Id" 20 | """Header to specify the user that is performing the REST operation.""" 21 | 22 | USER_OVERRIDE_QUERY_PARAM = "robotoUserId" 23 | """"Query parameter to specify the user that is performing the REST operation.""" 24 | 25 | BEARER_TOKEN_HEADER = "X-Roboto-Bearer-Token" 26 | """Bearer token which is parsed as a JWT to provide additional request context for invocations""" 27 | 28 | API_VERSION_HEADER = "X-Roboto-Api-Version" 29 | """Which expected rolling API version this request is being made against. If no value is provided, requests will 30 | be made against the latest API version.""" 31 | -------------------------------------------------------------------------------- /src/roboto/cli/images/delete_repo.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from ...image_registry import ImageRegistry 10 | from ..command import RobotoCommand 11 | from ..common_args import add_org_arg 12 | from ..context import CLIContext 13 | 14 | 15 | def delete_repository(args: argparse.Namespace, context: CLIContext, parser: argparse.ArgumentParser) -> None: 16 | image_registry = ImageRegistry(context.roboto_client) 17 | image_registry.delete_repository(args.repository_name, org_id=args.org, force=args.force) 18 | 19 | 20 | def delete_repository_parser(parser: argparse.ArgumentParser) -> None: 21 | add_org_arg(parser) 22 | 23 | parser.add_argument( 24 | "repository_name", 25 | action="store", 26 | help="The name of the image repository to delete.", 27 | ) 28 | 29 | parser.add_argument( 30 | "--force", 31 | action="store_true", 32 | help="Force deletion of the repository, even if it is not empty.", 33 | ) 34 | 35 | 36 | delete_repo_command = RobotoCommand( 37 | name="delete-repository", 38 | logic=delete_repository, 39 | setup_parser=delete_repository_parser, 40 | command_kwargs={"help": ("Delete an image repository hosted in Roboto's image registry. Requires Docker CLI.")}, 41 | ) 42 | -------------------------------------------------------------------------------- /src/roboto/cli/datasets/list_files.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from ...domain.datasets import Dataset 10 | from ..command import RobotoCommand 11 | from ..context import CLIContext 12 | from .shared_helpdoc import DATASET_ID_HELP 13 | 14 | 15 | def list_files(args, context: CLIContext, parser: argparse.ArgumentParser): 16 | dataset = Dataset.from_id(args.dataset_id, context.roboto_client) 17 | 18 | for f in dataset.list_files(args.include, args.exclude): 19 | print(f"{f.relative_path}") 20 | 21 | 22 | def list_files_setup_parser(parser): 23 | parser.add_argument("-d", "--dataset-id", type=str, required=True, help=DATASET_ID_HELP) 24 | parser.add_argument( 25 | "-i", 26 | "--include", 27 | type=str, 28 | nargs="*", 29 | help="Zero or more include filters (if path points to a directory)", 30 | ) 31 | parser.add_argument( 32 | "-x", 33 | "--exclude", 34 | type=str, 35 | nargs="*", 36 | help="Zero or more exclude filters (if path points to a directory)", 37 | ) 38 | 39 | 40 | list_files_command = RobotoCommand( 41 | name="list-files", 42 | logic=list_files, 43 | setup_parser=list_files_setup_parser, 44 | command_kwargs={"help": "List files in a dataset."}, 45 | ) 46 | -------------------------------------------------------------------------------- /src/roboto/cli/secrets/list.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from roboto.cli.command import RobotoCommand 10 | from roboto.cli.common_args import add_org_arg 11 | from roboto.cli.context import CLIContext 12 | from roboto.domain.orgs import Org 13 | from roboto.domain.secrets import Secret 14 | 15 | 16 | def list_logic(args, context: CLIContext, parser: argparse.ArgumentParser): 17 | org_id = args.org 18 | if org_id is None: 19 | orgs = list(Org.for_self(context.roboto_client)) 20 | if len(orgs) == 0: 21 | parser.error("No organizations found for the current user.") 22 | elif len(orgs) == 1: 23 | org_id = orgs[0].org_id 24 | else: 25 | parser.error("Multiple organizations found for the current user. Please specify one explicitly with --org.") 26 | 27 | for secret in Secret.for_org(org_id, context.roboto_client): 28 | print(secret) 29 | 30 | 31 | def list_setup_parser(parser): 32 | add_org_arg(parser) 33 | 34 | 35 | list_command = RobotoCommand( 36 | name="list", 37 | logic=list_logic, 38 | setup_parser=list_setup_parser, 39 | command_kwargs={ 40 | "help": "List all secrets in an organization. " 41 | "This does not reveal the secret values, only the secret names and metadata." 42 | }, 43 | ) 44 | -------------------------------------------------------------------------------- /src/roboto/domain/devices/record.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import datetime 8 | import typing 9 | 10 | import pydantic 11 | 12 | 13 | class DeviceRecord(pydantic.BaseModel): 14 | """A wire-transmissible representation of a device. 15 | 16 | This record contains all the essential information about a device that can be 17 | transmitted over the network. It includes metadata about when the device was 18 | created and modified, along with its organizational association. 19 | """ 20 | 21 | created: datetime.datetime 22 | """Date/time when this device was registered.""" 23 | 24 | created_by: str 25 | """The user who registered this device.""" 26 | 27 | device_id: str 28 | """A user-provided identifier for a device, which is unique within that device's org.""" 29 | 30 | modified: datetime.datetime 31 | """Date/time when this device record was last modified.""" 32 | 33 | modified_by: str 34 | """The user who last modified this device record.""" 35 | 36 | org_id: str 37 | """The org to which this device belongs.""" 38 | 39 | metadata: dict[str, typing.Any] = pydantic.Field(default_factory=dict) 40 | """Key-value metadata pairs associated with this device.""" 41 | 42 | tags: list[str] = pydantic.Field(default_factory=list) 43 | """List of tags associated with this device.""" 44 | -------------------------------------------------------------------------------- /src/roboto/cli/validation.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import contextlib 8 | import typing 9 | 10 | import pydantic 11 | 12 | from .terminal import print_error_and_exit 13 | 14 | 15 | class pydantic_validation_handler(contextlib.AbstractContextManager): 16 | """ 17 | Context manager to catch Pydantic validation errors and print the first error neatly to stderr. 18 | """ 19 | 20 | __parseable_name: str = "Roboto model" 21 | 22 | def __init__(self, parseable_name: typing.Optional[str] = None): 23 | if parseable_name is not None: 24 | self.__parseable_name = parseable_name 25 | 26 | def __enter__(self): 27 | pass 28 | 29 | def __exit__(self, exctype, excinst, exctb): 30 | if exctype is None: 31 | return 32 | 33 | if issubclass(exctype, pydantic.ValidationError): 34 | first_error, *_rest = excinst.errors() 35 | msg = first_error.get("msg") 36 | loc = first_error.get("loc", tuple()) 37 | if loc: 38 | path_to_error = ".".join([str(key_path) for key_path in loc]) 39 | msg = f"'{path_to_error}' {msg}" 40 | 41 | error_msg = [f"Error parsing {self.__parseable_name}"] 42 | if msg: 43 | error_msg.append(msg) 44 | 45 | print_error_and_exit(error_msg) 46 | 47 | return False 48 | -------------------------------------------------------------------------------- /src/roboto/domain/topics/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .message_path import MessagePath 8 | from .operations import ( 9 | AddMessagePathRepresentationRequest, 10 | AddMessagePathRequest, 11 | CreateTopicRequest, 12 | DeleteMessagePathRequest, 13 | MessagePathChangeset, 14 | MessagePathRepresentationMapping, 15 | SetDefaultRepresentationRequest, 16 | UpdateMessagePathRequest, 17 | UpdateTopicRequest, 18 | ) 19 | from .record import ( 20 | CanonicalDataType, 21 | MessagePathMetadataWellKnown, 22 | MessagePathRecord, 23 | MessagePathStatistic, 24 | RepresentationRecord, 25 | RepresentationStorageFormat, 26 | TopicRecord, 27 | ) 28 | from .topic import Topic 29 | from .topic_data_service import TopicDataService 30 | 31 | __all__ = ( 32 | "AddMessagePathRequest", 33 | "AddMessagePathRepresentationRequest", 34 | "CreateTopicRequest", 35 | "CanonicalDataType", 36 | "DeleteMessagePathRequest", 37 | "MessagePath", 38 | "MessagePathChangeset", 39 | "MessagePathRepresentationMapping", 40 | "MessagePathRecord", 41 | "MessagePathStatistic", 42 | "MessagePathMetadataWellKnown", 43 | "RepresentationRecord", 44 | "RepresentationStorageFormat", 45 | "SetDefaultRepresentationRequest", 46 | "Topic", 47 | "TopicDataService", 48 | "TopicRecord", 49 | "UpdateMessagePathRequest", 50 | "UpdateTopicRequest", 51 | ) 52 | -------------------------------------------------------------------------------- /src/roboto/ai/chat/event.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import typing 8 | 9 | import pydantic 10 | 11 | 12 | class ChatStartTextEvent(pydantic.BaseModel): 13 | """Signals the beginning of text generation in a chat response.""" 14 | 15 | 16 | class ChatTextDeltaEvent(pydantic.BaseModel): 17 | """Contains incremental text content as the AI generates its response.""" 18 | 19 | text: str 20 | """Text fragment from the streaming response.""" 21 | 22 | 23 | class ChatTextEndEvent(pydantic.BaseModel): 24 | """Signals the completion of text generation in a chat response.""" 25 | 26 | 27 | class ChatToolUseEvent(pydantic.BaseModel): 28 | """Signals that the AI is invoking a tool to gather information.""" 29 | 30 | name: str 31 | """Name of the tool being invoked.""" 32 | 33 | tool_use_id: str 34 | """Unique identifier for this tool invocation.""" 35 | 36 | 37 | class ChatToolResultEvent(pydantic.BaseModel): 38 | """Contains the result of a tool invocation.""" 39 | 40 | name: str 41 | """Name of the tool that was invoked.""" 42 | 43 | tool_use_id: str 44 | """Unique identifier for this tool invocation.""" 45 | 46 | success: bool 47 | """Whether the tool invocation succeeded.""" 48 | 49 | 50 | ChatEvent: typing.TypeAlias = typing.Union[ 51 | ChatStartTextEvent, 52 | ChatTextDeltaEvent, 53 | ChatTextEndEvent, 54 | ChatToolUseEvent, 55 | ChatToolResultEvent, 56 | ] 57 | -------------------------------------------------------------------------------- /src/roboto/cli/collections/changes.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from ...domain.collections import Collection 10 | from ..command import RobotoCommand 11 | from ..context import CLIContext 12 | from .shared_helpdoc import COLLECTION_ID_HELP 13 | 14 | 15 | def show(args, context: CLIContext, parser: argparse.ArgumentParser): 16 | collection = Collection.from_id( 17 | collection_id=args.collection_id, 18 | roboto_client=context.roboto_client, 19 | ) 20 | for change in collection.changes(from_version=args.from_version, to_version=args.to_version): 21 | print(change.model_dump_json(indent=2)) 22 | 23 | 24 | def show_setup_parser(parser): 25 | parser.add_argument("collection_id", type=str, help=COLLECTION_ID_HELP) 26 | parser.add_argument( 27 | "--from-version", 28 | type=int, 29 | required=False, 30 | help="A collection version to use as the starting point in change-set listing. Defaults to initial.", 31 | ) 32 | parser.add_argument( 33 | "--to-version", 34 | type=int, 35 | required=False, 36 | help="A collection version to use as the end point in change-set listing. Defaults to latest.", 37 | ) 38 | 39 | 40 | changes_command = RobotoCommand( 41 | name="changelog", 42 | logic=show, 43 | setup_parser=show_setup_parser, 44 | command_kwargs={"help": "Get a changelog for the revisions of a collection."}, 45 | ) 46 | -------------------------------------------------------------------------------- /src/roboto/serde/paths.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | 8 | from typing import Optional 9 | 10 | import pathspec 11 | 12 | 13 | def git_pathspec_escape_path(path: str) -> str: 14 | """ 15 | Turns a literal path into a gitignore glob that matches the literal path. 16 | """ 17 | escaped = path.replace("[", "\\[") 18 | escaped = escaped.replace("]", "\\]") 19 | return escaped 20 | 21 | 22 | def git_paths_to_spec(paths: list[str]) -> pathspec.PathSpec: 23 | return pathspec.PathSpec.from_lines(pathspec.patterns.GitWildMatchPattern, paths) 24 | 25 | 26 | def git_paths_match( 27 | include_patterns: Optional[list[str]], 28 | exclude_patterns: Optional[list[str]], 29 | file: str, 30 | ) -> bool: 31 | # Include patterns are provided, and the file isn't included, ignore it 32 | if include_patterns is not None: 33 | if not git_paths_to_spec(include_patterns).match_file(file): 34 | return False 35 | 36 | # Exclude pattern is provided, and the file is included, ignore it 37 | if exclude_patterns is not None: 38 | if git_paths_to_spec(exclude_patterns).match_file(file): 39 | return False 40 | 41 | return True 42 | 43 | 44 | def exclude_patterns_to_spec( 45 | exclude_patterns: Optional[list[str]] = None, 46 | ) -> Optional[pathspec.PathSpec]: 47 | return ( 48 | None 49 | if exclude_patterns is None 50 | else pathspec.GitIgnoreSpec.from_lines(exclude_patterns) 51 | ) 52 | -------------------------------------------------------------------------------- /src/roboto/domain/topics/parquet/ingestion.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from __future__ import annotations 8 | 9 | import pathlib 10 | import re 11 | import typing 12 | 13 | from ....association import Association 14 | from ....exceptions import IngestionException 15 | from ....file_infra import FileService 16 | from ....http import RobotoClient 17 | from ....logging import default_logger 18 | 19 | logger = default_logger() 20 | 21 | 22 | def upload_representation_file( 23 | file_path: pathlib.Path, 24 | association: Association, 25 | caller_org_id: typing.Optional[str] = None, 26 | roboto_client: typing.Optional[RobotoClient] = None, 27 | ) -> str: 28 | dest_path = pathlib.Path(".VISUALIZATION_ASSETS") / file_path.name 29 | file_service = FileService(roboto_client) 30 | file_ids = file_service.upload( 31 | [file_path], 32 | association, 33 | destination_paths={file_path: str(dest_path)}, 34 | caller_org_id=caller_org_id, 35 | ) 36 | 37 | if not file_ids: 38 | raise IngestionException( 39 | f"Failed to upload visualization asset to {association.association_id}. " 40 | "If this error persists after retry, please reach out to Roboto support." 41 | ) 42 | 43 | return file_ids[0] 44 | 45 | 46 | def make_topic_filename_safe(name: str, replacement_char: str = "_") -> str: 47 | unsafe_chars = re.compile(r'[<>:"/\\|?*\0]') 48 | return unsafe_chars.sub(replacement_char, name) 49 | -------------------------------------------------------------------------------- /.github/workflows/sdk.yml: -------------------------------------------------------------------------------- 1 | name: Build and release SDK 2 | on: 3 | push: 4 | tags: 5 | - "v[0-9]+.[0-9]+.[0-9]+" 6 | - "v[0-9]+.[0-9]+.[0-9]+a*" 7 | - "v[0-9]+.[0-9]+.[0-9]+b*" 8 | - "v[0-9]+.[0-9]+.[0-9]+rc*" 9 | 10 | jobs: 11 | dist: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v6 15 | 16 | - uses: actions/setup-python@v6 17 | 18 | - name: Install packaging deps 19 | run: | 20 | python -m venv .venv 21 | .venv/bin/python -m pip install build 22 | .venv/bin/python -m pip install twine 23 | 24 | - name: Build SDist and wheel 25 | run: .venv/bin/python -m build 26 | 27 | - uses: actions/upload-artifact@v4 28 | with: 29 | name: Packages 30 | path: dist/* 31 | 32 | - name: Check metadata 33 | run: .venv/bin/python -m twine check dist/* 34 | 35 | publish: 36 | needs: [dist] 37 | environment: pypi 38 | permissions: 39 | # Required for trusted publishing 40 | # https://docs.pypi.org/trusted-publishers/ 41 | id-token: write 42 | # Required for generating attestations 43 | attestations: write 44 | contents: read 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/download-artifact@v4 48 | with: 49 | name: Packages 50 | path: dist 51 | 52 | - name: Generate artifact attestation for sdist and wheel 53 | uses: actions/attest-build-provenance@v1.4.3 54 | with: 55 | subject-path: "dist/*" 56 | 57 | - uses: pypa/gh-action-pypi-publish@release/v1 58 | with: 59 | print-hash: true 60 | skip-existing: true -------------------------------------------------------------------------------- /src/roboto/domain/files/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .file import File 8 | from .file_creds import ( 9 | CredentialProvider, 10 | DatasetCredentials, 11 | S3Credentials, 12 | ) 13 | from .file_downloader import FileDownloader 14 | from .lazy_lookup_file import LazyLookupFile 15 | from .operations import ( 16 | AbortTransactionsRequest, 17 | DeleteFileRequest, 18 | DirectoryContentsPage, 19 | FileRecordRequest, 20 | ImportFileRequest, 21 | QueryFilesRequest, 22 | RenameFileRequest, 23 | SignedUrlResponse, 24 | UpdateFileRecordRequest, 25 | ) 26 | from .record import ( 27 | DirectoryRecord, 28 | FileRecord, 29 | FileStatus, 30 | FileStorageType, 31 | FileTag, 32 | FSType, 33 | IngestionStatus, 34 | is_directory, 35 | is_file, 36 | ) 37 | 38 | __all__ = ( 39 | "AbortTransactionsRequest", 40 | "CredentialProvider", 41 | "DatasetCredentials", 42 | "DeleteFileRequest", 43 | "DirectoryContentsPage", 44 | "DirectoryRecord", 45 | "FSType", 46 | "File", 47 | "FileDownloader", 48 | "FileRecord", 49 | "FileRecordRequest", 50 | "FileStatus", 51 | "FileStorageType", 52 | "FileTag", 53 | "ImportFileRequest", 54 | "IngestionStatus", 55 | "LazyLookupFile", 56 | "QueryFilesRequest", 57 | "RenameFileRequest", 58 | "S3Credentials", 59 | "SignedUrlResponse", 60 | "UpdateFileRecordRequest", 61 | "is_directory", 62 | "is_file", 63 | ) 64 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "roboto" 7 | dependencies = [ 8 | "boto3>=1.26", 9 | "botocore>=1.29", 10 | "cookiecutter>=2.5.0", 11 | "cron-converter>=1.2.1", 12 | "filelock~=3.15", 13 | "mcap>=1.3.0", 14 | "mcap-ros1-support>=0.7.1", 15 | "mcap-ros2-support>=0.5.3", 16 | "packaging>=24.0", 17 | "pathspec>=0.11", 18 | "platformdirs>=4.0", 19 | "pydantic>=2.5", 20 | "pydantic-core>=2.14", 21 | "pydantic-settings>=2.1", 22 | "rich>=14.2", 23 | "tenacity>=8.2", 24 | "tqdm>=4.65", 25 | "typing-extensions >=4.8 ; python_version<'3.10'", 26 | ] 27 | description = "Official Python toolkit for roboto.ai" 28 | dynamic = ["version"] 29 | license = { file = "LICENSE" } 30 | maintainers = [ 31 | { name = "Roboto AI", email = "info@roboto.ai" }, 32 | ] 33 | readme = "README.md" 34 | requires-python = ">=3.9,<4" 35 | 36 | 37 | [project.optional-dependencies] 38 | analytics = [ 39 | "fsspec[http]>=2025.5.1", 40 | "numpy>=1.19", 41 | "pandas>=2.0", 42 | "pyarrow>=20.0.0", 43 | "stumpy>=1.13", 44 | ] 45 | ingestion = [ 46 | "pandas>=2.0", 47 | "pyarrow>=20.0.0", 48 | ] 49 | 50 | examples = [ 51 | "ipython~=8.28", 52 | "jupyter~=1.1", 53 | "matplotlib~=3.9", 54 | "pillow~=10.4", 55 | ] 56 | 57 | [project.scripts] 58 | roboto = "roboto.cli:entry" 59 | 60 | [project.urls] 61 | Documentation = "https://docs.roboto.ai" 62 | Homepage = "https://www.roboto.ai" 63 | Issues = "https://github.com/roboto-ai/roboto-python-sdk/issues" 64 | Repository = "https://github.com/roboto-ai/roboto-python-sdk.git" 65 | 66 | [tool.setuptools.dynamic] 67 | version = {attr = "roboto.version.__version__"} -------------------------------------------------------------------------------- /src/roboto/query/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .api import ( 8 | QualifiedRoboqlQuery, 9 | QueryContext, 10 | QueryRecord, 11 | QueryScheme, 12 | QueryStatus, 13 | QueryStorageContext, 14 | QueryStorageScheme, 15 | QueryTarget, 16 | SubmitRoboqlQueryRequest, 17 | SubmitStructuredQueryRequest, 18 | SubmitTermQueryRequest, 19 | ) 20 | from .client import Query, QueryClient 21 | from .conditions import ( 22 | Comparator, 23 | Condition, 24 | ConditionGroup, 25 | ConditionOperator, 26 | ConditionType, 27 | ConditionValue, 28 | Field, 29 | ) 30 | from .specification import ( 31 | DEFAULT_PAGE_SIZE, 32 | MAX_PAGE_SIZE, 33 | QuerySpecification, 34 | SortDirection, 35 | ) 36 | from .visitor import BaseVisitor, ConditionVisitor 37 | 38 | __all__ = ( 39 | "BaseVisitor", 40 | "Comparator", 41 | "Condition", 42 | "ConditionGroup", 43 | "ConditionOperator", 44 | "ConditionType", 45 | "ConditionValue", 46 | "ConditionVisitor", 47 | "Field", 48 | "QualifiedRoboqlQuery", 49 | "Query", 50 | "QueryClient", 51 | "QueryContext", 52 | "QueryRecord", 53 | "QueryScheme", 54 | "QuerySpecification", 55 | "QueryStorageContext", 56 | "QueryStorageScheme", 57 | "QueryStatus", 58 | "QueryTarget", 59 | "SortDirection", 60 | "SubmitStructuredQueryRequest", 61 | "SubmitRoboqlQueryRequest", 62 | "SubmitTermQueryRequest", 63 | "MAX_PAGE_SIZE", 64 | "DEFAULT_PAGE_SIZE", 65 | ) 66 | -------------------------------------------------------------------------------- /vendor/robotpajamas.pants.sci/experimental/scie/subsystems.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Pants project contributors (see CONTRIBUTORS.md). 2 | # Licensed under the Apache License, Version 2.0 (see LICENSE). 3 | 4 | from __future__ import annotations 5 | 6 | from collections.abc import Iterable 7 | 8 | from pants.core.util_rules.external_tool import TemplatedExternalTool 9 | from pants.engine.rules import Rule, collect_rules 10 | from pants.engine.unions import UnionRule 11 | from pants.util.strutil import softwrap 12 | 13 | 14 | class Science(TemplatedExternalTool): 15 | options_scope = "science" 16 | help = softwrap("""A high level tool to build scies with.""") 17 | 18 | default_version = "0.15.1" 19 | default_known_versions = [ 20 | "0.15.1|linux_arm64|928dddc37d98117b4310d7779b677f04ce25839898e947531b3db9bf4b473d70|9444039", 21 | "0.15.1|linux_x86_64|a1b7986a8db40495ffdd7a8e8a20ed6d02f3213f8e2bdd5f3c9f4ce48dd8265f|10948381", 22 | "0.15.1|macos_arm64|51aec4fcea783dddc78d54aaa07bbb8a3002d3a0308c75bd12f27eadbc53b3d1|8880820", 23 | "0.15.1|macos_x86_64|d57b503c4e1073d7c89c12719b6fca43ffc956e2dfdcd68df180d5899c01b885|9617696", 24 | ] 25 | 26 | default_url_template = ( 27 | "https://github.com/a-scie/lift/releases/download/v{version}/science-{platform}" 28 | ) 29 | 30 | default_url_platform_mapping = { 31 | "linux_arm64": "linux-aarch64", 32 | "linux_x86_64": "linux-x86_64", 33 | "macos_arm64": "macos-aarch64", 34 | "macos_x86_64": "macos-x86_64", 35 | } 36 | 37 | # args = ArgsListOption(example="--release") 38 | 39 | 40 | def rules() -> Iterable[Rule | UnionRule]: 41 | return ( 42 | *collect_rules(), 43 | *Science.rules(), # type: ignore[call-arg] 44 | ) 45 | -------------------------------------------------------------------------------- /src/roboto/exceptions/http.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import http 8 | import json 9 | import typing 10 | import urllib.error 11 | 12 | 13 | class HttpError(Exception): 14 | __http_exc: urllib.error.HTTPError 15 | __msg: typing.Any = None 16 | __status: typing.Optional[http.HTTPStatus] = None 17 | 18 | def __init__(self, exc: urllib.error.HTTPError) -> None: 19 | super().__init__() 20 | self.__http_exc = exc 21 | 22 | def __repr__(self) -> str: 23 | return f"{self.__class__.__name__}({self.msg!r}, {self.status!r})" 24 | 25 | def __str__(self) -> str: 26 | return f"{self.msg!r}" 27 | 28 | @property 29 | def status(self) -> typing.Optional[http.HTTPStatus]: 30 | if self.__status is None: 31 | try: 32 | self.__status = http.HTTPStatus(int(self.__http_exc.code)) 33 | except ValueError: 34 | self.__status = None 35 | return self.__status 36 | 37 | @property 38 | def msg(self) -> typing.Any: 39 | if self.__msg is None: 40 | decoded = self.__http_exc.read().decode("utf-8") 41 | try: 42 | self.__msg = json.loads(decoded) 43 | except json.JSONDecodeError: 44 | self.__msg = decoded 45 | return self.__msg 46 | 47 | @property 48 | def headers(self) -> dict: 49 | return dict(self.__http_exc.headers.items()) 50 | 51 | 52 | class ClientError(HttpError): 53 | pass 54 | 55 | 56 | class ServerError(HttpError): 57 | pass 58 | -------------------------------------------------------------------------------- /src/roboto/cli/images/list_images.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | import json 9 | 10 | from ...image_registry import ImageRegistry 11 | from ..command import RobotoCommand 12 | from ..common_args import add_org_arg 13 | from ..context import CLIContext 14 | 15 | 16 | def list_images(args: argparse.Namespace, context: CLIContext, parser: argparse.ArgumentParser) -> None: 17 | image_registry = ImageRegistry(context.roboto_client) 18 | paginated_results = image_registry.list_images(repository_name=args.repository_name, org_id=args.org) 19 | while True: 20 | for image in paginated_results.items: 21 | print(json.dumps(image.model_dump(), indent=2)) 22 | if paginated_results.next_token: 23 | paginated_results = image_registry.list_images( 24 | repository_name=args.repository_name, 25 | page_token=paginated_results.next_token, 26 | org_id=args.org, 27 | ) 28 | else: 29 | break 30 | 31 | 32 | def list_images_parser(parser: argparse.ArgumentParser) -> None: 33 | add_org_arg(parser) 34 | 35 | parser.add_argument( 36 | "--repository-name", 37 | dest="repository_name", 38 | action="store", 39 | help="Filter list of images by repository name.", 40 | ) 41 | 42 | 43 | ls_images_command = RobotoCommand( 44 | name="list", 45 | logic=list_images, 46 | setup_parser=list_images_parser, 47 | command_kwargs={"help": "List container images hosted in Roboto's image registry."}, 48 | ) 49 | -------------------------------------------------------------------------------- /src/roboto/image_registry/http_resources.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import pydantic 8 | 9 | 10 | class ContainerUploadCredentials(pydantic.BaseModel): 11 | """Container upload credentials""" 12 | 13 | username: str 14 | password: str 15 | registry_url: str 16 | image_uri: str 17 | 18 | 19 | class CreateImageRepositoryRequest(pydantic.BaseModel): 20 | """Request payload to create an image repository""" 21 | 22 | repository_name: str 23 | immutable_image_tags: bool 24 | 25 | 26 | class CreateImageRepositoryResponse(pydantic.BaseModel): 27 | """Response payload to create an image repository""" 28 | 29 | repository_name: str 30 | repository_uri: str 31 | 32 | 33 | class DeleteImageRequest(pydantic.BaseModel): 34 | """Request payload to delete an image""" 35 | 36 | image_uri: str 37 | 38 | 39 | class DeleteImageRepositoryRequest(pydantic.BaseModel): 40 | """Request payload to delete an image repository""" 41 | 42 | repository_name: str 43 | """The name of the repository to delete.""" 44 | 45 | force: bool = False 46 | """Delete all images in the repository before deleting the repository if the repository is not empty.""" 47 | 48 | 49 | class RepositoryContainsImageResponse(pydantic.BaseModel): 50 | """Response payload to repository contains image""" 51 | 52 | contains_image: bool 53 | 54 | 55 | class SetImageRepositoryImmutableTagsRequest(pydantic.BaseModel): 56 | """Request payload to set image repository tags""" 57 | 58 | repository_name: str 59 | immutable_image_tags: bool 60 | -------------------------------------------------------------------------------- /src/roboto/file_infra/object_store/object_store.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from __future__ import annotations 8 | 9 | import pathlib 10 | import typing 11 | 12 | T_co = typing.TypeVar("T_co", covariant=True) 13 | 14 | 15 | class FutureLike(typing.Protocol[T_co]): 16 | """Protocol for future-like objects.""" 17 | 18 | def result(self) -> T_co: 19 | """Wait for and return the result.""" 20 | ... 21 | 22 | def done(self) -> bool: 23 | """Return True if the future is done.""" 24 | ... 25 | 26 | 27 | @typing.runtime_checkable 28 | class ObjectStore(typing.Protocol): 29 | @classmethod 30 | def create(cls, credential_provider: CredentialProvider, **kwargs) -> ObjectStore: ... 31 | 32 | def __enter__(self) -> ObjectStore: ... 33 | 34 | def __exit__(self, exc_type, exc_val, exc_tb) -> None: ... 35 | 36 | def put(self, source: pathlib.Path, destination_uri: str) -> FutureLike[None]: 37 | """ 38 | Uploads a local file to a specific cloud URI. 39 | 40 | Args: 41 | source: Local path to the file. 42 | destination_uri: Full URI (e.g., 's3://my-bucket/folder/data.csv') 43 | """ 44 | ... 45 | 46 | 47 | class Credentials(typing.TypedDict): 48 | """ 49 | This interface is driven by botocore.credentials.RefreshableCredentials 50 | """ 51 | 52 | access_key: str 53 | secret_key: str 54 | token: str 55 | region: str 56 | expiry_time: typing.Optional[str] 57 | 58 | 59 | CredentialProvider: typing.TypeAlias = typing.Callable[[], Credentials] 60 | -------------------------------------------------------------------------------- /src/roboto/file_infra/object_store/registry.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from __future__ import annotations 8 | 9 | import typing 10 | import urllib.parse 11 | 12 | from .object_store import ( 13 | CredentialProvider, 14 | ObjectStore, 15 | ) 16 | 17 | 18 | class StoreRegistry: 19 | _registry: typing.ClassVar[dict[str, typing.Type[ObjectStore]]] = {} 20 | 21 | @classmethod 22 | def register(cls, scheme: str): 23 | """ 24 | Class Decorator. 25 | Registers the decorated class to handle the specific URI scheme. 26 | """ 27 | 28 | def wrapper(store_cls: typing.Type[ObjectStore]): 29 | if scheme in cls._registry: 30 | raise ValueError(f"Scheme '{scheme}' is already registered to {cls._registry[scheme]}") 31 | cls._registry[scheme] = store_cls 32 | return store_cls 33 | 34 | return wrapper 35 | 36 | @classmethod 37 | def get_store_for_uri(cls, uri: str, credential_provider: CredentialProvider, **kwargs) -> ObjectStore: 38 | """ 39 | Parses URI -> Finds Class -> Calls Class.create() -> Returns Instance 40 | """ 41 | parsed = urllib.parse.urlparse(uri) 42 | scheme = parsed.scheme 43 | 44 | if scheme not in cls._registry: 45 | raise ValueError(f"No ObjectStore class registered for scheme: '{scheme}://'") 46 | 47 | store_class = cls._registry[scheme] 48 | 49 | # Delegate construction to the specific class 50 | return store_class.create(credential_provider=credential_provider, **kwargs) 51 | -------------------------------------------------------------------------------- /src/roboto/cli/datasets/download_files.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | import pathlib 9 | 10 | from ...domain.datasets import Dataset 11 | from ..command import RobotoCommand 12 | from ..context import CLIContext 13 | from .shared_helpdoc import DATASET_ID_HELP 14 | 15 | 16 | def download_files(args, context: CLIContext, parser: argparse.ArgumentParser): 17 | record = Dataset.from_id(args.dataset_id, context.roboto_client) 18 | 19 | record.download_files(out_path=args.path, include_patterns=args.include, exclude_patterns=args.exclude) 20 | 21 | 22 | def download_files_setup_parser(parser): 23 | parser.add_argument("-d", "--dataset-id", type=str, required=True, help=DATASET_ID_HELP) 24 | parser.add_argument( 25 | "-p", 26 | "--path", 27 | type=pathlib.Path, 28 | required=True, 29 | help="The download destination for this operation.", 30 | ) 31 | parser.add_argument( 32 | "-i", 33 | "--include", 34 | type=str, 35 | nargs="*", 36 | help="Zero or more include filters (if path points to a directory)", 37 | ) 38 | parser.add_argument( 39 | "-x", 40 | "--exclude", 41 | type=str, 42 | nargs="*", 43 | help="Zero or more exclude filters (if path points to a directory)", 44 | ) 45 | 46 | 47 | download_files_command = RobotoCommand( 48 | name="download-files", 49 | logic=download_files, 50 | setup_parser=download_files_setup_parser, 51 | command_kwargs={"help": "Download a file or directory from a dataset."}, 52 | ) 53 | -------------------------------------------------------------------------------- /src/roboto/cli/secrets/write.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | import sys 9 | 10 | from roboto.cli.command import RobotoCommand 11 | from roboto.cli.common_args import add_org_arg 12 | from roboto.cli.context import CLIContext 13 | from roboto.domain.secrets import Secret 14 | from roboto.exceptions import ( 15 | RobotoNotFoundException, 16 | ) 17 | 18 | 19 | def write_logic(args, context: CLIContext, parser: argparse.ArgumentParser): 20 | org_id = args.org 21 | secret_name = args.secret_name 22 | 23 | try: 24 | secret = Secret.from_name( 25 | name=secret_name, 26 | org_id=org_id, 27 | roboto_client=context.roboto_client, 28 | ) 29 | except RobotoNotFoundException: 30 | secret = Secret.create( 31 | name=secret_name, 32 | caller_org_id=org_id, 33 | roboto_client=context.roboto_client, 34 | ) 35 | 36 | secret.update_value(args.secret_value) 37 | print(f"Wrote new value to {secret_name}", file=sys.stderr) 38 | 39 | 40 | def write_setup_parser(parser): 41 | add_org_arg(parser) 42 | parser.add_argument( 43 | "secret_name", 44 | help="The name of the secret to write.", 45 | ) 46 | parser.add_argument( 47 | "secret_value", 48 | help="The value to write to the secret.", 49 | ) 50 | 51 | 52 | write_command = RobotoCommand( 53 | name="write", 54 | logic=write_logic, 55 | setup_parser=write_setup_parser, 56 | command_kwargs={"help": "Write a new value to a new or existing secret."}, 57 | ) 58 | -------------------------------------------------------------------------------- /src/roboto/cli/collections/show.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from ...domain.collections import ( 10 | Collection, 11 | CollectionContentMode, 12 | ) 13 | from ..command import RobotoCommand 14 | from ..context import CLIContext 15 | from .shared_helpdoc import ( 16 | COLLECTION_ID_HELP, 17 | COLLECTION_VERSION_HELP, 18 | CONTENT_MODE_HELP, 19 | ) 20 | 21 | 22 | def show(args, context: CLIContext, parser: argparse.ArgumentParser): 23 | content_mode = [x for x in CollectionContentMode if x.value == args.content_mode][0] 24 | 25 | collection = Collection.from_id( 26 | collection_id=args.collection_id, 27 | version=args.collection_version, 28 | roboto_client=context.roboto_client, 29 | content_mode=content_mode, 30 | ) 31 | print(collection.record.model_dump_json(indent=2)) 32 | 33 | 34 | def show_setup_parser(parser): 35 | parser.add_argument("collection_id", type=str, help=COLLECTION_ID_HELP) 36 | parser.add_argument( 37 | "-v", 38 | "--collection-version", 39 | type=int, 40 | required=False, 41 | help=COLLECTION_VERSION_HELP, 42 | ) 43 | parser.add_argument( 44 | "--content-mode", 45 | type=str, 46 | choices=[mode.value for mode in CollectionContentMode], 47 | help=CONTENT_MODE_HELP, 48 | default=CollectionContentMode.References.value, 49 | ) 50 | 51 | 52 | show_command = RobotoCommand( 53 | name="show", 54 | logic=show, 55 | setup_parser=show_setup_parser, 56 | command_kwargs={"help": "Show information about a collection."}, 57 | ) 58 | -------------------------------------------------------------------------------- /src/roboto/api_version.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from __future__ import annotations 8 | 9 | from .compat import StrEnum 10 | 11 | 12 | class RobotoApiVersion(StrEnum): 13 | """Enumeration of supported Roboto API versions. 14 | 15 | This enum defines the available API versions for the Roboto platform. Each version 16 | represents a specific date-based API version that may include breaking changes, 17 | new features, or deprecations compared to previous versions. 18 | 19 | API versions follow the YYYY-MM-DD format and are used to ensure backward 20 | compatibility while allowing the platform to evolve. Clients should specify 21 | the API version they were designed for to ensure consistent behavior. 22 | """ 23 | 24 | v2025_01_01 = "2025-01-01" 25 | v2025_07_14 = "2025-07-14" 26 | 27 | @staticmethod 28 | def latest() -> RobotoApiVersion: 29 | """Get the latest available API version. 30 | 31 | Returns: 32 | The most recent API version supported by the platform. 33 | """ 34 | return RobotoApiVersion.v2025_07_14 35 | 36 | def is_latest(self) -> bool: 37 | """Check if this API version is the latest available version. 38 | 39 | Returns: 40 | True if this version matches the latest API version, False otherwise. 41 | """ 42 | return self == RobotoApiVersion.latest() 43 | 44 | def __str__(self) -> str: 45 | """Return the string representation of the API version. 46 | 47 | Returns: 48 | The API version string in YYYY-MM-DD format. 49 | """ 50 | return self.value 51 | -------------------------------------------------------------------------------- /src/roboto/query/visitor.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import abc 8 | import typing 9 | 10 | from .conditions import ( 11 | Condition, 12 | ConditionGroup, 13 | ConditionType, 14 | ) 15 | 16 | 17 | class ConditionVisitor(abc.ABC): 18 | """Query condition visitor""" 19 | 20 | def visit(self, cond: ConditionType) -> typing.Optional[ConditionType]: 21 | if isinstance(cond, Condition): 22 | return self.visit_condition(cond) 23 | elif isinstance(cond, ConditionGroup): 24 | return self.visit_condition_group(cond) 25 | else: 26 | raise ValueError(f"Unknown condition type: {type(cond)}") 27 | 28 | @abc.abstractmethod 29 | def visit_condition(self, condition: Condition) -> typing.Optional[ConditionType]: 30 | raise NotImplementedError("visit_condition") 31 | 32 | @abc.abstractmethod 33 | def visit_condition_group(self, condition_group: ConditionGroup) -> typing.Optional[ConditionType]: 34 | raise NotImplementedError("visit_condition_group") 35 | 36 | 37 | class BaseVisitor(ConditionVisitor): 38 | """Query base visitor""" 39 | 40 | def visit_condition(self, condition: Condition) -> typing.Optional[ConditionType]: 41 | return condition 42 | 43 | def visit_condition_group(self, condition_group: ConditionGroup) -> typing.Optional[ConditionType]: 44 | conditions = [] 45 | for condition in condition_group.conditions: 46 | visited = self.visit(condition) 47 | if visited: 48 | conditions.append(visited) 49 | 50 | condition_group.conditions = conditions 51 | 52 | return condition_group 53 | -------------------------------------------------------------------------------- /src/roboto/domain/files/lazy_lookup_file.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from typing import Any, Callable 8 | 9 | from .file import File 10 | 11 | 12 | class LazyLookupFile(File): 13 | """ 14 | A File subclass that defers instantiation (hydration) of the real File until any non‐internal attribute is 15 | first accessed. 16 | 17 | This is useful for scenarios where we know how to dereference a File (e.g., by ID), and we want to return a handle 18 | in case the caller wants to work with it, but we don’t want to pay the cost of dereferencing it unless necessary. 19 | """ 20 | 21 | def __init__(self, hydrate_fn: Callable[[], File]) -> None: 22 | # We don’t call super().__init__; we’ll get a real File from hydrate_fn. 23 | object.__setattr__(self, "_hydrate_fn", hydrate_fn) 24 | object.__setattr__(self, "_file", None) 25 | 26 | def _hydrate(self) -> None: 27 | if object.__getattribute__(self, "_file") is None: 28 | real = object.__getattribute__(self, "_hydrate_fn")() 29 | object.__setattr__(self, "_file", real) 30 | 31 | def __getattribute__(self, name: str) -> Any: 32 | if name in ["_hydrate", "_hydrate_fn", "_file"]: 33 | return object.__getattribute__(self, name) 34 | 35 | self._hydrate() 36 | real = object.__getattribute__(self, "_file") 37 | return getattr(real, name) 38 | 39 | def __setattr__(self, name: str, value: Any) -> None: 40 | if name in ["_hydrate", "_hydrate_fn", "_file"]: 41 | object.__setattr__(self, name, value) 42 | else: 43 | self._hydrate() 44 | real = object.__getattribute__(self, "_file") 45 | setattr(real, name, value) 46 | -------------------------------------------------------------------------------- /src/roboto/cli/datasets/import_external_file.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | 9 | from ...domain.files import File 10 | from ..command import RobotoCommand 11 | from ..context import CLIContext 12 | from .shared_helpdoc import DATASET_ID_HELP 13 | 14 | 15 | def import_external_file(args, context: CLIContext, parser: argparse.ArgumentParser): 16 | normalized_relative_path = args.path if args.path else args.file.split("/")[-1] 17 | 18 | fl = File.import_one( 19 | dataset_id=args.dataset_id, 20 | relative_path=normalized_relative_path, 21 | uri=args.file, 22 | roboto_client=context.roboto_client, 23 | ) 24 | print(fl.file_id) 25 | 26 | 27 | def import_external_file_setup_parser(parser): 28 | parser.add_argument("-d", "--dataset-id", type=str, required=True, help=DATASET_ID_HELP) 29 | parser.add_argument( 30 | "-p", 31 | "--path", 32 | type=str, 33 | required=False, 34 | help=( 35 | "Destination path within the dataset (relative to the root) where the file will be imported. " 36 | "Defaults to the filename at the dataset root if not specified." 37 | ), 38 | ) 39 | parser.add_argument( 40 | "-f", 41 | "--file", 42 | type=str, 43 | help="The URI of the file to import, e.g. s3://my-bucket/path/to/file.txt for S3 files.", 44 | ) 45 | 46 | 47 | import_external_file_command = RobotoCommand( 48 | name="import-external-file", 49 | logic=import_external_file, 50 | setup_parser=import_external_file_setup_parser, 51 | command_kwargs={ 52 | "help": "Import a file from an external storage location (e.g. a pre-registered S3 bucket) into a dataset." 53 | }, 54 | ) 55 | -------------------------------------------------------------------------------- /src/roboto/cli/datasets/upload_files.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | import pathlib 9 | 10 | from ...domain.datasets import Dataset 11 | from ..command import ( 12 | ExistingPathlibPath, 13 | RobotoCommand, 14 | ) 15 | from ..context import CLIContext 16 | from .shared_helpdoc import DATASET_ID_HELP 17 | 18 | 19 | def upload_files(args, context: CLIContext, parser: argparse.ArgumentParser): 20 | path: pathlib.Path = args.path 21 | if args.exclude is not None and not path.is_dir(): 22 | parser.error("Exclude filters are only supported for directory uploads, not single files.") 23 | 24 | dataset = Dataset.from_id(args.dataset_id, context.roboto_client) 25 | 26 | if path.is_dir(): 27 | dataset.upload_directory( 28 | directory_path=path, 29 | exclude_patterns=args.exclude, 30 | ) 31 | else: 32 | dataset.upload_files( 33 | files=[path], 34 | ) 35 | 36 | 37 | def upload_files_setup_parser(parser): 38 | parser.add_argument("-d", "--dataset-id", type=str, required=True, help=DATASET_ID_HELP) 39 | parser.add_argument( 40 | "-p", 41 | "--path", 42 | type=ExistingPathlibPath, 43 | required=True, 44 | help="The path to a file or directory to upload.", 45 | ) 46 | parser.add_argument( 47 | "-x", 48 | "--exclude", 49 | type=str, 50 | nargs="*", 51 | help="Zero or more exclude filters (if path points to a directory)", 52 | ) 53 | 54 | 55 | upload_files_command = RobotoCommand( 56 | name="upload-files", 57 | logic=upload_files, 58 | setup_parser=upload_files_setup_parser, 59 | command_kwargs={"help": "Upload a file or directory to a dataset."}, 60 | ) 61 | -------------------------------------------------------------------------------- /src/roboto/domain/users/record.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from typing import Optional 8 | 9 | import pydantic 10 | 11 | from ...notifications import ( 12 | NotificationChannel, 13 | NotificationType, 14 | ) 15 | 16 | 17 | class UserRecord(pydantic.BaseModel): 18 | """A wire-transmissible representation of a user.""" 19 | 20 | user_id: str 21 | """Unique identifier for the user, typically an email address.""" 22 | 23 | is_service_user: bool = False 24 | """Whether this is a service user for automated operations. 25 | 26 | Service users can be used to perform actions on behalf of customers. 27 | For example, a service user can be associated with a Trigger, 28 | which will then invoke its corresponding Action as the service user. 29 | """ 30 | 31 | is_system_user: Optional[bool] = False 32 | """Whether this is a system user for internal platform operations.""" 33 | 34 | name: Optional[str] = None 35 | """Human-readable display name for the user.""" 36 | 37 | picture_url: Optional[str] = None 38 | """URL to the user's profile picture.""" 39 | 40 | notification_channels_enabled: dict[NotificationChannel, bool] = pydantic.Field(default_factory=dict) 41 | """Mapping of notification channels to their enabled/disabled status.""" 42 | 43 | notification_types_enabled: dict[NotificationType, bool] = pydantic.Field(default_factory=dict) 44 | """Mapping of notification types to their enabled/disabled status.""" 45 | 46 | def is_email_notifications_enabled(self) -> bool: 47 | return self.notification_channels_enabled.get(NotificationChannel.Email, False) 48 | 49 | def is_comment_mentions_enabled(self) -> bool: 50 | return self.notification_types_enabled.get(NotificationType.CommentMention, False) 51 | -------------------------------------------------------------------------------- /src/roboto/domain/users/operations.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from typing import Optional 8 | 9 | import pydantic 10 | 11 | from roboto.notifications import ( 12 | NotificationChannel, 13 | NotificationType, 14 | ) 15 | 16 | 17 | class CreateUserRequest(pydantic.BaseModel): 18 | """Request payload to create a new user.""" 19 | 20 | user_id: str 21 | """Unique identifier for the user, typically an email address.""" 22 | 23 | name: Optional[str] = None 24 | """Human-readable display name for the user.""" 25 | 26 | is_service_user: bool = False 27 | """Whether this is a service user for automated operations.""" 28 | 29 | is_system_user: bool = False 30 | """Whether this is a system user for internal platform operations.""" 31 | 32 | picture_url: Optional[str] = None 33 | """URL to the user's profile picture.""" 34 | 35 | default_notification_channels: Optional[list[NotificationChannel]] = [NotificationChannel.Email] 36 | """Default notification channels to enable for the user.""" 37 | 38 | default_notification_types: Optional[list[NotificationType]] = [NotificationType.CommentMention] 39 | """Default notification types to enable for the user.""" 40 | 41 | 42 | class UpdateUserRequest(pydantic.BaseModel): 43 | """Request payload to update an existing user.""" 44 | 45 | name: Optional[str] = None 46 | """Updated display name for the user.""" 47 | 48 | picture_url: Optional[str] = None 49 | """Updated URL to the user's profile picture.""" 50 | 51 | notification_channels_enabled: Optional[dict[NotificationChannel, bool]] = None 52 | """Updated notification channel preferences.""" 53 | 54 | notification_types_enabled: Optional[dict[NotificationType, bool]] = None 55 | """Updated notification type preferences.""" 56 | -------------------------------------------------------------------------------- /src/roboto/domain/topics/json_decoder_factory.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import json 8 | import typing 9 | 10 | from mcap.decoder import ( 11 | DecoderFactory as McapDecoderFactory, 12 | ) 13 | from mcap.records import Schema 14 | from mcap.well_known import MessageEncoding 15 | 16 | 17 | class JsonDecoderFactory(McapDecoderFactory): 18 | """Factory for creating JSON message decoders for MCAP readers. 19 | 20 | Provides functionality to decode JSON-encoded messages within MCAP files. 21 | This decoder factory is used by the MCAP reader to handle messages that 22 | were originally encoded in JSON format. 23 | """ 24 | 25 | def decoder_for( 26 | self, message_encoding: str, schema: typing.Optional[Schema] 27 | ) -> typing.Optional[typing.Callable[[bytes], typing.Any]]: 28 | """Create a decoder function for JSON-encoded messages. 29 | 30 | Returns a decoder function if the message encoding is JSON, otherwise returns None 31 | to indicate this factory cannot handle the encoding. 32 | 33 | Args: 34 | message_encoding: The encoding format of the message. 35 | schema: Optional schema information for the message. 36 | 37 | Returns: 38 | A decoder function for JSON messages, or None if the encoding is not JSON. 39 | 40 | Examples: 41 | >>> factory = JsonDecoderFactory() 42 | >>> decoder = factory.decoder_for(MessageEncoding.JSON, None) 43 | >>> if decoder: 44 | ... decoded = decoder(b'{"x": 1.5, "y": 2.0}') 45 | ... print(decoded) 46 | {'x': 1.5, 'y': 2.0} 47 | """ 48 | if message_encoding != MessageEncoding.JSON: 49 | return None 50 | 51 | def decoder(data: bytes): 52 | deserialized = data.decode() 53 | return json.loads(deserialized) 54 | 55 | return decoder 56 | -------------------------------------------------------------------------------- /src/roboto/query/precanned.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from typing import Optional 8 | 9 | from .conditions import ( 10 | Comparator, 11 | Condition, 12 | ConditionGroup, 13 | ConditionOperator, 14 | ConditionType, 15 | ) 16 | 17 | 18 | def __pathspec_to_like_value(pattern: str) -> str: 19 | return pattern.replace("**/*", "%").replace("**", "%").replace("*", "%") 20 | 21 | 22 | def git_paths_to_condition_group( 23 | include_patterns: Optional[list[str]], 24 | exclude_patterns: Optional[list[str]], 25 | path_field_name: str, 26 | ) -> ConditionGroup: 27 | conditions: list[ConditionType] = [] 28 | 29 | if include_patterns: 30 | include_pattern_conditions = list( 31 | map( 32 | lambda pattern: Condition( 33 | field=path_field_name, 34 | comparator=Comparator.Like, 35 | value=__pathspec_to_like_value(pattern), 36 | ), 37 | include_patterns, 38 | ) 39 | ) 40 | 41 | conditions.append( 42 | ConditionGroup( 43 | conditions=include_pattern_conditions, operator=ConditionOperator.Or 44 | ) 45 | ) 46 | 47 | if exclude_patterns: 48 | exclude_pattern_conditions = list( 49 | map( 50 | lambda pattern: Condition( 51 | field=path_field_name, 52 | comparator=Comparator.NotLike, 53 | value=__pathspec_to_like_value(pattern), 54 | ), 55 | exclude_patterns, 56 | ) 57 | ) 58 | 59 | conditions.append( 60 | ConditionGroup( 61 | conditions=exclude_pattern_conditions, operator=ConditionOperator.And 62 | ) 63 | ) 64 | 65 | return ConditionGroup(conditions=conditions, operator=ConditionOperator.And) 66 | -------------------------------------------------------------------------------- /src/roboto/cli/datasets/create.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | import json 9 | 10 | from ...domain import datasets 11 | from ..command import ( 12 | KeyValuePairsAction, 13 | RobotoCommand, 14 | ) 15 | from ..common_args import add_org_arg 16 | from ..context import CLIContext 17 | 18 | 19 | def create(args, context: CLIContext, parser: argparse.ArgumentParser): 20 | dataset = datasets.Dataset.create( 21 | description=args.description, 22 | metadata=args.metadata, 23 | name=args.name, 24 | tags=args.tag, 25 | roboto_client=context.roboto_client, 26 | caller_org_id=args.org, 27 | ) 28 | 29 | print(json.dumps(dataset.to_dict(), indent=2)) 30 | 31 | 32 | def create_setup_parser(parser): 33 | parser.add_argument( 34 | "-m", 35 | "--metadata", 36 | metavar="KEY=VALUE", 37 | nargs="*", 38 | action=KeyValuePairsAction, 39 | help="Zero or more ``=`` pairs to add to this dataset's metadata. " 40 | + "Metadata can be modified after creation.", 41 | ) 42 | parser.add_argument( 43 | "-t", 44 | "--tag", 45 | type=str, 46 | nargs="*", 47 | help="One or more tags to add to this dataset. Tags can be modified after creation.", 48 | action="extend", 49 | ) 50 | parser.add_argument( 51 | "-d", 52 | "--description", 53 | type=str, 54 | help="A human readable description of this dataset.", 55 | ) 56 | parser.add_argument( 57 | "-n", 58 | "--name", 59 | type=str, 60 | help="A human readable name for this dataset.", 61 | ) 62 | add_org_arg(parser=parser) 63 | 64 | 65 | create_command = RobotoCommand( 66 | name="create", 67 | logic=create, 68 | setup_parser=create_setup_parser, 69 | command_kwargs={"help": "Create a new dataset."}, 70 | ) 71 | -------------------------------------------------------------------------------- /BUILD.pants: -------------------------------------------------------------------------------- 1 | python_requirements( 2 | name="3rdparty", 3 | source="pyproject.toml", 4 | module_mapping={ 5 | "mcap-ros1-support": ("mcap_ros1",), 6 | "mcap-ros2-support": ("mcap_ros2",) 7 | }, 8 | ) 9 | 10 | resource( 11 | name="pyproject.toml", 12 | source="pyproject.toml", 13 | ) 14 | 15 | python_distribution( 16 | name="roboto-dist", 17 | dependencies=[ 18 | "//src/roboto:roboto-src", 19 | "//src/roboto:py_typed", 20 | "//:pyproject.toml", 21 | ], 22 | provides=python_artifact( 23 | name="roboto", 24 | ), 25 | generate_setup=False, 26 | wheel=True, 27 | sdist=False, 28 | ) 29 | 30 | pex_binary( 31 | name="roboto-cli", 32 | dependencies=[ 33 | "//:roboto-dist", 34 | ], 35 | entry_point="roboto.cli.entry:entry", 36 | execution_mode="venv", 37 | complete_platforms=[ 38 | "//build-support/pex_platforms:linux_py313_aarch64", 39 | "//build-support/pex_platforms:linux_py313_x86_64", 40 | "//build-support/pex_platforms:macos_py313_aarch64", 41 | "//build-support/pex_platforms:macos_py313_x86_64", 42 | ], 43 | ) 44 | 45 | pex_binary( 46 | name="roboto-agent-cli", 47 | dependencies=[ 48 | "//:roboto-dist", 49 | ], 50 | entry_point="roboto.upload_agent.__main__:main", 51 | execution_mode="venv", 52 | complete_platforms=[ 53 | "//build-support/pex_platforms:linux_py313_aarch64", 54 | "//build-support/pex_platforms:linux_py313_x86_64", 55 | "//build-support/pex_platforms:macos_py313_aarch64", 56 | "//build-support/pex_platforms:macos_py313_x86_64", 57 | ], 58 | ) 59 | 60 | scie_binary( 61 | name="roboto", 62 | dependencies=["//:roboto-cli"], 63 | platforms=[ 64 | "linux-aarch64", 65 | "linux-x86_64", 66 | "macos-aarch64", 67 | "macos-x86_64", 68 | ], 69 | lift="roboto-cli-lift.toml" 70 | ) 71 | 72 | scie_binary( 73 | name="roboto-agent", 74 | dependencies=["//:roboto-agent-cli"], 75 | platforms=[ 76 | "linux-aarch64", 77 | "linux-x86_64", 78 | "macos-aarch64", 79 | "macos-x86_64", 80 | ], 81 | lift="roboto-agent-lift.toml" 82 | ) -------------------------------------------------------------------------------- /src/roboto/cli/invocations/status.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | import json 9 | import time 10 | 11 | from ...domain import actions 12 | from ..command import RobotoCommand 13 | from ..context import CLIContext 14 | 15 | 16 | def status(args: argparse.Namespace, context: CLIContext, parser: argparse.ArgumentParser) -> None: 17 | if not args.tail: 18 | invocation = actions.Invocation.from_id( 19 | args.invocation_id, 20 | roboto_client=context.roboto_client, 21 | ) 22 | print( 23 | json.dumps( 24 | [status_record.to_presentable_dict() for status_record in invocation.status_log], 25 | indent=2, 26 | ) 27 | ) 28 | return 29 | 30 | printed: set[actions.InvocationStatus] = set() 31 | invocation = actions.Invocation.from_id( 32 | args.invocation_id, 33 | roboto_client=context.roboto_client, 34 | ) 35 | try: 36 | while True: 37 | status_records_to_print = [ 38 | status_record for status_record in invocation.status_log if status_record.status not in printed 39 | ] 40 | if status_records_to_print: 41 | for status_record in status_records_to_print: 42 | printed.add(status_record.status) 43 | print(json.dumps(status_record.to_presentable_dict(), indent=2)) 44 | 45 | if invocation.reached_terminal_status: 46 | break 47 | 48 | time.sleep(1) 49 | invocation.refresh() 50 | except KeyboardInterrupt: 51 | pass # Swallow 52 | 53 | 54 | def status_parser(parser: argparse.ArgumentParser): 55 | parser.add_argument("invocation_id") 56 | parser.add_argument("--tail", required=False, action="store_true") 57 | 58 | 59 | status_command = RobotoCommand( 60 | name="status", 61 | logic=status, 62 | setup_parser=status_parser, 63 | command_kwargs={"help": "Get invocation status."}, 64 | ) 65 | -------------------------------------------------------------------------------- /src/roboto/http/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .constants import ( 8 | BEARER_TOKEN_HEADER, 9 | ORG_OVERRIDE_HEADER, 10 | ORG_OVERRIDE_QUERY_PARAM, 11 | RESOURCE_OWNER_OVERRIDE_HEADER, 12 | RESOURCE_OWNER_OVERRIDE_QUERY_PARAM, 13 | USER_OVERRIDE_HEADER, 14 | USER_OVERRIDE_QUERY_PARAM, 15 | ) 16 | from .headers import ( 17 | CONTENT_TYPE_JSON_HEADER, 18 | roboto_headers, 19 | ) 20 | from .http_client import ( 21 | ClientError, 22 | HttpClient, 23 | HttpError, 24 | ServerError, 25 | ) 26 | from .request import BatchRequest 27 | from .request_decorators import ( 28 | BearerTokenDecorator, 29 | SigV4AuthDecorator, 30 | ) 31 | from .requester import ( 32 | ROBOTO_REQUESTER_HEADER, 33 | RobotoRequester, 34 | RobotoTool, 35 | ) 36 | from .response import ( 37 | BatchResponse, 38 | BatchResponseElement, 39 | PaginatedList, 40 | PaginationToken, 41 | PaginationTokenEncoding, 42 | PaginationTokenScheme, 43 | StreamedList, 44 | ) 45 | from .roboto_client import DEFAULT_HTTP_TIMEOUT, RobotoClient 46 | from .testing_util import FakeHttpResponseFactory 47 | 48 | __all__ = ( 49 | "BEARER_TOKEN_HEADER", 50 | "BatchRequest", 51 | "BatchResponse", 52 | "BatchResponseElement", 53 | "BearerTokenDecorator", 54 | "CONTENT_TYPE_JSON_HEADER", 55 | "ClientError", 56 | "DEFAULT_HTTP_TIMEOUT", 57 | "FakeHttpResponseFactory", 58 | "HttpClient", 59 | "HttpError", 60 | "ORG_OVERRIDE_HEADER", 61 | "ORG_OVERRIDE_QUERY_PARAM", 62 | "PaginatedList", 63 | "PaginationToken", 64 | "PaginationTokenEncoding", 65 | "PaginationTokenScheme", 66 | "RESOURCE_OWNER_OVERRIDE_HEADER", 67 | "RESOURCE_OWNER_OVERRIDE_QUERY_PARAM", 68 | "ROBOTO_REQUESTER_HEADER", 69 | "ServerError", 70 | "SigV4AuthDecorator", 71 | "StreamedList", 72 | "USER_OVERRIDE_HEADER", 73 | "USER_OVERRIDE_QUERY_PARAM", 74 | "roboto_headers", 75 | "RobotoClient", 76 | "RobotoRequester", 77 | "RobotoTool", 78 | ) 79 | -------------------------------------------------------------------------------- /src/roboto/serde/dicts.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import collections.abc 8 | import json 9 | import typing 10 | 11 | import pydantic 12 | 13 | 14 | def safe_dict_drill( 15 | target: dict[typing.Any, typing.Any], 16 | keys: list[typing.Any], 17 | case_insensitive: bool = False, 18 | ) -> typing.Any: 19 | value = target 20 | 21 | for key in keys: 22 | if type(value) is not dict: 23 | return None 24 | 25 | if case_insensitive: 26 | value = case_insensitive_get(value, key) 27 | else: 28 | value = value.get(key, None) 29 | 30 | if value is None: 31 | return None 32 | 33 | return value 34 | 35 | 36 | def case_insensitive_get( 37 | target: dict[str, typing.Any], key: str, default: typing.Any = None 38 | ) -> typing.Any: 39 | matches = list(filter(lambda k: k.lower() == key.lower(), target.keys())) 40 | n_matches = len(matches) 41 | if n_matches == 0: 42 | return default 43 | elif n_matches == 1: 44 | return target[matches[0]] 45 | else: 46 | raise ValueError( 47 | f"{n_matches} equivalent case-insensitive keys found for {key}" 48 | ) 49 | 50 | 51 | def pydantic_jsonable_dict( 52 | model: pydantic.BaseModel, exclude_none=False, exclude_unset=False 53 | ) -> dict: 54 | return json.loads( 55 | model.model_dump_json(exclude_none=exclude_none, exclude_unset=exclude_unset) 56 | ) 57 | 58 | 59 | def pydantic_jsonable_dicts( 60 | models: collections.abc.Iterable, exclude_none=False, exclude_unset=False 61 | ) -> list[dict]: 62 | return [ 63 | pydantic_jsonable_dict(model, exclude_none, exclude_unset) for model in models 64 | ] 65 | 66 | 67 | def pydantic_jsonable_dict_map( 68 | models: collections.abc.Mapping, exclude_none=False, exclude_unset=False 69 | ) -> dict: 70 | dict_map = {} 71 | 72 | for key, value in models.items(): 73 | dict_map[key] = pydantic_jsonable_dict(value, exclude_none, exclude_unset) 74 | 75 | return dict_map 76 | -------------------------------------------------------------------------------- /src/roboto/http/testing_util.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import email.message 8 | import io 9 | import json 10 | import typing 11 | import urllib.response 12 | 13 | from .http_client import HttpResponse 14 | 15 | 16 | class FakeHttpResponseFactory: 17 | """ 18 | A factory for creating fake HTTP responses, for use with the roboto.http.HttpClient. 19 | 20 | Example: 21 | >>> import contextlib 22 | >>> import unittest.mock 23 | >>> from roboto.http import HttpClient, FakeHttpResponseFactory 24 | >>> mock_http_client = unittest.mock.create_autospec(HttpClient, instance=True) 25 | >>> with contextlib.ExitStack() as stack: 26 | ... http_get_mock = stack.enter_context(unittest.mock.patch.object(mock_http_client, "get")) 27 | ... http_get_mock.side_effect = FakeHttpResponseFactory( 28 | ... "https://example.com", 29 | ... {"foo": "bar"}, 30 | ... status_code=200, 31 | ... headers={"Content-Type": "application/json"}, 32 | ... ) 33 | 34 | """ 35 | 36 | __headers: dict[str, str] 37 | __response_data: typing.Any 38 | __status_code: int 39 | __url: str 40 | 41 | def __init__( 42 | self, 43 | url: str = "https://iamverylazyanddonotseturls.com", 44 | response_data: typing.Any = "{}", 45 | status_code: int = 200, 46 | headers: typing.Optional[dict[str, str]] = None, 47 | ) -> None: 48 | self.__status_code = status_code 49 | self.__headers = headers or dict() 50 | self.__response_data = response_data 51 | self.__url = url 52 | 53 | def __call__(self, *args, **kwargs) -> HttpResponse: 54 | headers = email.message.Message() 55 | for k, v in self.__headers.items(): 56 | headers.add_header(k, v) 57 | data = io.BytesIO(json.dumps(self.__response_data).encode()) 58 | urllib_response = urllib.response.addinfourl(data, headers, self.__url, self.__status_code) 59 | return HttpResponse(urllib_response) 60 | -------------------------------------------------------------------------------- /src/roboto/domain/topics/topic_reader.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import abc 8 | import collections.abc 9 | import typing 10 | 11 | from ...time import TimeUnit 12 | from .operations import ( 13 | MessagePathRepresentationMapping, 14 | ) 15 | 16 | if typing.TYPE_CHECKING: 17 | import pandas # pants: no-infer-dep 18 | 19 | 20 | class TopicReader(abc.ABC): 21 | """Private interface for retrieving topic data of a particular format. 22 | 23 | Note: 24 | This is not intended as a public API. 25 | To access topic data, prefer the ``get_data`` or ``get_data_as_df`` methods 26 | on :py:class:`~roboto.domain.topics.Topic`, :py:class:`~roboto.domain.topics.MessagePath`, 27 | or :py:class:`~roboto.domain.events.Event`. 28 | """ 29 | 30 | @staticmethod 31 | @abc.abstractmethod 32 | def accepts( 33 | message_paths_to_representations: collections.abc.Iterable[MessagePathRepresentationMapping], 34 | ) -> bool: ... 35 | 36 | @abc.abstractmethod 37 | def get_data( 38 | self, 39 | message_paths_to_representations: collections.abc.Iterable[MessagePathRepresentationMapping], 40 | log_time_attr_name: str, 41 | log_time_unit: TimeUnit = TimeUnit.Nanoseconds, 42 | start_time: typing.Optional[int] = None, 43 | end_time: typing.Optional[int] = None, 44 | timestamp_message_path_representation_mapping: typing.Optional[MessagePathRepresentationMapping] = None, 45 | ) -> collections.abc.Generator[dict[str, typing.Any], None, None]: ... 46 | 47 | @abc.abstractmethod 48 | def get_data_as_df( 49 | self, 50 | message_paths_to_representations: collections.abc.Iterable[MessagePathRepresentationMapping], 51 | log_time_attr_name: str, 52 | log_time_unit: TimeUnit = TimeUnit.Nanoseconds, 53 | start_time: typing.Optional[int] = None, 54 | end_time: typing.Optional[int] = None, 55 | timestamp_message_path_representation_mapping: typing.Optional[MessagePathRepresentationMapping] = None, 56 | ) -> "pandas.DataFrame": ... 57 | -------------------------------------------------------------------------------- /src/roboto/waiters.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import collections.abc 8 | import time 9 | import typing 10 | 11 | Condition = typing.Callable[..., bool] 12 | Interval = typing.Union[int, typing.Callable[[int], int]] 13 | 14 | 15 | class TimeoutError(Exception): 16 | msg: str 17 | 18 | def __init__(self, msg: str, *args) -> None: 19 | super().__init__(*args) 20 | self.msg = msg 21 | 22 | 23 | def wait_for( 24 | condition: Condition, 25 | args: typing.Optional[collections.abc.Sequence[typing.Any]] = None, 26 | timeout: float = 60 * 5, 27 | interval: Interval = 5, 28 | timeout_msg: str = "wait_for timed out", 29 | ) -> None: 30 | """ 31 | Wait for a condition to be truthy. 32 | 33 | Args: 34 | condition: The condition to wait for. This should be a callable that returns a boolean. 35 | args: The arguments to pass to the condition callable on each iteration. 36 | timeout: The maximum amount of time to wait for the condition to be truthy. 37 | interval: The amount of time to wait between iterations. This can be an integer or a callable. 38 | If it is a callable, it will be called with the iteration number and should return an integer. 39 | timeout_msg: The message to include in the TimeoutError if the timeout is reached. 40 | 41 | Raises: 42 | TimeoutError: If the timeout is reached before the condition is truthy. 43 | 44 | Returns: 45 | None 46 | """ 47 | time_remaining = timeout 48 | args = args if args is not None else [] 49 | iteration = 0 50 | while time_remaining > 0: 51 | iteration_start = time.monotonic() 52 | if iteration > 0: 53 | if callable(interval): 54 | time.sleep(interval(iteration)) 55 | else: 56 | time.sleep(interval) 57 | if condition(*args): 58 | return 59 | 60 | iteration_duration = time.monotonic() - iteration_start 61 | time_remaining -= iteration_duration 62 | iteration += 1 63 | 64 | raise TimeoutError(timeout_msg) 65 | -------------------------------------------------------------------------------- /src/roboto/cli/images/login.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | import shlex 9 | import subprocess 10 | 11 | from ...auth import Permissions 12 | from ...image_registry import ImageRegistry 13 | from ..command import RobotoCommand 14 | from ..common_args import add_org_arg 15 | from ..context import CLIContext 16 | 17 | 18 | def login(args: argparse.Namespace, context: CLIContext, parser: argparse.ArgumentParser) -> None: 19 | image_registry = ImageRegistry(context.roboto_client) 20 | permissions = Permissions(args.permissions) 21 | credentials = image_registry.get_temporary_credentials(args.repository_uri, permissions, org_id=args.org) 22 | cmd = f"docker login --username {credentials.username} --password-stdin {credentials.registry_url}" 23 | docker_login_completed_process = subprocess.run( 24 | shlex.split(cmd), 25 | capture_output=True, 26 | check=True, 27 | input=credentials.password, 28 | text=True, 29 | ) 30 | print(docker_login_completed_process.stdout) 31 | 32 | 33 | def login_parser(parser: argparse.ArgumentParser) -> None: 34 | add_org_arg(parser) 35 | 36 | parser.add_argument( 37 | "--repository-uri", 38 | dest="repository_uri", 39 | required=True, 40 | action="store", 41 | help="Image repository within Roboto's image registry. Login credentials will be scoped to this repository.", 42 | ) 43 | 44 | parser.add_argument( 45 | "--permissions", 46 | required=False, 47 | action="store", 48 | choices=[p.value for p in Permissions], 49 | default=Permissions.ReadWrite.value, 50 | help="Specify the access level for the temporary credentials.", 51 | ) 52 | 53 | 54 | login_command = RobotoCommand( 55 | name="login", 56 | logic=login, 57 | setup_parser=login_parser, 58 | command_kwargs={ 59 | "help": ( 60 | "Temporarily login to Roboto's image registry. " 61 | "Requires Docker CLI. Login is valid for 12 hours and is scoped to a particular repository." 62 | ) 63 | }, 64 | ) 65 | -------------------------------------------------------------------------------- /src/roboto/domain/devices/operations.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import typing 8 | 9 | import pydantic 10 | 11 | from ...updates import MetadataChangeset 12 | 13 | 14 | class CreateDeviceRequest(pydantic.BaseModel): 15 | """Request payload to create a new device. 16 | 17 | This request is used to register a new device with the Roboto platform. 18 | The device will be associated with the specified organization and can 19 | subsequently be used for authentication and data operations. 20 | """ 21 | 22 | device_id: str 23 | """A user-provided identifier for a device, which is unique within that device's org.""" 24 | 25 | org_id: typing.Optional[str] = None 26 | """The org to which this device belongs. If None, the device will be registered 27 | under the caller's organization (if they belong to only one org) or an error 28 | will be raised if the caller belongs to multiple organizations.""" 29 | 30 | metadata: dict[str, typing.Any] = pydantic.Field( 31 | default_factory=dict, 32 | description="Initial key-value pairs to associate with this device for discovery and search, e.g. " 33 | + "`{ 'model': 'mk2', 'serial_number': 'SN001234' }`", 34 | ) 35 | """Key-value metadata pairs to associate with the device for discovery and search.""" 36 | 37 | tags: list[str] = pydantic.Field( 38 | default_factory=list, 39 | description="Initial tags to associate with this device for discovery and search, e.g. " 40 | + "`['production', 'warehouse-a']`", 41 | ) 42 | """List of tags for device discovery and organization.""" 43 | 44 | 45 | class UpdateDeviceRequest(pydantic.BaseModel): 46 | """Request payload for updating device properties. 47 | 48 | Used to modify device metadata and tags. Supports granular updates 49 | through metadata changesets that can add, update, or remove specific 50 | fields and tags without affecting other properties. 51 | """ 52 | 53 | metadata_changeset: typing.Optional[MetadataChangeset] = None 54 | """Metadata changes to apply (add, update, or remove fields/tags).""" 55 | -------------------------------------------------------------------------------- /src/roboto/exceptions/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from .domain import ( 8 | RobotoConditionException, 9 | RobotoConflictException, 10 | RobotoContextTooLongException, 11 | RobotoDatasetNotFoundException, 12 | RobotoDeprecatedException, 13 | RobotoDeviceNotFoundException, 14 | RobotoDomainException, 15 | RobotoExpiredException, 16 | RobotoFailedToGenerateException, 17 | RobotoHttpExceptionParse, 18 | RobotoIllegalArgumentException, 19 | RobotoInternalException, 20 | RobotoInvalidRequestException, 21 | RobotoInvalidStateTransitionException, 22 | RobotoLimitExceededException, 23 | RobotoNoOrgProvidedException, 24 | RobotoNotFoundException, 25 | RobotoNotImplementedException, 26 | RobotoNotReadyException, 27 | RobotoServiceException, 28 | RobotoServiceUnavailableException, 29 | RobotoUnauthorizedException, 30 | RobotoUnknownOperationException, 31 | ) 32 | from .http import ( 33 | ClientError, 34 | HttpError, 35 | ServerError, 36 | ) 37 | from .ingestion import ( 38 | IngestionException, 39 | TimestampFieldNotFoundException, 40 | ) 41 | 42 | __all__ = [ 43 | "ClientError", 44 | "HttpError", 45 | "IngestionException", 46 | "ServerError", 47 | "TimestampFieldNotFoundException", 48 | "RobotoConditionException", 49 | "RobotoConflictException", 50 | "RobotoContextTooLongException", 51 | "RobotoDatasetNotFoundException", 52 | "RobotoDeviceNotFoundException", 53 | "RobotoDeprecatedException", 54 | "RobotoDomainException", 55 | "RobotoExpiredException", 56 | "RobotoFailedToGenerateException", 57 | "RobotoHttpExceptionParse", 58 | "RobotoIllegalArgumentException", 59 | "RobotoInternalException", 60 | "RobotoInvalidRequestException", 61 | "RobotoInvalidStateTransitionException", 62 | "RobotoLimitExceededException", 63 | "RobotoNotFoundException", 64 | "RobotoNotImplementedException", 65 | "RobotoNotReadyException", 66 | "RobotoNoOrgProvidedException", 67 | "RobotoServiceException", 68 | "RobotoServiceUnavailableException", 69 | "RobotoUnauthorizedException", 70 | "RobotoUnknownOperationException", 71 | ] 72 | -------------------------------------------------------------------------------- /vendor/robotpajamas.pants.sci/experimental/scie/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Pants project contributors (see CONTRIBUTORS.md). 2 | # Licensed under the Apache License, Version 2.0 (see LICENSE). 3 | 4 | from __future__ import annotations 5 | 6 | import logging 7 | from dataclasses import dataclass 8 | 9 | import toml 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | 14 | @dataclass(frozen=True) 15 | class Config: 16 | lift: LiftConfig 17 | 18 | def __post_init__(self): 19 | if isinstance(self.lift, dict): 20 | object.__setattr__(self, "lift", LiftConfig(**self.lift)) 21 | 22 | @classmethod 23 | def from_toml(cls, file_path): 24 | with open(file_path) as f: 25 | data = toml.load(f) 26 | return cls(**data) 27 | 28 | 29 | @dataclass(frozen=True) 30 | class LiftConfig: 31 | """ 32 | This configuration is a subset of the configuration that can be found here: 33 | https://github.com/a-scie/lift/blob/main/science/model.py 34 | """ 35 | 36 | name: str 37 | description: str 38 | platforms: list[str] 39 | interpreters: list[Interpreter] 40 | files: list[File] 41 | commands: list[Command] 42 | bindings: frozenset[Command] = frozenset() 43 | 44 | def __post_init__(self): 45 | if any(isinstance(i, dict) for i in self.interpreters): 46 | object.__setattr__(self, "interpreters", [Interpreter(**i) for i in self.interpreters]) # type: ignore 47 | if any(isinstance(f, dict) for f in self.files): 48 | object.__setattr__(self, "files", [File(**f) for f in self.files]) # type: ignore 49 | if any(isinstance(c, dict) for c in self.commands): 50 | object.__setattr__(self, "commands", [Command(**c) for c in self.commands]) # type: ignore 51 | if any(isinstance(b, dict) for b in self.bindings): 52 | object.__setattr__(self, "bindings", [Command(**b) for b in self.bindings]) # type: ignore 53 | 54 | 55 | @dataclass(frozen=True) 56 | class Interpreter: 57 | version: str 58 | id: str = "cpython" 59 | provider: str = "PythonBuildStandalone" 60 | release: str = "20251031" 61 | lazy: bool = True 62 | 63 | 64 | @dataclass(frozen=True) 65 | class File: 66 | name: str 67 | 68 | 69 | @dataclass(frozen=True) 70 | class Command: 71 | exe: str 72 | args: list[str] 73 | env: dict[str, str] | None = None 74 | name: str | None = None 75 | description: str | None = None 76 | -------------------------------------------------------------------------------- /src/roboto/pydantic/validators.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import pydantic 8 | 9 | 10 | def remove_non_noneable_init_args(data: dict, model: pydantic.BaseModel) -> dict: 11 | """ 12 | Remove keys from ``data`` (e.g., kwargs passed to ``__init__``) set to ``None`` 13 | that are not allowed to be ``None`` according to ``model``'s type definition. 14 | 15 | This is particularly useful when marshalling data from the database into a 16 | Pydantic model. If a database row has a ``NULL`` value for a 17 | field that is not nullable, this function can be used to filter out those ``None`` 18 | values before passing the data to the model's ``__init__`` method. 19 | 20 | Example usage: 21 | 22 | >>> class Foo(pydantic.BaseModel): 23 | ... a: int = 3 24 | ... 25 | ... def __init__(self, **kwargs): 26 | ... filtered_kwargs = remove_non_noneable_init_args(kwargs, self) 27 | ... super().__init__(**filtered_kwargs) 28 | >>> Foo(a=None) 29 | Foo(a=3, b='bar') 30 | 31 | Without this function, the above would raise a ``ValidationError``. 32 | """ 33 | 34 | def _field_allows_none(field_name): 35 | field = model.model_fields.get(field_name) 36 | if field is None: 37 | return False 38 | 39 | # Allows none if field annotation is a union type and None is one of the types in the union 40 | try: 41 | # After dropping 3.9 support, add `isinstance(field.annotation, types.UnionType)` 42 | # to this statement to remove the `# type: ignore` and ditch the try/except. 43 | return type(None) in field.annotation.__args__ # type: ignore 44 | except AttributeError: 45 | return False 46 | 47 | return {k: v for k, v in data.items() if v is not None or _field_allows_none(k)} 48 | 49 | 50 | def validate_nonzero_gitpath_specs(value: list[str]) -> list[str]: 51 | filtered = list(filter(lambda p: p.strip() != "", value)) 52 | 53 | if len(value) == 0: 54 | raise ValueError("Paths must not be empty.") 55 | elif len(filtered) == 0: 56 | raise ValueError("Paths must have at least one entry which is not the empty string or just whitespace.") 57 | 58 | return value 59 | -------------------------------------------------------------------------------- /src/roboto/domain/collections/record.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import datetime 8 | import typing 9 | 10 | import pydantic 11 | 12 | from ...compat import StrEnum 13 | 14 | 15 | class CollectionResourceType(StrEnum): 16 | """Type of resource added to a collection""" 17 | 18 | Dataset = "dataset" 19 | File = "file" 20 | 21 | 22 | class CollectionContentMode(StrEnum): 23 | """Desired content mode for representing a collection""" 24 | 25 | SummaryOnly = "summary_only" 26 | References = "references" 27 | Full = "full" 28 | 29 | 30 | class CollectionResourceRef(pydantic.BaseModel): 31 | """Reference to a collection resource""" 32 | 33 | resource_type: CollectionResourceType 34 | resource_id: str 35 | resource_version: typing.Optional[str] = None 36 | 37 | 38 | class CollectionRecord(pydantic.BaseModel): 39 | """A wire-transmissible representation of a collection""" 40 | 41 | collection_id: str 42 | name: typing.Optional[str] = None 43 | description: typing.Optional[str] = None 44 | resources: dict[CollectionResourceType, list[typing.Any]] = pydantic.Field(default_factory=dict) 45 | missing: dict[CollectionResourceType, list[CollectionResourceRef]] = pydantic.Field(default_factory=dict) 46 | tags: list[str] = [] 47 | version: int 48 | created: datetime.datetime 49 | created_by: str 50 | updated: datetime.datetime 51 | updated_by: str 52 | org_id: str 53 | 54 | 55 | class CollectionChangeSet(pydantic.BaseModel): 56 | """Changeset for updating a collection""" 57 | 58 | added_resources: list[CollectionResourceRef] = pydantic.Field(default_factory=list) 59 | added_tags: list[str] = pydantic.Field(default_factory=list) 60 | removed_resources: list[CollectionResourceRef] = pydantic.Field(default_factory=list) 61 | removed_tags: list[str] = pydantic.Field(default_factory=list) 62 | field_changes: dict[str, typing.Any] = pydantic.Field(default_factory=dict) 63 | 64 | 65 | class CollectionChangeRecord(pydantic.BaseModel): 66 | """A wire-transmissible representation of a collection change record""" 67 | 68 | collection_id: str 69 | from_version: int 70 | to_version: int 71 | change_set: CollectionChangeSet 72 | applied: datetime.datetime 73 | applied_by: str 74 | -------------------------------------------------------------------------------- /src/roboto/cli/actions/invoke/validation.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | """Validation utilities. 8 | 9 | This module handles validating parameters, organizations, and other 10 | invocation prerequisites. 11 | """ 12 | 13 | import typing 14 | 15 | from ....domain import actions, orgs 16 | 17 | 18 | def validate_parameters( 19 | action_params: list[actions.ActionParameter], 20 | provided_params: dict[str, typing.Any], 21 | ) -> None: 22 | """Validate that provided parameters are defined in action. 23 | 24 | Args: 25 | action_params: Parameters defined in action 26 | provided_params: Parameters provided by user 27 | 28 | Raises: 29 | ValueError: If unknown parameters are provided 30 | """ 31 | known_params = set(param.name for param in action_params) 32 | provided_param_names = set(provided_params.keys()) 33 | unknown_params = provided_param_names - known_params 34 | 35 | if unknown_params: 36 | raise ValueError(f"The following parameter(s) are not defined in action: {', '.join(sorted(unknown_params))}") 37 | 38 | 39 | def resolve_organization(org_id: typing.Optional[str], roboto_client) -> str: 40 | """Resolve organization ID from arguments or user membership. 41 | 42 | Args: 43 | org_id: Optional organization ID from command-line 44 | roboto_client: Roboto client for API calls 45 | 46 | Returns: 47 | Resolved organization ID 48 | 49 | Raises: 50 | Exception: If org cannot be determined 51 | """ 52 | if org_id is not None: 53 | return org_id 54 | 55 | member_orgs = orgs.Org.for_self(roboto_client=roboto_client) 56 | 57 | if not member_orgs: 58 | raise Exception( 59 | "It appears you are not a member of a Roboto organization. " 60 | "Please create an organization by logging into the web application " 61 | "(https://app.roboto.ai/) or try specifying the --org argument." 62 | ) 63 | 64 | if len(member_orgs) == 1: 65 | return member_orgs[0].org_id 66 | 67 | formatted_org_list = "".join(f" - {org.name} ({org.org_id})\n" for org in member_orgs) 68 | raise Exception( 69 | f"You belong to multiple Roboto organizations:\n{formatted_org_list}Please specify the --org argument." 70 | ) 71 | -------------------------------------------------------------------------------- /src/roboto/cli/actions/init.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import argparse 8 | import pathlib 9 | import tempfile 10 | 11 | from cookiecutter.main import cookiecutter 12 | 13 | from ..command import RobotoCommand 14 | from ..context import CLIContext 15 | 16 | COOKIECUTTER_REPO = "https://github.com/roboto-ai/cookiecutter-roboto-actions.git" 17 | 18 | 19 | def init(args: argparse.Namespace, context: CLIContext, parser: argparse.ArgumentParser) -> None: 20 | path: pathlib.Path = args.path 21 | path = path.resolve() 22 | print(f"Creating Roboto action package under: {path}") 23 | 24 | if not path.is_dir(): 25 | parser.error(f"{path} does not exist, or is not a directory.") 26 | 27 | with tempfile.TemporaryDirectory() as tmpdir: 28 | # GM (2025-11-05) 29 | # cookiecutter currently always includes the following prompt: 30 | # > You've downloaded ... before. Is it okay to delete and re-download it? [y/n] (y) 31 | # I think this is annoying and useless. Also selecting "n" is always the wrong thing to do. 32 | # cookiecutter maintainers refuse to add an option for always redownloading the template: 33 | # see https://github.com/cookiecutter/cookiecutter/issues/1201. 34 | # So, this. 35 | # Refs: 36 | # 1. https://github.com/cookiecutter/cookiecutter/blob/main/cookiecutter/main.py#L79-L82 37 | # 2. https://github.com/cookiecutter/cookiecutter/blob/main/cookiecutter/config.py#L89-L113 38 | cookiecutter_config = {"cookiecutters_dir": tmpdir} 39 | 40 | try: 41 | cookiecutter( 42 | COOKIECUTTER_REPO, 43 | output_dir=str(path), 44 | default_config=cookiecutter_config, # type: ignore 45 | ) 46 | except KeyboardInterrupt: 47 | pass 48 | 49 | 50 | def init_parser(parser: argparse.ArgumentParser): 51 | parser.add_argument( 52 | "path", 53 | nargs="?", 54 | type=pathlib.Path, 55 | help="Existing directory under which an action package will be created.", 56 | default=pathlib.Path.cwd(), 57 | ) 58 | 59 | 60 | init_command = RobotoCommand( 61 | name="init", 62 | logic=init, 63 | setup_parser=init_parser, 64 | command_kwargs={"help": "Initialize a new action package."}, 65 | ) 66 | -------------------------------------------------------------------------------- /src/roboto/http/requester.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import platform 8 | import typing 9 | 10 | import pydantic 11 | 12 | from ..compat import StrEnum 13 | 14 | try: 15 | from ..version import ( 16 | __version__ as roboto_version, 17 | ) 18 | except ModuleNotFoundError: 19 | roboto_version = "0.0.0" 20 | 21 | 22 | class RobotoTool(StrEnum): 23 | """Tool used to access Roboto""" 24 | 25 | Cli = "cli" 26 | Sdk = "sdk" 27 | UploadAgent = "upload-agent" 28 | Website = "website" 29 | 30 | 31 | class RobotoRequester(pydantic.BaseModel): 32 | """ 33 | Details about the entity making a request to Roboto. These are embedded in a header in order to see what tool 34 | versions / operating systems are making requests, and to aid debugging. 35 | """ 36 | 37 | schema_version: typing.Literal["v1"] = "v1" 38 | """Roboto Requester payload schema version, used to ensure backward compatibility""" 39 | 40 | platform: typing.Optional[str] = None 41 | """The environment in which a request is being made, i.e. the user agent (for browser requests) or the results 42 | of platform.platform (for SDK requests)""" 43 | 44 | roboto_tool: typing.Optional[typing.Union[RobotoTool, str]] = None 45 | """If a request is being made from a Roboto vended tool, the name of the tool""" 46 | 47 | roboto_tool_version: typing.Optional[str] = None 48 | """If a request is being made from a Roboto vended tool, the version of the tool""" 49 | 50 | roboto_tool_details: typing.Optional[str] = None 51 | """If a request is being made from a Roboto vended tool, free text pertinent details about the tool""" 52 | 53 | @classmethod 54 | def for_tool(cls, tool: RobotoTool) -> "RobotoRequester": 55 | """ 56 | Called to intelligently populate a :class:`~RobotoRequester` for a request made from a named Roboto tool 57 | using the Python SDK. 58 | """ 59 | 60 | return RobotoRequester( 61 | schema_version="v1", 62 | platform=platform.platform(), 63 | roboto_tool=tool.value, 64 | roboto_tool_version=roboto_version, 65 | roboto_tool_details=None, 66 | ) 67 | 68 | 69 | ROBOTO_REQUESTER_HEADER = "X-Roboto-Requester" 70 | """A JSON serialized :class:`~RobotoRequester` representing the entity making a request to Roboto.""" 71 | -------------------------------------------------------------------------------- /src/roboto/domain/actions/scheduled_trigger_operations.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from __future__ import annotations 8 | 9 | from typing import Annotated, Any, Optional, Union 10 | 11 | import pydantic 12 | 13 | from ...sentinels import NotSet, NotSetType 14 | from .action_record import ( 15 | ComputeRequirements, 16 | ContainerParameters, 17 | ) 18 | from .invocation_record import ( 19 | InvocationInput, 20 | InvocationUploadDestination, 21 | ) 22 | 23 | 24 | class CreateScheduledTriggerRequest(pydantic.BaseModel): 25 | """Request payload to create a scheduled trigger. 26 | 27 | See :py:meth:`~roboto.domain.actions.ScheduledTrigger.create` for details 28 | on the request attributes. 29 | """ 30 | 31 | action_name: str = pydantic.Field(pattern=r"[\w\-]+") 32 | action_owner_id: Optional[str] = None 33 | compute_requirement_overrides: Optional[ComputeRequirements] = None 34 | container_parameter_overrides: Optional[ContainerParameters] = None 35 | enabled: bool 36 | invocation_input: Optional[InvocationInput] = None 37 | invocation_upload_destination: Optional[InvocationUploadDestination] = None 38 | name: str = pydantic.Field(pattern=r"[\w\-]+", max_length=256) 39 | parameter_values: Optional[dict[str, Any]] = None 40 | schedule: str 41 | timeout: Optional[Annotated[int, pydantic.Field(ge=0)]] = None 42 | 43 | 44 | class UpdateScheduledTriggerRequest(pydantic.BaseModel): 45 | """Request payload to update a scheduled trigger. 46 | 47 | See :py:meth:`~roboto.domain.actions.ScheduledTrigger.update` for details 48 | on the request attributes. 49 | """ 50 | 51 | action_name: Union[Annotated[str, pydantic.Field(pattern=r"[\w\-]+")], NotSetType] = NotSet 52 | action_owner_id: Union[str, NotSetType] = NotSet 53 | compute_requirement_overrides: Union[Optional[ComputeRequirements], NotSetType] = NotSet 54 | container_parameter_overrides: Union[Optional[ContainerParameters], NotSetType] = NotSet 55 | enabled: Union[bool, NotSetType] = NotSet 56 | invocation_input: Union[Optional[InvocationInput], NotSetType] = NotSet 57 | invocation_upload_destination: Union[Optional[InvocationUploadDestination], NotSetType] = NotSet 58 | parameter_values: Union[Optional[dict[str, Any]], NotSetType] = NotSet 59 | schedule: Union[str, NotSetType] = NotSet 60 | timeout: Union[Optional[Annotated[int, pydantic.Field(ge=0)]], NotSetType] = NotSet 61 | -------------------------------------------------------------------------------- /src/roboto/cli/config.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | import datetime 8 | import os 9 | import pathlib 10 | import sys 11 | from typing import Optional 12 | 13 | from packaging.version import Version 14 | import pydantic 15 | 16 | from ..http import HttpClient 17 | from ..time import utcnow 18 | from ..version import __version__ 19 | from .terminal import AnsiColor 20 | 21 | 22 | class CLIState(pydantic.BaseModel): 23 | last_checked_version: Optional[datetime.datetime] = None 24 | last_version: str = "0.0.0" 25 | out_of_date: bool = True 26 | 27 | 28 | def check_last_update(): 29 | roboto_tmp_dir = pathlib.Path.home() / ".roboto" / "tmp" 30 | roboto_tmp_dir.mkdir(parents=True, exist_ok=True) 31 | cli_state_file = roboto_tmp_dir / "cli_state.json" 32 | 33 | last_version = None 34 | 35 | state = CLIState(last_checked_version=None) 36 | if cli_state_file.is_file(): 37 | state = CLIState.model_validate_json(cli_state_file.read_text()) 38 | last_version = state.last_version 39 | 40 | if ( 41 | state.last_checked_version is None 42 | or __version__ != last_version 43 | or state.out_of_date is None 44 | or state.out_of_date is True 45 | or (utcnow() - datetime.timedelta(hours=1)) > state.last_checked_version 46 | ): 47 | http = HttpClient() 48 | 49 | releases = http.get(url="https://pypi.org/pypi/roboto/json").to_dict(json_path=["releases"]) 50 | versions = list(releases.keys()) 51 | versions.sort(key=Version) 52 | latest = versions[-1] 53 | 54 | state.last_checked_version = utcnow() 55 | state.last_version = __version__ 56 | state.out_of_date = __version__ != latest 57 | 58 | cli_state_file.write_text(state.model_dump_json()) 59 | 60 | suppress_message = os.getenv("ROBOTO_CLI_SUPPRESS_UPGRADE_PROMPT", "false").lower() != "false" 61 | 62 | if state.out_of_date and not suppress_message: 63 | notice = f"{AnsiColor.BLUE}[notice]{AnsiColor.END}" 64 | print( 65 | f"\n{notice} A new release of roboto is available: " 66 | + f"{AnsiColor.RED + __version__ + AnsiColor.END} -> {AnsiColor.GREEN + latest + AnsiColor.END}\n" 67 | + f"{notice} To update, follow Upgrade CLI instructions at " 68 | + "https://github.com/roboto-ai/roboto-python-sdk/blob/main/README.md", 69 | file=sys.stderr, 70 | ) 71 | -------------------------------------------------------------------------------- /src/roboto/domain/layouts/operations.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Roboto Technologies, Inc. 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | from typing import Any, Union 8 | 9 | import pydantic 10 | 11 | from roboto.domain.layouts.record import ( 12 | LayoutAccessibility, 13 | ) 14 | from roboto.sentinels import NotSet, NotSetType 15 | 16 | 17 | class CreateLayoutRequest(pydantic.BaseModel): 18 | """ 19 | Request payload to create a layout 20 | """ 21 | 22 | accessibility: LayoutAccessibility = pydantic.Field( 23 | description="Controls layout accessibility between organization-wide or user-only.", 24 | default=LayoutAccessibility.User, 25 | ) 26 | layout_definition: dict[str, Any] = pydantic.Field(description="The layout definition as a JSON object.") 27 | name: str = pydantic.Field(description="The name of the layout.", max_length=120) 28 | schema_version: int = pydantic.Field(description="The schema version associated with the layout definition.") 29 | tags: list[str] = pydantic.Field(description="The tags associated with the layout.", default_factory=list) 30 | 31 | 32 | class UpdateLayoutRequest(pydantic.BaseModel): 33 | """ 34 | Request payload to update a layout 35 | """ 36 | 37 | accessibility: Union[LayoutAccessibility, NotSetType] = pydantic.Field( 38 | description="Controls layout accessibility between organization-wide or user-only.", 39 | default=NotSet, 40 | ) 41 | layout_definition: Union[dict[str, Any], NotSetType] = pydantic.Field( 42 | description="The layout definition as a JSON object.", default=NotSet 43 | ) 44 | name: Union[str, NotSetType] = pydantic.Field(description="The name of the layout.", default=NotSet, max_length=120) 45 | schema_version: Union[int, NotSetType] = pydantic.Field( 46 | description="The schema version associated with the layout definition.", 47 | default=NotSet, 48 | ) 49 | 50 | # Q. We typically seem to use metadata changeset for updating tags 51 | tags: Union[list[str], NotSetType] = pydantic.Field( 52 | description="The tags associated with the layout.", default=NotSet 53 | ) 54 | 55 | @pydantic.model_validator(mode="after") 56 | def validate_schema_version_with_definition(self) -> "UpdateLayoutRequest": 57 | if not isinstance(self.layout_definition, NotSetType) and isinstance(self.schema_version, NotSetType): 58 | raise ValueError("schema_version must be provided when updating layout_definition") 59 | return self 60 | --------------------------------------------------------------------------------