This commit is contained in:
liubiren 2026-06-02 20:57:36 +08:00
parent 49d3a589ee
commit 5129459d47
4 changed files with 160 additions and 15 deletions

View File

@ -5,8 +5,8 @@
# 列举导入模块
from pathlib import Path
from typing import List, Optional
from typing_extensions import is_protocol
from sys import path
from typing import List, Optional, cast
from uuid import uuid4
from pydantic_ai import Agent as BaseAgent, AgentRunResult
@ -15,14 +15,15 @@ from pydantic_ai.capabilities import AgentCapability
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.output import OutputSpec
from pydantic_ai.providers.openai import OpenAIProvider
from pydantic_ai.settings import ModelSettings
from pydantic_core.core_schema import is_subclass_schema
from starlette.applications import Starlette
from starlette.routing import Mount, Route
from typing_extensions import is_protocol
from sys import path
from memory import Memory
path.append(Path(__file__).resolve().parent.as_posix())
from memory import Memory
class Agent:
@ -105,19 +106,144 @@ class Agent:
)
return result
def create_starlette_application(
def create_web_application(
self,
builtin_tools: Optional[List[AbstractBuiltinTool]] = None,
) -> Starlette:
"""
创建 Starlette 应用为智能体提供交互式对话界面
:param models: ModelsParam = None,
:param builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
:param builtin_tools: 前端可选工具列表
:return: Starlette 实例
"""
from starlette.requests import Request
from starlette.responses import JSONResponse, HTMLResponse, Response
def api(
self,
builtin_tools: Optional[List[AbstractBuiltinTool]] = None,
) -> Starlette:
"""
创建 API 应用
:param builtin_tools: 前端可选工具列表
:return: Starlette 实例
"""
from pydantic_ai.models import Model
from pydantic_ai.ui._web.api import ModelInfo
from pydantic_ai.ui._web.api import (
ConfigureFrontend,
BuiltinToolInfo,
ChatRequestExtra,
validate_request_options,
)
from pydantic_ai.ui.vercel_ai import VercelAIAdapter
from typing import TypeVar
agent_model = cast(Model, self.agent.model)
# 前端可选工具列表
frontend_builtin_tools = [
t
for t in (builtin_tools or [])
if t.unique_id
not in {
t.unique_id
for t in self.agent._cap_builtin_tools
if isinstance(t, AbstractBuiltinTool)
}
]
async def options_chat(request: Request) -> Response:
"""处理跨域预检请求"""
return Response()
async def configurations(request: Request) -> Response:
"""处理前端模型与工具配置请求"""
configurations = ConfigureFrontend(
models=[
ModelInfo(
id=agent_model.model_id,
name=agent_model.label,
builtin_tools=[
t.unique_id
for t in frontend_builtin_tools
if type(t) in agent_model.supported_builtin_tools()
],
)
], # 前端仅可选择智能体配置的模型
builtin_tools=[
BuiltinToolInfo(id=t.unique_id, name=t.label)
for t in frontend_builtin_tools
],
)
return JSONResponse(content=configurations.model_dump(by_alias=True))
async def chat(request: Request) -> Response:
"""处理对话请求"""
# 实例 Vercel AI 适配器
adapter = await VercelAIAdapter[
TypeVar("AgentDepsT"), TypeVar("OutputDataT")
].from_request(request=request, agent=self.agent)
# 解析请求中额外数据,包括前端选择的模型标识、工具标识和其它配置等
extra_data = ChatRequestExtra.model_validate(
adapter.run_input.__pydantic_extra__
)
if error := validate_request_options(
extra_data=extra_data,
model_ids={agent_model.model_id}, # 前端仅可选择智能体配置的模型
builtin_tool_ids={t.unique_id for t in frontend_builtin_tools},
):
return JSONResponse(content={"error": error}, status_code=400)
streaming_response = await VercelAIAdapter[
TypeVar("AgentDepsT"), TypeVar("OutputDataT")
].dispatch_request(
request=request,
agent=self.agent,
builtin_tools=[
t
for t in frontend_builtin_tools
if t.unique_id in extra_data.builtin_tools
],
)
return streaming_response
async def health(request: Request) -> Response:
"""处理健康检查请求"""
return JSONResponse(content={"ok": True})
return Starlette(
routes=[
Route("/configure", configurations, methods=["GET"]),
Route("/chat", options_chat, methods=["OPTIONS"]),
Route("/chat", chat, methods=["POST"]),
Route("/health", health, methods=["GET"]),
]
)
async def ui(request: Request) -> Response:
"""Serve the chat UI from filesystem cache or CDN."""
content = await _get_ui_html(html_source)
return HTMLResponse(
content=content,
headers={
"Cache-Control": "public, max-age=3600",
},
)
application = Starlette(
routes=[
Mount(
"/api",
app=api(self, builtin_tools=builtin_tools),
)
]
)
app.router.add_route("/", ui, methods=["GET"])
app.router.add_route("/{id}", ui, methods=["GET"])
exit()
exit()
return application

View File

@ -225,18 +225,18 @@ if __name__ == "__main__":
# 实例请求客户端
request = Request() # 不使用缓存
response = request.post(
url="http://192.168.3.103:30380/api/v1/riskgateway/product/invokeSync.json",
url="https://risk-gw.pangjukeji.com/api/v1/riskgateway/product/invokeSync.json",
headers={
"Authorization": "Bearer C52FB4D10BC424D9F",
"Authorization": "Bearer 337162980141699072",
"Content-Type": "application/json;charset=utf-8",
},
json=encryptor.encrypt(
payload={
"productId": "BANK_CARD_4",
"name": "刘弼仁",
"idNumber": "131002198705020000",
"bankCard": "1234567890123456",
"phone": "18058798752",
"certNo": "131002198705024619",
"mobilephone": "18058798752",
"bankCardNo": "6214835712066453",
},
fields_mapping={
"aes_key_encoded": "encryptedAesKey",
@ -259,3 +259,16 @@ if __name__ == "__main__":
tag_encoded=response.get("data").get("tag"),
)
print(data)
"""
{
"productId": "CREDIT_RISK_PRE_SCREEN",
"mobilephone": "13833652839",
"certNo": "131002196212124620"
}
"""

View File

@ -1,3 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApafHKXu1IwGJ1iQVT0susJ20MRl/6unJ+YMX6Lx9l43/+THA4VAJ8YQaZMH9+1eFeaFjwM9pT0ee+okBpKRuDWVkH8NxiKza4ohNHEtSUU37N3h8BEUBJHx74/6pDK8DwPsp4LzGhnUPof5/ZMqW2phqTrEYRwYOKGLYNnScNKkOWkQMSrUxGBkcMs1VsePChe0cJqrC03CTHJ/NRSyc62HqDZLp6G+DaR3VgWkhvhT4sN/3j/n9Ml5io/8I5ooPfBrYPNCfOaVqFWtqHqoG0uMLeROa8edqk6ZBBv2mERPllOG43f91FNmg/kF8XHij1LBNcf1WbO7rHg0Vo71j+wIDAQAB
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqj/EwmmABQLKD7fh1ydL
RDUqJjI+amaWPvX+urI9GFSl2cCJ6MlmtKgd0z/qDdJT+o5BjU0oU9wwnZSqu9HK
rvrzdEjX+74sWxRDi8GuHjGfdWmJO7tF1lHApZw9rnZt1JTp1zBUBeVOLmWP2Ub0
wjDOdYz2zKKZH6GShb1BUUqSi2hOD0S9iYMR4cz1smtMUTKzrJU8yIo6k26qXt5U
RlhalT+vChYEh7QhCHbbJKdlbmtMvwE2alRKnqeXFRpDOR1JPQDUN+n3C2FEuM0a
Hf2jmB93F4btqOFxZKSB+ZmXP0UMGeSGyuTOLn0QWWqkNBn1aYwO1rtPURR1y/mo
QQIDAQAB
-----END PUBLIC KEY-----

View File

@ -30,5 +30,5 @@ if __name__ == "__main__":
"严禁幻觉,保持专业严谨。"
),
)
a = agent.create_starlette_application()
a = agent.create_web_application()
uvicorn.run(app=a, host="127.0.0.1", port=7932)