260602
This commit is contained in:
parent
49d3a589ee
commit
5129459d47
142
utils/agent.py
142
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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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-----
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue