magickmind 0.1.1__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.
- magickmind-0.1.1/.env.example +4 -0
- magickmind-0.1.1/.github/workflows/ci.yml +87 -0
- magickmind-0.1.1/.gitignore +26 -0
- magickmind-0.1.1/.gitrepo +12 -0
- magickmind-0.1.1/.python-version +1 -0
- magickmind-0.1.1/AGENTS.md +270 -0
- magickmind-0.1.1/CHANGELOG.md +19 -0
- magickmind-0.1.1/CONTRIBUTING.md +74 -0
- magickmind-0.1.1/GETTING_STARTED.md +111 -0
- magickmind-0.1.1/PKG-INFO +593 -0
- magickmind-0.1.1/README.md +559 -0
- magickmind-0.1.1/back_uppyproject.toml +58 -0
- magickmind-0.1.1/magick_mind/__init__.py +39 -0
- magickmind-0.1.1/magick_mind/auth/__init__.py +9 -0
- magickmind-0.1.1/magick_mind/auth/base.py +46 -0
- magickmind-0.1.1/magick_mind/auth/email_password.py +268 -0
- magickmind-0.1.1/magick_mind/client.py +188 -0
- magickmind-0.1.1/magick_mind/config.py +28 -0
- magickmind-0.1.1/magick_mind/exceptions.py +107 -0
- magickmind-0.1.1/magick_mind/http/__init__.py +5 -0
- magickmind-0.1.1/magick_mind/http/client.py +313 -0
- magickmind-0.1.1/magick_mind/models/__init__.py +17 -0
- magickmind-0.1.1/magick_mind/models/auth.py +30 -0
- magickmind-0.1.1/magick_mind/models/common.py +32 -0
- magickmind-0.1.1/magick_mind/models/errors.py +73 -0
- magickmind-0.1.1/magick_mind/models/v1/__init__.py +83 -0
- magickmind-0.1.1/magick_mind/models/v1/api_keys.py +115 -0
- magickmind-0.1.1/magick_mind/models/v1/artifact.py +151 -0
- magickmind-0.1.1/magick_mind/models/v1/chat.py +104 -0
- magickmind-0.1.1/magick_mind/models/v1/corpus.py +82 -0
- magickmind-0.1.1/magick_mind/models/v1/end_user.py +75 -0
- magickmind-0.1.1/magick_mind/models/v1/history.py +94 -0
- magickmind-0.1.1/magick_mind/models/v1/mindspace.py +130 -0
- magickmind-0.1.1/magick_mind/models/v1/model.py +25 -0
- magickmind-0.1.1/magick_mind/models/v1/project.py +73 -0
- magickmind-0.1.1/magick_mind/realtime/__init__.py +5 -0
- magickmind-0.1.1/magick_mind/realtime/client.py +202 -0
- magickmind-0.1.1/magick_mind/realtime/handler.py +122 -0
- magickmind-0.1.1/magick_mind/resources/README.md +201 -0
- magickmind-0.1.1/magick_mind/resources/__init__.py +42 -0
- magickmind-0.1.1/magick_mind/resources/base.py +31 -0
- magickmind-0.1.1/magick_mind/resources/v1/__init__.py +19 -0
- magickmind-0.1.1/magick_mind/resources/v1/api_keys.py +181 -0
- magickmind-0.1.1/magick_mind/resources/v1/artifact.py +287 -0
- magickmind-0.1.1/magick_mind/resources/v1/chat.py +120 -0
- magickmind-0.1.1/magick_mind/resources/v1/corpus.py +156 -0
- magickmind-0.1.1/magick_mind/resources/v1/end_user.py +181 -0
- magickmind-0.1.1/magick_mind/resources/v1/history.py +88 -0
- magickmind-0.1.1/magick_mind/resources/v1/mindspace.py +331 -0
- magickmind-0.1.1/magick_mind/resources/v1/model.py +19 -0
- magickmind-0.1.1/magick_mind/resources/v1/project.py +155 -0
- magickmind-0.1.1/magick_mind/routes.py +76 -0
- magickmind-0.1.1/main.py +6 -0
- magickmind-0.1.1/pyproject.toml +70 -0
- magickmind-0.1.1/specs/openapi.dev.json +4084 -0
- magickmind-0.1.1/specs/openapi.main.json +4084 -0
- magickmind-0.1.1/specs/openapi.new.json +1909 -0
- magickmind-0.1.1/specs/openapi.parsed.json +1873 -0
- magickmind-0.1.1/uv.lock +1037 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: AGD Magick Mind CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
name: Format and/or Lint with Ruff and Push Changes
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout code
|
|
15
|
+
uses: actions/checkout@v4
|
|
16
|
+
with:
|
|
17
|
+
# 'ref:' determines which branch, tag, or commit the action checks out.
|
|
18
|
+
# For pull request events, 'github.head_ref' contains the source branch of the PR.
|
|
19
|
+
# For push events, 'github.head_ref' is empty, so we fall back to 'github.ref',
|
|
20
|
+
# which contains the full ref (e.g., "refs/heads/main").
|
|
21
|
+
ref: ${{ github.head_ref || github.ref }}
|
|
22
|
+
|
|
23
|
+
- name: Install and Format with Ruff
|
|
24
|
+
uses: astral-sh/ruff-action@v3
|
|
25
|
+
with:
|
|
26
|
+
args: "format"
|
|
27
|
+
# args: "check --fix"
|
|
28
|
+
# - run: ruff format
|
|
29
|
+
- name: Check for modified files
|
|
30
|
+
id: git_status
|
|
31
|
+
run: echo "modified=$(if git diff-index --quiet HEAD --; then echo "false"; else echo "true"; fi)" >> $GITHUB_OUTPUT
|
|
32
|
+
# run: |
|
|
33
|
+
# if git diff-index --quiet HEAD; then
|
|
34
|
+
# echo "::set-output name=modified::true"
|
|
35
|
+
# else
|
|
36
|
+
# echo "::set-output name=modified::false"
|
|
37
|
+
# fi
|
|
38
|
+
# - name: Debug set-output state
|
|
39
|
+
# run: |
|
|
40
|
+
# echo steps.git_status.outputs.modified
|
|
41
|
+
# echo steps.git_status.outputs.modified == 'true'
|
|
42
|
+
# - name: Ensure branch is checked out
|
|
43
|
+
# run: |
|
|
44
|
+
# # Determine branch name based on event type
|
|
45
|
+
# if [ -n "${GITHUB_HEAD_REF}" ]; then
|
|
46
|
+
# # Pull Request event: use head ref (source branch)
|
|
47
|
+
# branch="${GITHUB_HEAD_REF}"
|
|
48
|
+
# echo "Detected a pull request. Using head ref: $branch"
|
|
49
|
+
# # Fetch the branch from the remote if it isn’t available locally.
|
|
50
|
+
# git fetch origin "${branch}:${branch}"
|
|
51
|
+
# elif [[ "${GITHUB_REF}" == refs/heads/* ]]; then
|
|
52
|
+
# # Push event: extract branch name from GITHUB_REF
|
|
53
|
+
# branch="${GITHUB_REF#refs/heads/}"
|
|
54
|
+
# echo "Detected a push event on branch: $branch"
|
|
55
|
+
# else
|
|
56
|
+
# echo "Not a branch event (likely a tag); skipping commit step."
|
|
57
|
+
# exit 0
|
|
58
|
+
# fi
|
|
59
|
+
# echo "Switching to branch: $branch"
|
|
60
|
+
# git switch $branch
|
|
61
|
+
|
|
62
|
+
- name: Push changes
|
|
63
|
+
if: steps.git_status.outputs.modified == 'true'
|
|
64
|
+
run: |
|
|
65
|
+
git config --global user.name "github-actions[bot]"
|
|
66
|
+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
67
|
+
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
|
|
68
|
+
git commit -am "CI Workflow: Style fixes by Ruff"
|
|
69
|
+
git push
|
|
70
|
+
# test:
|
|
71
|
+
# name: Run Tests
|
|
72
|
+
# runs-on: ubuntu-latest
|
|
73
|
+
# steps:
|
|
74
|
+
# - name: Checkout code
|
|
75
|
+
# uses: actions/checkout@v4
|
|
76
|
+
#
|
|
77
|
+
# - name: Set up Python
|
|
78
|
+
# uses: actions/setup-python@v4
|
|
79
|
+
# with:
|
|
80
|
+
# python-version: "3.x"
|
|
81
|
+
#
|
|
82
|
+
# - name: Install dependencies
|
|
83
|
+
# run: pip install -r requirements.txt
|
|
84
|
+
#
|
|
85
|
+
# - name: Run tests
|
|
86
|
+
# run: pytest
|
|
87
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
**/__pycache__/
|
|
2
|
+
.vscode/
|
|
3
|
+
*.tar
|
|
4
|
+
logs/
|
|
5
|
+
.idea/
|
|
6
|
+
tmp/
|
|
7
|
+
|
|
8
|
+
# StarUML temporary files
|
|
9
|
+
*.mdj.bak
|
|
10
|
+
*.mdj~
|
|
11
|
+
semantic_working_dir
|
|
12
|
+
*.log
|
|
13
|
+
|
|
14
|
+
# Environments
|
|
15
|
+
.env
|
|
16
|
+
.venv
|
|
17
|
+
env/
|
|
18
|
+
venv/
|
|
19
|
+
ENV/
|
|
20
|
+
env.bak/
|
|
21
|
+
venv.bak/
|
|
22
|
+
|
|
23
|
+
*.egg-info/
|
|
24
|
+
|
|
25
|
+
# Hypothesis Cache
|
|
26
|
+
.hypothesis/
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
; DO NOT EDIT (unless you know what you are doing)
|
|
2
|
+
;
|
|
3
|
+
; This subdirectory is a git "subrepo", and this file is maintained by the
|
|
4
|
+
; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme
|
|
5
|
+
;
|
|
6
|
+
[subrepo]
|
|
7
|
+
remote = git@github.com:General-Magick-Industries/AGD_Magick_Mind_SDK.git
|
|
8
|
+
branch = main
|
|
9
|
+
commit = 93b35d17bc15bb09d080da87d028101f23fe2ee8
|
|
10
|
+
parent = 34e5948f872404c27c1278d8c3ddb2116302a61c
|
|
11
|
+
method = rebase
|
|
12
|
+
cmdver = 0.4.9
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# SDK - Official MagickMind Python Client
|
|
2
|
+
|
|
3
|
+
> Python SDK for the Bifrost API gateway. For **service users** (backends/bots) to authenticate and interact with MagickMind on behalf of their end users.
|
|
4
|
+
|
|
5
|
+
**Python runtime required** - Uses `httpx` and `centrifuge-python`. Runs in Python backends, CLI, scripts, desktop apps.
|
|
6
|
+
|
|
7
|
+
## Architecture: Service Users vs End Users
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
11
|
+
│ YOUR SYSTEM │
|
|
12
|
+
│ ┌──────────────┐ ┌──────────────────────────────────────┐ │
|
|
13
|
+
│ │ Your End │────▶│ Your Backend (Service User) │ │
|
|
14
|
+
│ │ Users │ │ - Authenticates with Bifrost (JWT) │ │
|
|
15
|
+
│ │ (own auth) │◀────│ - Manages end users via end-user svc│ │
|
|
16
|
+
│ └──────────────┘ │ - Calls Bifrost with sender_id param│ │
|
|
17
|
+
│ └──────────────────────────────────────┘ │
|
|
18
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
19
|
+
│
|
|
20
|
+
▼ SDK / REST
|
|
21
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
22
|
+
│ MAGICKMIND │
|
|
23
|
+
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
|
|
24
|
+
│ │ Bifrost │──│ Xavier │──│ Cortex │──│ Centrifugo │ │
|
|
25
|
+
│ │ (gateway)│ │ (chat) │ │ (AI) │ │ (streaming) │ │
|
|
26
|
+
│ └──────────┘ └──────────┘ └──────────┘ └──────────────────┘ │
|
|
27
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Key concepts:**
|
|
31
|
+
- **Service User**: Your backend/bot that authenticates with Bifrost via JWT (email/password login)
|
|
32
|
+
- **End User**: YOUR users, managed by YOU. Created via `end-user` gRPC service. No direct Bifrost auth.
|
|
33
|
+
- **sender_id/user_id**: End user identifier passed in API calls (e.g., chat requests)
|
|
34
|
+
|
|
35
|
+
**Centrifugo channel format:** `personal:{end_user_id}#{service_account_id}`
|
|
36
|
+
- Service subscribes to channels for its end users
|
|
37
|
+
- Streams responses back to end users via YOUR transport (WebSocket, Telegram, etc.)
|
|
38
|
+
|
|
39
|
+
## Purpose & Responsibilities
|
|
40
|
+
|
|
41
|
+
- **Authentication**: Service user email/password → JWT with auto-refresh
|
|
42
|
+
- **HTTP Client**: Authenticated requests to Bifrost REST API
|
|
43
|
+
- **Typed Resources**: v1/chat, mindspace, artifact, end-user, etc. with Pydantic models
|
|
44
|
+
- **Real-time**: Centrifugo pub/sub for streaming responses to relay to your end users
|
|
45
|
+
- **End User Management**: Create/query/update end users scoped to your service account
|
|
46
|
+
|
|
47
|
+
## Key Files & Entry Points
|
|
48
|
+
|
|
49
|
+
| Need | Look Here |
|
|
50
|
+
|------|-----------|
|
|
51
|
+
| Main client | `magick_mind/client.py` - `MagickMind` class |
|
|
52
|
+
| HTTP client | `magick_mind/http/client.py` - authenticated requests |
|
|
53
|
+
| Real-time | `magick_mind/realtime/client.py` - Centrifugo subscriptions |
|
|
54
|
+
| Auth provider | `magick_mind/auth/email_password.py` - JWT login/refresh |
|
|
55
|
+
| Chat resource | `magick_mind/resources/v1/chat.py` |
|
|
56
|
+
| Mindspace resource | `magick_mind/resources/v1/mindspace.py` |
|
|
57
|
+
| Artifact resource | `magick_mind/resources/v1/artifact.py` |
|
|
58
|
+
| Request/Response models | `magick_mind/models/v1/` |
|
|
59
|
+
| Exceptions | `magick_mind/exceptions.py` |
|
|
60
|
+
| Examples | `examples/` - usage patterns |
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from magick_mind import MagickMind
|
|
66
|
+
|
|
67
|
+
# Initialize client as SERVICE USER (your backend)
|
|
68
|
+
client = MagickMind(
|
|
69
|
+
email="myservice@example.com", # Your service account
|
|
70
|
+
password="secret",
|
|
71
|
+
base_url="https://bifrost.magickmind.io"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Create/get an end user (YOUR user, e.g., from your auth system)
|
|
75
|
+
end_user = await client.v1.end_user.create_or_get(
|
|
76
|
+
external_id="your-user-123", # Your user ID
|
|
77
|
+
metadata={"name": "Alice"}
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Send chat message ON BEHALF OF your end user
|
|
81
|
+
response = await client.v1.chat.send(
|
|
82
|
+
mindspace_id="ms-123",
|
|
83
|
+
sender_id=end_user.id, # End user ID
|
|
84
|
+
message="Hello, world!"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Subscribe to Centrifugo for this end user's responses
|
|
88
|
+
class MyHandler:
|
|
89
|
+
async def on_message(self, user_id: str, payload: dict):
|
|
90
|
+
# Relay this to YOUR end user via your transport
|
|
91
|
+
print(f"Response for {user_id}: {payload}")
|
|
92
|
+
|
|
93
|
+
await client.realtime.connect(events=MyHandler())
|
|
94
|
+
await client.realtime.subscribe(end_user.id) # Subscribe for this end user
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Service-Specific Patterns
|
|
98
|
+
|
|
99
|
+
### Authentication (Auto-Refresh)
|
|
100
|
+
```python
|
|
101
|
+
# magick_mind/auth/email_password.py
|
|
102
|
+
# JWT refreshed automatically (expires_in - 10s buffer)
|
|
103
|
+
client = MagickMind(email="...", password="...")
|
|
104
|
+
# Token injected into all requests automatically
|
|
105
|
+
|
|
106
|
+
# Check auth status
|
|
107
|
+
if client.is_authenticated():
|
|
108
|
+
...
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### HTTP Requests
|
|
112
|
+
```python
|
|
113
|
+
# Low-level (magick_mind/http/client.py)
|
|
114
|
+
response = await client.http.request("GET", "/v1/chat/messages")
|
|
115
|
+
|
|
116
|
+
# High-level via resources
|
|
117
|
+
messages = await client.v1.mindspace.get_messages(
|
|
118
|
+
mindspace_id="ms-123",
|
|
119
|
+
after_id="msg-456" # For pagination/sync
|
|
120
|
+
)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Real-time Subscriptions
|
|
124
|
+
```python
|
|
125
|
+
# magick_mind/realtime/client.py
|
|
126
|
+
# Channel format: personal:{end_user_id}#{service_account_id}
|
|
127
|
+
# Service subscribes to channels for its end users
|
|
128
|
+
|
|
129
|
+
class EventHandler:
|
|
130
|
+
async def on_message(self, end_user_id: str, payload: dict):
|
|
131
|
+
# Relay to YOUR end user via your transport (WebSocket, Telegram, etc.)
|
|
132
|
+
your_websocket.send(end_user_id, payload)
|
|
133
|
+
|
|
134
|
+
async def on_error(self, error: Exception):
|
|
135
|
+
# Handle connection error
|
|
136
|
+
pass
|
|
137
|
+
|
|
138
|
+
await client.realtime.connect(events=EventHandler())
|
|
139
|
+
await client.realtime.subscribe(end_user_id="your-end-user-123")
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### History Sync Pattern
|
|
143
|
+
```python
|
|
144
|
+
# Get messages since last known, then subscribe for real-time
|
|
145
|
+
last_known_id = get_last_message_id_from_db()
|
|
146
|
+
messages = await client.v1.mindspace.get_messages(
|
|
147
|
+
mindspace_id="ms-123",
|
|
148
|
+
after_id=last_known_id
|
|
149
|
+
)
|
|
150
|
+
save_to_db(messages)
|
|
151
|
+
|
|
152
|
+
# Now subscribe for new messages for this end user
|
|
153
|
+
await client.realtime.subscribe(end_user_id="your-end-user-123")
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Error Handling
|
|
157
|
+
```python
|
|
158
|
+
from magick_mind.exceptions import (
|
|
159
|
+
MagickMindError, # Base exception
|
|
160
|
+
AuthenticationError, # Login failed
|
|
161
|
+
TokenExpiredError, # Token refresh failed
|
|
162
|
+
APIError, # API returned error
|
|
163
|
+
RateLimitError, # Rate limited
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
await client.v1.chat.send(...)
|
|
168
|
+
except AuthenticationError:
|
|
169
|
+
# Re-authenticate
|
|
170
|
+
except APIError as e:
|
|
171
|
+
print(f"API error: {e.message}, status: {e.status_code}")
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Testing
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# Run all tests
|
|
178
|
+
uv run pytest
|
|
179
|
+
|
|
180
|
+
# With coverage
|
|
181
|
+
uv run pytest --cov=magick_mind
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Test structure**:
|
|
185
|
+
```
|
|
186
|
+
tests/
|
|
187
|
+
├── conftest.py # Fixtures: mock_auth, test_client, schema_registry
|
|
188
|
+
├── test_auth.py # Auth provider tests
|
|
189
|
+
├── test_resources/ # Resource client tests
|
|
190
|
+
├── test_realtime/ # Real-time subscription tests
|
|
191
|
+
└── contract/ # OpenAPI contract validation
|
|
192
|
+
├── openapi.json # API schema
|
|
193
|
+
└── test_contracts.py # Payload validation
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Fixtures** (`conftest.py`):
|
|
197
|
+
- `mock_client` - httpx.MockTransport for HTTP mocking
|
|
198
|
+
- `authenticated_client` - Pre-authenticated MagickMind instance
|
|
199
|
+
- `mock_auth` - Mocked auth provider
|
|
200
|
+
- `schema_registry` - OpenAPI schema validation
|
|
201
|
+
|
|
202
|
+
**Mocking patterns**:
|
|
203
|
+
```python
|
|
204
|
+
# Mock auth
|
|
205
|
+
@patch("magick_mind.auth.email_password._login")
|
|
206
|
+
def test_auth(mock_login):
|
|
207
|
+
mock_login.return_value = {"access_token": "...", "expires_in": 3600}
|
|
208
|
+
...
|
|
209
|
+
|
|
210
|
+
# Mock HTTP with pytest-httpx
|
|
211
|
+
def test_chat(httpx_mock):
|
|
212
|
+
httpx_mock.add_response(json={"message_id": "123"})
|
|
213
|
+
...
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Configuration
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
from magick_mind import MagickMind
|
|
220
|
+
|
|
221
|
+
client = MagickMind(
|
|
222
|
+
email="service@example.com",
|
|
223
|
+
password="secret",
|
|
224
|
+
base_url="https://bifrost.magickmind.io", # Or from BIFROST_BASE_URL env
|
|
225
|
+
)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**Environment variables**:
|
|
229
|
+
```bash
|
|
230
|
+
BIFROST_BASE_URL=https://bifrost.magickmind.io
|
|
231
|
+
# Credentials typically passed directly, not via env
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Dependencies
|
|
235
|
+
|
|
236
|
+
Key packages from `pyproject.toml`:
|
|
237
|
+
- `httpx>=0.27` - HTTP client
|
|
238
|
+
- `pydantic>=2.0` - Models and validation
|
|
239
|
+
- `centrifuge>=0.9` - Real-time WebSocket
|
|
240
|
+
- `pydantic-settings` - Configuration
|
|
241
|
+
|
|
242
|
+
**Dev dependencies**:
|
|
243
|
+
- `pytest`, `pytest-httpx`, `pytest-asyncio` - Testing
|
|
244
|
+
|
|
245
|
+
## Common Commands
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
# Install dependencies
|
|
249
|
+
uv sync
|
|
250
|
+
|
|
251
|
+
# Run tests
|
|
252
|
+
uv run pytest -v
|
|
253
|
+
|
|
254
|
+
# Type check
|
|
255
|
+
uv run pyright
|
|
256
|
+
|
|
257
|
+
# Format
|
|
258
|
+
uv run ruff format .
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Gotchas
|
|
262
|
+
|
|
263
|
+
1. **Service users only** - This SDK authenticates service accounts (backends/bots), not end users. End users are managed via `end-user` service and passed as `sender_id`/`user_id` params.
|
|
264
|
+
2. **Python only** - Uses httpx/centrifuge-python; for other languages, use Bifrost REST directly
|
|
265
|
+
3. **Async preferred** - Most methods are async; use `asyncio.to_thread` for sync contexts
|
|
266
|
+
4. **Auto-refresh timing** - Token refreshes 10s before expiry; long-running ops may still fail
|
|
267
|
+
5. **Real-time before REST** - For chat, subscribe to channel BEFORE sending message to catch response
|
|
268
|
+
6. **Contract tests** - Tests validate against `tests/contract/openapi.json`; update if API changes
|
|
269
|
+
7. **Channel format** - `personal:{end_user_id}#{service_account_id}` - service subscribes for its end users
|
|
270
|
+
8. **No direct frontend access** - End users don't call Bifrost directly; your backend relays via this SDK
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.0.1] - 2026-01-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Realtime WebSocket Client**: Full support for real-time interaction in `magick_mind.realtime`.
|
|
12
|
+
- **Resources**:
|
|
13
|
+
- `Chat` and `History` resources for messaging.
|
|
14
|
+
- `Mindspace`, `Project`, `EndUser`, `Corpus` resources for management.
|
|
15
|
+
- **Artifacts**: Support for file uploads and attachment handling.
|
|
16
|
+
- **Examples**:
|
|
17
|
+
- Detailed `setup_resources.py` script.
|
|
18
|
+
- Complete examples for Realtime Chat and Backend integration.
|
|
19
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Contributing to Magick Mind SDK
|
|
2
|
+
|
|
3
|
+
## Development Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Clone and install
|
|
7
|
+
git clone <repo>
|
|
8
|
+
cd AGD_Magick_Mind_SDK
|
|
9
|
+
uv sync
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Running Tests
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# All tests
|
|
16
|
+
uv run pytest
|
|
17
|
+
|
|
18
|
+
# Contract tests only
|
|
19
|
+
uv run pytest -m contract -v
|
|
20
|
+
|
|
21
|
+
# Unit tests
|
|
22
|
+
uv run pytest tests/unit/ -v
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Contract Testing Workflow
|
|
26
|
+
|
|
27
|
+
We use serialization-based contract testing to ensure SDK models match the bifrost API.
|
|
28
|
+
|
|
29
|
+
### How It Works
|
|
30
|
+
|
|
31
|
+
1. **Spec snapshots** in `specs/` represent the API contract
|
|
32
|
+
2. **Tests** validate that SDK payloads match the spec
|
|
33
|
+
3. **Serializers** transform optional SDK fields to API-required defaults
|
|
34
|
+
|
|
35
|
+
### Updating Specs
|
|
36
|
+
|
|
37
|
+
When bifrost API changes:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# 1. Generate new spec in bifrost
|
|
41
|
+
cd ../bifrost
|
|
42
|
+
goctl api plugin -p goctl-openapi -api api/v1/*.api -dir .
|
|
43
|
+
|
|
44
|
+
# 2. Copy to SDK (dev or main depending on branch)
|
|
45
|
+
cp api/openapi.json ../AGD_Magick_Mind_SDK/specs/openapi.dev.json
|
|
46
|
+
|
|
47
|
+
# 3. Run contract tests
|
|
48
|
+
cd ../AGD_Magick_Mind_SDK
|
|
49
|
+
uv run pytest -m contract -v
|
|
50
|
+
|
|
51
|
+
# 4. Fix any failures by updating SDK models
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Adding New Models
|
|
55
|
+
|
|
56
|
+
1. Add model to `tests/contract/test_payloads.py` → `MODEL_REGISTRY`
|
|
57
|
+
2. Add instance creation in `create_minimal_instance()`
|
|
58
|
+
3. Add `@field_serializer` for optional fields if needed
|
|
59
|
+
|
|
60
|
+
See `tests/contract/README.md` for detailed instructions.
|
|
61
|
+
|
|
62
|
+
## Code Style
|
|
63
|
+
|
|
64
|
+
- Format: `uv run ruff format .`
|
|
65
|
+
- Lint: `uv run ruff check .`
|
|
66
|
+
- Type check: `uv run pyright`
|
|
67
|
+
|
|
68
|
+
## Pull Request Process
|
|
69
|
+
|
|
70
|
+
1. Create feature branch
|
|
71
|
+
2. Make changes
|
|
72
|
+
3. Run tests: `uv run pytest`
|
|
73
|
+
4. Run contract tests: `uv run pytest -m contract`
|
|
74
|
+
5. Submit PR
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Getting Started with Magick Mind SDK
|
|
2
|
+
|
|
3
|
+
A 10-minute guide to running your first examples.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Python 3.8+
|
|
8
|
+
- Access to a Bifrost instance
|
|
9
|
+
- Service credentials (email/password)
|
|
10
|
+
|
|
11
|
+
## Step 1: Install SDK
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cd AGD_Magick_Mind_SDK
|
|
15
|
+
pip install -e .
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Step 2: Set Environment Variables
|
|
19
|
+
|
|
20
|
+
**Option A: Using .env file** (Easier, persists across sessions)
|
|
21
|
+
|
|
22
|
+
1. Copy `test.env` to `.env`:
|
|
23
|
+
```bash
|
|
24
|
+
cp test.env .env
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
2. Edit `.env` with your credentials
|
|
28
|
+
3. Run examples using the helper script:
|
|
29
|
+
```bash
|
|
30
|
+
python run_example.py examples/chat_example.py
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The helper script loads `.env` automatically (no extra dependencies needed).
|
|
34
|
+
|
|
35
|
+
**Alternative: Source manually (Bash/zsh only)**
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
set -a; source .env; set +a
|
|
39
|
+
python examples/chat_example.py
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Option B: Export manually** (Quick for testing)
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Bash/zsh
|
|
46
|
+
export BIFROST_BASE_URL="http://localhost:8888"
|
|
47
|
+
export BIFROST_EMAIL="service@example.com"
|
|
48
|
+
export BIFROST_PASSWORD="your-password"
|
|
49
|
+
|
|
50
|
+
# Fish
|
|
51
|
+
set -gx BIFROST_BASE_URL "http://localhost:8888"
|
|
52
|
+
set -gx BIFROST_EMAIL "service@example.com"
|
|
53
|
+
set -gx BIFROST_PASSWORD "your-password"
|
|
54
|
+
|
|
55
|
+
python examples/chat_example.py
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Step 3: Try Examples (In Order)
|
|
59
|
+
|
|
60
|
+
### 1. Authentication (30 seconds)
|
|
61
|
+
```bash
|
|
62
|
+
python examples/email_password_auth.py
|
|
63
|
+
```
|
|
64
|
+
**What it does:** Connects to Bifrost and authenticates.
|
|
65
|
+
|
|
66
|
+
### 2. Chat Message (1 minute)
|
|
67
|
+
```bash
|
|
68
|
+
python examples/chat_example.py
|
|
69
|
+
```
|
|
70
|
+
**What it does:** Sends a message to an AI mindspace.
|
|
71
|
+
|
|
72
|
+
### 3. Message History (1 minute)
|
|
73
|
+
```bash
|
|
74
|
+
python examples/history_example.py
|
|
75
|
+
```
|
|
76
|
+
**What it does:** Fetches past messages with pagination.
|
|
77
|
+
|
|
78
|
+
### 4. Realtime Messages (2 minutes)
|
|
79
|
+
```bash
|
|
80
|
+
python examples/realtime_chat.py
|
|
81
|
+
```
|
|
82
|
+
**What it does:** Listens for AI responses in real-time.
|
|
83
|
+
|
|
84
|
+
### 5. Complete Workflow (5 minutes)
|
|
85
|
+
```bash
|
|
86
|
+
python examples/complete_chat_workflow.py
|
|
87
|
+
```
|
|
88
|
+
**What it does:** Full loop - send via HTTP, receive via realtime.
|
|
89
|
+
|
|
90
|
+
## What's Next?
|
|
91
|
+
|
|
92
|
+
**Explore by Category:**
|
|
93
|
+
- **CRUD**: `mindspace_example.py`, `project_example.py`, `corpus_example.py`
|
|
94
|
+
- **Backend Patterns**: `caching_example.py`, `notification_pattern_example.py`
|
|
95
|
+
- **Production**: `backend_service.py`, `fan_out_relay.py`
|
|
96
|
+
|
|
97
|
+
**Read Documentation:**
|
|
98
|
+
- [Backend Architecture](docs/architecture/backend_architecture.md)
|
|
99
|
+
- [Event-Driven Patterns](docs/architecture/event_driven_patterns.md)
|
|
100
|
+
- [Realtime Guide](docs/realtime_guide.md)
|
|
101
|
+
|
|
102
|
+
## Troubleshooting
|
|
103
|
+
|
|
104
|
+
**"Connection refused"**
|
|
105
|
+
→ Start Bifrost: `docker-compose up bifrost`
|
|
106
|
+
|
|
107
|
+
**"Invalid credentials"**
|
|
108
|
+
→ Check `BIFROST_EMAIL` and `BIFROST_PASSWORD` in your environment
|
|
109
|
+
|
|
110
|
+
**"Module not found"**
|
|
111
|
+
→ Run `pip install -e .` from SDK root directory
|