chef_agent

This commit is contained in:
2026-09-06 19:25:10 +08:00
parent 8efcd72eb2
commit 86ece1926d
10 changed files with 445 additions and 55 deletions

View File

@@ -18,6 +18,13 @@ DATABASE_POOL_SIZE=10
DATABASE_MAX_OVERFLOW=20 DATABASE_MAX_OVERFLOW=20
DATABASE_ECHO=False DATABASE_ECHO=False
# 数据库Postgres配置
POSTGRES_USER="postgres"
POSTGRES_PASSWORD="123456"
POSTGRES_HOST="127.0.0.1"
POSTGRES_PORT=5432
POSTGRES_DB="postgres"
# CORS配置 # CORS配置
ALLOWED_ORIGINS=["*"] ALLOWED_ORIGINS=["*"]

52
api/agent_api.py Normal file
View File

@@ -0,0 +1,52 @@
from fastapi import APIRouter
from fastapi.responses import StreamingResponse
from pydantic import BaseModel, Field
from services.agent_server.chef_service import ChefService
# agent相关的路由
agent_router = APIRouter(prefix="/agent", tags=["Agent相关"])
class ChatRequest(BaseModel):
thread_id: str = Field(description="当前会话的id")
message: str = Field(description="当前会话的问题")
class ChatResponse(BaseModel):
thread_id: str
content: str
@agent_router.post("/chat", response_model=ChatResponse)
def chat(request: ChatRequest):
result = ChefService.chat(
thread_id=request.thread_id,
message=request.message,
)
messages = result["messages"]
# 最后一条 AI 消息
last_message = messages[-1]
return ChatResponse(
thread_id=request.thread_id,
content=last_message.content,
)
@agent_router.post("/chat_stream")
def chat(request: ChatRequest):
def generate_stream():
result = ChefService.chat_stream(
thread_id=request.thread_id,
message=request.message,
)
for chunk in result:
yield chunk
return StreamingResponse(
generate_stream(),
media_type="text/plain; charset=utf-8"
)

View File

@@ -94,6 +94,30 @@ class Settings(BaseSettings):
description="是否打印SQL语句" description="是否打印SQL语句"
) )
# ---------- 数据库Postgres配置 ----------
POSTGRES_USER: str = Field(
default='postgres',
description="postgres用户名"
)
POSTGRES_PASSWORD: str = Field(
default='123456',
description="postgres密码"
)
POSTGRES_HOST: str = Field(
default='127.0.0.1',
description="postgres主机地址"
)
POSTGRES_PORT: int = Field(
default=5432,
ge=1,
le=65535,
description="postgres端口"
)
POSTGRES_DB: str = Field(
default='postgres',
description="postgres数据库名"
)
# ---------- CORS配置 ---------- # ---------- CORS配置 ----------
ALLOWED_ORIGINS: List[str] = Field( ALLOWED_ORIGINS: List[str] = Field(
default=["*"], default=["*"],
@@ -161,6 +185,12 @@ class Settings(BaseSettings):
# 连接url格式mysql+pymysql://user:password@host:port/dbname # 连接url格式mysql+pymysql://user:password@host:port/dbname
return f"mysql+pymysql://{self.MYSQL_USER}:{self.MYSQL_PASSWORD}@{self.MYSQL_HOST}:{self.MYSQL_PORT}/{self.MYSQL_DB}?charset=utf8mb4" return f"mysql+pymysql://{self.MYSQL_USER}:{self.MYSQL_PASSWORD}@{self.MYSQL_HOST}:{self.MYSQL_PORT}/{self.MYSQL_DB}?charset=utf8mb4"
@property
def postgres_url(self) -> str:
"""postgres连接URL"""
# 连接url格式postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable
return f"postgresql://{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}@{self.POSTGRES_HOST}:{self.POSTGRES_PORT}/{self.POSTGRES_DB}?sslmode=disable"
@property @property
def is_production(self) -> bool: def is_production(self) -> bool:
"""是否为生产环境""" """是否为生产环境"""

View File

@@ -1,71 +1,250 @@
import os from __future__ import annotations
import sqlite3
from typing import cast, Optional
from langchain.agents import create_agent, AgentState import logging
from typing import Any, cast
from langchain.agents import create_agent
from langchain.agents.middleware import SummarizationMiddleware from langchain.agents.middleware import SummarizationMiddleware
from langchain.agents.middleware.summarization import ContextMessages from langchain.agents.middleware.summarization import ContextMessages
from langchain.chat_models import init_chat_model from langchain.chat_models import init_chat_model
from langchain_core.language_models import BaseChatModel from langchain_core.language_models import BaseChatModel
from langchain_core.messages import HumanMessage
from langchain_core.runnables import RunnableConfig
from langgraph.checkpoint.sqlite import SqliteSaver # 需要安装langgraph-checkpoint-sqlite
from langchain_tavily import TavilySearch from langchain_tavily import TavilySearch
from langgraph.checkpoint.postgres import PostgresSaver
from psycopg import Connection
from psycopg_pool import ConnectionPool
from core.config.settings import settings from core.config.settings import settings
# 初始化模型 需要多模态大模型 deepseek已支持 logger = logging.getLogger(__name__)
llm_model = init_chat_model(
model="deepseek-v4-flash",
model_provider="deepseek",
api_key=settings.DEEPSEEK_API_KEY
)
# 定义工具 使用tavily搜索工具 langchain-tavily class ChefAgent:
search_tool = TavilySearch( """厨师 Agent。
tavily_api_key=settings.TAVILY_API_KEY,
max_results=5, # 最大搜索结果条数
topic="general"
)
# 定义记忆策略 使用SummarizationMiddleware摘要策略中间件 负责:
summary = SummarizationMiddleware( - LLM 初始化
model=cast(BaseChatModel, llm_model), # 消息摘要的记忆管理策略的模型 - Tool 初始化
trigger=cast(ContextMessages, ("messages", 10)), # 触发策略的条件 - CheckPointer 初始化
keep=cast(ContextMessages, ("messages", 5)) # 触发策略后保留的消息条数 - Middleware 初始化
) - Agent 初始化
- 生命周期管理
"""
# 创建数据库目录(如果不存在) def __init__(self) -> None:
os.makedirs("sqlite", exist_ok=True) self._pool: ConnectionPool[Connection[Any]] | None = None
# 创建 SQLite 数据库连接 check_same_thread=False 是为了确保在多线程环境下的安全性 self._checkpointer: PostgresSaver | None = None
conn = sqlite3.connect("sqlite/checkpoints.db", check_same_thread=False) self._agent = None
# 初始化 checkpointer
checkpointer = SqliteSaver(conn)
# 自动建表
checkpointer.setup()
# agent提示词 def initialize(self) -> ChefAgent:
system_prompt = """ """初始化 Agent。
你是一名有名的国宴厨师。收到用户提供的食材照片或清单后,按照以下流程步骤操作:
1.识别和评估食材:若用户提供照片,首先辨别所有可见食材,基于食材的外观状态,评估其新鲜度和可用量,整理出一份“可用食材清单”。
2.智能食谱检索优先调用search_tool工具以“可用食材清单”为核心关键词查找可行菜谱。
3.多维度评估与排序从营养与烹饪难度这2个维度对检索到的候选食谱进行量化打分并根据得分进行排序制作简单且营养丰富的排名靠前。
4.结构化方案输出:把排序后的食谱整理成一份结构清晰的建议报告,要包括食谱信息、得分、推荐理由、食谱的参考图片,帮助用户快速做出决策。
"""
# 定义thread_config 用于记忆存储分组 Returns:
thread_config: RunnableConfig = {"configurable": {"thread_id": "2"}} 当前 Agent 实例,方便链式调用。
"""
# 创建agent logger.info("开始初始化chef_agent...")
agent = create_agent(
model=cast(BaseChatModel, llm_model), # 指定类型 避免idea报错
tools=[search_tool],
checkpointer=checkpointer,
middleware=[summary],
system_prompt=system_prompt
)
if __name__ == "__main__": if self._agent is not None:
question = input("> ") return self
res = agent.invoke({"messages": [HumanMessage(content=question)]}, thread_config)
print(res) # 1. 初始化 LLM
llm = self._create_llm()
# 2. 初始化工具
tools = self._create_tools()
# 3. 初始化 Middleware
middleware = self._create_middleware(llm)
# 4. 初始化 CheckPointer
self._checkpointer = self._create_checkpointer()
# 5. 初始化 Agent
logger.info("chef_agent的agent开始初始化...")
self._agent = create_agent(
model=llm,
tools=tools,
middleware=middleware,
checkpointer=self._checkpointer,
system_prompt=self._system_prompt(),
)
logger.info("chef_agent的agent初始化完成")
logger.info("chef_agent初始化完成")
return self
@staticmethod
def _create_llm() -> BaseChatModel:
"""创建 LLM。"""
logger.info("chef_agent初始化llm...")
llm = init_chat_model(
model="deepseek-v4-flash",
api_key=settings.DEEPSEEK_API_KEY,
)
logger.info("chef_agent的llm初始化完成")
return cast(BaseChatModel, llm)
@staticmethod
def _create_tools() -> list[Any]:
"""创建 Agent Tools。"""
logger.info("chef_agent工具开始初始化...")
search_tool = TavilySearch(
tavily_api_key=settings.TAVILY_API_KEY,
max_results=5,
topic="general",
)
logger.info("chef_agent工具初始化完成")
return [search_tool]
@staticmethod
def _create_middleware(
llm: BaseChatModel,
) -> list[Any]:
"""创建 Agent Middleware。"""
logger.info("chef_agent中间件开始初始化...")
summarization = SummarizationMiddleware(
model=llm,
trigger=cast(ContextMessages, ("messages", 10)),
keep=cast(ContextMessages, ("messages", 5)),
)
logger.info("chef_agent中间件初始化完成")
return [summarization]
def _create_checkpointer(self) -> PostgresSaver:
"""创建 PostgreSQL CheckPointer。"""
logger.info("chef_agent的CheckPointer开始初始化")
logger.info("创建PostgreSQL连接池")
self.pool: ConnectionPool[Connection[Any]] = ConnectionPool(
conninfo=settings.postgres_url,
max_size=10,
kwargs={
"autocommit": True,
},
)
logger.info("创建checkpointer")
checkpointer = PostgresSaver(self.pool)
logger.info("初始化checkpoint数据表")
# 创建 LangGraph checkpoint 所需的数据表
checkpointer.setup()
logger.info("chef_agent的CheckPointer初始化完成")
return checkpointer
@staticmethod
def _system_prompt() -> str:
"""Agent System Prompt。"""
return """
你是一名专业厨师。
你的任务是根据用户提供的食材照片或食材清单,为用户推荐合适的菜谱。
请严格按照以下流程执行:
## 1. 识别和评估食材
如果用户提供的是食材照片:
- 识别照片中可见的食材
- 根据外观判断食材的新鲜程度
- 估算大致可用量
- 排除明显不可食用或状态异常的食材
整理成「可用食材清单」。
如果用户直接提供食材清单,则直接使用用户提供的信息。
## 2. 搜索菜谱
优先使用搜索工具。
以「可用食材清单」作为核心关键词,搜索适合这些食材的菜谱。
优先考虑:
- 食材匹配度高
- 操作简单
- 营养均衡
- 家庭烹饪可执行
除非搜索不到合适结果,否则不要直接凭经验编造菜谱。
## 3. 评估和排序
对搜索到的候选菜谱进行综合评价。
从以下维度进行评分:
- 食材匹配度
- 营养价值
- 烹饪难度
- 烹饪时间
综合评分后进行排序。
优先推荐:
「简单 + 食材匹配度高 + 营养丰富」
的菜谱。
## 4. 输出结果
最终输出结构化的菜谱推荐报告。
每个推荐至少包含:
- 菜谱名称
- 所需食材
- 核心烹饪步骤
- 烹饪时间
- 难度
- 综合评分
- 推荐理由
- 参考来源
如果搜索结果中存在可靠的菜谱图片,则提供图片参考。
如果搜索不到合适菜谱,再根据已有知识进行合理推荐,并明确说明这是基于模型知识给出的建议。
""".strip()
@property
def agent(self):
"""获取 Agent。"""
if self._agent is None:
raise RuntimeError(
"ChefAgent 尚未初始化,请先调用 initialize()"
)
return self._agent
def close(self) -> None:
"""释放资源。"""
if self._pool is not None:
self._pool.close()
self._pool = None
self._checkpointer = None
self._agent = None
# 使用with上下文管理方式来使用的话需要这2个方法
# def __enter__(self) -> ChefAgent:
# return self.initialize()
#
# def __exit__(self, exc_type, exc_value, traceback) -> None:
# self.close()
chef_agent = ChefAgent()

View File

@@ -5,11 +5,14 @@ from contextlib import asynccontextmanager
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from sqlmodel import SQLModel from sqlmodel import SQLModel
from api.agent_api import agent_router
from core.db import engine from core.db import engine
from core.config.settings import settings from core.config.settings import settings
from api.scheduler_api import scheduler_router from api.scheduler_api import scheduler_router
from api.lottery_api import lottery_router from api.lottery_api import lottery_router
from core.task.dlt_scheduler import scheduler from core.task.dlt_scheduler import scheduler
from lc.chef_agent import chef_agent
# 配置日志 # 配置日志
logging.basicConfig( logging.basicConfig(
@@ -30,14 +33,19 @@ async def lifespan(app: FastAPI):
# 启动定时调度器 # 启动定时调度器
scheduler.start() scheduler.start()
# 初始化chef_agent
chef_agent.initialize()
yield # 此处交出控制权,服务开始运行 yield # 此处交出控制权,服务开始运行
# ========== 关闭时执行 ========== # ========== 关闭时执行 ==========
logger.info("服务关闭,释放资源") logger.info("服务关闭,释放资源")
chef_agent.close()
scheduler.shutdown() scheduler.shutdown()
app = FastAPI(lifespan=lifespan) app = FastAPI(lifespan=lifespan)
app.include_router(scheduler_router) app.include_router(scheduler_router)
app.include_router(lottery_router) app.include_router(lottery_router)
app.include_router(agent_router)
app.add_middleware( app.add_middleware(
CORSMiddleware, # type: ignore[arg-type] CORSMiddleware, # type: ignore[arg-type]

View File

@@ -11,9 +11,13 @@ dependencies = [
"langchain-deepseek==1.1.0", "langchain-deepseek==1.1.0",
"langchain-tavily==0.2.18", "langchain-tavily==0.2.18",
"langgraph>=1.2.11", "langgraph>=1.2.11",
"langgraph-checkpoint-postgres>=3.1.2",
"langgraph-checkpoint-sqlite==3.1.1", "langgraph-checkpoint-sqlite==3.1.1",
"notebook>=7.6.2", "notebook>=7.6.2",
"openai==3.8.0", "openai==3.8.0",
"psycopg>=3.3.5",
"psycopg-binary>=3.3.5",
"psycopg-pool>=3.3.1",
"pydantic==2.13.5", "pydantic==2.13.5",
"pydantic-settings==2.15.0", "pydantic-settings==2.15.0",
"pymysql==1.2.0", "pymysql==1.2.0",

View File

View File

@@ -0,0 +1,23 @@
from langchain_core.messages import HumanMessage
from langchain_core.runnables import RunnableConfig
from lc.chef_agent import chef_agent
class ChefService:
@staticmethod
def chat(
thread_id: str,
message: str,
):
config: RunnableConfig = {"configurable": {"thread_id": thread_id}}
result = chef_agent.agent.invoke({"messages": [HumanMessage(content=message)]}, config)
return result
@staticmethod
def chat_stream(thread_id: str, message: str):
config: RunnableConfig = {"configurable": {"thread_id": thread_id}}
result = chef_agent.agent.stream({"messages": [("user", message)]}, config=config, stream_mode="messages")
return result

11
test.py Normal file
View File

@@ -0,0 +1,11 @@
sys_prompt = """
你是一名有名的厨师。收到用户提供的食材照片或清单后,按照以下流程步骤操作:
1.识别和评估食材:若用户提供照片,首先辨别所有可见食材,基于食材的外观状态,评估其新鲜度和可用量,整理出一份“可用食材清单”。
2.智能食谱检索:优先调用搜索工具,以“可用食材清单”为核心关键词,查找可行菜谱。
3.多维度评估与排序从营养与烹饪难度这2个维度对检索到的候选食谱进行量化打分并根据得分进行排序制作简单且营养丰富的排名靠前。
4.结构化方案输出:把排序后的食谱整理成一份结构清晰的建议报告,要包括食谱信息、得分、推荐理由、食谱的参考图片,帮助用户快速做出决策。
严格按照流程进行操作,搜索不到再自己发挥。
"""
if __name__ == "__main__":
print(sys_prompt)

76
uv.lock generated
View File

@@ -1309,6 +1309,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/05/71/3b475f09bd57d3a5649792c66353312b4432afd843f301739dfcebd157f0/langgraph_checkpoint-4.2.0-py3-none-any.whl", hash = "sha256:0547fd228935a0b758865de3a3d6d7a2537c308895d0f9ab092ce9151b5da942", size = 56833, upload-time = "2026-08-07T20:05:02.655Z" }, { url = "https://files.pythonhosted.org/packages/05/71/3b475f09bd57d3a5649792c66353312b4432afd843f301739dfcebd157f0/langgraph_checkpoint-4.2.0-py3-none-any.whl", hash = "sha256:0547fd228935a0b758865de3a3d6d7a2537c308895d0f9ab092ce9151b5da942", size = 56833, upload-time = "2026-08-07T20:05:02.655Z" },
] ]
[[package]]
name = "langgraph-checkpoint-postgres"
version = "3.1.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "langgraph-checkpoint" },
{ name = "orjson" },
{ name = "psycopg" },
{ name = "psycopg-pool" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9d/52/6e732f7bf702ef4918b64ea4107e7da21d4276b808f9a651fe34be9b0abe/langgraph_checkpoint_postgres-3.1.2.tar.gz", hash = "sha256:1cd404803ff895a2b79f3ac04ce92b775e6b999715f8333fce674c6d927bba95", size = 155091, upload-time = "2026-08-07T20:40:00.049Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0f/62/8899c9f4d9b97d5b3eb0c07c13cdf5f63342f15f5ef93eaf3477958df602/langgraph_checkpoint_postgres-3.1.2-py3-none-any.whl", hash = "sha256:6a7e38ef16985b54e356cba7bdaf447943aae33d5aaf290026c593bb6b4a6264", size = 52084, upload-time = "2026-08-07T20:39:58.994Z" },
]
[[package]] [[package]]
name = "langgraph-checkpoint-sqlite" name = "langgraph-checkpoint-sqlite"
version = "3.1.1" version = "3.1.1"
@@ -1399,9 +1414,13 @@ dependencies = [
{ name = "langchain-deepseek" }, { name = "langchain-deepseek" },
{ name = "langchain-tavily" }, { name = "langchain-tavily" },
{ name = "langgraph" }, { name = "langgraph" },
{ name = "langgraph-checkpoint-postgres" },
{ name = "langgraph-checkpoint-sqlite" }, { name = "langgraph-checkpoint-sqlite" },
{ name = "notebook" }, { name = "notebook" },
{ name = "openai" }, { name = "openai" },
{ name = "psycopg" },
{ name = "psycopg-binary" },
{ name = "psycopg-pool" },
{ name = "pydantic" }, { name = "pydantic" },
{ name = "pydantic-settings" }, { name = "pydantic-settings" },
{ name = "pymysql" }, { name = "pymysql" },
@@ -1422,9 +1441,13 @@ requires-dist = [
{ name = "langchain-deepseek", specifier = "==1.1.0" }, { name = "langchain-deepseek", specifier = "==1.1.0" },
{ name = "langchain-tavily", specifier = "==0.2.18" }, { name = "langchain-tavily", specifier = "==0.2.18" },
{ name = "langgraph", specifier = ">=1.2.11" }, { name = "langgraph", specifier = ">=1.2.11" },
{ name = "langgraph-checkpoint-postgres", specifier = ">=3.1.2" },
{ name = "langgraph-checkpoint-sqlite", specifier = "==3.1.1" }, { name = "langgraph-checkpoint-sqlite", specifier = "==3.1.1" },
{ name = "notebook", specifier = ">=7.6.2" }, { name = "notebook", specifier = ">=7.6.2" },
{ name = "openai", specifier = "==3.8.0" }, { name = "openai", specifier = "==3.8.0" },
{ name = "psycopg", specifier = ">=3.3.5" },
{ name = "psycopg-binary", specifier = ">=3.3.5" },
{ name = "psycopg-pool", specifier = ">=3.3.1" },
{ name = "pydantic", specifier = "==2.13.5" }, { name = "pydantic", specifier = "==2.13.5" },
{ name = "pydantic-settings", specifier = "==2.15.0" }, { name = "pydantic-settings", specifier = "==2.15.0" },
{ name = "pymysql", specifier = "==1.2.0" }, { name = "pymysql", specifier = "==1.2.0" },
@@ -1944,6 +1967,59 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" },
] ]
[[package]]
name = "psycopg"
version = "3.3.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "tzdata", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/72/73/8fb739d0f6bba247b9b93c9840c402a4f88545be5f1d4b02b23366371c00/psycopg-3.3.5.tar.gz", hash = "sha256:d0a3d9ccf5788af054cbd745278cb02401b5c312aeaafbf2c6144460aec47da4", size = 166508, upload-time = "2026-08-31T22:45:43.151Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3d/2e/d0a645bcaadde68bd6d93c43f02f14b0191bdda367ce3f7722abe3da744a/psycopg-3.3.5-py3-none-any.whl", hash = "sha256:ce5aa5cdb4f9379f00f487590e5890bfa7df9a164648c969ffa628505e21af4e", size = 213598, upload-time = "2026-08-31T22:39:02.184Z" },
]
[[package]]
name = "psycopg-binary"
version = "3.3.5"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/1c/e718752cc63cf4e99e4a10fd36e3a3364dabdd0819484a24c0d79fbb9685/psycopg_binary-3.3.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6e85d50b87257fb117675a19ee59daa7bf9a57f6431500adf7059df799232ef4", size = 4704421, upload-time = "2026-08-31T22:42:59.564Z" },
{ url = "https://files.pythonhosted.org/packages/af/cf/a0e748e27c09b92738e4460582d121ba1908be3e36791e150f435e54b332/psycopg_binary-3.3.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e5becd311f9af8d180bad372f51fb2252fd02cb2073056e2b170c9274f95fe7f", size = 4765054, upload-time = "2026-08-31T22:43:05.421Z" },
{ url = "https://files.pythonhosted.org/packages/39/62/0cbac0266d56c94dd1f702d9af2b5d54bf80b6e658048f4f2c5bd63dd7e3/psycopg_binary-3.3.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:19e5bf9872dbd164c220567fd385ba2309c7d9df1541f78343510c6b0f36a1b7", size = 5547137, upload-time = "2026-08-31T22:43:11.768Z" },
{ url = "https://files.pythonhosted.org/packages/fb/3a/73c6f8871f38fc07a9c0b4cbc9467beb116a2783cecf87dfa53900a396dc/psycopg_binary-3.3.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb3b3bffebfe07110730626e76238161124f35ac87b748d663316a28d22f58b0", size = 5227577, upload-time = "2026-08-31T22:43:20.767Z" },
{ url = "https://files.pythonhosted.org/packages/59/7b/9f17b9f4d297b774dc574199c4dcd02dadc32a00c4056265918de5c70635/psycopg_binary-3.3.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2111f880add40fb03c60556069ad68e884a0908a74d2debafc603caf93b73552", size = 6824606, upload-time = "2026-08-31T22:43:33.698Z" },
{ url = "https://files.pythonhosted.org/packages/94/86/d84dadd94a004dbbb43ce0579f1f766fdc6b8cbef745090e24bea77b5283/psycopg_binary-3.3.5-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:40505676b1526b9ea387dace034040a8c8b0bcf984cd6bd4720a2ab15e813586", size = 5060854, upload-time = "2026-08-31T22:43:42.258Z" },
{ url = "https://files.pythonhosted.org/packages/30/d0/e5078be2c7d2490c0d6cd4cb7b3601aec44be2fa8b3fe927e9419b06004f/psycopg_binary-3.3.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5816472e3bb05615f33a741e0835043d1f4bf9709ff30d2f4aed71815cfc6b5e", size = 4589511, upload-time = "2026-08-31T22:43:50.012Z" },
{ url = "https://files.pythonhosted.org/packages/f1/01/08dfb5b18fa482e025864fd91a022310d0782c42e4cf5dda5d0e010b6790/psycopg_binary-3.3.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:358748fc4c8ccdc0e2bdf55420494930e19c3ade586ea9c3a6de3dad1f897311", size = 4268144, upload-time = "2026-08-31T22:43:56.993Z" },
{ url = "https://files.pythonhosted.org/packages/88/b9/cb01dc1d63f3241b49b2ca7fb9f98d0f5c76127f0dbb9440635a6ad0233e/psycopg_binary-3.3.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1ef2e498be47800f6202b9a2304c22646325ca6d54001b7c785bcfdb24a1e8ab", size = 4001036, upload-time = "2026-08-31T22:44:04.429Z" },
{ url = "https://files.pythonhosted.org/packages/95/49/7c17dd832c05b380562ff2ff5f6ab2bcaeca8b7fff2c2b355854368a5bdb/psycopg_binary-3.3.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:88e01aa2e938a45655a8a5213fc3a44ba78cb4cab8a569b3e0bcb3d1d0eaba16", size = 4313112, upload-time = "2026-08-31T22:44:12.446Z" },
{ url = "https://files.pythonhosted.org/packages/7c/2c/b0b2f887185d6a2ec0b3bef948cc07656d2d1a5d96fa7f2bb03f6ef06ca4/psycopg_binary-3.3.5-cp313-cp313-win_amd64.whl", hash = "sha256:ba466011569297114449df9d523438e1adeedf3e4f31ffb78e897ec3fef3076b", size = 3647313, upload-time = "2026-08-31T22:44:19.139Z" },
{ url = "https://files.pythonhosted.org/packages/46/7f/4e2395da194558533bd9c31f35e4dc58ecbbae6a7176b0d2f72d629e8a51/psycopg_binary-3.3.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:40f8b132c7243ef5f503f0b6f986bf16d38a51b0df1c6ba2577743f128be03e3", size = 4712612, upload-time = "2026-08-31T22:44:25.723Z" },
{ url = "https://files.pythonhosted.org/packages/ae/94/fdb2093c8ccd7048449156db526ad746740cf34fe9c01e5dc1b7a7a8b257/psycopg_binary-3.3.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c0cac998b9b1e82dec853d2e53b3d34d56a525cf231f9441a636cfd5992929a9", size = 4775139, upload-time = "2026-08-31T22:44:36.719Z" },
{ url = "https://files.pythonhosted.org/packages/31/52/5195e87960715f7be2005761b72d56fce4e7757d5333fd40384f071c2de1/psycopg_binary-3.3.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:479b96fd78149cfa10369dc53fbfb89ee729be13146b584a23dbc7e164c0cf1e", size = 5556807, upload-time = "2026-08-31T22:44:42.668Z" },
{ url = "https://files.pythonhosted.org/packages/f7/42/2d616210a91e1327516ed5ae71961aaa31bb740e4a61d7190f2221685a40/psycopg_binary-3.3.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f45d77e398542ce0937d9fa3cd9d84e9c5fc6b34c50a66404ae840bada312750", size = 5236206, upload-time = "2026-08-31T22:44:47.943Z" },
{ url = "https://files.pythonhosted.org/packages/08/2e/e54b0d4cc263b3526e3728bb50a69660d5e79e52255ccd2a1a71e40e6f9a/psycopg_binary-3.3.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98a388509306e5e08a4203253ac52846bc1b034e5cbd0ae6da1211593cc28594", size = 6838066, upload-time = "2026-08-31T22:44:55.701Z" },
{ url = "https://files.pythonhosted.org/packages/77/80/ec22a110f81a44c411965982097efeee86006bdd5a1f51f628318106c84c/psycopg_binary-3.3.5-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ab39e2794b95af61a2ff69e33e5ab6ac5df36e9ffea9a3b18e38b2aaca8c5ad5", size = 5072036, upload-time = "2026-08-31T22:45:02.838Z" },
{ url = "https://files.pythonhosted.org/packages/93/55/7bc3c3ac769ab4fe0c619f2179b4aab3ae043f4d478283dd8279d24c0be4/psycopg_binary-3.3.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9c071bf78e5c2e6efa40bc9089a954d7b41221347a72f35c6bf2d8c96e632f75", size = 4604058, upload-time = "2026-08-31T22:45:10.444Z" },
{ url = "https://files.pythonhosted.org/packages/a3/69/8e7414f7dc10b2959e664330cdcf393e412f67355975dafa876cef265264/psycopg_binary-3.3.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:14fdfd65a96ecbd8b586d14546105641f4a6ac7cbe335c786830ea4de94bbe60", size = 4284766, upload-time = "2026-08-31T22:45:17.881Z" },
{ url = "https://files.pythonhosted.org/packages/c1/72/33f293c1d3ee9114f47e9ef4880f31c9de864e84a9b09454c26e106c25ed/psycopg_binary-3.3.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:8dbd694f3741dd4ac5bc60b70e17f7841aefb3f0f38cef4d2756de270e03af43", size = 4011958, upload-time = "2026-08-31T22:45:24.792Z" },
{ url = "https://files.pythonhosted.org/packages/b7/c9/8e38840e5a7d006987bbc9acb29951912253fa50549a4db92b3aa535f089/psycopg_binary-3.3.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:14f432430fd9e1a9e7d9ab2fe14956c77f5d074ebdc556a1ad04e9a1bd3fca04", size = 4323273, upload-time = "2026-08-31T22:45:32.973Z" },
{ url = "https://files.pythonhosted.org/packages/fc/c7/b7ebf601c307f93e7c4c4ebac0edc9db3b2729ca038efe700a18f86b5517/psycopg_binary-3.3.5-cp314-cp314-win_amd64.whl", hash = "sha256:df209e64674a34b41662c67fdc8b4e0ffd77d2136393790691d086a09f9a6cab", size = 3745885, upload-time = "2026-08-31T22:45:40.537Z" },
]
[[package]]
name = "psycopg-pool"
version = "3.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/90/82/7a23d26039827ecd4ebe93905651029ddd307c5182ad59296dfb6f67b528/psycopg_pool-3.3.1.tar.gz", hash = "sha256:b10b10b7a175d5cc1592147dc5b7eec8a9e0834eb3ed2c4a92c858e2f51eb63c", size = 31661, upload-time = "2026-05-01T23:31:59.809Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/37/ed/89c2c620af0e1660354cd8aabf9f5b21f911597ce22acb37c805d6c86bc8/psycopg_pool-3.3.1-py3-none-any.whl", hash = "sha256:2af5b432941c4c9ad5c87b3fa410aec910ec8f7c122855897983a06c45f2e4b5", size = 40023, upload-time = "2026-05-01T23:31:53.136Z" },
]
[[package]] [[package]]
name = "ptyprocess" name = "ptyprocess"
version = "0.7.0" version = "0.7.0"