216 lines
5.9 KiB
Markdown
216 lines
5.9 KiB
Markdown
# 示例(curl / Python / JS / Flutter)
|
||
|
||
默认服务端:`http://localhost:8091`
|
||
|
||
通用 Header:
|
||
|
||
- `Authorization: Bearer <TOKEN>`
|
||
|
||
下文示例使用:
|
||
|
||
- `TOKEN=<TOKEN>`
|
||
- `WS=ws1`
|
||
|
||
---
|
||
|
||
## 1) curl:完整流程(工作区 + 对话 + 发送 + 轮询)
|
||
|
||
```bash
|
||
BASE_URL="http://localhost:8091"
|
||
TOKEN="<TOKEN>"
|
||
WS="ws1"
|
||
|
||
# 1) 创建/确保工作区存在
|
||
curl -sS -X POST \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d "{\"workspace_id\":\"$WS\"}" \
|
||
"$BASE_URL/api/v1/workspaces"
|
||
|
||
# 2) 创建对话
|
||
CONV=$(curl -sS -X POST \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
"$BASE_URL/api/v1/workspaces/$WS/conversations" \
|
||
| python3 -c 'import sys,json; print(json.load(sys.stdin)["conversation_id"])')
|
||
echo "CONV=$CONV"
|
||
|
||
# 3) 发送消息 -> task_id
|
||
TASK=$(curl -sS -X POST \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d "{\"conversation_id\":\"$CONV\",\"message\":\"介绍明日方舟:终末地\",\"run_mode\":\"fast\"}" \
|
||
"$BASE_URL/api/v1/workspaces/$WS/messages" \
|
||
| python3 -c 'import sys,json; print(json.load(sys.stdin)["task_id"])')
|
||
echo "TASK=$TASK"
|
||
|
||
# 4) 轮询增量事件
|
||
FROM=0
|
||
while true; do
|
||
RESP=$(curl -sS -H "Authorization: Bearer $TOKEN" "$BASE_URL/api/v1/tasks/$TASK?from=$FROM")
|
||
STATUS=$(echo "$RESP" | python3 -c 'import sys,json; print(json.load(sys.stdin)["data"]["status"])')
|
||
NEXT=$(echo "$RESP" | python3 -c 'import sys,json; print(json.load(sys.stdin)["data"]["next_offset"])')
|
||
|
||
# 把本轮新增 text_chunk 直接输出(按 token 拼接)
|
||
echo "$RESP" | python3 - <<'PY'
|
||
import sys,json
|
||
obj=json.load(sys.stdin)
|
||
for ev in obj.get("data",{}).get("events",[]):
|
||
if ev.get("type")=="text_chunk":
|
||
print(ev.get("data",{}).get("content",""), end="")
|
||
PY
|
||
|
||
if [ "$STATUS" != "running" ]; then
|
||
echo
|
||
echo "status=$STATUS"
|
||
break
|
||
fi
|
||
FROM=$NEXT
|
||
sleep 0.5
|
||
done
|
||
```
|
||
|
||
---
|
||
|
||
## 2) Python:requests(推荐写法)
|
||
|
||
```python
|
||
import time
|
||
import requests
|
||
|
||
BASE_URL = "http://localhost:8091"
|
||
TOKEN = "<TOKEN>"
|
||
WS = "ws1"
|
||
|
||
H = {"Authorization": f"Bearer {TOKEN}"}
|
||
|
||
def post_json(path, payload):
|
||
r = requests.post(BASE_URL + path, headers={**H, "Content-Type": "application/json"}, json=payload, timeout=30)
|
||
r.raise_for_status()
|
||
return r.json()
|
||
|
||
def get_json(path):
|
||
r = requests.get(BASE_URL + path, headers=H, timeout=30)
|
||
r.raise_for_status()
|
||
return r.json()
|
||
|
||
# workspace
|
||
post_json("/api/v1/workspaces", {"workspace_id": WS})
|
||
|
||
# conversation
|
||
conv = requests.post(BASE_URL + f"/api/v1/workspaces/{WS}/conversations", headers=H, timeout=30).json()
|
||
conv_id = conv["conversation_id"]
|
||
|
||
# message -> task
|
||
task = post_json(f"/api/v1/workspaces/{WS}/messages", {
|
||
"conversation_id": conv_id,
|
||
"message": "介绍明日方舟:终末地",
|
||
"run_mode": "fast",
|
||
"max_iterations": 100,
|
||
})
|
||
task_id = task["task_id"]
|
||
|
||
# poll
|
||
offset = 0
|
||
while True:
|
||
poll = get_json(f"/api/v1/tasks/{task_id}?from={offset}")["data"]
|
||
offset = poll["next_offset"]
|
||
for ev in poll["events"]:
|
||
if ev["type"] == "text_chunk":
|
||
print(ev["data"]["content"], end="", flush=True)
|
||
if poll["status"] != "running":
|
||
print()
|
||
print("status:", poll["status"])
|
||
break
|
||
time.sleep(0.5)
|
||
```
|
||
|
||
---
|
||
|
||
## 3) JS:fetch(浏览器/Node)
|
||
|
||
```js
|
||
const BASE_URL = "http://localhost:8091";
|
||
const TOKEN = "<TOKEN>";
|
||
const WS = "ws1";
|
||
|
||
async function api(path, { method = "GET", body } = {}) {
|
||
const headers = { Authorization: `Bearer ${TOKEN}` };
|
||
if (body !== undefined) headers["Content-Type"] = "application/json";
|
||
const res = await fetch(BASE_URL + path, { method, headers, body: body ? JSON.stringify(body) : undefined });
|
||
if (!res.ok) throw new Error(await res.text());
|
||
return await res.json();
|
||
}
|
||
|
||
await api("/api/v1/workspaces", { method: "POST", body: { workspace_id: WS } });
|
||
const conv = await api(`/api/v1/workspaces/${WS}/conversations`, { method: "POST" });
|
||
const msg = await api(`/api/v1/workspaces/${WS}/messages`, {
|
||
method: "POST",
|
||
body: { conversation_id: conv.conversation_id, message: "你好", run_mode: "fast" },
|
||
});
|
||
|
||
let from = 0;
|
||
for (;;) {
|
||
const poll = await api(`/api/v1/tasks/${msg.task_id}?from=${from}`);
|
||
const data = poll.data;
|
||
from = data.next_offset;
|
||
for (const ev of data.events) {
|
||
if (ev.type === "text_chunk") process.stdout?.write?.(ev.data.content) ?? console.log(ev.data.content);
|
||
}
|
||
if (data.status !== "running") break;
|
||
await new Promise((r) => setTimeout(r, 500));
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4) Flutter:http(示意)
|
||
|
||
```dart
|
||
import 'dart:convert';
|
||
import 'package:http/http.dart' as http;
|
||
|
||
const baseUrl = "http://localhost:8091";
|
||
const token = "<TOKEN>";
|
||
const ws = "ws1";
|
||
|
||
Map<String, String> headersJson() => {
|
||
"Authorization": "Bearer $token",
|
||
"Content-Type": "application/json",
|
||
};
|
||
|
||
Future<void> main() async {
|
||
await http.post(Uri.parse("$baseUrl/api/v1/workspaces"),
|
||
headers: headersJson(), body: jsonEncode({"workspace_id": ws}));
|
||
|
||
final convResp = await http.post(Uri.parse("$baseUrl/api/v1/workspaces/$ws/conversations"),
|
||
headers: {"Authorization": "Bearer $token"});
|
||
final conv = jsonDecode(convResp.body);
|
||
final convId = conv["conversation_id"];
|
||
|
||
final msgResp = await http.post(Uri.parse("$baseUrl/api/v1/workspaces/$ws/messages"),
|
||
headers: headersJson(),
|
||
body: jsonEncode({"conversation_id": convId, "message": "你好", "run_mode": "fast"}));
|
||
final msg = jsonDecode(msgResp.body);
|
||
final taskId = msg["task_id"];
|
||
|
||
var from = 0;
|
||
while (true) {
|
||
final pollResp = await http.get(
|
||
Uri.parse("$baseUrl/api/v1/tasks/$taskId?from=$from"),
|
||
headers: {"Authorization": "Bearer $token"},
|
||
);
|
||
final poll = jsonDecode(pollResp.body)["data"];
|
||
from = poll["next_offset"];
|
||
for (final ev in poll["events"]) {
|
||
if (ev["type"] == "text_chunk") {
|
||
// append to UI
|
||
print(ev["data"]["content"]);
|
||
}
|
||
}
|
||
if (poll["status"] != "running") break;
|
||
await Future.delayed(const Duration(milliseconds: 500));
|
||
}
|
||
}
|
||
```
|
||
|