socialrobot 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hung-Chun Chang (MEME — Social Robot Platform)
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.
@@ -0,0 +1,212 @@
1
+ Metadata-Version: 2.4
2
+ Name: socialrobot
3
+ Version: 0.2.0
4
+ Summary: Python runtime client for the Social Robot Platform — connect a robot body (NAO, Pi, any Python process) to a live session and drive it.
5
+ Author-email: Hung-Chun Chang <jonathanchang9@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://sociallab.duckdns.org/ntu_social_robot/
8
+ Project-URL: Repository, https://github.com/hungchunchang/srdp
9
+ Project-URL: Issues, https://github.com/hungchunchang/srdp/issues
10
+ Keywords: social robot,robotics,wizard-of-oz,woz,kebbi,nao,hri,sdk
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Human Machine Interfaces
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: httpx>=0.24
25
+ Requires-Dist: websockets>=11
26
+ Provides-Extra: dev
27
+ Requires-Dist: build>=1.0; extra == "dev"
28
+ Requires-Dist: twine>=5.0; extra == "dev"
29
+ Provides-Extra: mcp
30
+ Requires-Dist: mcp<2,>=1.0; extra == "mcp"
31
+ Dynamic: license-file
32
+
33
+ # socialrobot — Social Robot Platform Python SDK
34
+
35
+ 把一個**機器人身體**(NAO、Raspberry Pi、任何 Python 程式)接上平台的 live session:
36
+ meme 是大腦,你的程式是身體。負責驅動、監看、以及把外部系統接成中斷條件。
37
+
38
+ > **角色設計不在這裡做**——那是網頁編輯器 canvas 的工作。詳見下方
39
+ > 「角色設計請用網頁編輯器」。
40
+
41
+ > 這個目錄同步鏡像到公開 repo [github.com/hungchunchang/srdp](https://github.com/hungchunchang/srdp)(`main` 分支),供 PyPI 套件頁與外部使用者參考;開發仍在這個 monorepo 的 `sdk/` 進行,鏡像不接受直接 PR。
42
+
43
+ ## 在哪裡用?怎麼用?
44
+
45
+ **在你自己的電腦(或任何伺服器/CI)跑,不是在網頁裡跑。** 流程:
46
+
47
+ 1. **安裝**(任何 Python ≥3.10 環境):
48
+
49
+ ```bash
50
+ pip install socialrobot # PyPI
51
+ pip install -e sdk/ # 或從 repo checkout 安裝
52
+ ```
53
+
54
+ > 安裝名稱和 import 名稱都是 `socialrobot`(下面所有範例都是)。
55
+ >
56
+ > ⚠️ **不要 `pip install srdp`。** PyPI 上的 `srdp` 是一個無關的專案(Single Repo Data Platform),不是這個 SDK,照打會裝到別人的程式。
57
+
58
+ 2. **取得憑證**——擇一:
59
+ - **API key(建議)**:登入網頁 → 右上角使用者設定 → API Key → 產生。
60
+ `srk_` 開頭、只顯示一次,只能存取**你自己**帳號的資源。
61
+ - 或直接用 email + password(SDK 會替你登入換 JWT)。該 JWT 預設只有 60 分鐘壽命,
62
+ 但 `SocialRobotClient` 會在收到 401 時,用登入時一併拿到的 refresh cookie 靜默換發
63
+ 新 token 再重送原請求,所以長時間執行的 `RobotSession` 不必自行處理過期。
64
+ **只在台大網段(或 VPN)內有效**:密碼登入拿到的是網頁 token,校外的請求與換發
65
+ 都會被拒(`ntu_network_only`,#830);開了兩步驟驗證的帳號還要附 30 秒內的驗證碼。
66
+ 在校外、或寫成長期跑的程式,請用 API key。
67
+
68
+ 3. **指向平台**——`SocialRobotClient(base_url=…)` **必填指向你自己的部署**:
69
+ - 部署環境:`https://sociallab.duckdns.org/ntu_social_robot/api/v1`
70
+ - 本機開發:`http://localhost:6868/api/v1`
71
+ - ⚠️ 不傳 `base_url` 時預設是 `http://localhost:6868/api/v1`(只適合本機);
72
+ 用 `pip install` 在別台機器上跑時,務必傳入你部署的 URL。
73
+
74
+ 4. **在網頁編輯器把角色設計好**,然後用這個 SDK 把機器人身體接上去:
75
+ `RobotSession` 連進該角色的 live session,逐則 `state_update` render 到你的
76
+ 硬體上(TTS、`motor_keyframes`、表情),並把感測器/ASR 事件送回平台。
77
+ 完整範例見 `examples/nao_meme_bridge.py`。
78
+
79
+ ## 角色設計請用網頁編輯器(Builder DSL 已退出公開 API)
80
+
81
+ **這個 SDK 不再提供「用程式碼建角色」的公開介面。** 角色設計請在網頁編輯器
82
+ 的 canvas 上進行。原因很單純:Builder 腳本寫完仍然要 `deploy()` 把結果上傳
83
+ 到後端,跟在 canvas 上編輯的是同一份資料、同一套 REST API,所以它給設計師的
84
+ 好處等於零,而真正持續維護、持續長功能的介面是 canvas。
85
+
86
+ `socialrobot.builder` 這個模組仍然留在 repo 裡、也仍然 import 得到,但它現在
87
+ 的定位是**平台自己的測試/佈建基礎設施**:`server/scripts/seed_*.py`
88
+ (Museum、Origami、搶答主持人、聽力認知搶答主持人)和約 15 支
89
+ `server/scripts/e2e_*.py` 都用它產生 fixture 角色。它不在 `__all__` 裡、不在
90
+ 本文件裡、也不在套件描述裡,對外**沒有相容性承諾**——請不要在它上面開發新東西。
91
+
92
+ 需要用程式產生角色的話,直接打 REST API(就是 canvas 用的那套)。
93
+
94
+ ## Runtime client — 驅動正在跑的機器人(這才是這個 SDK 的用途)
95
+
96
+ ```python
97
+ import asyncio
98
+ from socialrobot import RobotSession
99
+
100
+ async def main():
101
+ async with await RobotSession.start(client, "我的導覽員") as s:
102
+ await s.send_face(embedding=[0.1] * 128) # 模擬鏡頭
103
+ ev = await s.expect(state="UI", timeout=10) # 等 ask_name UI
104
+ await s.send_event("UI_INPUT", {"variable": "name", "value": "小明"})
105
+ await s.expect(state="SPEAKING", text="小明", timeout=20)
106
+
107
+ asyncio.run(main())
108
+ ```
109
+
110
+ ## 外部系統整合 — 把 API 結果變成中斷條件
111
+
112
+ **Inbound**(你的程式推進來)——不需要 robot WS,純 HTTP:
113
+
114
+ ```python
115
+ for sess in client.list_sessions(): # 只看得到自己的機器人
116
+ client.inject(sess["session_id"],
117
+ event="order_ready", # 觸發 OnSensor("order_ready") hook
118
+ variables={"yolo_result": "1"}) # 餵 Switch / {{var}} 模板
119
+ ```
120
+
121
+ **Outbound — 後端替你輪詢/訂閱**(掛在節點上的 hook):
122
+
123
+ ```python
124
+ waiting = Script("等待製作", loop=True, hooks=[
125
+ # HTTP 輪詢
126
+ OnApiPoll("https://your-service/jobs/123",
127
+ path="data.status", equals="done", every=2.0,
128
+ write_to="job_status", goto="完成播報"),
129
+ # 或 WebSocket 串流(後端持續訂閱外部 feed)
130
+ OnWebSocket("wss://your-service/jobs/123/stream",
131
+ path="status", equals="done",
132
+ subscribe={"action": "subscribe"}, # 連上後送一次(可省)
133
+ write_to="job_status", goto="完成播報"),
134
+ ])
135
+ ```
136
+
137
+ **Inbound 串流**——高頻推送時用持久 WS(取代一次一個 `inject`):
138
+
139
+ ```python
140
+ async with await client.open_stream(session_id) as stream:
141
+ await stream.send(variables={"price": 42}) # → VARIABLE switch
142
+ await stream.send(event="order_ready") # → OnSensor hook
143
+ ```
144
+
145
+ 注意:有台詞的 loop 在播台詞時一直處於 SPEAKING,`OnSensor` / `OnFace` 會被延後,
146
+ 補送時機是**這一輪台詞播完回捲的那一刻**(2026-08 起;在那之前是永遠不觸發),所以
147
+ 最慢會晚一輪才離開節點。要求「按下去就立刻打斷」的等待節點(例如搶答)仍請用**無台詞
148
+ 的 loop**。`OnWs`(控制面板/外部程式的推送)以及後端自己輪詢的 `OnApiPoll` /
149
+ `OnWebSocket` / `OnTimeout` / `OnVision` / `OnSchedule` 不受此限,隨時都會立刻打斷。
150
+
151
+ ## 自訂視覺模型
152
+
153
+ 上傳自己的分類/偵測模型(`.onnx` / `.pt` / `.tflite` …),掛成 `OnVision`
154
+ 中斷條件,讓機器人「看到某個標籤 → 換節點」:
155
+
156
+ ```python
157
+ model = client.upload_vision_model("origami_step.onnx", name="摺紙判斷",
158
+ model_type="onnx", labels=["step1", "step2"])
159
+ step2 = Script("步驟2", loop=True, lines=[Say("把紙對折")], hooks=[
160
+ OnVision(model["id"], fire_on_label="step2", threshold=0.7, goto=correct_node),
161
+ ])
162
+ ```
163
+
164
+ 節點開著時後端持續對機器人鏡頭的畫面跑模型,標籤信心值過 threshold 就跳。
165
+ Teachable Machine 匯出的 `.tflite` 可直接上傳。
166
+
167
+ 同一個模型猜每一步時,相鄰步驟長得像,前幾步的機率會一直搶走答案。`OnVision`
168
+ 的 `allowed_labels` 讓這一步只在指定標籤裡挑最高分並重新算比例(step1–3 的機率被忽略):
169
+
170
+ ```python
171
+ step4 = Script("步驟4", loop=True, lines=[Say("把角往內折")], hooks=[
172
+ OnVision(model["id"], fire_on_label="step4", threshold=0.7, goto=step5,
173
+ allowed_labels=["step3", "step4"]), # 觸發標籤一定在名單內
174
+ ])
175
+ ```
176
+
177
+ 名單裡至少要留一個不會觸發的標籤(這裡是 `step3`)——只剩觸發標籤的名單不管鏡頭看到什麼都會立刻觸發,後端會拒收並改回全部允許。同一步的所有 `OnVision` 共用一份名單(取聯集);不給就是全部允許。
178
+
179
+ ## 已驗證的環境
180
+
181
+ 從一般使用者環境(容器外的 venv/conda)對 localhost 與公開 prod URL 全程
182
+ 驗證過:登入、DSL deploy、WS runtime、HTTP/WS inject。`make e2e-sdk` 是
183
+ 完整迴歸(HTTP poll、WS subscribe、HTTP inject、WS stream、vision 全涵蓋)。
184
+
185
+ ## MCP server (drive the platform from a terminal agent)
186
+
187
+ `socialrobot.mcp_server` exposes the SDK as [MCP](https://modelcontextprotocol.io)
188
+ tools, so an agent (Claude Code, etc.) can run the platform conversationally:
189
+
190
+ | tool | does |
191
+ |---|---|
192
+ | `list_characters` | your characters (id/name/public) |
193
+ | `list_sessions` | running robot sessions to inject into |
194
+ | `inject_event` | push an event/variables to a live session — `buzz` `{winner}`, `judged` `{result}`, `next_question` `{answer, clip_url}` |
195
+ | `upload_media` | upload an audio/video clip → `{url, signed_url}` |
196
+ | `get_scripts` | read a workflow's WoZ lines (incl. `audio_clip_url`) |
197
+
198
+ Install + register with Claude Code:
199
+
200
+ ```bash
201
+ pip install "socialrobot[mcp]" # or from a checkout: pip install -e "sdk/[mcp]"
202
+ # NOT `pip install srdp` — that PyPI name is an unrelated project
203
+ claude mcp add socialrobot \
204
+ -e SOCIALROBOT_API_KEY=srk_… \
205
+ -e SOCIALROBOT_BASE_URL=https://sociallab.duckdns.org/ntu_social_robot/api/v1 \
206
+ -- socialrobot-mcp
207
+ ```
208
+
209
+ Auth via `SOCIALROBOT_API_KEY` (an `srk_` developer key — mint in 個人設定 → API key)
210
+ or `SOCIALROBOT_EMAIL` + `SOCIALROBOT_PASSWORD` (the latter only from the NTU network:
211
+ a password login is a web session, refused elsewhere with `ntu_network_only`). The key lives only in the MCP
212
+ process's env, never in tool arguments.
@@ -0,0 +1,180 @@
1
+ # socialrobot — Social Robot Platform Python SDK
2
+
3
+ 把一個**機器人身體**(NAO、Raspberry Pi、任何 Python 程式)接上平台的 live session:
4
+ meme 是大腦,你的程式是身體。負責驅動、監看、以及把外部系統接成中斷條件。
5
+
6
+ > **角色設計不在這裡做**——那是網頁編輯器 canvas 的工作。詳見下方
7
+ > 「角色設計請用網頁編輯器」。
8
+
9
+ > 這個目錄同步鏡像到公開 repo [github.com/hungchunchang/srdp](https://github.com/hungchunchang/srdp)(`main` 分支),供 PyPI 套件頁與外部使用者參考;開發仍在這個 monorepo 的 `sdk/` 進行,鏡像不接受直接 PR。
10
+
11
+ ## 在哪裡用?怎麼用?
12
+
13
+ **在你自己的電腦(或任何伺服器/CI)跑,不是在網頁裡跑。** 流程:
14
+
15
+ 1. **安裝**(任何 Python ≥3.10 環境):
16
+
17
+ ```bash
18
+ pip install socialrobot # PyPI
19
+ pip install -e sdk/ # 或從 repo checkout 安裝
20
+ ```
21
+
22
+ > 安裝名稱和 import 名稱都是 `socialrobot`(下面所有範例都是)。
23
+ >
24
+ > ⚠️ **不要 `pip install srdp`。** PyPI 上的 `srdp` 是一個無關的專案(Single Repo Data Platform),不是這個 SDK,照打會裝到別人的程式。
25
+
26
+ 2. **取得憑證**——擇一:
27
+ - **API key(建議)**:登入網頁 → 右上角使用者設定 → API Key → 產生。
28
+ `srk_` 開頭、只顯示一次,只能存取**你自己**帳號的資源。
29
+ - 或直接用 email + password(SDK 會替你登入換 JWT)。該 JWT 預設只有 60 分鐘壽命,
30
+ 但 `SocialRobotClient` 會在收到 401 時,用登入時一併拿到的 refresh cookie 靜默換發
31
+ 新 token 再重送原請求,所以長時間執行的 `RobotSession` 不必自行處理過期。
32
+ **只在台大網段(或 VPN)內有效**:密碼登入拿到的是網頁 token,校外的請求與換發
33
+ 都會被拒(`ntu_network_only`,#830);開了兩步驟驗證的帳號還要附 30 秒內的驗證碼。
34
+ 在校外、或寫成長期跑的程式,請用 API key。
35
+
36
+ 3. **指向平台**——`SocialRobotClient(base_url=…)` **必填指向你自己的部署**:
37
+ - 部署環境:`https://sociallab.duckdns.org/ntu_social_robot/api/v1`
38
+ - 本機開發:`http://localhost:6868/api/v1`
39
+ - ⚠️ 不傳 `base_url` 時預設是 `http://localhost:6868/api/v1`(只適合本機);
40
+ 用 `pip install` 在別台機器上跑時,務必傳入你部署的 URL。
41
+
42
+ 4. **在網頁編輯器把角色設計好**,然後用這個 SDK 把機器人身體接上去:
43
+ `RobotSession` 連進該角色的 live session,逐則 `state_update` render 到你的
44
+ 硬體上(TTS、`motor_keyframes`、表情),並把感測器/ASR 事件送回平台。
45
+ 完整範例見 `examples/nao_meme_bridge.py`。
46
+
47
+ ## 角色設計請用網頁編輯器(Builder DSL 已退出公開 API)
48
+
49
+ **這個 SDK 不再提供「用程式碼建角色」的公開介面。** 角色設計請在網頁編輯器
50
+ 的 canvas 上進行。原因很單純:Builder 腳本寫完仍然要 `deploy()` 把結果上傳
51
+ 到後端,跟在 canvas 上編輯的是同一份資料、同一套 REST API,所以它給設計師的
52
+ 好處等於零,而真正持續維護、持續長功能的介面是 canvas。
53
+
54
+ `socialrobot.builder` 這個模組仍然留在 repo 裡、也仍然 import 得到,但它現在
55
+ 的定位是**平台自己的測試/佈建基礎設施**:`server/scripts/seed_*.py`
56
+ (Museum、Origami、搶答主持人、聽力認知搶答主持人)和約 15 支
57
+ `server/scripts/e2e_*.py` 都用它產生 fixture 角色。它不在 `__all__` 裡、不在
58
+ 本文件裡、也不在套件描述裡,對外**沒有相容性承諾**——請不要在它上面開發新東西。
59
+
60
+ 需要用程式產生角色的話,直接打 REST API(就是 canvas 用的那套)。
61
+
62
+ ## Runtime client — 驅動正在跑的機器人(這才是這個 SDK 的用途)
63
+
64
+ ```python
65
+ import asyncio
66
+ from socialrobot import RobotSession
67
+
68
+ async def main():
69
+ async with await RobotSession.start(client, "我的導覽員") as s:
70
+ await s.send_face(embedding=[0.1] * 128) # 模擬鏡頭
71
+ ev = await s.expect(state="UI", timeout=10) # 等 ask_name UI
72
+ await s.send_event("UI_INPUT", {"variable": "name", "value": "小明"})
73
+ await s.expect(state="SPEAKING", text="小明", timeout=20)
74
+
75
+ asyncio.run(main())
76
+ ```
77
+
78
+ ## 外部系統整合 — 把 API 結果變成中斷條件
79
+
80
+ **Inbound**(你的程式推進來)——不需要 robot WS,純 HTTP:
81
+
82
+ ```python
83
+ for sess in client.list_sessions(): # 只看得到自己的機器人
84
+ client.inject(sess["session_id"],
85
+ event="order_ready", # 觸發 OnSensor("order_ready") hook
86
+ variables={"yolo_result": "1"}) # 餵 Switch / {{var}} 模板
87
+ ```
88
+
89
+ **Outbound — 後端替你輪詢/訂閱**(掛在節點上的 hook):
90
+
91
+ ```python
92
+ waiting = Script("等待製作", loop=True, hooks=[
93
+ # HTTP 輪詢
94
+ OnApiPoll("https://your-service/jobs/123",
95
+ path="data.status", equals="done", every=2.0,
96
+ write_to="job_status", goto="完成播報"),
97
+ # 或 WebSocket 串流(後端持續訂閱外部 feed)
98
+ OnWebSocket("wss://your-service/jobs/123/stream",
99
+ path="status", equals="done",
100
+ subscribe={"action": "subscribe"}, # 連上後送一次(可省)
101
+ write_to="job_status", goto="完成播報"),
102
+ ])
103
+ ```
104
+
105
+ **Inbound 串流**——高頻推送時用持久 WS(取代一次一個 `inject`):
106
+
107
+ ```python
108
+ async with await client.open_stream(session_id) as stream:
109
+ await stream.send(variables={"price": 42}) # → VARIABLE switch
110
+ await stream.send(event="order_ready") # → OnSensor hook
111
+ ```
112
+
113
+ 注意:有台詞的 loop 在播台詞時一直處於 SPEAKING,`OnSensor` / `OnFace` 會被延後,
114
+ 補送時機是**這一輪台詞播完回捲的那一刻**(2026-08 起;在那之前是永遠不觸發),所以
115
+ 最慢會晚一輪才離開節點。要求「按下去就立刻打斷」的等待節點(例如搶答)仍請用**無台詞
116
+ 的 loop**。`OnWs`(控制面板/外部程式的推送)以及後端自己輪詢的 `OnApiPoll` /
117
+ `OnWebSocket` / `OnTimeout` / `OnVision` / `OnSchedule` 不受此限,隨時都會立刻打斷。
118
+
119
+ ## 自訂視覺模型
120
+
121
+ 上傳自己的分類/偵測模型(`.onnx` / `.pt` / `.tflite` …),掛成 `OnVision`
122
+ 中斷條件,讓機器人「看到某個標籤 → 換節點」:
123
+
124
+ ```python
125
+ model = client.upload_vision_model("origami_step.onnx", name="摺紙判斷",
126
+ model_type="onnx", labels=["step1", "step2"])
127
+ step2 = Script("步驟2", loop=True, lines=[Say("把紙對折")], hooks=[
128
+ OnVision(model["id"], fire_on_label="step2", threshold=0.7, goto=correct_node),
129
+ ])
130
+ ```
131
+
132
+ 節點開著時後端持續對機器人鏡頭的畫面跑模型,標籤信心值過 threshold 就跳。
133
+ Teachable Machine 匯出的 `.tflite` 可直接上傳。
134
+
135
+ 同一個模型猜每一步時,相鄰步驟長得像,前幾步的機率會一直搶走答案。`OnVision`
136
+ 的 `allowed_labels` 讓這一步只在指定標籤裡挑最高分並重新算比例(step1–3 的機率被忽略):
137
+
138
+ ```python
139
+ step4 = Script("步驟4", loop=True, lines=[Say("把角往內折")], hooks=[
140
+ OnVision(model["id"], fire_on_label="step4", threshold=0.7, goto=step5,
141
+ allowed_labels=["step3", "step4"]), # 觸發標籤一定在名單內
142
+ ])
143
+ ```
144
+
145
+ 名單裡至少要留一個不會觸發的標籤(這裡是 `step3`)——只剩觸發標籤的名單不管鏡頭看到什麼都會立刻觸發,後端會拒收並改回全部允許。同一步的所有 `OnVision` 共用一份名單(取聯集);不給就是全部允許。
146
+
147
+ ## 已驗證的環境
148
+
149
+ 從一般使用者環境(容器外的 venv/conda)對 localhost 與公開 prod URL 全程
150
+ 驗證過:登入、DSL deploy、WS runtime、HTTP/WS inject。`make e2e-sdk` 是
151
+ 完整迴歸(HTTP poll、WS subscribe、HTTP inject、WS stream、vision 全涵蓋)。
152
+
153
+ ## MCP server (drive the platform from a terminal agent)
154
+
155
+ `socialrobot.mcp_server` exposes the SDK as [MCP](https://modelcontextprotocol.io)
156
+ tools, so an agent (Claude Code, etc.) can run the platform conversationally:
157
+
158
+ | tool | does |
159
+ |---|---|
160
+ | `list_characters` | your characters (id/name/public) |
161
+ | `list_sessions` | running robot sessions to inject into |
162
+ | `inject_event` | push an event/variables to a live session — `buzz` `{winner}`, `judged` `{result}`, `next_question` `{answer, clip_url}` |
163
+ | `upload_media` | upload an audio/video clip → `{url, signed_url}` |
164
+ | `get_scripts` | read a workflow's WoZ lines (incl. `audio_clip_url`) |
165
+
166
+ Install + register with Claude Code:
167
+
168
+ ```bash
169
+ pip install "socialrobot[mcp]" # or from a checkout: pip install -e "sdk/[mcp]"
170
+ # NOT `pip install srdp` — that PyPI name is an unrelated project
171
+ claude mcp add socialrobot \
172
+ -e SOCIALROBOT_API_KEY=srk_… \
173
+ -e SOCIALROBOT_BASE_URL=https://sociallab.duckdns.org/ntu_social_robot/api/v1 \
174
+ -- socialrobot-mcp
175
+ ```
176
+
177
+ Auth via `SOCIALROBOT_API_KEY` (an `srk_` developer key — mint in 個人設定 → API key)
178
+ or `SOCIALROBOT_EMAIL` + `SOCIALROBOT_PASSWORD` (the latter only from the NTU network:
179
+ a password login is a web session, refused elsewhere with `ntu_network_only`). The key lives only in the MCP
180
+ process's env, never in tool arguments.
@@ -0,0 +1,50 @@
1
+ [project]
2
+ name = "socialrobot"
3
+ version = "0.2.0"
4
+ description = "Python runtime client for the Social Robot Platform — connect a robot body (NAO, Pi, any Python process) to a live session and drive it."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [{ name = "Hung-Chun Chang", email = "jonathanchang9@gmail.com" }]
10
+ keywords = ["social robot", "robotics", "wizard-of-oz", "woz", "kebbi", "nao", "hri", "sdk"]
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Intended Audience :: Developers",
14
+ "Intended Audience :: Science/Research",
15
+ "Operating System :: OS Independent",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Topic :: Scientific/Engineering :: Human Machine Interfaces",
21
+ "Topic :: Software Development :: Libraries :: Python Modules",
22
+ ]
23
+ dependencies = [
24
+ "httpx>=0.24",
25
+ "websockets>=11",
26
+ ]
27
+
28
+ [project.optional-dependencies]
29
+ dev = ["build>=1.0", "twine>=5.0"]
30
+ # MCP server (socialrobot.mcp_server) — drive the platform from a terminal agent.
31
+ # <2: mcp 2.x renamed FastMCP to MCPServer; mcp_server.py is written against 1.x.
32
+ mcp = ["mcp>=1.0,<2"]
33
+
34
+ [project.scripts]
35
+ socialrobot-mcp = "socialrobot.mcp_server:main"
36
+
37
+ [project.urls]
38
+ Homepage = "https://sociallab.duckdns.org/ntu_social_robot/"
39
+ Repository = "https://github.com/hungchunchang/srdp"
40
+ Issues = "https://github.com/hungchunchang/srdp/issues"
41
+
42
+ [build-system]
43
+ requires = ["setuptools>=77"]
44
+ build-backend = "setuptools.build_meta"
45
+
46
+ [tool.setuptools.packages.find]
47
+ include = ["socialrobot*"]
48
+
49
+ [tool.setuptools.package-data]
50
+ socialrobot = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,67 @@
1
+ """Social Robot Platform SDK — a RUNTIME client, not an authoring tool.
2
+
3
+ The supported public API is the runtime half: connect a non-Android robot
4
+ body (or any program) to a live platform session, and drive/observe it.
5
+
6
+ from socialrobot import SocialRobotClient, RobotSession
7
+
8
+ `RobotSession` is how a NAO, a Raspberry Pi rig, or any Python-driven body
9
+ participates as a robot — meme is the brain, your process is the body (see
10
+ `examples/nao_meme_bridge.py`). `SocialRobotClient` is the authenticated REST
11
+ client underneath it, plus the asset/session endpoints those bodies need.
12
+
13
+ INTERNAL, NOT PUBLIC API — the Builder DSL (`Character`, `Script`, `Say`,
14
+ `Agent`, `Screen`, `Identify`, hooks, …). Authoring a character is the
15
+ WEB EDITOR's job: a Builder script still has to `deploy()` its result up to
16
+ the backend, so it buys nothing a designer can't do on the canvas, and the
17
+ canvas is the surface that actually gets maintained. The module stays in the
18
+ repo because the platform's own seed scripts and ~15 e2e drivers provision
19
+ their fixture characters with it (`server/scripts/seed_*.py`,
20
+ `server/scripts/e2e_*.py`) — it is test/provisioning infrastructure now.
21
+
22
+ Those names are still importable so that in-repo tooling keeps working, but
23
+ they are deliberately absent from `__all__`, excluded from the documented
24
+ surface, and carry no compatibility promise for external callers. Do not
25
+ build anything new on them; use the web editor, or the REST API directly.
26
+ """
27
+ from .builder import (
28
+ Agent,
29
+ Button,
30
+ Call,
31
+ Character,
32
+ Data,
33
+ DataSource,
34
+ DeployedCharacter,
35
+ Hook,
36
+ Identify,
37
+ Judgment,
38
+ Mirror,
39
+ Node,
40
+ OnApiPoll,
41
+ OnFace,
42
+ OnPlaybackDone,
43
+ OnSchedule,
44
+ OnSensor,
45
+ OnTimeout,
46
+ OnUtterance,
47
+ OnVision,
48
+ OnWebSocket,
49
+ OnWs,
50
+ Return,
51
+ Say,
52
+ Screen,
53
+ Script,
54
+ subflow,
55
+ )
56
+ from .client import MfaRequiredError, SocialRobotClient
57
+ from .runtime import ExternalStream, RobotSession, SubscribeStream
58
+
59
+ __version__ = "0.2.0"
60
+
61
+ # The PUBLIC surface: the runtime client only. Builder DSL names are
62
+ # imported above for in-repo tooling (seeds + e2e drivers) but are
63
+ # deliberately NOT listed here — `__all__` is what this package promises.
64
+ __all__ = [
65
+ "__version__",
66
+ "ExternalStream", "MfaRequiredError", "RobotSession", "SocialRobotClient", "SubscribeStream",
67
+ ]