refactor: split streaming endpoint from Azure Functions
Disable /search/stream in Function runtime with a 501 guidance response, keep streaming on always-on FastAPI service, and update README with the new deployment and routing model. Made-with: Cursor
This commit is contained in:
@@ -103,9 +103,10 @@ uvicorn serve:app --host 0.0.0.0 --port 8080 --reload
|
||||
|------|------|------|
|
||||
| GET | `/health` | 健康检查(本地 FastAPI) |
|
||||
| POST | `/search` | 同步搜索(阻塞式,本地 FastAPI) |
|
||||
| POST | `/search/stream` | 流式搜索(SSE 事件,本地 FastAPI) |
|
||||
| POST | `/search/stream` | 流式搜索(SSE 事件,仅常驻 FastAPI 服务) |
|
||||
|
||||
> Azure Functions 部署时由 `function_app.py` 挂载到 `/api` 前缀,对外路径分别为 `/api/health`、`/api/search`、`/api/search/stream`。
|
||||
> Azure Functions 部署时由 `function_app.py` 挂载到 `/api` 前缀,仅提供 `/api/health`、`/api/search`。
|
||||
> `/search/stream` 必须走常驻 `uvicorn` 服务(避免 Functions 网关缓冲导致假流式)。
|
||||
|
||||
### 1. 健康检查
|
||||
|
||||
@@ -144,10 +145,10 @@ Jina 搜索结果固定上限:
|
||||
- `cache_hit` - 是否缓存命中
|
||||
- `audit_id` - 审计追踪 ID
|
||||
|
||||
### 3. SSE 流式搜索
|
||||
### 3. SSE 流式搜索(常驻 FastAPI)
|
||||
|
||||
```bash
|
||||
curl -N -X POST https://aisousuo.azurewebsites.net/api/search/stream \
|
||||
curl -N -X POST https://your-stream-service/search/stream \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"PydanticAI 适合哪些场景?","search_mode":"deep"}'
|
||||
```
|
||||
@@ -157,6 +158,8 @@ curl -N -X POST https://aisousuo.azurewebsites.net/api/search/stream \
|
||||
|
||||
> 当请求超时时,SSE 会发送 `timeout` 事件并结束流。
|
||||
|
||||
> 如果你误调 Azure Functions 的 `/api/search/stream`,会返回 `501`,并提示使用独立流式服务地址。
|
||||
|
||||
---
|
||||
|
||||
## 环境变量配置
|
||||
@@ -233,6 +236,17 @@ func azure functionapp publish aisousuo --python
|
||||
curl https://aisousuo.azurewebsites.net/api/health
|
||||
```
|
||||
|
||||
### 流式服务部署(推荐 App Service / Container)
|
||||
|
||||
```bash
|
||||
# 启动常驻服务(示例)
|
||||
uvicorn serve:app --host 0.0.0.0 --port 8080
|
||||
```
|
||||
|
||||
- 将前端流式请求改为 `https://<stream-service>/search/stream`
|
||||
- Azure Functions 继续承载同步接口:`/api/search`
|
||||
- 可在 Functions 环境变量中配置 `STREAM_SERVICE_BASE_URL=https://<stream-service>`,用于错误提示回传目标流式地址
|
||||
|
||||
---
|
||||
|
||||
## 设计架构
|
||||
|
||||
+18
-2
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import JSONResponse
|
||||
@@ -13,7 +14,7 @@ from .config import Settings
|
||||
from .models import SearchRequest, SearchResponse
|
||||
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
def create_app(enable_streaming: bool = True) -> FastAPI:
|
||||
settings = Settings.from_env()
|
||||
agent = AISearchAgent(settings)
|
||||
audit_logger = AuditLogger(settings.audit_log_path)
|
||||
@@ -44,7 +45,22 @@ def create_app() -> FastAPI:
|
||||
return JSONResponse(content=response.model_dump())
|
||||
|
||||
@app.post("/search/stream")
|
||||
async def search_stream(request: SearchRequest) -> StreamingResponse:
|
||||
async def search_stream(request: SearchRequest):
|
||||
if not enable_streaming:
|
||||
stream_base_url = os.getenv("STREAM_SERVICE_BASE_URL", "").rstrip("/")
|
||||
return JSONResponse(
|
||||
status_code=501,
|
||||
content={
|
||||
"error": "STREAMING_NOT_AVAILABLE_IN_FUNCTIONS",
|
||||
"message": "Use the always-on stream service for /search/stream.",
|
||||
"stream_endpoint": (
|
||||
f"{stream_base_url}/search/stream" if stream_base_url else None
|
||||
),
|
||||
"query": request.query,
|
||||
"search_mode": agent.resolve_search_mode(request),
|
||||
},
|
||||
)
|
||||
|
||||
async def event_generator():
|
||||
# Send an immediate event so clients/proxies flush the SSE channel early.
|
||||
yield "event: connected\ndata: {}\n\n"
|
||||
|
||||
+3
-1
@@ -6,7 +6,9 @@ from starlette.routing import Mount
|
||||
|
||||
from ai_search_agent.api import create_app
|
||||
|
||||
_fastapi = create_app()
|
||||
# Azure Functions keeps only synchronous search endpoints.
|
||||
# Stream endpoint should be served by always-on uvicorn service.
|
||||
_fastapi = create_app(enable_streaming=False)
|
||||
|
||||
# Azure Functions delivers the full path including /api/ prefix to the ASGI app.
|
||||
# Mount FastAPI under /api so that /api/health -> FastAPI /health, etc.
|
||||
|
||||
Reference in New Issue
Block a user