Vibe(原 Le Chat)背后的开发接口是
POST https://api.mistral.ai/v1/chat/completions,Bearer 认证。这篇客户端 + mock 演示最小对话。
先看清成品
- mock 回显用户消息。
- 官方无效密钥 HTTP 401。
- 1 项 pytest 通过。
合规提醒:面向用户的助手原名 Le Chat,官方称已更名为 Vibe(入口与账号保留,以官网公告为准);开发调用走 Mistral API(Studio 管理密钥)。密钥勿入库;生成内容需人工审,避免违法与侵权输出。免费额度与价格以 Studio/官网为准,本文不编造。
运行说明:无正式
MISTRAL_API_KEY。A/B 用本地 mock;对官方api.mistral.ai无效密钥实测 HTTP 401。C 为文档粘贴体积守卫。2026-10-23(Asia/Shanghai)pytest 通过。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
mistral_chat.py |
客户端 |
mock_mistral.py |
假服务 |
test_104a.py |
测试 |
需要准备哪些环境?
| 项目 | 说明 |
|---|---|
| Python | 3.10+ |
| 环境变量 | MISTRAL_API_KEY;可选 MISTRAL_BASE_URL / MISTRAL_MODEL |
关键事实
| 事实 | 来源 |
|---|---|
Chat Completions 路径 /v1/chat/completions,Authorization: Bearer |
官方 API |
| Studio 可创建 API Key;免费模式说明以官网为准 | 产品说明 |
| 消费级 Vibe 与 API 计费/配额可能不同 | 定位 |
文件结构
104-mistral-le-chat-A-src/
├── mistral_chat.py
├── mock_mistral.py
└── test_104a.py
第 1 步:客户端
#!/usr/bin/env python3
"""Mistral chat completions client (API behind Vibe / Le Chat products)."""
from __future__ import annotations
import argparse, json, os, sys, urllib.error, urllib.request
def chat(base, key, model, message, system=None):
msgs = []
if system:
msgs.append({"role": "system", "content": system})
msgs.append({"role": "user", "content": message})
req = urllib.request.Request(
f"{base.rstrip('/')}/v1/chat/completions",
data=json.dumps({"model": model, "messages": msgs}).encode(),
headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=60) as resp:
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
raise SystemExit(f"HTTP {e.code}: {e.read().decode(errors='replace')}") from e
def main(argv=None) -> int:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("message", nargs="?", default="用一句话介绍你自己")
ap.add_argument("--base", default=os.environ.get("MISTRAL_BASE_URL", "https://api.mistral.ai"))
ap.add_argument("--key", default=os.environ.get("MISTRAL_API_KEY", ""))
ap.add_argument("--model", default=os.environ.get("MISTRAL_MODEL", "mistral-small-latest"))
ap.add_argument("--system", default="")
args = ap.parse_args(argv)
if not args.key:
print("设置 MISTRAL_API_KEY(Studio 可创建;额度以官网为准)", file=sys.stderr)
return 2
r = chat(args.base, args.key, args.model, args.message, args.system or None)
print(r["choices"][0]["message"]["content"])
return 0
if __name__ == "__main__":
raise SystemExit(main())
第 2 步:Mock
#!/usr/bin/env python3
"""Mock Mistral chat completions."""
from __future__ import annotations
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
API_KEY = "mistral-test-key"
class H(BaseHTTPRequestHandler):
def log_message(self, *a):
pass
def _send(self, code, obj):
b = json.dumps(obj).encode()
self.send_response(code)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(b)))
self.end_headers()
self.wfile.write(b)
def do_POST(self):
if self.path != "/v1/chat/completions":
self.send_error(404); return
if self.headers.get("Authorization") != f"Bearer {API_KEY}":
self._send(401, {"message": "Unauthorized"}); return
n = int(self.headers.get("Content-Length", 0))
body = json.loads(self.rfile.read(n) or b"{}")
msgs = body.get("messages") or []
last = msgs[-1]["content"] if msgs else ""
model = body.get("model") or "mistral-small-latest"
self._send(200, {
"id": "chatcmpl-mock",
"model": model,
"choices": [{"index": 0, "message": {"role": "assistant", "content": f"[{model}] 收到:{last[:200]}"}, "finish_reason": "stop"}],
"usage": {"prompt_tokens": 10, "completion_tokens": 20, "total_tokens": 30},
})
def do_GET(self):
if self.path != "/v1/models":
self.send_error(404); return
if self.headers.get("Authorization") != f"Bearer {API_KEY}":
self._send(401, {"message": "Unauthorized"}); return
self._send(200, {"data": [
{"id": "mistral-small-latest"},
{"id": "mistral-large-latest"},
{"id": "open-mistral-nemo"},
]})
def main():
import argparse
ap = argparse.ArgumentParser(); ap.add_argument("--port", type=int, default=9104)
args = ap.parse_args()
HTTPServer(("127.0.0.1", args.port), H).serve_forever()
if __name__ == "__main__":
main()
第 3 步:测试
import subprocess, sys, time
from pathlib import Path
HERE = Path(__file__).resolve().parent
def test_chat():
port = 19104
proc = subprocess.Popen([sys.executable, str(HERE/"mock_mistral.py"), "--port", str(port)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
try:
time.sleep(0.3)
r = subprocess.run([sys.executable, str(HERE/"mistral_chat.py"), "你好", "--base", f"http://127.0.0.1:{port}", "--key", "mistral-test-key"], capture_output=True, text=True, check=True)
assert "你好" in r.stdout
finally:
proc.terminate(); proc.wait(timeout=5)
第 4 步:运行
$ pytest + mock chat
. [100%]
1 passed in 0.36s
$ invalid key api.mistral.ai
HTTP 401
{"detail":"Invalid API Key"}
怎么检查结果?
- pytest 绿。
- 正式调用:
MISTRAL_API_KEY=... python mistral_chat.py "你好"。
调试示例
401 Invalid API Key。 到 console/Studio 重新复制密钥;确认没有多余空格,也没有把 Vibe 网页 Cookie 当成 API Key。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 选哪个模型 | 见 Models 文档 | 或跑 B 篇 pick |
| 要流式 | stream 参数 | 以 API 文档为准 |
| Le Chat 改名 | Vibe | API 主机未变 |
模板规则
- 密钥保密。
- 输出人工审。
- 遵守使用政策。
项目资源
| 用途 | 地址 |
|---|---|
| Quickstart(官方) | https://docs.mistral.ai/getting-started/quickstart |
| API 参考(官方) | https://docs.mistral.ai/api/ |
| Mistral 官网 | https://mistral.ai/ |
先打通 completions,再接到你的业务提示词。