阶段4(下):LangChain 1.x、提示词与注入防护、模型侧(路由/vLLM/LoRA)、AI 编程工具与验证、Dify/Coze 专题
This commit is contained in:
1 parent
caaec830b3
commit
7a32d2c9a6
38 files changed
+3059
-67
No files matched your search
@@ -0,0 +1,49 @@
|
||||
# requires: fastapi, pydantic
|
||||
"""给 Dify / Coze 当“自定义工具”的后端接口:提供 OpenAPI 描述 + API Key 鉴权。
|
||||
|
||||
在 Dify 中:工具 → 自定义 → 创建自定义工具 → 填入 http://<你的地址>/openapi.json 导入,
|
||||
鉴权方式选 API Key(请求头 Authorization: Bearer <key>)。具体界面以 Dify 官方文档为准。
|
||||
本地运行:uvicorn app:app --port 8100(直接 python app.py 只做导入检查)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import secrets
|
||||
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
API_KEY = os.environ.get("TOOL_API_KEY", "dev-only-key") # 生产环境从密钥管理读取
|
||||
|
||||
app = FastAPI(
|
||||
title="订单查询工具",
|
||||
version="1.0.0",
|
||||
description="供低代码平台调用的订单查询接口。只读,按调用方的店铺隔离数据。",
|
||||
servers=[{"url": "http://127.0.0.1:8100"}], # Dify 导入时需要 servers 字段,部署后改成实际地址
|
||||
)
|
||||
bearer = HTTPBearer()
|
||||
|
||||
ORDERS = {"A1001": {"shop": "s1", "status": "已发货", "eta": "明天"},
|
||||
"A1002": {"shop": "s2", "status": "待支付", "eta": None}}
|
||||
|
||||
|
||||
def auth(cred: HTTPAuthorizationCredentials = Depends(bearer)) -> str:
|
||||
if not secrets.compare_digest(cred.credentials, API_KEY):
|
||||
raise HTTPException(401, "无效的 API Key")
|
||||
return "s1" # 示例:一个 Key 对应一个店铺;真实项目查表
|
||||
|
||||
|
||||
class OrderStatus(BaseModel):
|
||||
order_id: str
|
||||
status: str = Field(description="订单状态")
|
||||
eta: str | None = Field(default=None, description="预计送达时间")
|
||||
|
||||
|
||||
@app.get("/orders/{order_id}", operation_id="getOrderStatus", response_model=OrderStatus,
|
||||
summary="查询订单状态", description="根据订单号查询订单状态和预计送达时间。订单号形如 A1001。")
|
||||
def get_order_status(order_id: str, shop: str = Depends(auth)) -> OrderStatus:
|
||||
o = ORDERS.get(order_id)
|
||||
if not o or o["shop"] != shop:
|
||||
raise HTTPException(404, "订单不存在")
|
||||
return OrderStatus(order_id=order_id, status=o["status"], eta=o["eta"])
|
||||
@@ -0,0 +1,21 @@
|
||||
# requires: fastapi, httpx, pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app import app
|
||||
|
||||
c = TestClient(app)
|
||||
H = {"Authorization": "Bearer dev-only-key"}
|
||||
|
||||
|
||||
def test_openapi_has_operation_and_server():
|
||||
spec = c.get("/openapi.json").json()
|
||||
op = spec["paths"]["/orders/{order_id}"]["get"]
|
||||
assert op["operationId"] == "getOrderStatus" and spec["servers"][0]["url"].startswith("http")
|
||||
assert "HTTPBearer" in spec["components"]["securitySchemes"]
|
||||
|
||||
|
||||
def test_auth_and_isolation():
|
||||
assert c.get("/orders/A1001").status_code in (401, 403)
|
||||
assert c.get("/orders/A1001", headers={"Authorization": "Bearer wrong"}).status_code == 401
|
||||
assert c.get("/orders/A1001", headers=H).json()["status"] == "已发货"
|
||||
assert c.get("/orders/A1002", headers=H).status_code == 404 # 其他店铺的订单
|
||||
Reference in new issue
Block a user