modus-sdk 0.2.0__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.
- modus_sdk-0.2.0/.gitignore +39 -0
- modus_sdk-0.2.0/CHANGELOG.md +39 -0
- modus_sdk-0.2.0/CONTRIBUTING.md +46 -0
- modus_sdk-0.2.0/LICENSE +21 -0
- modus_sdk-0.2.0/PKG-INFO +225 -0
- modus_sdk-0.2.0/README.md +205 -0
- modus_sdk-0.2.0/assets/modus-logo.png +0 -0
- modus_sdk-0.2.0/assets/modus-logo.svg +13 -0
- modus_sdk-0.2.0/examples/apps/modus_skill_chat/.streamlit/config.toml +9 -0
- modus_sdk-0.2.0/examples/apps/modus_skill_chat/README.md +28 -0
- modus_sdk-0.2.0/examples/apps/modus_skill_chat/app.py +322 -0
- modus_sdk-0.2.0/examples/apps/modus_skill_chat/pyproject.toml +9 -0
- modus_sdk-0.2.0/examples/scripts/chat.py +90 -0
- modus_sdk-0.2.0/examples/scripts/manage_skill.py +106 -0
- modus_sdk-0.2.0/examples/scripts/modus_chat.py +68 -0
- modus_sdk-0.2.0/examples/scripts/quickstart.py +45 -0
- modus_sdk-0.2.0/modus/__init__.py +144 -0
- modus_sdk-0.2.0/modus/_auth.py +111 -0
- modus_sdk-0.2.0/modus/_chat.py +411 -0
- modus_sdk-0.2.0/modus/_client.py +240 -0
- modus_sdk-0.2.0/modus/_config.py +89 -0
- modus_sdk-0.2.0/modus/_deprecation.py +29 -0
- modus_sdk-0.2.0/modus/_exceptions.py +149 -0
- modus_sdk-0.2.0/modus/_generated/__init__.py +341 -0
- modus_sdk-0.2.0/modus/_generated/models.py +3182 -0
- modus_sdk-0.2.0/modus/_generated/operations.py +891 -0
- modus_sdk-0.2.0/modus/_http.py +471 -0
- modus_sdk-0.2.0/modus/_openapi_invoke.py +137 -0
- modus_sdk-0.2.0/modus/_pagination.py +175 -0
- modus_sdk-0.2.0/modus/_query.py +38 -0
- modus_sdk-0.2.0/modus/_retry.py +123 -0
- modus_sdk-0.2.0/modus/_streaming.py +115 -0
- modus_sdk-0.2.0/modus/_validation.py +61 -0
- modus_sdk-0.2.0/modus/_version.py +24 -0
- modus_sdk-0.2.0/modus/management/__init__.py +74 -0
- modus_sdk-0.2.0/modus/management/_client.py +168 -0
- modus_sdk-0.2.0/modus/management/resources/__init__.py +21 -0
- modus_sdk-0.2.0/modus/management/resources/_access_config_write.py +31 -0
- modus_sdk-0.2.0/modus/management/resources/context/__init__.py +0 -0
- modus_sdk-0.2.0/modus/management/resources/context/_content_merge.py +128 -0
- modus_sdk-0.2.0/modus/management/resources/context/context.py +362 -0
- modus_sdk-0.2.0/modus/management/resources/context/custom_items.py +318 -0
- modus_sdk-0.2.0/modus/management/resources/context/items.py +234 -0
- modus_sdk-0.2.0/modus/management/resources/organization.py +52 -0
- modus_sdk-0.2.0/modus/management/resources/scopes/__init__.py +0 -0
- modus_sdk-0.2.0/modus/management/resources/scopes/_update_body.py +38 -0
- modus_sdk-0.2.0/modus/management/resources/scopes/conversations.py +177 -0
- modus_sdk-0.2.0/modus/management/resources/scopes/evaluations.py +142 -0
- modus_sdk-0.2.0/modus/management/resources/scopes/memories.py +273 -0
- modus_sdk-0.2.0/modus/management/resources/scopes/scopes.py +764 -0
- modus_sdk-0.2.0/modus/management/resources/scopes/supervision.py +85 -0
- modus_sdk-0.2.0/modus/management/resources/usage.py +92 -0
- modus_sdk-0.2.0/modus/management/resources/users.py +56 -0
- modus_sdk-0.2.0/modus/management/resources/workflows/__init__.py +0 -0
- modus_sdk-0.2.0/modus/management/resources/workflows/_update_body.py +29 -0
- modus_sdk-0.2.0/modus/management/resources/workflows/interfaces.py +136 -0
- modus_sdk-0.2.0/modus/management/resources/workflows/workflows.py +647 -0
- modus_sdk-0.2.0/modus/management/types/__init__.py +57 -0
- modus_sdk-0.2.0/modus/management/types/_guardrails_access.py +33 -0
- modus_sdk-0.2.0/modus/management/types/_serialize.py +52 -0
- modus_sdk-0.2.0/modus/management/types/agent_selection.py +66 -0
- modus_sdk-0.2.0/modus/management/types/agents.py +5 -0
- modus_sdk-0.2.0/modus/management/types/context.py +25 -0
- modus_sdk-0.2.0/modus/management/types/conversations.py +5 -0
- modus_sdk-0.2.0/modus/management/types/evaluations.py +25 -0
- modus_sdk-0.2.0/modus/management/types/json_blob.py +51 -0
- modus_sdk-0.2.0/modus/management/types/memories.py +69 -0
- modus_sdk-0.2.0/modus/management/types/ownership.py +9 -0
- modus_sdk-0.2.0/modus/management/types/skills.py +5 -0
- modus_sdk-0.2.0/modus/management/types/toolset.py +236 -0
- modus_sdk-0.2.0/modus/management/types/triggers.py +284 -0
- modus_sdk-0.2.0/modus/management/types/usage.py +13 -0
- modus_sdk-0.2.0/modus/management/types/workflow.py +45 -0
- modus_sdk-0.2.0/modus/py.typed +0 -0
- modus_sdk-0.2.0/modus/resources/__init__.py +21 -0
- modus_sdk-0.2.0/modus/resources/connections/__init__.py +3 -0
- modus_sdk-0.2.0/modus/resources/connections/connections.py +189 -0
- modus_sdk-0.2.0/modus/resources/context/__init__.py +3 -0
- modus_sdk-0.2.0/modus/resources/context/context.py +48 -0
- modus_sdk-0.2.0/modus/resources/context/items.py +410 -0
- modus_sdk-0.2.0/modus/resources/modus/__init__.py +3 -0
- modus_sdk-0.2.0/modus/resources/modus/conversations.py +144 -0
- modus_sdk-0.2.0/modus/resources/modus/modus.py +138 -0
- modus_sdk-0.2.0/modus/resources/scopes/__init__.py +3 -0
- modus_sdk-0.2.0/modus/resources/scopes/conversations.py +213 -0
- modus_sdk-0.2.0/modus/resources/scopes/scopes.py +431 -0
- modus_sdk-0.2.0/modus/resources/suggestions/__init__.py +3 -0
- modus_sdk-0.2.0/modus/resources/suggestions/suggestions.py +207 -0
- modus_sdk-0.2.0/modus/resources/workflows/__init__.py +15 -0
- modus_sdk-0.2.0/modus/resources/workflows/runs.py +833 -0
- modus_sdk-0.2.0/modus/resources/workflows/workflow_actions.py +122 -0
- modus_sdk-0.2.0/modus/resources/workflows/workflows.py +358 -0
- modus_sdk-0.2.0/modus/types/__init__.py +80 -0
- modus_sdk-0.2.0/modus/types/agent_runs.py +70 -0
- modus_sdk-0.2.0/modus/types/agents.py +17 -0
- modus_sdk-0.2.0/modus/types/chat.py +9 -0
- modus_sdk-0.2.0/modus/types/compose.py +21 -0
- modus_sdk-0.2.0/modus/types/connections.py +9 -0
- modus_sdk-0.2.0/modus/types/context.py +28 -0
- modus_sdk-0.2.0/modus/types/context_accessors.py +89 -0
- modus_sdk-0.2.0/modus/types/context_compose.py +18 -0
- modus_sdk-0.2.0/modus/types/context_values.py +21 -0
- modus_sdk-0.2.0/modus/types/conversations.py +30 -0
- modus_sdk-0.2.0/modus/types/json_blob.py +24 -0
- modus_sdk-0.2.0/modus/types/pagination.py +142 -0
- modus_sdk-0.2.0/modus/types/runs.py +71 -0
- modus_sdk-0.2.0/modus/types/skills.py +16 -0
- modus_sdk-0.2.0/modus/types/suggestions.py +16 -0
- modus_sdk-0.2.0/modus/types/tool_calls.py +34 -0
- modus_sdk-0.2.0/modus/types/views.py +6 -0
- modus_sdk-0.2.0/project.json +115 -0
- modus_sdk-0.2.0/pyproject.toml +83 -0
- modus_sdk-0.2.0/pyrightconfig.json +12 -0
- modus_sdk-0.2.0/requirements.txt +37 -0
- modus_sdk-0.2.0/uv.lock +1140 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
*.egg
|
|
7
|
+
*.egg-info/
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
.eggs/
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
env/
|
|
16
|
+
|
|
17
|
+
# Testing
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.coverage
|
|
20
|
+
htmlcov/
|
|
21
|
+
.tox/
|
|
22
|
+
|
|
23
|
+
# IDEs
|
|
24
|
+
.idea/
|
|
25
|
+
.vscode/
|
|
26
|
+
*.swp
|
|
27
|
+
|
|
28
|
+
# OS
|
|
29
|
+
.DS_Store
|
|
30
|
+
|
|
31
|
+
# Environment variables
|
|
32
|
+
.env
|
|
33
|
+
.env.*
|
|
34
|
+
|
|
35
|
+
# Test / lint artifacts
|
|
36
|
+
reports/
|
|
37
|
+
|
|
38
|
+
# hatch-vcs: auto-generated at build time, never committed
|
|
39
|
+
modus/_version.py
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
**Current release:** [`0.2.0`](https://pypi.org/project/modus-sdk/0.2.0/).
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## [0.2.0] — 2026-07-20
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **`modus.Modus` / `AsyncModus`** — read and invoke: scopes, workflows, context, connections, Modus assistant chat, runs, suggestions.
|
|
11
|
+
- **`modus.management.ModusManagement` / `AsyncModusManagement`** — configure: CRUD, deploy, context creators, usage, org admin, scope evaluations.
|
|
12
|
+
- Canonical **`client.scopes` / `client.workflows`** (and management equivalents) against `/api/v1/scopes` and `/api/v1/workflows`.
|
|
13
|
+
- **`client.modus`** — org-wide Modus assistant (`chat`, `chat_stream`, `get_context`, `conversations`).
|
|
14
|
+
- **`client.suggestions`** — list approved suggested questions; record impression/click/dismiss events.
|
|
15
|
+
- **`client.workflows.runs.active()` / `active_by_session(...)`** — list or look up active conversation runs.
|
|
16
|
+
- **`mgmt.scopes.evaluations(scope_id)`** — evaluation config, manual trigger, run history.
|
|
17
|
+
- Bounded conversation windows via optional `message_limit` / `before_message_index` on `conversations(...).get()`.
|
|
18
|
+
- MCP **`get_context_data`** batch + ACL: `{ items: [{ itemId, field?, mode?, limit? }, …], sessionId? }` → `{ results: [...] }` (max 20; per-item partial failures).
|
|
19
|
+
- Cursor pagination, retries on 429/5xx, SSE streaming chat.
|
|
20
|
+
- Full public OpenAPI operation coverage (contract-tested).
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Public surface uses **Scope** / **Workflow** vocabulary (types remain `Skill` / `Agent` where the API still uses those names).
|
|
25
|
+
- Scope-run helper: `client.workflows.runs.create_scope(scope_id, body)` (not `create_skill`).
|
|
26
|
+
|
|
27
|
+
### Removed
|
|
28
|
+
|
|
29
|
+
- Legacy **`client.skills` / `client.agents`** accessors — use `scopes` / `workflows`.
|
|
30
|
+
- Unimplemented context-creator stubs (`dimension` / `fact` / `metric` / `filter` create DTOs).
|
|
31
|
+
|
|
32
|
+
## Prior (TestPyPI only)
|
|
33
|
+
|
|
34
|
+
| Version | Notes |
|
|
35
|
+
|---------|--------|
|
|
36
|
+
| 0.1.0–0.1.7 | Internal / TestPyPI previews. Not published to production PyPI. Superseded by `0.2.0`. |
|
|
37
|
+
|
|
38
|
+
## [Unreleased]
|
|
39
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in the Modus Python SDK.
|
|
4
|
+
|
|
5
|
+
## Reporting Issues
|
|
6
|
+
|
|
7
|
+
Please open a [GitHub issue](https://github.com/modus-data/modus-sdk-python/issues)
|
|
8
|
+
for bug reports and feature requests. Include:
|
|
9
|
+
|
|
10
|
+
- SDK version (`python -c "import modus; print(modus.__version__)"`)
|
|
11
|
+
- Python version
|
|
12
|
+
- A minimal reproducible example
|
|
13
|
+
|
|
14
|
+
For security vulnerabilities, do not open a public GitHub issue — contact the
|
|
15
|
+
Modus team through your existing support channel.
|
|
16
|
+
|
|
17
|
+
## Questions & Support
|
|
18
|
+
|
|
19
|
+
For usage questions and general support, use the
|
|
20
|
+
[Modus community forum](https://modus.com) or email **support@modus.com**.
|
|
21
|
+
|
|
22
|
+
## Pull Requests
|
|
23
|
+
|
|
24
|
+
This repository is a **release mirror** of the Python SDK package. Pull requests
|
|
25
|
+
are not accepted here.
|
|
26
|
+
|
|
27
|
+
Development happens in the Modus monorepo. If you have a proposed fix or feature,
|
|
28
|
+
open an issue in this repository and the team will route it internally.
|
|
29
|
+
|
|
30
|
+
## Running tests (from a source checkout)
|
|
31
|
+
|
|
32
|
+
If you have cloned this mirror with the `tests/` directory:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cd modus-sdk # repository root after mirror split
|
|
36
|
+
python -m venv .venv && source .venv/bin/activate
|
|
37
|
+
pip install -e ".[dev]"
|
|
38
|
+
pytest tests/ -m "not integration"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Optional live API smoke tests (require a valid token and reachable API):
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
export MODUS_API_KEY=modus_xxx
|
|
45
|
+
pytest tests/integration/ -m integration
|
|
46
|
+
```
|
modus_sdk-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Modus
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
modus_sdk-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modus-sdk
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Official Python client for Modus — your organization's context layer for AI
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: ai,data,modus,sdk
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Requires-Dist: httpx<1.0.0,>=0.27.0
|
|
10
|
+
Requires-Dist: pydantic<3.0.0,>=2.0.0
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: datamodel-code-generator<0.61,>=0.60.0; extra == 'dev'
|
|
13
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest>=9.0.3; extra == 'dev'
|
|
17
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
18
|
+
Requires-Dist: ruff>=0.15; extra == 'dev'
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
<p align="center">
|
|
22
|
+
<img
|
|
23
|
+
src="https://raw.githubusercontent.com/modus-data/modus-sdk-python/main/assets/modus-logo.png"
|
|
24
|
+
alt="Modus"
|
|
25
|
+
width="280"
|
|
26
|
+
/>
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
# Modus Python SDK
|
|
30
|
+
|
|
31
|
+
[](https://pypi.org/project/modus-sdk/)
|
|
32
|
+
[](https://pypi.org/project/modus-sdk/)
|
|
33
|
+
[](https://opensource.org/licenses/MIT)
|
|
34
|
+
|
|
35
|
+
Official Python client for Modus — your organization's context layer for AI.
|
|
36
|
+
|
|
37
|
+
## What is Modus?
|
|
38
|
+
|
|
39
|
+
Modus is your organization's context layer: it connects assistants to curated knowledge about your data, systems, and workflows so answers use org-specific context instead of generic guesses.
|
|
40
|
+
|
|
41
|
+
- **Modus** — your org-wide assistant (same capability as the Modus home page)
|
|
42
|
+
- **Scopes** — published assistants you chat with, each with its own context and tools
|
|
43
|
+
- **Workflows** — automations that run on a schedule or trigger
|
|
44
|
+
- **Context items** — curated knowledge Modus composes at runtime when answering
|
|
45
|
+
|
|
46
|
+
## Two clients in one package
|
|
47
|
+
|
|
48
|
+
**`modus.Modus()`** is what most people need. Use it to chat with Modus, run scopes, browse context, and inspect workflow runs — the same things you do in the Modus app day to day.
|
|
49
|
+
|
|
50
|
+
**`modus.management.ModusManagement()`** is for org setup: create, update, and deploy scopes, workflows, and context — the CRUD operations you would use in the Modus UI as an admin.
|
|
51
|
+
|
|
52
|
+
If you are getting started, use `Modus()` only. Reach for `ModusManagement()` when you are automating configuration.
|
|
53
|
+
|
|
54
|
+
Async variants: `AsyncModus` and `AsyncModusManagement`.
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install modus-sdk
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Requires Python 3.10+.
|
|
63
|
+
|
|
64
|
+
## Getting started
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import modus
|
|
68
|
+
|
|
69
|
+
# Reads MODUS_API_KEY env var, or pass api_key= explicitly
|
|
70
|
+
client = modus.Modus()
|
|
71
|
+
|
|
72
|
+
for scope in client.scopes.list():
|
|
73
|
+
print(scope.name, scope.status)
|
|
74
|
+
|
|
75
|
+
scope = client.scopes.get("revenue-analysis")
|
|
76
|
+
print(scope.name, scope.description)
|
|
77
|
+
|
|
78
|
+
for item in client.context.items.list():
|
|
79
|
+
print(item.uid, item.context_type, modus.context_item_label(item))
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Authentication
|
|
83
|
+
|
|
84
|
+
Create an API token in the Modus app (**Settings → API Tokens** on your Modus home page). Prefer an environment variable over hardcoding keys in source:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
export MODUS_API_KEY=modus_xxx
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
client = modus.Modus(api_key="modus_xxx")
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Using Modus
|
|
95
|
+
|
|
96
|
+
### Modus vs scopes
|
|
97
|
+
|
|
98
|
+
| Surface | SDK | Use when |
|
|
99
|
+
|---------|-----|----------|
|
|
100
|
+
| **Modus** (org-wide) | `client.modus.*` | Full-environment assistant — same as the Modus home page |
|
|
101
|
+
| **Scope** | `client.scopes.*` | A specific published scope and its configured context/tools |
|
|
102
|
+
|
|
103
|
+
Use `client.modus.chat()` for native Modus — not `scopes.chat(0)` or any other scope id shortcut.
|
|
104
|
+
|
|
105
|
+
### Org-wide Modus assistant
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
MODEL = "claude-sonnet-5"
|
|
109
|
+
|
|
110
|
+
result = client.modus.chat("What were our top revenue drivers last quarter?", model=MODEL)
|
|
111
|
+
print(result.content, result.thread_id)
|
|
112
|
+
|
|
113
|
+
follow_up = client.modus.chat(
|
|
114
|
+
"Break that down by region.",
|
|
115
|
+
model=MODEL,
|
|
116
|
+
thread_id=result.thread_id,
|
|
117
|
+
)
|
|
118
|
+
print(follow_up.content)
|
|
119
|
+
|
|
120
|
+
with client.modus.chat_stream("Summarize this week", model=MODEL) as stream:
|
|
121
|
+
for chunk in stream.text_stream:
|
|
122
|
+
print(chunk, end="")
|
|
123
|
+
|
|
124
|
+
ctx = client.modus.get_context("What tables describe customer churn?", limit=10)
|
|
125
|
+
print(ctx.original_count, ctx.session_id)
|
|
126
|
+
|
|
127
|
+
for row in client.modus.conversations.list(kind="modus", page_size=10):
|
|
128
|
+
print(row.thread_id, row.first_message)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Scopes
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
MODEL = "claude-sonnet-5"
|
|
135
|
+
|
|
136
|
+
result = client.scopes.chat(scope_id, "Hello", model=MODEL)
|
|
137
|
+
print(result.content, result.thread_id)
|
|
138
|
+
|
|
139
|
+
with client.scopes.chat_stream(scope_id, "Hi", model=MODEL) as stream:
|
|
140
|
+
for chunk in stream.text_stream:
|
|
141
|
+
print(chunk, end="")
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Context
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
for item in client.context.items.list():
|
|
148
|
+
print(item.uid, modus.context_item_label(item))
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Workflows
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
workflow = client.workflows.get(workflow_id)
|
|
155
|
+
for run in client.workflows.runs.list(workflow_id, page_size=20):
|
|
156
|
+
print(run.id, run.status)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Workflow chat is not on the public PAT surface. Use `client.modus.chat()` for org-wide conversation or `client.scopes.chat()` for a published scope.
|
|
160
|
+
|
|
161
|
+
## Managing your org
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
from modus.management import ModusManagement
|
|
165
|
+
|
|
166
|
+
mgmt = ModusManagement() # reads MODUS_API_KEY
|
|
167
|
+
scope = mgmt.scopes.create(name="Analyst", model="claude-sonnet-5")
|
|
168
|
+
mgmt.scopes.deploy(scope.id)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Requires a token with write access to scopes and workflows (same as the Modus UI).
|
|
172
|
+
|
|
173
|
+
## Pagination
|
|
174
|
+
|
|
175
|
+
List endpoints return a `Page[T]`. Iterate the current page, or use `.auto_paging_iter()` for all pages:
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
for scope in client.scopes.list().auto_paging_iter():
|
|
179
|
+
print(scope.name)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`client.modus.conversations.list()` accepts `kind="modus"`, `"skills"`, or `"all"` (default).
|
|
183
|
+
|
|
184
|
+
## Async
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
import asyncio
|
|
188
|
+
import modus
|
|
189
|
+
|
|
190
|
+
async def main():
|
|
191
|
+
async with modus.AsyncModus() as client:
|
|
192
|
+
result = await client.modus.chat("Hello", model="claude-sonnet-5")
|
|
193
|
+
print(result.content)
|
|
194
|
+
|
|
195
|
+
async for scope in client.scopes.list().auto_paging_iter():
|
|
196
|
+
print(scope.name)
|
|
197
|
+
|
|
198
|
+
asyncio.run(main())
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Error handling
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
try:
|
|
205
|
+
scope = client.scopes.get(999)
|
|
206
|
+
except modus.NotFoundError:
|
|
207
|
+
print("Scope not found")
|
|
208
|
+
except modus.AuthenticationError:
|
|
209
|
+
print("Check your MODUS_API_KEY")
|
|
210
|
+
except modus.RateLimitError as e:
|
|
211
|
+
print(f"Rate limited. Retry after {e.retry_after}s")
|
|
212
|
+
except modus.ModusError as e:
|
|
213
|
+
print(f"API error {e.status_code}: {e.message}")
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Examples
|
|
217
|
+
|
|
218
|
+
Runnable scripts ship with the package:
|
|
219
|
+
|
|
220
|
+
- `examples/scripts/quickstart.py` — list scopes and context (`Modus`)
|
|
221
|
+
- `examples/scripts/modus_chat.py` — buffered and streaming Modus chat, context compose, conversations
|
|
222
|
+
- `examples/scripts/chat.py` — buffered scope chat with thread follow-up
|
|
223
|
+
- `examples/scripts/manage_skill.py` — create and deploy a scope (`ModusManagement`, `--write` optional)
|
|
224
|
+
|
|
225
|
+
Optional Streamlit demo: `examples/apps/modus_skill_chat/` (streaming vs buffered scope chat side by side).
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img
|
|
3
|
+
src="https://raw.githubusercontent.com/modus-data/modus-sdk-python/main/assets/modus-logo.png"
|
|
4
|
+
alt="Modus"
|
|
5
|
+
width="280"
|
|
6
|
+
/>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
# Modus Python SDK
|
|
10
|
+
|
|
11
|
+
[](https://pypi.org/project/modus-sdk/)
|
|
12
|
+
[](https://pypi.org/project/modus-sdk/)
|
|
13
|
+
[](https://opensource.org/licenses/MIT)
|
|
14
|
+
|
|
15
|
+
Official Python client for Modus — your organization's context layer for AI.
|
|
16
|
+
|
|
17
|
+
## What is Modus?
|
|
18
|
+
|
|
19
|
+
Modus is your organization's context layer: it connects assistants to curated knowledge about your data, systems, and workflows so answers use org-specific context instead of generic guesses.
|
|
20
|
+
|
|
21
|
+
- **Modus** — your org-wide assistant (same capability as the Modus home page)
|
|
22
|
+
- **Scopes** — published assistants you chat with, each with its own context and tools
|
|
23
|
+
- **Workflows** — automations that run on a schedule or trigger
|
|
24
|
+
- **Context items** — curated knowledge Modus composes at runtime when answering
|
|
25
|
+
|
|
26
|
+
## Two clients in one package
|
|
27
|
+
|
|
28
|
+
**`modus.Modus()`** is what most people need. Use it to chat with Modus, run scopes, browse context, and inspect workflow runs — the same things you do in the Modus app day to day.
|
|
29
|
+
|
|
30
|
+
**`modus.management.ModusManagement()`** is for org setup: create, update, and deploy scopes, workflows, and context — the CRUD operations you would use in the Modus UI as an admin.
|
|
31
|
+
|
|
32
|
+
If you are getting started, use `Modus()` only. Reach for `ModusManagement()` when you are automating configuration.
|
|
33
|
+
|
|
34
|
+
Async variants: `AsyncModus` and `AsyncModusManagement`.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install modus-sdk
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Requires Python 3.10+.
|
|
43
|
+
|
|
44
|
+
## Getting started
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import modus
|
|
48
|
+
|
|
49
|
+
# Reads MODUS_API_KEY env var, or pass api_key= explicitly
|
|
50
|
+
client = modus.Modus()
|
|
51
|
+
|
|
52
|
+
for scope in client.scopes.list():
|
|
53
|
+
print(scope.name, scope.status)
|
|
54
|
+
|
|
55
|
+
scope = client.scopes.get("revenue-analysis")
|
|
56
|
+
print(scope.name, scope.description)
|
|
57
|
+
|
|
58
|
+
for item in client.context.items.list():
|
|
59
|
+
print(item.uid, item.context_type, modus.context_item_label(item))
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Authentication
|
|
63
|
+
|
|
64
|
+
Create an API token in the Modus app (**Settings → API Tokens** on your Modus home page). Prefer an environment variable over hardcoding keys in source:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
export MODUS_API_KEY=modus_xxx
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
client = modus.Modus(api_key="modus_xxx")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Using Modus
|
|
75
|
+
|
|
76
|
+
### Modus vs scopes
|
|
77
|
+
|
|
78
|
+
| Surface | SDK | Use when |
|
|
79
|
+
|---------|-----|----------|
|
|
80
|
+
| **Modus** (org-wide) | `client.modus.*` | Full-environment assistant — same as the Modus home page |
|
|
81
|
+
| **Scope** | `client.scopes.*` | A specific published scope and its configured context/tools |
|
|
82
|
+
|
|
83
|
+
Use `client.modus.chat()` for native Modus — not `scopes.chat(0)` or any other scope id shortcut.
|
|
84
|
+
|
|
85
|
+
### Org-wide Modus assistant
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
MODEL = "claude-sonnet-5"
|
|
89
|
+
|
|
90
|
+
result = client.modus.chat("What were our top revenue drivers last quarter?", model=MODEL)
|
|
91
|
+
print(result.content, result.thread_id)
|
|
92
|
+
|
|
93
|
+
follow_up = client.modus.chat(
|
|
94
|
+
"Break that down by region.",
|
|
95
|
+
model=MODEL,
|
|
96
|
+
thread_id=result.thread_id,
|
|
97
|
+
)
|
|
98
|
+
print(follow_up.content)
|
|
99
|
+
|
|
100
|
+
with client.modus.chat_stream("Summarize this week", model=MODEL) as stream:
|
|
101
|
+
for chunk in stream.text_stream:
|
|
102
|
+
print(chunk, end="")
|
|
103
|
+
|
|
104
|
+
ctx = client.modus.get_context("What tables describe customer churn?", limit=10)
|
|
105
|
+
print(ctx.original_count, ctx.session_id)
|
|
106
|
+
|
|
107
|
+
for row in client.modus.conversations.list(kind="modus", page_size=10):
|
|
108
|
+
print(row.thread_id, row.first_message)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Scopes
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
MODEL = "claude-sonnet-5"
|
|
115
|
+
|
|
116
|
+
result = client.scopes.chat(scope_id, "Hello", model=MODEL)
|
|
117
|
+
print(result.content, result.thread_id)
|
|
118
|
+
|
|
119
|
+
with client.scopes.chat_stream(scope_id, "Hi", model=MODEL) as stream:
|
|
120
|
+
for chunk in stream.text_stream:
|
|
121
|
+
print(chunk, end="")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Context
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
for item in client.context.items.list():
|
|
128
|
+
print(item.uid, modus.context_item_label(item))
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Workflows
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
workflow = client.workflows.get(workflow_id)
|
|
135
|
+
for run in client.workflows.runs.list(workflow_id, page_size=20):
|
|
136
|
+
print(run.id, run.status)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Workflow chat is not on the public PAT surface. Use `client.modus.chat()` for org-wide conversation or `client.scopes.chat()` for a published scope.
|
|
140
|
+
|
|
141
|
+
## Managing your org
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from modus.management import ModusManagement
|
|
145
|
+
|
|
146
|
+
mgmt = ModusManagement() # reads MODUS_API_KEY
|
|
147
|
+
scope = mgmt.scopes.create(name="Analyst", model="claude-sonnet-5")
|
|
148
|
+
mgmt.scopes.deploy(scope.id)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Requires a token with write access to scopes and workflows (same as the Modus UI).
|
|
152
|
+
|
|
153
|
+
## Pagination
|
|
154
|
+
|
|
155
|
+
List endpoints return a `Page[T]`. Iterate the current page, or use `.auto_paging_iter()` for all pages:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
for scope in client.scopes.list().auto_paging_iter():
|
|
159
|
+
print(scope.name)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`client.modus.conversations.list()` accepts `kind="modus"`, `"skills"`, or `"all"` (default).
|
|
163
|
+
|
|
164
|
+
## Async
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
import asyncio
|
|
168
|
+
import modus
|
|
169
|
+
|
|
170
|
+
async def main():
|
|
171
|
+
async with modus.AsyncModus() as client:
|
|
172
|
+
result = await client.modus.chat("Hello", model="claude-sonnet-5")
|
|
173
|
+
print(result.content)
|
|
174
|
+
|
|
175
|
+
async for scope in client.scopes.list().auto_paging_iter():
|
|
176
|
+
print(scope.name)
|
|
177
|
+
|
|
178
|
+
asyncio.run(main())
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Error handling
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
try:
|
|
185
|
+
scope = client.scopes.get(999)
|
|
186
|
+
except modus.NotFoundError:
|
|
187
|
+
print("Scope not found")
|
|
188
|
+
except modus.AuthenticationError:
|
|
189
|
+
print("Check your MODUS_API_KEY")
|
|
190
|
+
except modus.RateLimitError as e:
|
|
191
|
+
print(f"Rate limited. Retry after {e.retry_after}s")
|
|
192
|
+
except modus.ModusError as e:
|
|
193
|
+
print(f"API error {e.status_code}: {e.message}")
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Examples
|
|
197
|
+
|
|
198
|
+
Runnable scripts ship with the package:
|
|
199
|
+
|
|
200
|
+
- `examples/scripts/quickstart.py` — list scopes and context (`Modus`)
|
|
201
|
+
- `examples/scripts/modus_chat.py` — buffered and streaming Modus chat, context compose, conversations
|
|
202
|
+
- `examples/scripts/chat.py` — buffered scope chat with thread follow-up
|
|
203
|
+
- `examples/scripts/manage_skill.py` — create and deploy a scope (`ModusManagement`, `--write` optional)
|
|
204
|
+
|
|
205
|
+
Optional Streamlit demo: `examples/apps/modus_skill_chat/` (streaming vs buffered scope chat side by side).
|
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg width="285" height="61" viewBox="0 0 285 61" fill="none" color="#3262FF" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g clip-path="url(#clip0_286_163)">
|
|
3
|
+
<path d="M55.2983 42.8834C54.751 42.8368 54.2105 42.808 53.6606 42.7975C43.9042 42.4159 33.1793 49.1184 31.4067 59.7994C31.3068 60.3796 31.2328 60.9615 31.186 61.5553H24.4956C24.4488 60.9615 24.3748 60.3796 24.2749 59.7994C22.5023 49.1183 11.7775 42.4158 2.021 42.7975C1.4711 42.808 0.930604 42.8368 0.383301 42.8834V33.9069C1.12817 33.9537 1.85781 34.0252 2.59521 34.1207C15.2942 35.53 26.8692 45.713 27.8403 58.0631C28.8114 45.7131 40.3875 35.5301 53.0864 34.1207C53.8238 34.0252 54.5535 33.9537 55.2983 33.9069V42.8834Z" fill="currentColor"/>
|
|
4
|
+
<path d="M55.301 18.2284C54.7538 18.2751 54.2131 18.3038 53.6633 18.3143C43.9068 18.696 33.182 11.9935 31.4094 1.31238C31.3095 0.732203 31.2355 0.15036 31.1887 -0.443481H24.4983C24.4515 0.150316 24.3775 0.732243 24.2776 1.31238C22.505 11.9934 11.7802 18.6959 2.02368 18.3143C1.47379 18.3038 0.933275 18.2751 0.385986 18.2284V27.205C1.13082 27.1581 1.86052 27.0867 2.5979 26.9911C15.2955 25.5819 26.8698 15.4008 27.843 3.05261C28.8162 15.4009 40.3914 25.582 53.0891 26.9911C53.8264 27.0866 54.5562 27.1581 55.301 27.205V18.2284Z" fill="currentColor"/>
|
|
5
|
+
<path d="M27.8416 15.1049C27.8416 15.1049 29.1317 23.1704 32.1291 26.1678C35.1265 29.1651 43.192 30.4553 43.192 30.4553C43.192 30.4553 35.1265 31.7455 32.1291 34.7428C29.1317 37.7402 27.8416 45.8057 27.8416 45.8057C27.8416 45.8057 26.5514 37.7402 23.554 34.7428C20.5567 31.7455 12.4911 30.4553 12.4911 30.4553C12.4911 30.4553 20.5567 29.1651 23.554 26.1678C26.5514 23.1704 27.8416 15.1049 27.8416 15.1049Z" fill="currentColor"/>
|
|
6
|
+
<path d="M69.9199 53.5372V18.4094H76.2005V22.7183C78.2454 19.0667 81.4587 17.387 85.7675 17.387C90.5876 17.387 94.1661 19.724 95.8458 23.6677C98.1097 19.4319 101.761 17.387 106.362 17.387C114.469 17.387 118.924 22.2801 118.924 31.0438V53.5372H111.913V31.4089C111.913 26.5159 109.576 23.8137 105.121 23.8137C100.958 23.8137 97.9637 27.0271 97.9637 31.628V53.5372H90.8797V31.4089C90.8797 26.5159 88.5427 23.8137 84.2339 23.8137C79.9981 23.8137 77.0039 27.0271 77.0039 31.628V53.5372H69.9199ZM145.316 54.5597C134.653 54.5597 127.204 47.1105 127.204 35.9368C127.204 24.8361 134.653 17.387 145.316 17.387C156.051 17.387 163.427 24.8361 163.427 35.9368C163.427 47.1105 156.051 54.5597 145.316 54.5597ZM134.507 35.9368C134.507 43.2399 138.889 48.133 145.316 48.133C151.815 48.133 156.124 43.2399 156.124 35.9368C156.124 28.7068 151.815 23.8137 145.316 23.8137C138.889 23.8137 134.507 28.7068 134.507 35.9368ZM186.865 54.5597C176.86 54.5597 169.995 47.1105 169.995 35.9368C169.995 24.8361 176.86 17.387 186.865 17.387C191.978 17.387 196.14 19.2858 198.331 22.7183V2.41571H205.342V53.5372H199.062V48.6442C196.725 52.3687 192.343 54.5597 186.865 54.5597ZM177.298 35.9368C177.298 43.2399 181.607 48.133 187.961 48.133C194.315 48.133 198.477 43.2399 198.477 35.9368C198.477 28.7068 194.315 23.8137 187.961 23.8137C181.607 23.8137 177.298 28.7068 177.298 35.9368ZM229.692 54.5597C221.148 54.5597 215.89 49.3014 215.89 39.5884V18.4094H222.974V38.9311C222.974 45.2117 225.895 48.133 231.007 48.133C236.338 48.133 239.771 44.3354 239.771 38.712V18.4094H246.855V53.5372H240.574V49.5205C238.237 52.953 234.22 54.5597 229.692 54.5597ZM270.113 54.5597C260.619 54.5597 254.996 50.7621 254.631 43.532H261.934C262.299 47.0375 265.147 48.7902 270.259 48.7902C274.933 48.7902 277.781 46.8184 277.781 43.605C277.781 40.9759 275.81 39.4423 272.158 39.0771L266.535 38.4929C259.962 37.8356 255.799 34.1841 255.799 28.3416C255.799 21.6228 261.422 17.387 270.04 17.387C278.804 17.387 284.208 21.0385 284.646 28.1956H277.562C277.343 24.8361 274.568 23.1564 269.967 23.1564C265.585 23.1564 262.81 24.9822 262.81 27.9765C262.81 30.5325 264.709 31.9201 268.799 32.4313L274.495 33.0886C281.141 33.8189 284.938 37.5435 284.938 43.3129C284.938 50.1778 279.169 54.5597 270.113 54.5597Z" fill="currentColor"/>
|
|
7
|
+
</g>
|
|
8
|
+
<defs>
|
|
9
|
+
<clipPath id="clip0_286_163">
|
|
10
|
+
<rect width="285" height="61" fill="currentColor"/>
|
|
11
|
+
</clipPath>
|
|
12
|
+
</defs>
|
|
13
|
+
</svg>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Modus Skill Chat (Streamlit)
|
|
2
|
+
|
|
3
|
+
Optional interactive demo: compare **streaming** (`client.skills.chat_stream()`) vs **buffered** (`client.skills.chat()`) skill chat side by side.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
Create an API token in the Modus app (**Settings → API Tokens** on your Modus home page), then export it in your shell (the app does **not** read `.env` files):
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
export MODUS_API_KEY=modus_xxx
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Run
|
|
14
|
+
|
|
15
|
+
From this directory:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install modus-sdk streamlit
|
|
19
|
+
streamlit run app.py
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Open the URL Streamlit prints (default `http://localhost:8501`).
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
- Pick an **active** skill and model in the sidebar.
|
|
27
|
+
- Type a message at the bottom — it is sent to **both** panels in parallel.
|
|
28
|
+
- Left: tokens arrive as they stream. Right: the full reply appears when complete.
|