imperal-sdk 5.2.0__tar.gz → 5.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/CHANGELOG.md +44 -7
- imperal_sdk-5.2.2/PKG-INFO +148 -0
- imperal_sdk-5.2.2/README.md +111 -0
- imperal_sdk-5.2.2/src/imperal_sdk/__init__.py +188 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/chat/extension.py +14 -15
- imperal_sdk-5.2.2/tests/test_import_light.py +103 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_manifest_no_orchestrator_tool.py +29 -12
- imperal_sdk-5.2.0/PKG-INFO +0 -264
- imperal_sdk-5.2.0/README.md +0 -227
- imperal_sdk-5.2.0/src/imperal_sdk/__init__.py +0 -73
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/.github/workflows/identity-contract.yml +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/.github/workflows/publish.yml +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/.github/workflows/test.yml +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/.gitignore +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/LICENSE +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/api_surface.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/docs/sdl-facets.md +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/pyproject.toml +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/sdk_claims.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/sdl_roles.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/.codebase-index-cache.pkl +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/ai/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/ai/client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/auth/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/auth/client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/auth/middleware.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/billing/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/billing/client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/cache/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/cache/client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/cache/protocol.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/chat/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/chat/action_result.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/chat/error_codes.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/chat/exceptions.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/chat/filters.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/chat/guards.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/chat/kernel_primitives.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/chat/narration.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/chat/narration_guard.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/chat/prompt.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/chat/refusal.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/cli/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/cli/main.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/config/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/config/client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/context.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/devtools/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/devtools/contract_checks.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/devtools/generate_api_surface.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/devtools/generate_sdk_claims.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/devtools/validate_identity_contract.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/errors.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/extension.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/extensions/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/extensions/client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/http/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/http/client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/manifest.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/manifest_schema.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/notify/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/notify/client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/prompts/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/prompts/icnli_integrity_rules.txt +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/prompts/kernel_formatting_rule.txt +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/prompts/kernel_proactivity_rule.txt +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/protocols.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/rpc/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/rpc/codec.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/rpc/contract.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/runtime/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/runtime/executor.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/runtime/llm_provider.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/runtime/message_adapter.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/schemas/action_result.schema.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/schemas/balance_info.schema.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/schemas/chat_result.schema.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/schemas/completion_result.schema.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/schemas/document.schema.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/schemas/event.schema.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/schemas/file_info.schema.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/schemas/function_call.schema.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/schemas/http_response.schema.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/schemas/imperal.schema.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/schemas/limits_result.schema.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/schemas/subscription_info.schema.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/_generate_roles_json.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/entity.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/catalog.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/comm.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/content.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/device.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/event.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/geo.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/identity.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/media.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/metric.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/money.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/net.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/people.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/quantity.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/rating.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/security.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/task.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/facets/time.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/field.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/sdl/roles.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/secrets/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/secrets/client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/secrets/exceptions.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/secrets/panel_handler.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/secrets/spec.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/security/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/security/call_token.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/skeleton/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/skeleton/client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/storage/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/storage/client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/store/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/store/client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/store/exceptions.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/testing/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/testing/mock_context.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/testing/mock_secrets.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/types/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/types/action_result.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/types/chat_result.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/types/client_contracts.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/types/contracts.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/types/contributions.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/types/events.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/types/health.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/types/identity.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/types/models.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/types/pagination.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/types/store_contracts.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/ui/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/ui/actions.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/ui/base.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/ui/data.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/ui/display.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/ui/feedback.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/ui/graph.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/ui/input_components.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/ui/interactive.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/ui/layout.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/ui/theme.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/validator.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/src/imperal_sdk/validator_v1_6_0.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/conftest.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/contract/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/contract/test_contract_checks_selftest.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/contract/test_generate_sdk_claims.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/contract/test_sample_contract_shape.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/contract/test_sdk_matches_kernel_contract.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/contracts/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/contracts/test_store_contracts.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/fixtures/contract/kernel-contract.sample.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/fixtures/openapi/auth-gateway.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/fixtures/openapi/registry.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/fixtures/openapi/sharelock-cases.json +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/rpc/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/rpc/test_codec.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/rpc/test_contract.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/runtime/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/runtime/test_llm_provider_config_store.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/runtime/test_llm_provider_ctx_injection.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/store/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/store/test_list_users_client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/store/test_query_all_client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_action_result_typed.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_as_user.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_auth.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_billing.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_cache_client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_cache_model.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_call_token.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_chat_extension_deprecation.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_chat_extension_no_llm_router.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_chat_filters.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_chat_function_background_flag.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_chat_guards.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_chat_guards_bleed.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_chat_prompt.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_chat_result.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_cli.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_client_contracts.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_config_client.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_context.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_context_background_task.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_context_deliver_chat_message.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_context_guards.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_contracts.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_contracts_live.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_contributions.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_data_model_kwarg.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_document_contract.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_emits_decorator.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_error_codes.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_errors.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_event_schema_v2.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_events_health.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_extension.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_extension_v2.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_extensions_emit.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_http_timeout_override.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_id_shape_guard.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_identity_contract.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_imperal_schema_v2.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_kernel_primitives.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_manifest.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_manifest_roundtrip_gate.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_manifest_schema.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_manifest_v2_events.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_manifest_v2_other_sections.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_manifest_v2_webhooks.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_manifest_validator_v2.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_mock_context.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_models.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_narration_emission.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_narration_guard.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_openai_max_completion_tokens.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_pagination.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_panel_rendering_contract.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_panels.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_phase_a_dead_removal.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_phase_a_drift.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_phase_a_text.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdk_version_stamp.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_entity.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_entity_marker.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_exports.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_catalog.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_collisions.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_comm.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_content.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_device.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_event.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_exports.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_field.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_geo.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_identity.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_media.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_metric.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_money.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_net.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_people.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_quantity.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_rating.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_security.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_task.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facet_time.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facets_catalog.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facets_doc.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_facets_pkg.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_field.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_roles.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_roles_json.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_sdl_roles_of_facets.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_skeleton_decorator.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_spec_validation.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_ui.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_ui_fileupload_enhanced.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_ui_html.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_ui_image_enhanced.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_ui_open.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_ui_theme.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_user.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_v7_emit_refusal.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_validator.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_validator_drift.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_validator_pep563.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_validator_v1_6_0_rules.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_validator_v25.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/test_write_arg_bleed.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/tools/__init__.py +0 -0
- {imperal_sdk-5.2.0 → imperal_sdk-5.2.2}/tests/tools/test_generate_api_surface.py +0 -0
|
@@ -2,14 +2,52 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `imperal-sdk` are documented here.
|
|
4
4
|
|
|
5
|
+
## 5.2.2 — 2026-06-11 — Import-light package root
|
|
6
|
+
|
|
7
|
+
Performance / robustness release. **Zero API changes** — every public name,
|
|
8
|
+
submodule attribute, star-import and `dir()` entry resolves exactly as before
|
|
9
|
+
(verified by an eager-parity test over the whole surface).
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **The package root is now import-light (PEP 562 lazy surface).**
|
|
14
|
+
`import imperal_sdk` — and importing transport-free helpers such as
|
|
15
|
+
`imperal_sdk.chat.filters` or `imperal_sdk.chat.error_codes` — no longer
|
|
16
|
+
loads the HTTP client stack. Heavy dependencies load on first use of the
|
|
17
|
+
names that actually need them (`Context`, the service clients,
|
|
18
|
+
`get_llm_provider`, …). Benefits: faster cold imports, and helper modules
|
|
19
|
+
are now safe to import from restricted/sandboxed execution contexts that
|
|
20
|
+
forbid network-stack loading.
|
|
21
|
+
|
|
22
|
+
Nothing to migrate — rebuild against `imperal-sdk>=5.2.2` at your convenience.
|
|
23
|
+
|
|
24
|
+
## 5.2.1 — 2026-06-01 — ChatExtension ergonomics & honest deprecations
|
|
25
|
+
|
|
26
|
+
Small, fully backward-compatible cleanup of `ChatExtension`. No API removals;
|
|
27
|
+
existing extensions are unaffected.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- `ChatExtension(tool_name=...)` no longer emits a `DeprecationWarning`. The
|
|
32
|
+
kwarg is the **canonical** chat-registration key — it groups your
|
|
33
|
+
`@chat.function` tools in the manifest, anchors the per-turn prompt, and
|
|
34
|
+
labels scope-guard audit lines. It is load-bearing and not going away; the
|
|
35
|
+
prior "removed in 5.1.0" warning was incorrect and has been removed.
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- `ChatExtension(...)` now accepts an **optional** `tool_name`. When omitted it
|
|
40
|
+
defaults to `f"tool_{ext.app_id}_chat"`. Pass it explicitly to pin a stable
|
|
41
|
+
routing name (recommended for production extensions). `description=` is now
|
|
42
|
+
optional as well.
|
|
43
|
+
|
|
5
44
|
## 5.2.0 — 2026-05-31 — Structured Data Layer (SDL) foundation
|
|
6
45
|
|
|
7
46
|
Introduces the **SDL (`imperal_sdk.sdl`)** — a typed, semantic vocabulary for the
|
|
8
47
|
data an extension returns, so the platform can read an entity's id / title / kind
|
|
9
48
|
and its facets directly instead of inferring them from field names. This release
|
|
10
49
|
ships the SDK foundation (canonical types + the standard facet library + a schema
|
|
11
|
-
marker for platform detection)
|
|
12
|
-
flag in a later release. Fully **additive** — the existing API and existing
|
|
50
|
+
marker for platform detection). **The platform reads SDL in production today.** Fully **additive** — the existing API and existing
|
|
13
51
|
extensions are unchanged; adopting SDL is opt-in via `data_model=`.
|
|
14
52
|
|
|
15
53
|
### Added
|
|
@@ -28,8 +66,8 @@ extensions are unchanged; adopting SDL is opt-in via `data_model=`.
|
|
|
28
66
|
- `sdl.roles_of(model)` — introspect a model's field→role map.
|
|
29
67
|
|
|
30
68
|
Use it via `data_model=` on `@chat.function` (e.g.
|
|
31
|
-
`class Note(sdl.Entity): ...` → `data_model=Note`).
|
|
32
|
-
|
|
69
|
+
`class Note(sdl.Entity): ...` → `data_model=Note`). **The platform reads SDL
|
|
70
|
+
entities in production today.**
|
|
33
71
|
- **SDL — Standard Facet Library (Phase 2).** 123 composable facet mixins across
|
|
34
72
|
17 families (Identity, Time, People, Content, Communication, Media, Quantities,
|
|
35
73
|
Money, Catalog, Tasks, Location, Tech/Network, Analytics, Events, Ratings,
|
|
@@ -43,9 +81,8 @@ extensions are unchanged; adopting SDL is opt-in via `data_model=`.
|
|
|
43
81
|
|
|
44
82
|
564 standard roles are catalogued in `sdl_roles.json`. Every facet field is
|
|
45
83
|
optional; for anything not covered, use `sdl.field(role="yourapp.x")` with a
|
|
46
|
-
non-reserved namespace. Full guide: `docs/sdl-facets.md`.
|
|
47
|
-
|
|
48
|
-
them in a later phase.
|
|
84
|
+
non-reserved namespace. Full guide: `docs/sdl-facets.md`. **Live in production** —
|
|
85
|
+
extensions adopt the types and the platform reads them today.
|
|
49
86
|
- **SDL — schema marker on `Entity` / `EntityList`.** Both stamp
|
|
50
87
|
`x-sdl: "entity"` / `"entity-list"` into their JSON schema so the platform can
|
|
51
88
|
detect an SDL-typed result from a function's return schema alone. Inherited by
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: imperal-sdk
|
|
3
|
+
Version: 5.2.2
|
|
4
|
+
Summary: SDK for building Imperal Cloud extensions
|
|
5
|
+
Author: Valentin Scerbacov, Imperal, Inc.
|
|
6
|
+
License-Expression: AGPL-3.0-or-later
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Framework :: FastAPI
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Requires-Dist: click>=8.1.0
|
|
20
|
+
Requires-Dist: httpx>=0.28.0
|
|
21
|
+
Requires-Dist: pydantic>=2.10.0
|
|
22
|
+
Requires-Dist: pyjwt[crypto]>=2.10.0
|
|
23
|
+
Provides-Extra: contract
|
|
24
|
+
Requires-Dist: schemathesis>=3.30.0; extra == 'contract'
|
|
25
|
+
Provides-Extra: db
|
|
26
|
+
Requires-Dist: aiomysql>=0.2.0; extra == 'db'
|
|
27
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.0.30; extra == 'db'
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: jsonschema>=4.21.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: openapi-spec-validator>=0.7.1; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.25.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: respx>=0.22.0; extra == 'dev'
|
|
34
|
+
Provides-Extra: fastapi
|
|
35
|
+
Requires-Dist: fastapi>=0.115.0; extra == 'fastapi'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
<div align="center">
|
|
39
|
+
|
|
40
|
+
# Imperal SDK
|
|
41
|
+
|
|
42
|
+
### Build extensions for Webbee 🐝 — the agent of Imperal Cloud, the world's first AI Cloud OS.
|
|
43
|
+
|
|
44
|
+
**Write a small Python function. Webbee calls it when a user asks for it — in their own words. Ship it to the Marketplace and get paid.**
|
|
45
|
+
|
|
46
|
+
[](https://pypi.org/project/imperal-sdk/)
|
|
47
|
+
[](https://pypi.org/project/imperal-sdk/)
|
|
48
|
+
[](LICENSE)
|
|
49
|
+
|
|
50
|
+
[Documentation](https://docs.imperal.io) · [Quickstart](https://docs.imperal.io/en/getting-started/quick-start/) · [PyPI](https://pypi.org/project/imperal-sdk/) · [imperal.io](https://imperal.io)
|
|
51
|
+
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## What this is
|
|
57
|
+
|
|
58
|
+
**Imperal Cloud** is the world's first AI Cloud OS — a cloud you connect the contexts of your life into (mail, money, projects, servers, notes, anything) and then run entirely in your own native language. **Webbee 🐝** is the AI agent that lives inside it and does the work for you, safely.
|
|
59
|
+
|
|
60
|
+
**The Imperal SDK is how you give Webbee a new skill.** You write a small, typed Python *extension*; Webbee picks it up and calls it whenever a user asks for what it does — in plain language. The platform handles the hard parts — authentication, billing, multi-tenancy, audit, recovery, LLM routing — so your code stays small.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install imperal-sdk
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
> Python 3.11+ · AGPL-3.0-or-later
|
|
67
|
+
|
|
68
|
+
## Why build here
|
|
69
|
+
|
|
70
|
+
- **Webbee calls your function directly.** Typed, structured calls — no LLM guessing your arguments, no silent write failures.
|
|
71
|
+
- **The platform does the plumbing.** Auth, per-user isolation, billing, audit, retries and recovery, multi-tenant safety — handled for you.
|
|
72
|
+
- **You get paid.** Publish to the Imperal Marketplace, price your extension, earn on every use.
|
|
73
|
+
- **Bring any LLM.** Users connect their own model keys — Anthropic, OpenAI, Google, Ollama, any OpenAI-compatible API.
|
|
74
|
+
|
|
75
|
+
## A 60-second extension
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from imperal_sdk import Extension, ChatExtension, ActionResult
|
|
79
|
+
from pydantic import BaseModel, Field
|
|
80
|
+
|
|
81
|
+
ext = Extension(
|
|
82
|
+
"hello-world",
|
|
83
|
+
version="1.0.0",
|
|
84
|
+
display_name="Hello World",
|
|
85
|
+
description="Greets people by name with a friendly message.",
|
|
86
|
+
icon="icon.svg",
|
|
87
|
+
actions_explicit=True,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
chat = ChatExtension(ext, tool_name="hello_world", description="Friendly greetings.")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class GreetParams(BaseModel):
|
|
94
|
+
name: str = Field(..., description="Person to greet")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@chat.function("greet", description="Greet someone by name.", action_type="read")
|
|
98
|
+
async def greet(ctx, params: GreetParams) -> ActionResult:
|
|
99
|
+
return ActionResult.success(
|
|
100
|
+
data={"message": f"Hello, {params.name}! 🐝"},
|
|
101
|
+
summary=f"Greeted {params.name}",
|
|
102
|
+
)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
That's a real, working extension. When a user types *"say hi to Alex"*, Webbee calls `greet(name="Alex")`.
|
|
106
|
+
|
|
107
|
+
→ Full walkthrough, from zero to published: **[docs.imperal.io](https://docs.imperal.io/en/getting-started/quick-start/)**
|
|
108
|
+
|
|
109
|
+
## What you can build
|
|
110
|
+
|
|
111
|
+
- **Chat tools** — typed `@chat.function`s Webbee calls straight from natural language.
|
|
112
|
+
- **Panels** — UI surfaces rendered inside the Imperal Panel.
|
|
113
|
+
- **Skeletons** — live data feeds that keep Webbee aware of a user's state.
|
|
114
|
+
- **Scheduled jobs & webhooks** — act on a timer, or react to outside events.
|
|
115
|
+
- **Typed entities (SDL)** — return `sdl.Entity` objects and the platform reads their meaning (id, title, kind, …) directly instead of guessing field names. Live in production.
|
|
116
|
+
|
|
117
|
+
Every published extension passes the **federal contract** — the validators that let Webbee call your code safely. The SDK checks it locally before you ship.
|
|
118
|
+
|
|
119
|
+
## Test without a server
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from imperal_sdk.testing import MockContext
|
|
123
|
+
|
|
124
|
+
async def test_greet():
|
|
125
|
+
ctx = MockContext()
|
|
126
|
+
result = await greet(ctx, GreetParams(name="Alex"))
|
|
127
|
+
assert result.status == "success"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Documentation
|
|
131
|
+
|
|
132
|
+
The full API, the manifest schema, every validator, recipes, and the federal contract live at **[docs.imperal.io](https://docs.imperal.io)** — that's the source of truth; this README is just the doorway.
|
|
133
|
+
|
|
134
|
+
| | |
|
|
135
|
+
|---|---|
|
|
136
|
+
| Documentation | [docs.imperal.io](https://docs.imperal.io) |
|
|
137
|
+
| Quickstart | [docs.imperal.io/en/getting-started/quick-start](https://docs.imperal.io/en/getting-started/quick-start/) |
|
|
138
|
+
| PyPI | [pypi.org/project/imperal-sdk](https://pypi.org/project/imperal-sdk/) |
|
|
139
|
+
| Changelog | [CHANGELOG.md](CHANGELOG.md) |
|
|
140
|
+
| License | [AGPL-3.0-or-later](LICENSE) |
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
<div align="center">
|
|
145
|
+
|
|
146
|
+
**Built by [Imperal, Inc.](https://imperal.io) — Webbee 🐝 is its agent.**
|
|
147
|
+
|
|
148
|
+
</div>
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# Imperal SDK
|
|
4
|
+
|
|
5
|
+
### Build extensions for Webbee 🐝 — the agent of Imperal Cloud, the world's first AI Cloud OS.
|
|
6
|
+
|
|
7
|
+
**Write a small Python function. Webbee calls it when a user asks for it — in their own words. Ship it to the Marketplace and get paid.**
|
|
8
|
+
|
|
9
|
+
[](https://pypi.org/project/imperal-sdk/)
|
|
10
|
+
[](https://pypi.org/project/imperal-sdk/)
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
|
|
13
|
+
[Documentation](https://docs.imperal.io) · [Quickstart](https://docs.imperal.io/en/getting-started/quick-start/) · [PyPI](https://pypi.org/project/imperal-sdk/) · [imperal.io](https://imperal.io)
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## What this is
|
|
20
|
+
|
|
21
|
+
**Imperal Cloud** is the world's first AI Cloud OS — a cloud you connect the contexts of your life into (mail, money, projects, servers, notes, anything) and then run entirely in your own native language. **Webbee 🐝** is the AI agent that lives inside it and does the work for you, safely.
|
|
22
|
+
|
|
23
|
+
**The Imperal SDK is how you give Webbee a new skill.** You write a small, typed Python *extension*; Webbee picks it up and calls it whenever a user asks for what it does — in plain language. The platform handles the hard parts — authentication, billing, multi-tenancy, audit, recovery, LLM routing — so your code stays small.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install imperal-sdk
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
> Python 3.11+ · AGPL-3.0-or-later
|
|
30
|
+
|
|
31
|
+
## Why build here
|
|
32
|
+
|
|
33
|
+
- **Webbee calls your function directly.** Typed, structured calls — no LLM guessing your arguments, no silent write failures.
|
|
34
|
+
- **The platform does the plumbing.** Auth, per-user isolation, billing, audit, retries and recovery, multi-tenant safety — handled for you.
|
|
35
|
+
- **You get paid.** Publish to the Imperal Marketplace, price your extension, earn on every use.
|
|
36
|
+
- **Bring any LLM.** Users connect their own model keys — Anthropic, OpenAI, Google, Ollama, any OpenAI-compatible API.
|
|
37
|
+
|
|
38
|
+
## A 60-second extension
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from imperal_sdk import Extension, ChatExtension, ActionResult
|
|
42
|
+
from pydantic import BaseModel, Field
|
|
43
|
+
|
|
44
|
+
ext = Extension(
|
|
45
|
+
"hello-world",
|
|
46
|
+
version="1.0.0",
|
|
47
|
+
display_name="Hello World",
|
|
48
|
+
description="Greets people by name with a friendly message.",
|
|
49
|
+
icon="icon.svg",
|
|
50
|
+
actions_explicit=True,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
chat = ChatExtension(ext, tool_name="hello_world", description="Friendly greetings.")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class GreetParams(BaseModel):
|
|
57
|
+
name: str = Field(..., description="Person to greet")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@chat.function("greet", description="Greet someone by name.", action_type="read")
|
|
61
|
+
async def greet(ctx, params: GreetParams) -> ActionResult:
|
|
62
|
+
return ActionResult.success(
|
|
63
|
+
data={"message": f"Hello, {params.name}! 🐝"},
|
|
64
|
+
summary=f"Greeted {params.name}",
|
|
65
|
+
)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
That's a real, working extension. When a user types *"say hi to Alex"*, Webbee calls `greet(name="Alex")`.
|
|
69
|
+
|
|
70
|
+
→ Full walkthrough, from zero to published: **[docs.imperal.io](https://docs.imperal.io/en/getting-started/quick-start/)**
|
|
71
|
+
|
|
72
|
+
## What you can build
|
|
73
|
+
|
|
74
|
+
- **Chat tools** — typed `@chat.function`s Webbee calls straight from natural language.
|
|
75
|
+
- **Panels** — UI surfaces rendered inside the Imperal Panel.
|
|
76
|
+
- **Skeletons** — live data feeds that keep Webbee aware of a user's state.
|
|
77
|
+
- **Scheduled jobs & webhooks** — act on a timer, or react to outside events.
|
|
78
|
+
- **Typed entities (SDL)** — return `sdl.Entity` objects and the platform reads their meaning (id, title, kind, …) directly instead of guessing field names. Live in production.
|
|
79
|
+
|
|
80
|
+
Every published extension passes the **federal contract** — the validators that let Webbee call your code safely. The SDK checks it locally before you ship.
|
|
81
|
+
|
|
82
|
+
## Test without a server
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from imperal_sdk.testing import MockContext
|
|
86
|
+
|
|
87
|
+
async def test_greet():
|
|
88
|
+
ctx = MockContext()
|
|
89
|
+
result = await greet(ctx, GreetParams(name="Alex"))
|
|
90
|
+
assert result.status == "success"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Documentation
|
|
94
|
+
|
|
95
|
+
The full API, the manifest schema, every validator, recipes, and the federal contract live at **[docs.imperal.io](https://docs.imperal.io)** — that's the source of truth; this README is just the doorway.
|
|
96
|
+
|
|
97
|
+
| | |
|
|
98
|
+
|---|---|
|
|
99
|
+
| Documentation | [docs.imperal.io](https://docs.imperal.io) |
|
|
100
|
+
| Quickstart | [docs.imperal.io/en/getting-started/quick-start](https://docs.imperal.io/en/getting-started/quick-start/) |
|
|
101
|
+
| PyPI | [pypi.org/project/imperal-sdk](https://pypi.org/project/imperal-sdk/) |
|
|
102
|
+
| Changelog | [CHANGELOG.md](CHANGELOG.md) |
|
|
103
|
+
| License | [AGPL-3.0-or-later](LICENSE) |
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
<div align="center">
|
|
108
|
+
|
|
109
|
+
**Built by [Imperal, Inc.](https://imperal.io) — Webbee 🐝 is its agent.**
|
|
110
|
+
|
|
111
|
+
</div>
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Imperal Cloud SDK — build extensions for the Imperal platform."""
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
__version__ = "5.2.2"
|
|
5
|
+
|
|
6
|
+
# 5.2.2 (2026-06-11): the package root resolves its public surface lazily
|
|
7
|
+
# (PEP 562). The eager imports pulled the HTTP transport (Context / client
|
|
8
|
+
# modules -> httpx) into EVERY consumer of ANY submodule — including
|
|
9
|
+
# Temporal workflow code that lazily imports transport-free helpers such as
|
|
10
|
+
# ``imperal_sdk.chat.filters`` (httpx subclasses urllib.request.Request,
|
|
11
|
+
# which the workflow sandbox restricts; kernel federal
|
|
12
|
+
# I-SANDBOX-SAFE-LAZY-IMPORTS, live incident 2026-06-10). The public
|
|
13
|
+
# surface is unchanged: every name resolves to the same object, star-import
|
|
14
|
+
# honors __all__, and resolved names are cached back into module globals.
|
|
15
|
+
|
|
16
|
+
# name -> defining module (resolved on first attribute access)
|
|
17
|
+
_LAZY_ATTRS = {
|
|
18
|
+
# Core
|
|
19
|
+
"Extension": "imperal_sdk.extension",
|
|
20
|
+
"ToolDef": "imperal_sdk.extension",
|
|
21
|
+
"SignalDef": "imperal_sdk.extension",
|
|
22
|
+
"ScheduleDef": "imperal_sdk.extension",
|
|
23
|
+
"LifecycleHook": "imperal_sdk.extension",
|
|
24
|
+
"HealthCheckDef": "imperal_sdk.extension",
|
|
25
|
+
"WebhookDef": "imperal_sdk.extension",
|
|
26
|
+
"EventHandlerDef": "imperal_sdk.extension",
|
|
27
|
+
"ExposedMethod": "imperal_sdk.extension",
|
|
28
|
+
"TrayDef": "imperal_sdk.extension",
|
|
29
|
+
"Context": "imperal_sdk.context",
|
|
30
|
+
"ImperalAuth": "imperal_sdk.auth",
|
|
31
|
+
"AuthError": "imperal_sdk.auth",
|
|
32
|
+
"User": "imperal_sdk.types.identity",
|
|
33
|
+
"UserContext": "imperal_sdk.types.identity",
|
|
34
|
+
"Tenant": "imperal_sdk.types.identity",
|
|
35
|
+
"TenantContext": "imperal_sdk.types.identity",
|
|
36
|
+
"generate_manifest": "imperal_sdk.manifest",
|
|
37
|
+
"save_manifest": "imperal_sdk.manifest",
|
|
38
|
+
"ChatExtension": "imperal_sdk.chat",
|
|
39
|
+
"ActionResult": "imperal_sdk.chat.action_result",
|
|
40
|
+
# LLM
|
|
41
|
+
"get_llm_provider": "imperal_sdk.runtime.llm_provider",
|
|
42
|
+
"LLMProvider": "imperal_sdk.runtime.llm_provider",
|
|
43
|
+
"LLMConfig": "imperal_sdk.runtime.llm_provider",
|
|
44
|
+
"LLMUsage": "imperal_sdk.runtime.llm_provider",
|
|
45
|
+
"MessageAdapter": "imperal_sdk.runtime.message_adapter",
|
|
46
|
+
# IPC
|
|
47
|
+
"ExtensionsClient": "imperal_sdk.extensions.client",
|
|
48
|
+
"CircularCallError": "imperal_sdk.extensions.client",
|
|
49
|
+
# Errors
|
|
50
|
+
"ImperalError": "imperal_sdk.errors",
|
|
51
|
+
"APIError": "imperal_sdk.errors",
|
|
52
|
+
"NotFoundError": "imperal_sdk.errors",
|
|
53
|
+
"RateLimitError": "imperal_sdk.errors",
|
|
54
|
+
"ValidationError": "imperal_sdk.errors",
|
|
55
|
+
"ExtensionError": "imperal_sdk.errors",
|
|
56
|
+
"QuotaExceededError": "imperal_sdk.errors",
|
|
57
|
+
"SkeletonAccessForbidden": "imperal_sdk.errors",
|
|
58
|
+
# Types
|
|
59
|
+
"Page": "imperal_sdk.types",
|
|
60
|
+
"ChatResult": "imperal_sdk.types",
|
|
61
|
+
"FunctionCall": "imperal_sdk.types",
|
|
62
|
+
"Document": "imperal_sdk.types",
|
|
63
|
+
"CompletionResult": "imperal_sdk.types",
|
|
64
|
+
"LimitsResult": "imperal_sdk.types",
|
|
65
|
+
"SubscriptionInfo": "imperal_sdk.types",
|
|
66
|
+
"BalanceInfo": "imperal_sdk.types",
|
|
67
|
+
"FileInfo": "imperal_sdk.types",
|
|
68
|
+
"HTTPResponse": "imperal_sdk.types",
|
|
69
|
+
"Event": "imperal_sdk.types",
|
|
70
|
+
"WebhookRequest": "imperal_sdk.types",
|
|
71
|
+
"WebhookResponse": "imperal_sdk.types",
|
|
72
|
+
"HealthStatus": "imperal_sdk.types",
|
|
73
|
+
# Protocol + Validator
|
|
74
|
+
"ExtensionProtocol": "imperal_sdk.protocols",
|
|
75
|
+
"validate_extension": "imperal_sdk.validator",
|
|
76
|
+
"ValidationReport": "imperal_sdk.validator",
|
|
77
|
+
"ValidationIssue": "imperal_sdk.validator",
|
|
78
|
+
"validate_source_tree": "imperal_sdk.validator_v1_6_0",
|
|
79
|
+
"validate_manifest_v1_6_0": "imperal_sdk.validator_v1_6_0",
|
|
80
|
+
# Secrets (importable from the root since 5.1.0; not in __all__ —
|
|
81
|
+
# preserved as-is)
|
|
82
|
+
"SecretSpec": "imperal_sdk.secrets",
|
|
83
|
+
"SecretClient": "imperal_sdk.secrets",
|
|
84
|
+
"SecretStatus": "imperal_sdk.secrets",
|
|
85
|
+
"SecretNotDeclaredError": "imperal_sdk.secrets",
|
|
86
|
+
"SecretWriteForbidden": "imperal_sdk.secrets",
|
|
87
|
+
"SecretVaultUnavailable": "imperal_sdk.secrets",
|
|
88
|
+
"SecretValueTooLarge": "imperal_sdk.secrets",
|
|
89
|
+
"SecretDeclarationConflict": "imperal_sdk.secrets",
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Any imperal_sdk submodule also resolves as a root attribute (the eager
|
|
93
|
+
# imports used to expose chat/context/errors/ui/sdl/... as side effects;
|
|
94
|
+
# the generic fallback in __getattr__ preserves that surface).
|
|
95
|
+
|
|
96
|
+
if TYPE_CHECKING: # pragma: no cover — IDE / type-checker surface only
|
|
97
|
+
from imperal_sdk.extension import (
|
|
98
|
+
Extension, ToolDef, SignalDef, ScheduleDef,
|
|
99
|
+
LifecycleHook, HealthCheckDef, WebhookDef, EventHandlerDef, ExposedMethod, TrayDef,
|
|
100
|
+
)
|
|
101
|
+
from imperal_sdk.context import Context
|
|
102
|
+
from imperal_sdk.auth import ImperalAuth, AuthError
|
|
103
|
+
from imperal_sdk.types.identity import User, UserContext, Tenant, TenantContext
|
|
104
|
+
from imperal_sdk.manifest import generate_manifest, save_manifest
|
|
105
|
+
from imperal_sdk.chat import ChatExtension
|
|
106
|
+
from imperal_sdk.chat.action_result import ActionResult
|
|
107
|
+
from imperal_sdk import ui
|
|
108
|
+
from imperal_sdk import sdl
|
|
109
|
+
from imperal_sdk.runtime.llm_provider import get_llm_provider, LLMProvider, LLMConfig, LLMUsage
|
|
110
|
+
from imperal_sdk.runtime.message_adapter import MessageAdapter
|
|
111
|
+
from imperal_sdk.extensions.client import ExtensionsClient, CircularCallError
|
|
112
|
+
from imperal_sdk.errors import (
|
|
113
|
+
ImperalError, APIError, NotFoundError, RateLimitError,
|
|
114
|
+
ValidationError, ExtensionError, QuotaExceededError,
|
|
115
|
+
SkeletonAccessForbidden,
|
|
116
|
+
)
|
|
117
|
+
from imperal_sdk.types import (
|
|
118
|
+
Page, ChatResult, FunctionCall,
|
|
119
|
+
Document, CompletionResult, LimitsResult, SubscriptionInfo,
|
|
120
|
+
BalanceInfo, FileInfo, HTTPResponse,
|
|
121
|
+
Event, WebhookRequest, WebhookResponse, HealthStatus,
|
|
122
|
+
)
|
|
123
|
+
from imperal_sdk.protocols import ExtensionProtocol
|
|
124
|
+
from imperal_sdk.validator import validate_extension, ValidationReport, ValidationIssue
|
|
125
|
+
from imperal_sdk.validator_v1_6_0 import (
|
|
126
|
+
validate_source_tree,
|
|
127
|
+
validate_manifest_v1_6_0,
|
|
128
|
+
)
|
|
129
|
+
from imperal_sdk.secrets import (
|
|
130
|
+
SecretSpec, SecretClient, SecretStatus,
|
|
131
|
+
SecretNotDeclaredError, SecretWriteForbidden, SecretVaultUnavailable,
|
|
132
|
+
SecretValueTooLarge, SecretDeclarationConflict,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def __getattr__(name: str):
|
|
137
|
+
import importlib
|
|
138
|
+
|
|
139
|
+
src = _LAZY_ATTRS.get(name)
|
|
140
|
+
if src is not None:
|
|
141
|
+
obj = getattr(importlib.import_module(src), name)
|
|
142
|
+
globals()[name] = obj
|
|
143
|
+
return obj
|
|
144
|
+
if name.startswith("_"):
|
|
145
|
+
raise AttributeError(f"module 'imperal_sdk' has no attribute {name!r}")
|
|
146
|
+
try:
|
|
147
|
+
mod = importlib.import_module(f"imperal_sdk.{name}")
|
|
148
|
+
except ImportError:
|
|
149
|
+
raise AttributeError(
|
|
150
|
+
f"module 'imperal_sdk' has no attribute {name!r}"
|
|
151
|
+
) from None
|
|
152
|
+
globals()[name] = mod
|
|
153
|
+
return mod
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def __dir__():
|
|
157
|
+
return sorted(set(globals()) | set(_LAZY_ATTRS) | {"ui", "sdl"})
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
__all__ = [
|
|
161
|
+
# Core
|
|
162
|
+
"Extension", "ToolDef", "SignalDef", "ScheduleDef",
|
|
163
|
+
"LifecycleHook", "HealthCheckDef", "WebhookDef", "EventHandlerDef", "ExposedMethod", "TrayDef",
|
|
164
|
+
"Context", "ImperalAuth", "AuthError",
|
|
165
|
+
"User", "UserContext", "Tenant", "TenantContext",
|
|
166
|
+
"ChatExtension", "ActionResult",
|
|
167
|
+
"generate_manifest", "save_manifest",
|
|
168
|
+
# IPC
|
|
169
|
+
"ExtensionsClient", "CircularCallError",
|
|
170
|
+
# LLM
|
|
171
|
+
"get_llm_provider", "LLMProvider", "LLMConfig", "LLMUsage", "MessageAdapter",
|
|
172
|
+
# Errors
|
|
173
|
+
"ImperalError", "APIError", "NotFoundError", "RateLimitError",
|
|
174
|
+
"ValidationError", "ExtensionError", "QuotaExceededError",
|
|
175
|
+
"SkeletonAccessForbidden",
|
|
176
|
+
# Types
|
|
177
|
+
"Page", "ChatResult", "FunctionCall",
|
|
178
|
+
"Document", "CompletionResult", "LimitsResult", "SubscriptionInfo",
|
|
179
|
+
"BalanceInfo", "FileInfo", "HTTPResponse",
|
|
180
|
+
"Event", "WebhookRequest", "WebhookResponse", "HealthStatus",
|
|
181
|
+
# Protocol + Validator
|
|
182
|
+
"ExtensionProtocol", "validate_extension", "ValidationReport", "ValidationIssue",
|
|
183
|
+
"validate_source_tree", "validate_manifest_v1_6_0",
|
|
184
|
+
# UI
|
|
185
|
+
"ui",
|
|
186
|
+
# SDL — Structured Data Layer
|
|
187
|
+
"sdl",
|
|
188
|
+
]
|
|
@@ -64,9 +64,16 @@ class FunctionDef:
|
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
class ChatExtension:
|
|
67
|
-
def __init__(self, ext, tool_name: str
|
|
67
|
+
def __init__(self, ext, tool_name: str | None = None, description: str = "",
|
|
68
|
+
system_prompt: str = "",
|
|
68
69
|
model: "str | None" = None, max_rounds: int = 10):
|
|
69
70
|
self.ext = ext
|
|
71
|
+
# tool_name is the canonical chat-registration key: it groups the
|
|
72
|
+
# extension's @chat.function tools in the manifest, anchors the per-turn
|
|
73
|
+
# system prompt, and labels scope-guard audit lines. Optional — derived
|
|
74
|
+
# from the extension app_id (``tool_<app_id>_chat``) when omitted; pass
|
|
75
|
+
# it explicitly to pin a production-stable routing name.
|
|
76
|
+
tool_name = tool_name or f"tool_{ext.app_id}_chat"
|
|
70
77
|
self.tool_name = tool_name
|
|
71
78
|
self.description = description
|
|
72
79
|
self.system_prompt = system_prompt
|
|
@@ -94,20 +101,12 @@ class ChatExtension:
|
|
|
94
101
|
"Example: 'Notes module — manage user notes and folders.'"
|
|
95
102
|
)
|
|
96
103
|
|
|
97
|
-
# v5.0.0
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
|
|
103
|
-
_warnings.warn(
|
|
104
|
-
f"ChatExtension(tool_name={tool_name!r}): kwarg deprecated in SDK 5.0.0 "
|
|
105
|
-
"(orchestrator-tool auto-registration removed). Move classifier-readable "
|
|
106
|
-
"text into Extension(description=...) + per-@chat.function(description=...). "
|
|
107
|
-
"Will be removed in 5.1.0.",
|
|
108
|
-
DeprecationWarning,
|
|
109
|
-
stacklevel=2,
|
|
110
|
-
)
|
|
104
|
+
# v5.0.0 removed orchestrator-tool auto-registration: ChatExtension is
|
|
105
|
+
# purely a @chat.function bundle declaration, and the platform chain
|
|
106
|
+
# executor dispatches each function directly via typed dispatch — the
|
|
107
|
+
# host LLM no longer sees a single ``tool_<app_id>_chat`` umbrella tool.
|
|
108
|
+
# ``tool_name`` is retained as the canonical registration key (see the
|
|
109
|
+
# __init__ note above) — it is NOT deprecated.
|
|
111
110
|
ext._chat_extensions = getattr(ext, "_chat_extensions", {})
|
|
112
111
|
ext._chat_extensions[tool_name] = self
|
|
113
112
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""5.2.2 — the package root is import-light (PEP 562 lazy surface).
|
|
3
|
+
|
|
4
|
+
Kernel federal I-SANDBOX-SAFE-LAZY-IMPORTS (live incident 2026-06-10): the
|
|
5
|
+
eager root imports pulled httpx (Context / client modules) into EVERY
|
|
6
|
+
consumer of ANY submodule — including Temporal workflow code lazily
|
|
7
|
+
importing transport-free helpers like ``imperal_sdk.chat.filters``
|
|
8
|
+
(httpx._models subclasses urllib.request.Request, restricted inside the
|
|
9
|
+
workflow sandbox -> RestrictedWorkflowAccessError -> whole chat turns
|
|
10
|
+
crashed).
|
|
11
|
+
|
|
12
|
+
Contracts:
|
|
13
|
+
1. ``import imperal_sdk`` and ``import imperal_sdk.chat.filters`` must NOT
|
|
14
|
+
load httpx.
|
|
15
|
+
2. The public surface is unchanged: every ``__all__`` name (and the
|
|
16
|
+
root-importable secrets names) resolves lazily to the SAME object the
|
|
17
|
+
defining module exports; submodules stay reachable as root attributes;
|
|
18
|
+
star-import works; unknown names raise AttributeError.
|
|
19
|
+
"""
|
|
20
|
+
import subprocess
|
|
21
|
+
import sys
|
|
22
|
+
|
|
23
|
+
import imperal_sdk
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _probe(code: str) -> str:
|
|
27
|
+
r = subprocess.run(
|
|
28
|
+
[sys.executable, "-c", code],
|
|
29
|
+
capture_output=True, text=True, timeout=120,
|
|
30
|
+
)
|
|
31
|
+
out = (r.stdout or "").strip()
|
|
32
|
+
if r.returncode != 0 and not out:
|
|
33
|
+
out = "PROBE_ERROR: " + (r.stderr or "").strip()[-400:]
|
|
34
|
+
return out
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_root_import_is_transport_free():
|
|
38
|
+
out = _probe(
|
|
39
|
+
"import imperal_sdk, sys; "
|
|
40
|
+
"print('HTTPX' if 'httpx' in sys.modules else 'CLEAN')"
|
|
41
|
+
)
|
|
42
|
+
assert out == "CLEAN"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_chat_filters_import_is_transport_free():
|
|
46
|
+
out = _probe(
|
|
47
|
+
"from imperal_sdk.chat.filters import enforce_os_identity, enforce_response_style; "
|
|
48
|
+
"import sys; print('HTTPX' if 'httpx' in sys.modules else 'CLEAN')"
|
|
49
|
+
)
|
|
50
|
+
assert out == "CLEAN"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_version():
|
|
54
|
+
assert imperal_sdk.__version__ == "5.2.2"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
_SECRETS_NAMES = [
|
|
58
|
+
"SecretSpec", "SecretClient", "SecretStatus",
|
|
59
|
+
"SecretNotDeclaredError", "SecretWriteForbidden", "SecretVaultUnavailable",
|
|
60
|
+
"SecretValueTooLarge", "SecretDeclarationConflict",
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_every_public_name_resolves_to_the_defining_object():
|
|
65
|
+
import importlib
|
|
66
|
+
|
|
67
|
+
from imperal_sdk import _LAZY_ATTRS
|
|
68
|
+
|
|
69
|
+
for name in list(imperal_sdk.__all__) + _SECRETS_NAMES:
|
|
70
|
+
obj = getattr(imperal_sdk, name)
|
|
71
|
+
if name in ("ui", "sdl"):
|
|
72
|
+
assert obj is importlib.import_module(f"imperal_sdk.{name}")
|
|
73
|
+
continue
|
|
74
|
+
src = _LAZY_ATTRS[name]
|
|
75
|
+
assert obj is getattr(importlib.import_module(src), name), name
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_submodules_reachable_as_root_attributes():
|
|
79
|
+
for sub in ("chat", "context", "errors", "types", "ui", "sdl", "manifest"):
|
|
80
|
+
mod = getattr(imperal_sdk, sub)
|
|
81
|
+
assert mod.__name__ == f"imperal_sdk.{sub}"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_star_import_covers_all():
|
|
85
|
+
ns: dict = {}
|
|
86
|
+
exec("from imperal_sdk import *", ns) # noqa: S102 — surface test
|
|
87
|
+
missing = [n for n in imperal_sdk.__all__ if n not in ns]
|
|
88
|
+
assert not missing, missing
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_unknown_attribute_raises_attribute_error():
|
|
92
|
+
try:
|
|
93
|
+
imperal_sdk.definitely_not_a_real_name
|
|
94
|
+
except AttributeError:
|
|
95
|
+
pass
|
|
96
|
+
else:
|
|
97
|
+
raise AssertionError("expected AttributeError")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_dir_lists_the_surface():
|
|
101
|
+
d = dir(imperal_sdk)
|
|
102
|
+
for name in ("ChatExtension", "Context", "ui", "sdl", "SecretClient"):
|
|
103
|
+
assert name in d
|