7.2 KiB
7.2 KiB
示例(curl / Python / JS / Flutter)
本文提供“最小可用”的端到端示例:创建对话 → 发送消息 → 轮询输出 →(可选)停止任务 → 文件上传/下载。
请先阅读 auth.md 并准备好 token。
0. 统一变量
BASE_URL:例如http://localhost:8091TOKEN:你的 Bearer Token(明文)
1) curl:完整对话流程
1.1 创建对话
BASE_URL="http://localhost:8091"
TOKEN="<TOKEN>"
curl -sS -X POST \
-H "Authorization: Bearer $TOKEN" \
"$BASE_URL/api/v1/conversations"
假设返回:
{ "success": true, "conversation_id": "conv_20260123_234245_036" }
1.2 发送消息(创建任务)
curl -sS -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "conv_20260123_234245_036",
"message": "请用中文简要介绍《明日方舟:终末地》",
"run_mode": "fast",
"max_iterations": 100
}' \
"$BASE_URL/api/v1/messages"
假设返回:
{ "success": true, "task_id": "60322db3-...", "status": "running", "conversation_id": "conv_...", "created_at": 1769182965.30 }
1.3 轮询(建议脚本处理 next_offset)
TASK_ID="60322db3-..."
OFFSET=0
curl -sS -H "Authorization: Bearer $TOKEN" \
"$BASE_URL/api/v1/tasks/$TASK_ID?from=$OFFSET"
2) Python:轮询并实时拼接文本
依赖:pip install requests
import time
import requests
BASE_URL = "http://localhost:8091"
TOKEN = "<TOKEN>"
H = {"Authorization": f"Bearer {TOKEN}"}
def post_json(path, payload):
r = requests.post(BASE_URL + path, json=payload, headers={**H, "Content-Type":"application/json"}, timeout=30)
r.raise_for_status()
return r.json()
def get_json(path, params=None):
r = requests.get(BASE_URL + path, params=params or {}, headers=H, timeout=30)
r.raise_for_status()
return r.json()
# 1) create conversation
conv = requests.post(BASE_URL + "/api/v1/conversations", headers=H, timeout=30).json()
assert conv["success"]
conversation_id = conv["conversation_id"]
# 2) send message
task = post_json("/api/v1/messages", {
"conversation_id": conversation_id,
"message": "请用中文简要介绍《明日方舟:终末地》",
"run_mode": "fast",
"max_iterations": 100
})
task_id = task["task_id"]
# 3) poll events
offset = 0
text_buf = []
think_buf = []
while True:
data = get_json(f"/api/v1/tasks/{task_id}", params={"from": offset})
if not data["success"]:
raise RuntimeError(data.get("error"))
info = data["data"]
events = info["events"]
offset = info["next_offset"]
for ev in events:
t = ev["type"]
d = ev["data"] or {}
if t == "text_chunk":
text_buf.append(d.get("content",""))
print(d.get("content",""), end="", flush=True)
elif t == "text_end":
print("\\n--- text_end ---\\n")
elif t == "thinking_chunk":
think_buf.append(d.get("content",""))
elif t == "tool_start":
print(f\"\\n[tool_start] {d.get('name')}\\n\")
elif t == "update_action":
# 工具状态更新
st = d.get("status") or ""
msg = d.get("message") or ""
if st or msg:
print(f\"\\n[update_action] {st} {msg}\\n\")
elif t == "error":
print(f\"\\n[error] {d.get('message')}\\n\")
if info["status"] != "running":
break
time.sleep(1.0)
final_text = "".join(text_buf)
final_thinking = "".join(think_buf)
print("final status:", info["status"])
print("final text length:", len(final_text))
print("final thinking length:", len(final_thinking))
3) JavaScript(浏览器/Node)要点
浏览器端直接跨域请求时,请确保服务端允许 CORS(当前服务端已启用 CORS)。请求示例:
const BASE_URL = "http://localhost:8091";
const TOKEN = "<TOKEN>";
async function api(path, options = {}) {
const resp = await fetch(BASE_URL + path, {
...options,
headers: {
"Authorization": `Bearer ${TOKEN}`,
...(options.headers || {})
}
});
const data = await resp.json();
if (!resp.ok || !data.success) throw new Error(data.error || resp.statusText);
return data;
}
const conv = await api("/api/v1/conversations", { method: "POST" });
const msg = await api("/api/v1/messages", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
conversation_id: conv.conversation_id,
message: "请用中文简要介绍《明日方舟:终末地》",
run_mode: "fast",
max_iterations: 100
})
});
let offset = 0;
let text = "";
while (true) {
const poll = await api(`/api/v1/tasks/${msg.task_id}?from=${offset}`);
const info = poll.data;
for (const ev of info.events) {
if (ev.type === "text_chunk") text += ev.data.content || "";
if (ev.type === "tool_start") console.log("tool:", ev.data.name);
}
offset = info.next_offset;
if (info.status !== "running") break;
await new Promise(r => setTimeout(r, 1000));
}
console.log("done:", text);
4) Flutter(Dart)轮询示例(伪代码)
依赖:http 包或 dio 包均可,这里用 http 表达逻辑。
import 'dart:convert';
import 'dart:async';
import 'package:http/http.dart' as http;
const baseUrl = "http://localhost:8091";
const token = "<TOKEN>";
Map<String,String> headersJson() => {
"Authorization": "Bearer $token",
"Content-Type": "application/json",
};
Future<String> createConversation() async {
final resp = await http.post(Uri.parse("$baseUrl/api/v1/conversations"),
headers: {"Authorization":"Bearer $token"});
final data = jsonDecode(resp.body);
if (resp.statusCode != 200 || data["success"] != true) throw Exception(data["error"]);
return data["conversation_id"];
}
Future<String> sendMessage(String convId, String message) async {
final resp = await http.post(Uri.parse("$baseUrl/api/v1/messages"),
headers: headersJson(),
body: jsonEncode({
"conversation_id": convId,
"message": message,
"run_mode": "fast",
"max_iterations": 100,
}));
final data = jsonDecode(resp.body);
if (resp.statusCode != 202 || data["success"] != true) throw Exception(data["error"]);
return data["task_id"];
}
Stream<String> pollText(String taskId) async* {
int offset = 0;
while (true) {
final resp = await http.get(Uri.parse("$baseUrl/api/v1/tasks/$taskId?from=$offset"),
headers: {"Authorization":"Bearer $token"});
final data = jsonDecode(resp.body);
if (resp.statusCode != 200 || data["success"] != true) throw Exception(data["error"]);
final info = data["data"];
final events = (info["events"] as List);
for (final ev in events) {
if (ev["type"] == "text_chunk") {
yield (ev["data"]["content"] ?? "");
}
}
offset = info["next_offset"];
if (info["status"] != "running") break;
await Future.delayed(Duration(milliseconds: 800));
}
}
提示:
- Flutter UI 展示建议:把
pollText()的输出 append 到一个StringBuffer,并用setState()/状态管理更新。 - 同时可以订阅 tool_* 事件在 UI 中显示“正在搜索/正在执行工具”等状态。