diff --git a/utils/agent.py b/utils/agent.py index 89cea2c..ee50462 100644 --- a/utils/agent.py +++ b/utils/agent.py @@ -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 diff --git a/安全加密请求/main.py b/安全加密请求/main.py index 99f2c87..21d865e 100644 --- a/安全加密请求/main.py +++ b/安全加密请求/main.py @@ -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" + } + + + + + +""" diff --git a/安全加密请求/rsa_public_key.pem b/安全加密请求/rsa_public_key.pem index 491f5fc..2d72c07 100644 --- a/安全加密请求/rsa_public_key.pem +++ b/安全加密请求/rsa_public_key.pem @@ -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----- \ No newline at end of file diff --git a/耐烧蚀合金智能体/main.py b/耐烧蚀合金智能体/main.py index 0e3356b..35c7e9a 100644 --- a/耐烧蚀合金智能体/main.py +++ b/耐烧蚀合金智能体/main.py @@ -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)